Angel Pons | 118a9c7 | 2020-04-02 23:48:34 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 2 | |
| 3 | /* |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 4 | * Provide a simple API around the Wuffs JPEG decoder |
| 5 | * Uses the heap (and lots of it) for the image-size specific |
| 6 | * work buffer, so ramstage-only. |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 7 | */ |
| 8 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 9 | #include <stdint.h> |
| 10 | |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 11 | #include "jpeg.h" |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 12 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 13 | #define WUFFS_CONFIG__AVOID_CPU_ARCH |
| 14 | #define WUFFS_CONFIG__MODULES |
| 15 | #define WUFFS_CONFIG__MODULE__BASE |
| 16 | #define WUFFS_CONFIG__MODULE__JPEG |
| 17 | #define WUFFS_CONFIG__STATIC_FUNCTIONS |
| 18 | #define WUFFS_IMPLEMENTATION |
| 19 | #include "../vendorcode/wuffs/wuffs-v0.4.c" |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 20 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 21 | /* ~16K is big enough to move this off the stack */ |
| 22 | static wuffs_jpeg__decoder dec; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 23 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 24 | int jpeg_fetch_size(unsigned char *filedata, size_t filesize, unsigned int *width, |
| 25 | unsigned int *height) |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 26 | { |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 27 | if (!width || !height) { |
| 28 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 29 | } |
| 30 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 31 | wuffs_base__status status = wuffs_jpeg__decoder__initialize( |
| 32 | &dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS); |
| 33 | if (status.repr) { |
| 34 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 35 | } |
| 36 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 37 | wuffs_base__image_config imgcfg; |
| 38 | wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true); |
| 39 | status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src); |
| 40 | if (status.repr) { |
| 41 | return JPEG_DECODE_FAILED; |
| 42 | } |
| 43 | |
| 44 | *width = wuffs_base__pixel_config__width(&imgcfg.pixcfg); |
| 45 | *height = wuffs_base__pixel_config__height(&imgcfg.pixcfg); |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 46 | |
| 47 | return 0; |
| 48 | } |
| 49 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 50 | int jpeg_decode(unsigned char *filedata, size_t filesize, unsigned char *pic, |
| 51 | unsigned int width, unsigned int height, unsigned int bytes_per_line, |
| 52 | unsigned int depth) |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 53 | { |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 54 | if (!filedata || !pic) { |
| 55 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 56 | } |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 57 | /* Relatively arbitrary limit that shouldn't hurt anybody. |
| 58 | * 300M (10k*10k*3bytes/pixel) is already larger than our heap, so |
| 59 | * it's on the safe side. |
| 60 | * This avoids overflows when width or height are used for |
| 61 | * calculations in this function. |
| 62 | */ |
| 63 | if ((width > 10000) || (height > 10000)) { |
| 64 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 65 | } |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 66 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 67 | uint32_t pixfmt; |
| 68 | switch (depth) { |
| 69 | case 16: |
| 70 | pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR_565; |
| 71 | break; |
| 72 | case 24: |
| 73 | pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR; |
| 74 | break; |
| 75 | case 32: |
| 76 | pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL; |
| 77 | break; |
| 78 | default: |
| 79 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 80 | } |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 81 | |
| 82 | wuffs_base__status status = wuffs_jpeg__decoder__initialize( |
| 83 | &dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS); |
| 84 | if (status.repr) { |
| 85 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 86 | } |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 87 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 88 | wuffs_base__image_config imgcfg; |
| 89 | wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true); |
| 90 | status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src); |
| 91 | if (status.repr) { |
| 92 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 93 | } |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 94 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 95 | wuffs_base__pixel_config pixcfg; |
| 96 | wuffs_base__pixel_config__set(&pixcfg, pixfmt, 0, width, height); |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 97 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 98 | wuffs_base__pixel_buffer pixbuf; |
| 99 | status = wuffs_base__pixel_buffer__set_interleaved( |
| 100 | &pixbuf, &pixcfg, |
| 101 | wuffs_base__make_table_u8(pic, width * (depth / 8), height, bytes_per_line), |
| 102 | wuffs_base__empty_slice_u8()); |
| 103 | if (status.repr) { |
| 104 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 105 | } |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 106 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 107 | uint64_t workbuf_len_min_incl = wuffs_jpeg__decoder__workbuf_len(&dec).min_incl; |
| 108 | uint8_t *workbuf_array = malloc(workbuf_len_min_incl); |
| 109 | if ((workbuf_array == NULL) && workbuf_len_min_incl) { |
| 110 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 111 | } |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 112 | |
| 113 | wuffs_base__slice_u8 workbuf = |
| 114 | wuffs_base__make_slice_u8(workbuf_array, workbuf_len_min_incl); |
| 115 | status = wuffs_jpeg__decoder__decode_frame(&dec, &pixbuf, &src, |
| 116 | WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL); |
| 117 | |
| 118 | free(workbuf_array); |
| 119 | |
| 120 | if (status.repr) { |
| 121 | return JPEG_DECODE_FAILED; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 122 | } |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 123 | |
Patrick Georgi | 1d029b4 | 2023-10-06 20:19:15 +0200 | [diff] [blame] | 124 | return 0; |
Stefan Reinauer | d650e99 | 2010-02-22 04:33:13 +0000 | [diff] [blame] | 125 | } |