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 }