/ src / lib / jpeg.c
jpeg.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  /*
  4   * Provide a simple API around the Wuffs JPEG decoder.
  5   */
  6  
  7  #include <stdint.h>
  8  
  9  #include "jpeg.h"
 10  
 11  #define WUFFS_CONFIG__AVOID_CPU_ARCH
 12  #define WUFFS_CONFIG__MODULES
 13  #define WUFFS_CONFIG__MODULE__BASE
 14  #define WUFFS_CONFIG__MODULE__JPEG
 15  #define WUFFS_CONFIG__STATIC_FUNCTIONS
 16  #define WUFFS_IMPLEMENTATION
 17  #include "../vendorcode/wuffs/wuffs-v0.4.c"
 18  
 19  /* ~16K is big enough to move this off the stack */
 20  static wuffs_jpeg__decoder dec;
 21  
 22  const char *jpeg_fetch_size(unsigned char *filedata, size_t filesize, unsigned int *width,
 23  			    unsigned int *height)
 24  {
 25  	if (!width || !height) {
 26  		return "invalid arg";
 27  	}
 28  
 29  	wuffs_base__status status = wuffs_jpeg__decoder__initialize(
 30  		&dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
 31  	if (status.repr) {
 32  		return status.repr;
 33  	}
 34  
 35  	wuffs_base__image_config imgcfg;
 36  	wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true);
 37  	status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src);
 38  	if (status.repr) {
 39  		return status.repr;
 40  	}
 41  
 42  	*width = wuffs_base__pixel_config__width(&imgcfg.pixcfg);
 43  	*height = wuffs_base__pixel_config__height(&imgcfg.pixcfg);
 44  
 45  	return NULL;
 46  }
 47  
 48  const char *jpeg_decode(unsigned char *filedata, size_t filesize, unsigned char *pic,
 49  			unsigned int width, unsigned int height, unsigned int bytes_per_line,
 50  			unsigned int depth)
 51  {
 52  	if (!filedata || !pic) {
 53  		return "invalid arg";
 54  	}
 55  	/* Relatively arbitrary limit that shouldn't hurt anybody.
 56  	 * 300M (10k*10k*3bytes/pixel) is already larger than our heap, so
 57  	 * it's on the safe side.
 58  	 * This avoids overflows when width or height are used for
 59  	 * calculations in this function.
 60  	 */
 61  	if ((width > 10000) || (height > 10000)) {
 62  		return "invalid arg";
 63  	}
 64  
 65  	uint32_t pixfmt;
 66  	switch (depth) {
 67  	case 16:
 68  		pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR_565;
 69  		break;
 70  	case 24:
 71  		pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR;
 72  		break;
 73  	case 32:
 74  		pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
 75  		break;
 76  	default:
 77  		return "invalid arg";
 78  	}
 79  
 80  	wuffs_base__status status = wuffs_jpeg__decoder__initialize(
 81  		&dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
 82  	if (status.repr) {
 83  		return status.repr;
 84  	}
 85  
 86  	/* Opting in to lower quality means that we can pass an empty slice as the
 87  	 * "work buffer" argument to wuffs_jpeg__decoder__decode_frame below.
 88  	 *
 89  	 * Decoding progressive (not sequential) JPEGs would still require dynamic
 90  	 * memory allocation (and the amount of work buffer required depends on the
 91  	 * image dimensions), but we choose to just reject progressive JPEGs. It is
 92  	 * simpler than sometimes calling malloc (which can fail, especially for
 93  	 * large allocations) and free.
 94  	 *
 95  	 * More commentary about these quirks is at
 96  	 * https://github.com/google/wuffs/blob/beaf45650085a16780b5f708b72daaeb1aa865c8/std/jpeg/decode_quirks.wuffs
 97  	 */
 98  	wuffs_jpeg__decoder__set_quirk(
 99  		&dec, WUFFS_BASE__QUIRK_QUALITY,
100  		WUFFS_BASE__QUIRK_QUALITY__VALUE__LOWER_QUALITY);
101  	wuffs_jpeg__decoder__set_quirk(
102  		&dec, WUFFS_JPEG__QUIRK_REJECT_PROGRESSIVE_JPEGS, 1);
103  
104  	wuffs_base__image_config imgcfg;
105  	wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true);
106  	status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src);
107  	if (status.repr) {
108  		return status.repr;
109  	}
110  
111  	wuffs_base__pixel_config pixcfg;
112  	wuffs_base__pixel_config__set(&pixcfg, pixfmt, 0, width, height);
113  
114  	wuffs_base__pixel_buffer pixbuf;
115  	status = wuffs_base__pixel_buffer__set_interleaved(
116  		&pixbuf, &pixcfg,
117  		wuffs_base__make_table_u8(pic, width * (depth / 8), height, bytes_per_line),
118  		wuffs_base__empty_slice_u8());
119  	if (status.repr) {
120  		return status.repr;
121  	}
122  
123  	status = wuffs_jpeg__decoder__decode_frame(&dec, &pixbuf, &src,
124  						   WUFFS_BASE__PIXEL_BLEND__SRC,
125  						   wuffs_base__empty_slice_u8(), NULL);
126  	return status.repr;
127  }