summaryrefslogtreecommitdiff
path: root/src/lib/jpeg.c
blob: 617ab0b22aa9a6bafdfe4663b1253e91b65b5c68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* SPDX-License-Identifier: GPL-2.0-only */

/*
 * Provide a simple API around the Wuffs JPEG decoder.
 */

#include <stdint.h>

#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;
}