/* SPDX-License-Identifier: GPL-2.0-only */ /* * Provide a simple API around the Wuffs JPEG decoder. */ #include #include "jpeg.h" #define WUFFS_CONFIG__AVOID_CPU_ARCH #define WUFFS_CONFIG__MODULES #define WUFFS_CONFIG__MODULE__BASE #define WUFFS_CONFIG__MODULE__JPEG #define WUFFS_CONFIG__STATIC_FUNCTIONS #define WUFFS_IMPLEMENTATION #include "../vendorcode/wuffs/wuffs-v0.4.c" /* ~16K is big enough to move this off the stack */ static wuffs_jpeg__decoder dec; int jpeg_fetch_size(unsigned char *filedata, size_t filesize, unsigned int *width, unsigned int *height) { if (!width || !height) { return JPEG_DECODE_FAILED; } wuffs_base__status status = wuffs_jpeg__decoder__initialize( &dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS); if (status.repr) { return JPEG_DECODE_FAILED; } wuffs_base__image_config imgcfg; wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true); status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src); if (status.repr) { return JPEG_DECODE_FAILED; } *width = wuffs_base__pixel_config__width(&imgcfg.pixcfg); *height = wuffs_base__pixel_config__height(&imgcfg.pixcfg); return 0; } int jpeg_decode(unsigned char *filedata, size_t filesize, unsigned char *pic, unsigned int width, unsigned int height, unsigned int bytes_per_line, unsigned int depth) { if (!filedata || !pic) { return JPEG_DECODE_FAILED; } /* Relatively arbitrary limit that shouldn't hurt anybody. * 300M (10k*10k*3bytes/pixel) is already larger than our heap, so * it's on the safe side. * This avoids overflows when width or height are used for * calculations in this function. */ if ((width > 10000) || (height > 10000)) { return JPEG_DECODE_FAILED; } uint32_t pixfmt; switch (depth) { case 16: pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR_565; break; case 24: pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR; break; case 32: pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL; break; default: return JPEG_DECODE_FAILED; } wuffs_base__status status = wuffs_jpeg__decoder__initialize( &dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS); if (status.repr) { return JPEG_DECODE_FAILED; } /* Opting in to lower quality means that we can pass an empty slice as the * "work buffer" argument to wuffs_jpeg__decoder__decode_frame below. * * Decoding progressive (not sequential) JPEGs would still require dynamic * memory allocation (and the amount of work buffer required depends on the * image dimensions), but we choose to just reject progressive JPEGs. It is * simpler than sometimes calling malloc (which can fail, especially for * large allocations) and free. * * More commentary about these quirks is at * https://github.com/google/wuffs/blob/beaf45650085a16780b5f708b72daaeb1aa865c8/std/jpeg/decode_quirks.wuffs */ wuffs_jpeg__decoder__set_quirk( &dec, WUFFS_BASE__QUIRK_QUALITY, WUFFS_BASE__QUIRK_QUALITY__VALUE__LOWER_QUALITY); wuffs_jpeg__decoder__set_quirk( &dec, WUFFS_JPEG__QUIRK_REJECT_PROGRESSIVE_JPEGS, 1); wuffs_base__image_config imgcfg; wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true); status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src); if (status.repr) { return JPEG_DECODE_FAILED; } wuffs_base__pixel_config pixcfg; wuffs_base__pixel_config__set(&pixcfg, pixfmt, 0, width, height); wuffs_base__pixel_buffer pixbuf; status = wuffs_base__pixel_buffer__set_interleaved( &pixbuf, &pixcfg, wuffs_base__make_table_u8(pic, width * (depth / 8), height, bytes_per_line), wuffs_base__empty_slice_u8()); if (status.repr) { return JPEG_DECODE_FAILED; } status = wuffs_jpeg__decoder__decode_frame(&dec, &pixbuf, &src, WUFFS_BASE__PIXEL_BLEND__SRC, wuffs_base__empty_slice_u8(), NULL); if (status.repr) { return JPEG_DECODE_FAILED; } return 0; }