blob: 793a3eedc2ab60990ffce086ef9870fd7320278e [file] [log] [blame]
hbono@chromium.org98626972011-08-03 03:13:08 +00001/*
Jonathan Wright24e31052021-04-26 12:10:48 +01002 * Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
hbono@chromium.org98626972011-08-03 03:13:08 +00003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of the libjpeg-turbo Project nor the names of its
13 * contributors may be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
noel@chromium.org3395bcc2014-04-14 06:56:00 +000029/* TurboJPEG/LJT: this implements the TurboJPEG API using libjpeg or
30 libjpeg-turbo */
hbono@chromium.org98626972011-08-03 03:13:08 +000031
32#include <stdio.h>
33#include <stdlib.h>
Tom Hudson0d47d2d2016-05-04 13:22:56 -040034#include <ctype.h>
hbono@chromium.org98626972011-08-03 03:13:08 +000035#include <jinclude.h>
36#define JPEG_INTERNALS
37#include <jpeglib.h>
38#include <jerror.h>
39#include <setjmp.h>
Chris Blumecca8c4d2019-03-01 01:09:50 -080040#include <errno.h>
hbono@chromium.org98626972011-08-03 03:13:08 +000041#include "./turbojpeg.h"
42#include "./tjutil.h"
43#include "transupp.h"
Tom Hudson0d47d2d2016-05-04 13:22:56 -040044#include "./jpegcomp.h"
Chris Blumecca8c4d2019-03-01 01:09:50 -080045#include "./cdjpeg.h"
Jonathan Wrightdb870df2020-08-05 11:42:22 +010046#include "jconfigint.h"
hbono@chromium.org98626972011-08-03 03:13:08 +000047
Chris Blumecca8c4d2019-03-01 01:09:50 -080048extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
49 boolean);
Tom Hudson0d47d2d2016-05-04 13:22:56 -040050extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
Chris Blumecca8c4d2019-03-01 01:09:50 -080051 unsigned long);
hbono@chromium.org98626972011-08-03 03:13:08 +000052
Chris Blumecca8c4d2019-03-01 01:09:50 -080053#define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
Jonathan Wrightdb870df2020-08-05 11:42:22 +010054#define IS_POW2(x) (((x) & (x - 1)) == 0)
hbono@chromium.org98626972011-08-03 03:13:08 +000055
56
Chris Blumecca8c4d2019-03-01 01:09:50 -080057/* Error handling (based on example in example.txt) */
hbono@chromium.org98626972011-08-03 03:13:08 +000058
Jonathan Wrightdb870df2020-08-05 11:42:22 +010059static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
hbono@chromium.org98626972011-08-03 03:13:08 +000060
Chris Blumecca8c4d2019-03-01 01:09:50 -080061struct my_error_mgr {
62 struct jpeg_error_mgr pub;
63 jmp_buf setjmp_buffer;
64 void (*emit_message) (j_common_ptr, int);
65 boolean warning, stopOnWarning;
hbono@chromium.org98626972011-08-03 03:13:08 +000066};
67typedef struct my_error_mgr *my_error_ptr;
68
Chris Blumecca8c4d2019-03-01 01:09:50 -080069#define JMESSAGE(code, string) string,
70static const char *turbojpeg_message_table[] = {
71#include "cderror.h"
72 NULL
73};
74
hbono@chromium.org98626972011-08-03 03:13:08 +000075static void my_error_exit(j_common_ptr cinfo)
76{
Chris Blumecca8c4d2019-03-01 01:09:50 -080077 my_error_ptr myerr = (my_error_ptr)cinfo->err;
78
79 (*cinfo->err->output_message) (cinfo);
80 longjmp(myerr->setjmp_buffer, 1);
hbono@chromium.org98626972011-08-03 03:13:08 +000081}
82
83/* Based on output_message() in jerror.c */
84
85static void my_output_message(j_common_ptr cinfo)
86{
Chris Blumecca8c4d2019-03-01 01:09:50 -080087 (*cinfo->err->format_message) (cinfo, errStr);
hbono@chromium.org98626972011-08-03 03:13:08 +000088}
89
Tom Hudson0d47d2d2016-05-04 13:22:56 -040090static void my_emit_message(j_common_ptr cinfo, int msg_level)
91{
Chris Blumecca8c4d2019-03-01 01:09:50 -080092 my_error_ptr myerr = (my_error_ptr)cinfo->err;
93
94 myerr->emit_message(cinfo, msg_level);
95 if (msg_level < 0) {
96 myerr->warning = TRUE;
97 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
98 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -040099}
100
hbono@chromium.org98626972011-08-03 03:13:08 +0000101
102/* Global structures, macros, etc. */
103
Chris Blumecca8c4d2019-03-01 01:09:50 -0800104enum { COMPRESS = 1, DECOMPRESS = 2 };
hbono@chromium.org98626972011-08-03 03:13:08 +0000105
Chris Blumecca8c4d2019-03-01 01:09:50 -0800106typedef struct _tjinstance {
107 struct jpeg_compress_struct cinfo;
108 struct jpeg_decompress_struct dinfo;
109 struct my_error_mgr jerr;
110 int init, headerRead;
111 char errStr[JMSG_LENGTH_MAX];
112 boolean isInstanceError;
hbono@chromium.org98626972011-08-03 03:13:08 +0000113} tjinstance;
114
Jonathan Wright24e31052021-04-26 12:10:48 +0100115struct my_progress_mgr {
116 struct jpeg_progress_mgr pub;
117 tjinstance *this;
118};
119typedef struct my_progress_mgr *my_progress_ptr;
120
121static void my_progress_monitor(j_common_ptr dinfo)
122{
123 my_error_ptr myerr = (my_error_ptr)dinfo->err;
124 my_progress_ptr myprog = (my_progress_ptr)dinfo->progress;
125
126 if (dinfo->is_decompressor) {
127 int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number;
128
129 if (scan_no > 500) {
130 snprintf(myprog->this->errStr, JMSG_LENGTH_MAX,
131 "Progressive JPEG image has more than 500 scans");
132 snprintf(errStr, JMSG_LENGTH_MAX,
133 "Progressive JPEG image has more than 500 scans");
134 myprog->this->isInstanceError = TRUE;
135 myerr->warning = FALSE;
136 longjmp(myerr->setjmp_buffer, 1);
137 }
138 }
139}
140
Chris Blumecca8c4d2019-03-01 01:09:50 -0800141static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
hbono@chromium.org98626972011-08-03 03:13:08 +0000142
Chris Blumecca8c4d2019-03-01 01:09:50 -0800143static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
144 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
145 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
hbono@chromium.org98626972011-08-03 03:13:08 +0000146};
147
Chris Blumecca8c4d2019-03-01 01:09:50 -0800148#define NUMSF 16
149static const tjscalingfactor sf[NUMSF] = {
150 { 2, 1 },
151 { 15, 8 },
152 { 7, 4 },
153 { 13, 8 },
154 { 3, 2 },
155 { 11, 8 },
156 { 5, 4 },
157 { 9, 8 },
158 { 1, 1 },
159 { 7, 8 },
160 { 3, 4 },
161 { 5, 8 },
162 { 1, 2 },
163 { 3, 8 },
164 { 1, 4 },
165 { 1, 8 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000166};
167
Chris Blumecca8c4d2019-03-01 01:09:50 -0800168static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
169 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
170 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
171 JCS_EXT_ARGB, JCS_CMYK
172};
173
174static int cs2pf[JPEG_NUMCS] = {
175 TJPF_UNKNOWN, TJPF_GRAY,
176#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
177 TJPF_RGB,
178#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
179 TJPF_BGR,
180#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
181 TJPF_RGBX,
182#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
183 TJPF_BGRX,
184#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
185 TJPF_XBGR,
186#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
187 TJPF_XRGB,
188#endif
189 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
190 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
191 TJPF_UNKNOWN
192};
193
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100194#define THROWG(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800195 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
196 retval = -1; goto bailout; \
197}
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100198#define THROW_UNIX(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800199 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
200 retval = -1; goto bailout; \
201}
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100202#define THROW(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800203 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100204 this->isInstanceError = TRUE; THROWG(m) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800205}
206
Jonathan Wright24e31052021-04-26 12:10:48 +0100207#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
208/* Private flag that triggers different TurboJPEG API behavior when fuzzing */
209#define TJFLAG_FUZZING (1 << 30)
210#endif
211
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100212#define GET_INSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800213 tjinstance *this = (tjinstance *)handle; \
214 j_compress_ptr cinfo = NULL; \
215 j_decompress_ptr dinfo = NULL; \
216 \
217 if (!this) { \
218 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
219 return -1; \
220 } \
221 cinfo = &this->cinfo; dinfo = &this->dinfo; \
222 this->jerr.warning = FALSE; \
223 this->isInstanceError = FALSE;
224
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100225#define GET_CINSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800226 tjinstance *this = (tjinstance *)handle; \
227 j_compress_ptr cinfo = NULL; \
228 \
229 if (!this) { \
230 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
231 return -1; \
232 } \
233 cinfo = &this->cinfo; \
234 this->jerr.warning = FALSE; \
235 this->isInstanceError = FALSE;
236
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100237#define GET_DINSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800238 tjinstance *this = (tjinstance *)handle; \
239 j_decompress_ptr dinfo = NULL; \
240 \
241 if (!this) { \
242 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
243 return -1; \
244 } \
245 dinfo = &this->dinfo; \
246 this->jerr.warning = FALSE; \
247 this->isInstanceError = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000248
249static int getPixelFormat(int pixelSize, int flags)
250{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800251 if (pixelSize == 1) return TJPF_GRAY;
252 if (pixelSize == 3) {
253 if (flags & TJ_BGR) return TJPF_BGR;
254 else return TJPF_RGB;
255 }
256 if (pixelSize == 4) {
257 if (flags & TJ_ALPHAFIRST) {
258 if (flags & TJ_BGR) return TJPF_XBGR;
259 else return TJPF_XRGB;
260 } else {
261 if (flags & TJ_BGR) return TJPF_BGRX;
262 else return TJPF_RGBX;
263 }
264 }
265 return -1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000266}
267
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000268static void setCompDefaults(struct jpeg_compress_struct *cinfo,
269 int pixelFormat, int subsamp, int jpegQual,
270 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000271{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100272#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800273 char *env = NULL;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100274#endif
hbono@chromium.orgc6beb742011-11-29 05:16:26 +0000275
Chris Blumecca8c4d2019-03-01 01:09:50 -0800276 cinfo->in_color_space = pf2cs[pixelFormat];
277 cinfo->input_components = tjPixelSize[pixelFormat];
278 jpeg_set_defaults(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400279
280#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800281 if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
282 !strcmp(env, "1"))
283 cinfo->optimize_coding = TRUE;
284 if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
285 !strcmp(env, "1"))
286 cinfo->arith_code = TRUE;
287 if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
288 int temp = -1;
289 char tempc = 0;
290
291 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
292 temp <= 65535) {
293 if (toupper(tempc) == 'B') {
294 cinfo->restart_interval = temp;
295 cinfo->restart_in_rows = 0;
296 } else
297 cinfo->restart_in_rows = temp;
298 }
299 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400300#endif
301
Chris Blumecca8c4d2019-03-01 01:09:50 -0800302 if (jpegQual >= 0) {
303 jpeg_set_quality(cinfo, jpegQual, TRUE);
304 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
305 cinfo->dct_method = JDCT_ISLOW;
306 else
307 cinfo->dct_method = JDCT_FASTEST;
308 }
309 if (subsamp == TJSAMP_GRAY)
310 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
311 else if (pixelFormat == TJPF_CMYK)
312 jpeg_set_colorspace(cinfo, JCS_YCCK);
313 else
314 jpeg_set_colorspace(cinfo, JCS_YCbCr);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400315
Chris Blumecca8c4d2019-03-01 01:09:50 -0800316 if (flags & TJFLAG_PROGRESSIVE)
317 jpeg_simple_progression(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400318#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800319 else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
320 !strcmp(env, "1"))
321 jpeg_simple_progression(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400322#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000323
Chris Blumecca8c4d2019-03-01 01:09:50 -0800324 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
325 cinfo->comp_info[1].h_samp_factor = 1;
326 cinfo->comp_info[2].h_samp_factor = 1;
327 if (cinfo->num_components > 3)
328 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
329 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
330 cinfo->comp_info[1].v_samp_factor = 1;
331 cinfo->comp_info[2].v_samp_factor = 1;
332 if (cinfo->num_components > 3)
333 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
hbono@chromium.org98626972011-08-03 03:13:08 +0000334}
335
336
337static int getSubsamp(j_decompress_ptr dinfo)
338{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800339 int retval = -1, i, k;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400340
Chris Blumecca8c4d2019-03-01 01:09:50 -0800341 /* The sampling factors actually have no meaning with grayscale JPEG files,
342 and in fact it's possible to generate grayscale JPEGs with sampling
343 factors > 1 (even though those sampling factors are ignored by the
344 decompressor.) Thus, we need to treat grayscale as a special case. */
345 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
346 return TJSAMP_GRAY;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400347
Chris Blumecca8c4d2019-03-01 01:09:50 -0800348 for (i = 0; i < NUMSUBOPT; i++) {
349 if (dinfo->num_components == pixelsize[i] ||
350 ((dinfo->jpeg_color_space == JCS_YCCK ||
351 dinfo->jpeg_color_space == JCS_CMYK) &&
352 pixelsize[i] == 3 && dinfo->num_components == 4)) {
353 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
354 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
355 int match = 0;
356
357 for (k = 1; k < dinfo->num_components; k++) {
358 int href = 1, vref = 1;
359
360 if ((dinfo->jpeg_color_space == JCS_YCCK ||
361 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
362 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
363 }
364 if (dinfo->comp_info[k].h_samp_factor == href &&
365 dinfo->comp_info[k].v_samp_factor == vref)
366 match++;
367 }
368 if (match == dinfo->num_components - 1) {
369 retval = i; break;
370 }
371 }
372 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
373 in non-standard ways. */
374 if (dinfo->comp_info[0].h_samp_factor == 2 &&
375 dinfo->comp_info[0].v_samp_factor == 2 &&
376 (i == TJSAMP_422 || i == TJSAMP_440)) {
377 int match = 0;
378
379 for (k = 1; k < dinfo->num_components; k++) {
380 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
381
382 if ((dinfo->jpeg_color_space == JCS_YCCK ||
383 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
384 href = vref = 2;
385 }
386 if (dinfo->comp_info[k].h_samp_factor == href &&
387 dinfo->comp_info[k].v_samp_factor == vref)
388 match++;
389 }
390 if (match == dinfo->num_components - 1) {
391 retval = i; break;
392 }
393 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100394 /* Handle 4:4:4 images whose sampling factors are specified in
395 non-standard ways. */
396 if (dinfo->comp_info[0].h_samp_factor *
397 dinfo->comp_info[0].v_samp_factor <=
398 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
399 int match = 0;
400 for (k = 1; k < dinfo->num_components; k++) {
401 if (dinfo->comp_info[k].h_samp_factor ==
402 dinfo->comp_info[0].h_samp_factor &&
403 dinfo->comp_info[k].v_samp_factor ==
404 dinfo->comp_info[0].v_samp_factor)
405 match++;
406 if (match == dinfo->num_components - 1) {
407 retval = i; break;
408 }
409 }
410 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800411 }
412 }
413 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000414}
415
416
417/* General API functions */
418
Chris Blumecca8c4d2019-03-01 01:09:50 -0800419DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
hbono@chromium.org98626972011-08-03 03:13:08 +0000420{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800421 tjinstance *this = (tjinstance *)handle;
422
423 if (this && this->isInstanceError) {
424 this->isInstanceError = FALSE;
425 return this->errStr;
426 } else
427 return errStr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000428}
429
430
Chris Blumecca8c4d2019-03-01 01:09:50 -0800431DLLEXPORT char *tjGetErrorStr(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000432{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800433 return errStr;
434}
435
436
437DLLEXPORT int tjGetErrorCode(tjhandle handle)
438{
439 tjinstance *this = (tjinstance *)handle;
440
441 if (this && this->jerr.warning) return TJERR_WARNING;
442 else return TJERR_FATAL;
443}
444
445
446DLLEXPORT int tjDestroy(tjhandle handle)
447{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100448 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800449
450 if (setjmp(this->jerr.setjmp_buffer)) return -1;
451 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
452 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
453 free(this);
454 return 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000455}
456
457
458/* These are exposed mainly because Windows can't malloc() and free() across
459 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
460 with turbojpeg.dll for compatibility reasons. However, these functions
461 can potentially be used for other purposes by different implementations. */
462
Chris Blumecca8c4d2019-03-01 01:09:50 -0800463DLLEXPORT void tjFree(unsigned char *buf)
hbono@chromium.org98626972011-08-03 03:13:08 +0000464{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100465 free(buf);
hbono@chromium.org98626972011-08-03 03:13:08 +0000466}
467
468
Chris Blumecca8c4d2019-03-01 01:09:50 -0800469DLLEXPORT unsigned char *tjAlloc(int bytes)
hbono@chromium.org98626972011-08-03 03:13:08 +0000470{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800471 return (unsigned char *)malloc(bytes);
hbono@chromium.org98626972011-08-03 03:13:08 +0000472}
473
474
475/* Compressor */
476
477static tjhandle _tjInitCompress(tjinstance *this)
478{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800479 static unsigned char buffer[1];
480 unsigned char *buf = buffer;
481 unsigned long size = 1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000482
Chris Blumecca8c4d2019-03-01 01:09:50 -0800483 /* This is also straight out of example.txt */
484 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
485 this->jerr.pub.error_exit = my_error_exit;
486 this->jerr.pub.output_message = my_output_message;
487 this->jerr.emit_message = this->jerr.pub.emit_message;
488 this->jerr.pub.emit_message = my_emit_message;
489 this->jerr.pub.addon_message_table = turbojpeg_message_table;
490 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
491 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000492
Chris Blumecca8c4d2019-03-01 01:09:50 -0800493 if (setjmp(this->jerr.setjmp_buffer)) {
494 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100495 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800496 return NULL;
497 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000498
Chris Blumecca8c4d2019-03-01 01:09:50 -0800499 jpeg_create_compress(&this->cinfo);
500 /* Make an initial call so it will create the destination manager */
501 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
hbono@chromium.org98626972011-08-03 03:13:08 +0000502
Chris Blumecca8c4d2019-03-01 01:09:50 -0800503 this->init |= COMPRESS;
504 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +0000505}
506
Chris Blumecca8c4d2019-03-01 01:09:50 -0800507DLLEXPORT tjhandle tjInitCompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000508{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800509 tjinstance *this = NULL;
510
511 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
512 snprintf(errStr, JMSG_LENGTH_MAX,
513 "tjInitCompress(): Memory allocation failure");
514 return NULL;
515 }
516 MEMZERO(this, sizeof(tjinstance));
517 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
518 return _tjInitCompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +0000519}
520
521
Chris Blumecca8c4d2019-03-01 01:09:50 -0800522DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000523{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100524 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800525 int mcuw, mcuh, chromasf;
hbono@chromium.org98626972011-08-03 03:13:08 +0000526
Chris Blumecca8c4d2019-03-01 01:09:50 -0800527 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100528 THROWG("tjBufSize(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000529
Chris Blumecca8c4d2019-03-01 01:09:50 -0800530 /* This allows for rare corner cases in which a JPEG image can actually be
531 larger than the uncompressed input (we wouldn't mention it if it hadn't
532 happened before.) */
533 mcuw = tjMCUWidth[jpegSubsamp];
534 mcuh = tjMCUHeight[jpegSubsamp];
535 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100536 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
537 if (retval > (unsigned long long)((unsigned long)-1))
538 THROWG("tjBufSize(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800539
540bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100541 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000542}
543
Chris Blumecca8c4d2019-03-01 01:09:50 -0800544DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
hbono@chromium.org98626972011-08-03 03:13:08 +0000545{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100546 unsigned long long retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000547
Chris Blumecca8c4d2019-03-01 01:09:50 -0800548 if (width < 1 || height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100549 THROWG("TJBUFSIZE(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000550
Chris Blumecca8c4d2019-03-01 01:09:50 -0800551 /* This allows for rare corner cases in which a JPEG image can actually be
552 larger than the uncompressed input (we wouldn't mention it if it hadn't
553 happened before.) */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100554 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
555 if (retval > (unsigned long long)((unsigned long)-1))
556 THROWG("TJBUFSIZE(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800557
558bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100559 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000560}
561
562
Chris Blumecca8c4d2019-03-01 01:09:50 -0800563DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
564 int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000565{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100566 unsigned long long retval = 0;
567 int nc, i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400568
Chris Blumecca8c4d2019-03-01 01:09:50 -0800569 if (subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100570 THROWG("tjBufSizeYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400571
Chris Blumecca8c4d2019-03-01 01:09:50 -0800572 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
573 for (i = 0; i < nc; i++) {
574 int pw = tjPlaneWidth(i, width, subsamp);
575 int stride = PAD(pw, pad);
576 int ph = tjPlaneHeight(i, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000577
Chris Blumecca8c4d2019-03-01 01:09:50 -0800578 if (pw < 0 || ph < 0) return -1;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100579 else retval += (unsigned long long)stride * ph;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800580 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100581 if (retval > (unsigned long long)((unsigned long)-1))
582 THROWG("tjBufSizeYUV2(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800583
584bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100585 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000586}
587
Chris Blumecca8c4d2019-03-01 01:09:50 -0800588DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400589{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800590 return tjBufSizeYUV2(width, 4, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400591}
hbono@chromium.org98626972011-08-03 03:13:08 +0000592
Chris Blumecca8c4d2019-03-01 01:09:50 -0800593DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000594{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800595 return tjBufSizeYUV(width, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000596}
597
598
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400599DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
600{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800601 int pw, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400602
Chris Blumecca8c4d2019-03-01 01:09:50 -0800603 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100604 THROWG("tjPlaneWidth(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800605 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
606 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100607 THROWG("tjPlaneWidth(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400608
Chris Blumecca8c4d2019-03-01 01:09:50 -0800609 pw = PAD(width, tjMCUWidth[subsamp] / 8);
610 if (componentID == 0)
611 retval = pw;
612 else
613 retval = pw * 8 / tjMCUWidth[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400614
Chris Blumecca8c4d2019-03-01 01:09:50 -0800615bailout:
616 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400617}
618
619
620DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
621{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800622 int ph, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400623
Chris Blumecca8c4d2019-03-01 01:09:50 -0800624 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100625 THROWG("tjPlaneHeight(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800626 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
627 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100628 THROWG("tjPlaneHeight(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400629
Chris Blumecca8c4d2019-03-01 01:09:50 -0800630 ph = PAD(height, tjMCUHeight[subsamp] / 8);
631 if (componentID == 0)
632 retval = ph;
633 else
634 retval = ph * 8 / tjMCUHeight[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400635
Chris Blumecca8c4d2019-03-01 01:09:50 -0800636bailout:
637 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400638}
639
640
Chris Blumecca8c4d2019-03-01 01:09:50 -0800641DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
642 int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400643{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100644 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800645 int pw, ph;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400646
Chris Blumecca8c4d2019-03-01 01:09:50 -0800647 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100648 THROWG("tjPlaneSizeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400649
Chris Blumecca8c4d2019-03-01 01:09:50 -0800650 pw = tjPlaneWidth(componentID, width, subsamp);
651 ph = tjPlaneHeight(componentID, height, subsamp);
652 if (pw < 0 || ph < 0) return -1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400653
Chris Blumecca8c4d2019-03-01 01:09:50 -0800654 if (stride == 0) stride = pw;
655 else stride = abs(stride);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400656
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100657 retval = (unsigned long long)stride * (ph - 1) + pw;
658 if (retval > (unsigned long long)((unsigned long)-1))
659 THROWG("tjPlaneSizeYUV(): Image is too large");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400660
Chris Blumecca8c4d2019-03-01 01:09:50 -0800661bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100662 return (unsigned long)retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400663}
664
665
Chris Blumecca8c4d2019-03-01 01:09:50 -0800666DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
667 int width, int pitch, int height, int pixelFormat,
668 unsigned char **jpegBuf, unsigned long *jpegSize,
669 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000670{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800671 int i, retval = 0, alloc = 1;
672 JSAMPROW *row_pointer = NULL;
hbono@chromium.org98626972011-08-03 03:13:08 +0000673
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100674 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800675 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
676 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100677 THROW("tjCompress2(): Instance has not been initialized for compression");
hbono@chromium.org98626972011-08-03 03:13:08 +0000678
Chris Blumecca8c4d2019-03-01 01:09:50 -0800679 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
680 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
681 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
682 jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100683 THROW("tjCompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000684
Chris Blumecca8c4d2019-03-01 01:09:50 -0800685 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +0000686
Chris Blumecca8c4d2019-03-01 01:09:50 -0800687 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100688 THROW("tjCompress2(): Memory allocation failure");
hbono@chromium.org98626972011-08-03 03:13:08 +0000689
Chris Blumecca8c4d2019-03-01 01:09:50 -0800690 if (setjmp(this->jerr.setjmp_buffer)) {
691 /* If we get here, the JPEG code has signaled an error. */
692 retval = -1; goto bailout;
693 }
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000694
Chris Blumecca8c4d2019-03-01 01:09:50 -0800695 cinfo->image_width = width;
696 cinfo->image_height = height;
hbono@chromium.org98626972011-08-03 03:13:08 +0000697
Chris Blumecca8c4d2019-03-01 01:09:50 -0800698#ifndef NO_PUTENV
699 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
700 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
701 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
702#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000703
Chris Blumecca8c4d2019-03-01 01:09:50 -0800704 if (flags & TJFLAG_NOREALLOC) {
705 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
706 }
707 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000708 setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000709
Chris Blumecca8c4d2019-03-01 01:09:50 -0800710 jpeg_start_compress(cinfo, TRUE);
711 for (i = 0; i < height; i++) {
712 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100713 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800714 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100715 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800716 }
717 while (cinfo->next_scanline < cinfo->image_height)
718 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
719 cinfo->image_height - cinfo->next_scanline);
720 jpeg_finish_compress(cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000721
Chris Blumecca8c4d2019-03-01 01:09:50 -0800722bailout:
Jonathan Wright24e31052021-04-26 12:10:48 +0100723 if (cinfo->global_state > CSTATE_START) {
724 if (alloc) (*cinfo->dest->term_destination) (cinfo);
725 jpeg_abort_compress(cinfo);
726 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100727 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800728 if (this->jerr.warning) retval = -1;
729 this->jerr.stopOnWarning = FALSE;
730 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000731}
732
Chris Blumecca8c4d2019-03-01 01:09:50 -0800733DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
734 int pitch, int height, int pixelSize,
735 unsigned char *jpegBuf, unsigned long *jpegSize,
736 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000737{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800738 int retval = 0;
739 unsigned long size;
740
741 if (flags & TJ_YUV) {
742 size = tjBufSizeYUV(width, height, jpegSubsamp);
743 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
744 getPixelFormat(pixelSize, flags), jpegBuf,
745 jpegSubsamp, flags);
746 } else {
747 retval = tjCompress2(handle, srcBuf, width, pitch, height,
748 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
749 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
750 }
751 *jpegSize = size;
752 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000753}
754
755
Chris Blumecca8c4d2019-03-01 01:09:50 -0800756DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
757 int width, int pitch, int height,
758 int pixelFormat, unsigned char **dstPlanes,
759 int *strides, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000760{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800761 JSAMPROW *row_pointer = NULL;
762 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
763 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
764 JSAMPROW *outbuf[MAX_COMPONENTS];
765 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
766 JSAMPLE *ptr;
767 jpeg_component_info *compptr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000768
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100769 GET_CINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800770 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000771
Chris Blumecca8c4d2019-03-01 01:09:50 -0800772 for (i = 0; i < MAX_COMPONENTS; i++) {
773 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
774 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
775 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000776
Chris Blumecca8c4d2019-03-01 01:09:50 -0800777 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100778 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000779
Chris Blumecca8c4d2019-03-01 01:09:50 -0800780 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
781 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
782 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100783 THROW("tjEncodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800784 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100785 THROW("tjEncodeYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000786
Chris Blumecca8c4d2019-03-01 01:09:50 -0800787 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100788 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
hbono@chromium.org98626972011-08-03 03:13:08 +0000789
Chris Blumecca8c4d2019-03-01 01:09:50 -0800790 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400791
Chris Blumecca8c4d2019-03-01 01:09:50 -0800792 if (setjmp(this->jerr.setjmp_buffer)) {
793 /* If we get here, the JPEG code has signaled an error. */
794 retval = -1; goto bailout;
795 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000796
Chris Blumecca8c4d2019-03-01 01:09:50 -0800797 cinfo->image_width = width;
798 cinfo->image_height = height;
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000799
Chris Blumecca8c4d2019-03-01 01:09:50 -0800800#ifndef NO_PUTENV
801 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
802 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
803 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
804#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000805
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000806 setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000807
Chris Blumecca8c4d2019-03-01 01:09:50 -0800808 /* Execute only the parts of jpeg_start_compress() that we need. If we
809 were to call the whole jpeg_start_compress() function, then it would try
810 to write the file headers, which could overflow the output buffer if the
811 YUV image were very small. */
812 if (cinfo->global_state != CSTATE_START)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100813 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800814 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
815 jinit_c_master_control(cinfo, FALSE);
816 jinit_color_converter(cinfo);
817 jinit_downsampler(cinfo);
818 (*cinfo->cconvert->start_pass) (cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000819
Chris Blumecca8c4d2019-03-01 01:09:50 -0800820 pw0 = PAD(width, cinfo->max_h_samp_factor);
821 ph0 = PAD(height, cinfo->max_v_samp_factor);
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000822
Chris Blumecca8c4d2019-03-01 01:09:50 -0800823 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100824 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800825 for (i = 0; i < height; i++) {
826 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100827 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800828 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100829 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800830 }
831 if (height < ph0)
832 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
hbono@chromium.org98626972011-08-03 03:13:08 +0000833
Chris Blumecca8c4d2019-03-01 01:09:50 -0800834 for (i = 0; i < cinfo->num_components; i++) {
835 compptr = &cinfo->comp_info[i];
836 _tmpbuf[i] = (JSAMPLE *)malloc(
837 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
838 compptr->h_samp_factor, 32) *
839 cinfo->max_v_samp_factor + 32);
840 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100841 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800842 tmpbuf[i] =
843 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
844 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100845 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800846 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
847 unsigned char *_tmpbuf_aligned =
848 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000849
Chris Blumecca8c4d2019-03-01 01:09:50 -0800850 tmpbuf[i][row] = &_tmpbuf_aligned[
851 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
852 compptr->h_samp_factor, 32) * row];
853 }
854 _tmpbuf2[i] =
855 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
856 compptr->v_samp_factor + 32);
857 if (!_tmpbuf2[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100858 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800859 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
860 if (!tmpbuf2[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100861 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800862 for (row = 0; row < compptr->v_samp_factor; row++) {
863 unsigned char *_tmpbuf2_aligned =
864 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000865
Chris Blumecca8c4d2019-03-01 01:09:50 -0800866 tmpbuf2[i][row] =
867 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
868 }
869 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
870 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
871 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
872 if (!outbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100873 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800874 ptr = dstPlanes[i];
875 for (row = 0; row < ph[i]; row++) {
876 outbuf[i][row] = ptr;
877 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
878 }
879 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000880
Chris Blumecca8c4d2019-03-01 01:09:50 -0800881 if (setjmp(this->jerr.setjmp_buffer)) {
882 /* If we get here, the JPEG code has signaled an error. */
883 retval = -1; goto bailout;
884 }
885
886 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
887 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
888 cinfo->max_v_samp_factor);
889 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
890 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
891 i++, compptr++)
892 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
893 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
894 compptr->v_samp_factor, pw[i]);
895 }
896 cinfo->next_scanline += height;
897 jpeg_abort_compress(cinfo);
898
899bailout:
900 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100901 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800902 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100903 free(tmpbuf[i]);
904 free(_tmpbuf[i]);
905 free(tmpbuf2[i]);
906 free(_tmpbuf2[i]);
907 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800908 }
909 if (this->jerr.warning) retval = -1;
910 this->jerr.stopOnWarning = FALSE;
911 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000912}
913
Chris Blumecca8c4d2019-03-01 01:09:50 -0800914DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
915 int width, int pitch, int height, int pixelFormat,
916 unsigned char *dstBuf, int pad, int subsamp,
917 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400918{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800919 unsigned char *dstPlanes[3];
920 int pw0, ph0, strides[3], retval = -1;
921 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400922
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100923 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800924 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400925
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100926 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
927 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
928 THROW("tjEncodeYUV3(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400929
Chris Blumecca8c4d2019-03-01 01:09:50 -0800930 pw0 = tjPlaneWidth(0, width, subsamp);
931 ph0 = tjPlaneHeight(0, height, subsamp);
932 dstPlanes[0] = dstBuf;
933 strides[0] = PAD(pw0, pad);
934 if (subsamp == TJSAMP_GRAY) {
935 strides[1] = strides[2] = 0;
936 dstPlanes[1] = dstPlanes[2] = NULL;
937 } else {
938 int pw1 = tjPlaneWidth(1, width, subsamp);
939 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400940
Chris Blumecca8c4d2019-03-01 01:09:50 -0800941 strides[1] = strides[2] = PAD(pw1, pad);
942 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
943 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
944 }
945
946 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
947 dstPlanes, strides, subsamp, flags);
948
949bailout:
950 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400951}
952
Chris Blumecca8c4d2019-03-01 01:09:50 -0800953DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
954 int pitch, int height, int pixelFormat,
955 unsigned char *dstBuf, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400956{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800957 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
958 dstBuf, 4, subsamp, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400959}
960
Chris Blumecca8c4d2019-03-01 01:09:50 -0800961DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
962 int pitch, int height, int pixelSize,
963 unsigned char *dstBuf, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000964{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800965 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
966 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
967 flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000968}
969
970
Chris Blumecca8c4d2019-03-01 01:09:50 -0800971DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
972 const unsigned char **srcPlanes,
973 int width, const int *strides,
974 int height, int subsamp,
975 unsigned char **jpegBuf,
976 unsigned long *jpegSize, int jpegQual,
977 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400978{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800979 int i, row, retval = 0, alloc = 1;
980 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
981 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
982 JSAMPLE *_tmpbuf = NULL, *ptr;
983 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400984
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100985 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800986 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400987
Chris Blumecca8c4d2019-03-01 01:09:50 -0800988 for (i = 0; i < MAX_COMPONENTS; i++) {
989 tmpbuf[i] = NULL; inbuf[i] = NULL;
990 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400991
Chris Blumecca8c4d2019-03-01 01:09:50 -0800992 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100993 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400994
Chris Blumecca8c4d2019-03-01 01:09:50 -0800995 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
996 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
997 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100998 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800999 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001000 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001001
Chris Blumecca8c4d2019-03-01 01:09:50 -08001002 if (setjmp(this->jerr.setjmp_buffer)) {
1003 /* If we get here, the JPEG code has signaled an error. */
1004 retval = -1; goto bailout;
1005 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001006
Chris Blumecca8c4d2019-03-01 01:09:50 -08001007 cinfo->image_width = width;
1008 cinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001009
Chris Blumecca8c4d2019-03-01 01:09:50 -08001010#ifndef NO_PUTENV
1011 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1012 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1013 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1014#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001015
Chris Blumecca8c4d2019-03-01 01:09:50 -08001016 if (flags & TJFLAG_NOREALLOC) {
1017 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
1018 }
1019 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
Jonathan Wrightbbb82822020-11-25 13:36:43 +00001020 setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001021 cinfo->raw_data_in = TRUE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001022
Chris Blumecca8c4d2019-03-01 01:09:50 -08001023 jpeg_start_compress(cinfo, TRUE);
1024 for (i = 0; i < cinfo->num_components; i++) {
1025 jpeg_component_info *compptr = &cinfo->comp_info[i];
1026 int ih;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001027
Chris Blumecca8c4d2019-03-01 01:09:50 -08001028 iw[i] = compptr->width_in_blocks * DCTSIZE;
1029 ih = compptr->height_in_blocks * DCTSIZE;
1030 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1031 compptr->h_samp_factor / cinfo->max_h_samp_factor;
1032 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1033 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1034 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1035 th[i] = compptr->v_samp_factor * DCTSIZE;
1036 tmpbufsize += iw[i] * th[i];
1037 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001038 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001039 ptr = (JSAMPLE *)srcPlanes[i];
1040 for (row = 0; row < ph[i]; row++) {
1041 inbuf[i][row] = ptr;
1042 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1043 }
1044 }
1045 if (usetmpbuf) {
1046 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001047 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001048 ptr = _tmpbuf;
1049 for (i = 0; i < cinfo->num_components; i++) {
1050 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001051 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001052 for (row = 0; row < th[i]; row++) {
1053 tmpbuf[i][row] = ptr;
1054 ptr += iw[i];
1055 }
1056 }
1057 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001058
Chris Blumecca8c4d2019-03-01 01:09:50 -08001059 if (setjmp(this->jerr.setjmp_buffer)) {
1060 /* If we get here, the JPEG code has signaled an error. */
1061 retval = -1; goto bailout;
1062 }
1063
1064 for (row = 0; row < (int)cinfo->image_height;
1065 row += cinfo->max_v_samp_factor * DCTSIZE) {
1066 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1067 int crow[MAX_COMPONENTS];
1068
1069 for (i = 0; i < cinfo->num_components; i++) {
1070 jpeg_component_info *compptr = &cinfo->comp_info[i];
1071
1072 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1073 if (usetmpbuf) {
1074 int j, k;
1075
1076 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1077 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1078 /* Duplicate last sample in row to fill out MCU */
1079 for (k = pw[i]; k < iw[i]; k++)
1080 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1081 }
1082 /* Duplicate last row to fill out MCU */
1083 for (j = ph[i] - crow[i]; j < th[i]; j++)
1084 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1085 yuvptr[i] = tmpbuf[i];
1086 } else
1087 yuvptr[i] = &inbuf[i][crow[i]];
1088 }
1089 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1090 }
1091 jpeg_finish_compress(cinfo);
1092
1093bailout:
Jonathan Wright24e31052021-04-26 12:10:48 +01001094 if (cinfo->global_state > CSTATE_START) {
1095 if (alloc) (*cinfo->dest->term_destination) (cinfo);
1096 jpeg_abort_compress(cinfo);
1097 }
Chris Blumecca8c4d2019-03-01 01:09:50 -08001098 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001099 free(tmpbuf[i]);
1100 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001101 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001102 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001103 if (this->jerr.warning) retval = -1;
1104 this->jerr.stopOnWarning = FALSE;
1105 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001106}
1107
Chris Blumecca8c4d2019-03-01 01:09:50 -08001108DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1109 int width, int pad, int height, int subsamp,
1110 unsigned char **jpegBuf,
1111 unsigned long *jpegSize, int jpegQual,
1112 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001113{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001114 const unsigned char *srcPlanes[3];
1115 int pw0, ph0, strides[3], retval = -1;
1116 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001117
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001118 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001119 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001120
Chris Blumecca8c4d2019-03-01 01:09:50 -08001121 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1122 subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001123 THROW("tjCompressFromYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001124
Chris Blumecca8c4d2019-03-01 01:09:50 -08001125 pw0 = tjPlaneWidth(0, width, subsamp);
1126 ph0 = tjPlaneHeight(0, height, subsamp);
1127 srcPlanes[0] = srcBuf;
1128 strides[0] = PAD(pw0, pad);
1129 if (subsamp == TJSAMP_GRAY) {
1130 strides[1] = strides[2] = 0;
1131 srcPlanes[1] = srcPlanes[2] = NULL;
1132 } else {
1133 int pw1 = tjPlaneWidth(1, width, subsamp);
1134 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001135
Chris Blumecca8c4d2019-03-01 01:09:50 -08001136 strides[1] = strides[2] = PAD(pw1, pad);
1137 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1138 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1139 }
1140
1141 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1142 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1143
1144bailout:
1145 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001146}
1147
1148
hbono@chromium.org98626972011-08-03 03:13:08 +00001149/* Decompressor */
1150
1151static tjhandle _tjInitDecompress(tjinstance *this)
1152{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001153 static unsigned char buffer[1];
hbono@chromium.org98626972011-08-03 03:13:08 +00001154
Chris Blumecca8c4d2019-03-01 01:09:50 -08001155 /* This is also straight out of example.txt */
1156 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1157 this->jerr.pub.error_exit = my_error_exit;
1158 this->jerr.pub.output_message = my_output_message;
1159 this->jerr.emit_message = this->jerr.pub.emit_message;
1160 this->jerr.pub.emit_message = my_emit_message;
1161 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1162 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1163 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001164
Chris Blumecca8c4d2019-03-01 01:09:50 -08001165 if (setjmp(this->jerr.setjmp_buffer)) {
1166 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001167 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001168 return NULL;
1169 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001170
Chris Blumecca8c4d2019-03-01 01:09:50 -08001171 jpeg_create_decompress(&this->dinfo);
1172 /* Make an initial call so it will create the source manager */
1173 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
hbono@chromium.org98626972011-08-03 03:13:08 +00001174
Chris Blumecca8c4d2019-03-01 01:09:50 -08001175 this->init |= DECOMPRESS;
1176 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +00001177}
1178
Chris Blumecca8c4d2019-03-01 01:09:50 -08001179DLLEXPORT tjhandle tjInitDecompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001180{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001181 tjinstance *this;
1182
1183 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1184 snprintf(errStr, JMSG_LENGTH_MAX,
1185 "tjInitDecompress(): Memory allocation failure");
1186 return NULL;
1187 }
1188 MEMZERO(this, sizeof(tjinstance));
1189 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1190 return _tjInitDecompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +00001191}
1192
1193
Chris Blumecca8c4d2019-03-01 01:09:50 -08001194DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1195 const unsigned char *jpegBuf,
1196 unsigned long jpegSize, int *width,
1197 int *height, int *jpegSubsamp,
1198 int *jpegColorspace)
hbono@chromium.org98626972011-08-03 03:13:08 +00001199{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001200 int retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001201
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001202 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001203 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001204 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001205
Chris Blumecca8c4d2019-03-01 01:09:50 -08001206 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1207 jpegSubsamp == NULL || jpegColorspace == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001208 THROW("tjDecompressHeader3(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001209
Chris Blumecca8c4d2019-03-01 01:09:50 -08001210 if (setjmp(this->jerr.setjmp_buffer)) {
1211 /* If we get here, the JPEG code has signaled an error. */
1212 return -1;
1213 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001214
Chris Blumecca8c4d2019-03-01 01:09:50 -08001215 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1216 jpeg_read_header(dinfo, TRUE);
hbono@chromium.org98626972011-08-03 03:13:08 +00001217
Chris Blumecca8c4d2019-03-01 01:09:50 -08001218 *width = dinfo->image_width;
1219 *height = dinfo->image_height;
1220 *jpegSubsamp = getSubsamp(dinfo);
1221 switch (dinfo->jpeg_color_space) {
1222 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1223 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1224 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1225 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1226 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1227 default: *jpegColorspace = -1; break;
1228 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001229
Chris Blumecca8c4d2019-03-01 01:09:50 -08001230 jpeg_abort_decompress(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001231
Chris Blumecca8c4d2019-03-01 01:09:50 -08001232 if (*jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001233 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001234 if (*jpegColorspace < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001235 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001236 if (*width < 1 || *height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001237 THROW("tjDecompressHeader3(): Invalid data returned in header");
hbono@chromium.org98626972011-08-03 03:13:08 +00001238
Chris Blumecca8c4d2019-03-01 01:09:50 -08001239bailout:
1240 if (this->jerr.warning) retval = -1;
1241 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001242}
1243
Chris Blumecca8c4d2019-03-01 01:09:50 -08001244DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1245 unsigned long jpegSize, int *width,
1246 int *height, int *jpegSubsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001247{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001248 int jpegColorspace;
1249
1250 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1251 jpegSubsamp, &jpegColorspace);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001252}
1253
Chris Blumecca8c4d2019-03-01 01:09:50 -08001254DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1255 unsigned long jpegSize, int *width,
1256 int *height)
hbono@chromium.org98626972011-08-03 03:13:08 +00001257{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001258 int jpegSubsamp;
1259
1260 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1261 &jpegSubsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +00001262}
1263
1264
Chris Blumecca8c4d2019-03-01 01:09:50 -08001265DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
hbono@chromium.org98626972011-08-03 03:13:08 +00001266{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001267 if (numscalingfactors == NULL) {
1268 snprintf(errStr, JMSG_LENGTH_MAX,
1269 "tjGetScalingFactors(): Invalid argument");
1270 return NULL;
1271 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001272
Chris Blumecca8c4d2019-03-01 01:09:50 -08001273 *numscalingfactors = NUMSF;
1274 return (tjscalingfactor *)sf;
hbono@chromium.org98626972011-08-03 03:13:08 +00001275}
1276
1277
Chris Blumecca8c4d2019-03-01 01:09:50 -08001278DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1279 unsigned long jpegSize, unsigned char *dstBuf,
1280 int width, int pitch, int height, int pixelFormat,
1281 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001282{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001283 JSAMPROW *row_pointer = NULL;
1284 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
Jonathan Wright24e31052021-04-26 12:10:48 +01001285 struct my_progress_mgr progress;
hbono@chromium.org98626972011-08-03 03:13:08 +00001286
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001287 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001288 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1289 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001290 THROW("tjDecompress2(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001291
Chris Blumecca8c4d2019-03-01 01:09:50 -08001292 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1293 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001294 THROW("tjDecompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001295
Chris Blumecca8c4d2019-03-01 01:09:50 -08001296#ifndef NO_PUTENV
1297 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1298 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1299 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1300#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001301
Jonathan Wright24e31052021-04-26 12:10:48 +01001302 if (flags & TJFLAG_LIMITSCANS) {
1303 MEMZERO(&progress, sizeof(struct my_progress_mgr));
1304 progress.pub.progress_monitor = my_progress_monitor;
1305 progress.this = this;
1306 dinfo->progress = &progress.pub;
1307 } else
1308 dinfo->progress = NULL;
1309
Chris Blumecca8c4d2019-03-01 01:09:50 -08001310 if (setjmp(this->jerr.setjmp_buffer)) {
1311 /* If we get here, the JPEG code has signaled an error. */
1312 retval = -1; goto bailout;
1313 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001314
Chris Blumecca8c4d2019-03-01 01:09:50 -08001315 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1316 jpeg_read_header(dinfo, TRUE);
1317 this->dinfo.out_color_space = pf2cs[pixelFormat];
1318 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1319 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001320
Chris Blumecca8c4d2019-03-01 01:09:50 -08001321 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1322 if (width == 0) width = jpegwidth;
1323 if (height == 0) height = jpegheight;
1324 for (i = 0; i < NUMSF; i++) {
1325 scaledw = TJSCALED(jpegwidth, sf[i]);
1326 scaledh = TJSCALED(jpegheight, sf[i]);
1327 if (scaledw <= width && scaledh <= height)
1328 break;
1329 }
1330 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001331 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001332 width = scaledw; height = scaledh;
1333 dinfo->scale_num = sf[i].num;
1334 dinfo->scale_denom = sf[i].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001335
Chris Blumecca8c4d2019-03-01 01:09:50 -08001336 jpeg_start_decompress(dinfo);
1337 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +00001338
Chris Blumecca8c4d2019-03-01 01:09:50 -08001339 if ((row_pointer =
1340 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001341 THROW("tjDecompress2(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001342 if (setjmp(this->jerr.setjmp_buffer)) {
1343 /* If we get here, the JPEG code has signaled an error. */
1344 retval = -1; goto bailout;
1345 }
1346 for (i = 0; i < (int)dinfo->output_height; i++) {
1347 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001348 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001349 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001350 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001351 }
1352 while (dinfo->output_scanline < dinfo->output_height)
1353 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1354 dinfo->output_height - dinfo->output_scanline);
1355 jpeg_finish_decompress(dinfo);
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +00001356
Chris Blumecca8c4d2019-03-01 01:09:50 -08001357bailout:
1358 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001359 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001360 if (this->jerr.warning) retval = -1;
1361 this->jerr.stopOnWarning = FALSE;
1362 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001363}
1364
Chris Blumecca8c4d2019-03-01 01:09:50 -08001365DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1366 unsigned long jpegSize, unsigned char *dstBuf,
1367 int width, int pitch, int height, int pixelSize,
1368 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001369{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001370 if (flags & TJ_YUV)
1371 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1372 else
1373 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1374 height, getPixelFormat(pixelSize, flags), flags);
hbono@chromium.org98626972011-08-03 03:13:08 +00001375}
1376
1377
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001378static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001379 int pixelFormat, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001380{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001381 int i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001382
Chris Blumecca8c4d2019-03-01 01:09:50 -08001383 dinfo->scale_num = dinfo->scale_denom = 1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001384
Chris Blumecca8c4d2019-03-01 01:09:50 -08001385 if (subsamp == TJSAMP_GRAY) {
1386 dinfo->num_components = dinfo->comps_in_scan = 1;
1387 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1388 } else {
1389 dinfo->num_components = dinfo->comps_in_scan = 3;
1390 dinfo->jpeg_color_space = JCS_YCbCr;
1391 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001392
Chris Blumecca8c4d2019-03-01 01:09:50 -08001393 dinfo->comp_info = (jpeg_component_info *)
1394 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1395 dinfo->num_components *
1396 sizeof(jpeg_component_info));
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001397
Chris Blumecca8c4d2019-03-01 01:09:50 -08001398 for (i = 0; i < dinfo->num_components; i++) {
1399 jpeg_component_info *compptr = &dinfo->comp_info[i];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001400
Chris Blumecca8c4d2019-03-01 01:09:50 -08001401 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1402 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1403 compptr->component_index = i;
1404 compptr->component_id = i + 1;
1405 compptr->quant_tbl_no = compptr->dc_tbl_no =
1406 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1407 dinfo->cur_comp_info[i] = compptr;
1408 }
1409 dinfo->data_precision = 8;
1410 for (i = 0; i < 2; i++) {
1411 if (dinfo->quant_tbl_ptrs[i] == NULL)
1412 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1413 }
1414
1415 return 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001416}
1417
1418
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001419static int my_read_markers(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001420{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001421 return JPEG_REACHED_SOS;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001422}
1423
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001424static void my_reset_marker_reader(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001425{
1426}
1427
Chris Blumecca8c4d2019-03-01 01:09:50 -08001428DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1429 const unsigned char **srcPlanes,
1430 const int *strides, int subsamp,
1431 unsigned char *dstBuf, int width, int pitch,
1432 int height, int pixelFormat, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001433{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001434 JSAMPROW *row_pointer = NULL;
1435 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1436 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1437 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1438 JSAMPLE *ptr;
1439 jpeg_component_info *compptr;
1440 int (*old_read_markers) (j_decompress_ptr);
1441 void (*old_reset_marker_reader) (j_decompress_ptr);
hbono@chromium.org98626972011-08-03 03:13:08 +00001442
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001443 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001444 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001445
Chris Blumecca8c4d2019-03-01 01:09:50 -08001446 for (i = 0; i < MAX_COMPONENTS; i++) {
1447 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1448 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001449
Chris Blumecca8c4d2019-03-01 01:09:50 -08001450 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001451 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001452
Chris Blumecca8c4d2019-03-01 01:09:50 -08001453 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1454 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1455 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001456 THROW("tjDecodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001457 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001458 THROW("tjDecodeYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001459
Chris Blumecca8c4d2019-03-01 01:09:50 -08001460 if (setjmp(this->jerr.setjmp_buffer)) {
1461 /* If we get here, the JPEG code has signaled an error. */
1462 retval = -1; goto bailout;
1463 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001464
Chris Blumecca8c4d2019-03-01 01:09:50 -08001465 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001466 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001467
Chris Blumecca8c4d2019-03-01 01:09:50 -08001468 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1469 dinfo->image_width = width;
1470 dinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001471
Chris Blumecca8c4d2019-03-01 01:09:50 -08001472#ifndef NO_PUTENV
1473 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1474 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1475 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1476#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001477
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001478 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1479 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1480 dinfo->Se = DCTSIZE2 - 1;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001481 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1482 retval = -1; goto bailout;
1483 }
1484 old_read_markers = dinfo->marker->read_markers;
1485 dinfo->marker->read_markers = my_read_markers;
1486 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1487 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1488 jpeg_read_header(dinfo, TRUE);
1489 dinfo->marker->read_markers = old_read_markers;
1490 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001491
Chris Blumecca8c4d2019-03-01 01:09:50 -08001492 this->dinfo.out_color_space = pf2cs[pixelFormat];
1493 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1494 dinfo->do_fancy_upsampling = FALSE;
1495 dinfo->Se = DCTSIZE2 - 1;
1496 jinit_master_decompress(dinfo);
1497 (*dinfo->upsample->start_pass) (dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001498
Chris Blumecca8c4d2019-03-01 01:09:50 -08001499 pw0 = PAD(width, dinfo->max_h_samp_factor);
1500 ph0 = PAD(height, dinfo->max_v_samp_factor);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001501
Chris Blumecca8c4d2019-03-01 01:09:50 -08001502 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001503
Chris Blumecca8c4d2019-03-01 01:09:50 -08001504 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001505 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001506 for (i = 0; i < height; i++) {
1507 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001508 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001509 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001510 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001511 }
1512 if (height < ph0)
1513 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001514
Chris Blumecca8c4d2019-03-01 01:09:50 -08001515 for (i = 0; i < dinfo->num_components; i++) {
1516 compptr = &dinfo->comp_info[i];
1517 _tmpbuf[i] =
1518 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1519 compptr->v_samp_factor + 32);
1520 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001521 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001522 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1523 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001524 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001525 for (row = 0; row < compptr->v_samp_factor; row++) {
1526 unsigned char *_tmpbuf_aligned =
1527 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001528
Chris Blumecca8c4d2019-03-01 01:09:50 -08001529 tmpbuf[i][row] =
1530 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1531 }
1532 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1533 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1534 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1535 if (!inbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001536 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001537 ptr = (JSAMPLE *)srcPlanes[i];
1538 for (row = 0; row < ph[i]; row++) {
1539 inbuf[i][row] = ptr;
1540 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1541 }
1542 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001543
Chris Blumecca8c4d2019-03-01 01:09:50 -08001544 if (setjmp(this->jerr.setjmp_buffer)) {
1545 /* If we get here, the JPEG code has signaled an error. */
1546 retval = -1; goto bailout;
1547 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001548
Chris Blumecca8c4d2019-03-01 01:09:50 -08001549 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1550 JDIMENSION inrow = 0, outrow = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001551
Chris Blumecca8c4d2019-03-01 01:09:50 -08001552 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1553 i++, compptr++)
1554 jcopy_sample_rows(inbuf[i],
1555 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1556 compptr->v_samp_factor, pw[i]);
1557 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1558 dinfo->max_v_samp_factor, &row_pointer[row],
1559 &outrow, dinfo->max_v_samp_factor);
1560 }
1561 jpeg_abort_decompress(dinfo);
1562
1563bailout:
1564 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001565 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001566 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001567 free(tmpbuf[i]);
1568 free(_tmpbuf[i]);
1569 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001570 }
1571 if (this->jerr.warning) retval = -1;
1572 this->jerr.stopOnWarning = FALSE;
1573 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001574}
1575
Chris Blumecca8c4d2019-03-01 01:09:50 -08001576DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1577 int pad, int subsamp, unsigned char *dstBuf,
1578 int width, int pitch, int height, int pixelFormat,
1579 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001580{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001581 const unsigned char *srcPlanes[3];
1582 int pw0, ph0, strides[3], retval = -1;
1583 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001584
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001585 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001586 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001587
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001588 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
Chris Blumecca8c4d2019-03-01 01:09:50 -08001589 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001590 THROW("tjDecodeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001591
Chris Blumecca8c4d2019-03-01 01:09:50 -08001592 pw0 = tjPlaneWidth(0, width, subsamp);
1593 ph0 = tjPlaneHeight(0, height, subsamp);
1594 srcPlanes[0] = srcBuf;
1595 strides[0] = PAD(pw0, pad);
1596 if (subsamp == TJSAMP_GRAY) {
1597 strides[1] = strides[2] = 0;
1598 srcPlanes[1] = srcPlanes[2] = NULL;
1599 } else {
1600 int pw1 = tjPlaneWidth(1, width, subsamp);
1601 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001602
Chris Blumecca8c4d2019-03-01 01:09:50 -08001603 strides[1] = strides[2] = PAD(pw1, pad);
1604 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1605 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1606 }
1607
1608 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1609 pitch, height, pixelFormat, flags);
1610
1611bailout:
1612 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001613}
1614
Chris Blumecca8c4d2019-03-01 01:09:50 -08001615DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1616 const unsigned char *jpegBuf,
1617 unsigned long jpegSize,
1618 unsigned char **dstPlanes, int width,
1619 int *strides, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001620{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001621 int i, sfi, row, retval = 0;
1622 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1623 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1624 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1625 JSAMPLE *_tmpbuf = NULL, *ptr;
1626 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1627 int dctsize;
Jonathan Wright24e31052021-04-26 12:10:48 +01001628 struct my_progress_mgr progress;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001629
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001630 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001631 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001632
Chris Blumecca8c4d2019-03-01 01:09:50 -08001633 for (i = 0; i < MAX_COMPONENTS; i++) {
1634 tmpbuf[i] = NULL; outbuf[i] = NULL;
1635 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001636
Chris Blumecca8c4d2019-03-01 01:09:50 -08001637 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001638 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +00001639
Chris Blumecca8c4d2019-03-01 01:09:50 -08001640 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1641 width < 0 || height < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001642 THROW("tjDecompressToYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001643
Chris Blumecca8c4d2019-03-01 01:09:50 -08001644#ifndef NO_PUTENV
1645 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1646 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1647 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1648#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001649
Jonathan Wright24e31052021-04-26 12:10:48 +01001650 if (flags & TJFLAG_LIMITSCANS) {
1651 MEMZERO(&progress, sizeof(struct my_progress_mgr));
1652 progress.pub.progress_monitor = my_progress_monitor;
1653 progress.this = this;
1654 dinfo->progress = &progress.pub;
1655 } else
1656 dinfo->progress = NULL;
1657
Chris Blumecca8c4d2019-03-01 01:09:50 -08001658 if (setjmp(this->jerr.setjmp_buffer)) {
1659 /* If we get here, the JPEG code has signaled an error. */
1660 retval = -1; goto bailout;
1661 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001662
Chris Blumecca8c4d2019-03-01 01:09:50 -08001663 if (!this->headerRead) {
1664 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1665 jpeg_read_header(dinfo, TRUE);
1666 }
1667 this->headerRead = 0;
1668 jpegSubsamp = getSubsamp(dinfo);
1669 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001670 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001671
Chris Blumecca8c4d2019-03-01 01:09:50 -08001672 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001673 THROW("tjDecompressToYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001674
Chris Blumecca8c4d2019-03-01 01:09:50 -08001675 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1676 if (width == 0) width = jpegwidth;
1677 if (height == 0) height = jpegheight;
1678 for (i = 0; i < NUMSF; i++) {
1679 scaledw = TJSCALED(jpegwidth, sf[i]);
1680 scaledh = TJSCALED(jpegheight, sf[i]);
1681 if (scaledw <= width && scaledh <= height)
1682 break;
1683 }
1684 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001685 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001686 if (dinfo->num_components > 3)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001687 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001688
Chris Blumecca8c4d2019-03-01 01:09:50 -08001689 width = scaledw; height = scaledh;
1690 dinfo->scale_num = sf[i].num;
1691 dinfo->scale_denom = sf[i].denom;
1692 sfi = i;
1693 jpeg_calc_output_dimensions(dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001694
Chris Blumecca8c4d2019-03-01 01:09:50 -08001695 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001696
Chris Blumecca8c4d2019-03-01 01:09:50 -08001697 for (i = 0; i < dinfo->num_components; i++) {
1698 jpeg_component_info *compptr = &dinfo->comp_info[i];
1699 int ih;
hbono@chromium.org98626972011-08-03 03:13:08 +00001700
Chris Blumecca8c4d2019-03-01 01:09:50 -08001701 iw[i] = compptr->width_in_blocks * dctsize;
1702 ih = compptr->height_in_blocks * dctsize;
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001703 pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp);
1704 ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001705 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1706 th[i] = compptr->v_samp_factor * dctsize;
1707 tmpbufsize += iw[i] * th[i];
1708 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001709 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001710 ptr = dstPlanes[i];
1711 for (row = 0; row < ph[i]; row++) {
1712 outbuf[i][row] = ptr;
1713 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1714 }
1715 }
1716 if (usetmpbuf) {
1717 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001718 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001719 ptr = _tmpbuf;
1720 for (i = 0; i < dinfo->num_components; i++) {
1721 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001722 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001723 for (row = 0; row < th[i]; row++) {
1724 tmpbuf[i][row] = ptr;
1725 ptr += iw[i];
1726 }
1727 }
1728 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001729
Chris Blumecca8c4d2019-03-01 01:09:50 -08001730 if (setjmp(this->jerr.setjmp_buffer)) {
1731 /* If we get here, the JPEG code has signaled an error. */
1732 retval = -1; goto bailout;
1733 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001734
Chris Blumecca8c4d2019-03-01 01:09:50 -08001735 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1736 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1737 dinfo->raw_data_out = TRUE;
1738
1739 jpeg_start_decompress(dinfo);
1740 for (row = 0; row < (int)dinfo->output_height;
1741 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1742 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1743 int crow[MAX_COMPONENTS];
1744
1745 for (i = 0; i < dinfo->num_components; i++) {
1746 jpeg_component_info *compptr = &dinfo->comp_info[i];
1747
1748 if (jpegSubsamp == TJ_420) {
1749 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1750 to be clever and use the IDCT to perform upsampling on the U and V
1751 planes. For instance, if the output image is to be scaled by 1/2
1752 relative to the JPEG image, then the scaling factor and upsampling
1753 effectively cancel each other, so a normal 8x8 IDCT can be used.
1754 However, this is not desirable when using the decompress-to-YUV
1755 functionality in TurboJPEG, since we want to output the U and V
1756 planes in their subsampled form. Thus, we have to override some
1757 internal libjpeg parameters to force it to use the "scaled" IDCT
1758 functions on the U and V planes. */
1759 compptr->_DCT_scaled_size = dctsize;
1760 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1761 sf[sfi].num / sf[sfi].denom *
1762 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1763 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1764 }
1765 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1766 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1767 else yuvptr[i] = &outbuf[i][crow[i]];
1768 }
1769 jpeg_read_raw_data(dinfo, yuvptr,
1770 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1771 if (usetmpbuf) {
1772 int j;
1773
1774 for (i = 0; i < dinfo->num_components; i++) {
1775 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1776 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1777 }
1778 }
1779 }
1780 }
1781 jpeg_finish_decompress(dinfo);
1782
1783bailout:
1784 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1785 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001786 free(tmpbuf[i]);
1787 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001788 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001789 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001790 if (this->jerr.warning) retval = -1;
1791 this->jerr.stopOnWarning = FALSE;
1792 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001793}
1794
Chris Blumecca8c4d2019-03-01 01:09:50 -08001795DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1796 unsigned long jpegSize, unsigned char *dstBuf,
1797 int width, int pad, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001798{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001799 unsigned char *dstPlanes[3];
1800 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1801 int i, jpegwidth, jpegheight, scaledw, scaledh;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001802
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001803 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001804 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001805
Chris Blumecca8c4d2019-03-01 01:09:50 -08001806 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001807 pad < 1 || !IS_POW2(pad) || height < 0)
1808 THROW("tjDecompressToYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001809
Chris Blumecca8c4d2019-03-01 01:09:50 -08001810 if (setjmp(this->jerr.setjmp_buffer)) {
1811 /* If we get here, the JPEG code has signaled an error. */
1812 return -1;
1813 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001814
Chris Blumecca8c4d2019-03-01 01:09:50 -08001815 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1816 jpeg_read_header(dinfo, TRUE);
1817 jpegSubsamp = getSubsamp(dinfo);
1818 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001819 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001820
Chris Blumecca8c4d2019-03-01 01:09:50 -08001821 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1822 if (width == 0) width = jpegwidth;
1823 if (height == 0) height = jpegheight;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001824
Chris Blumecca8c4d2019-03-01 01:09:50 -08001825 for (i = 0; i < NUMSF; i++) {
1826 scaledw = TJSCALED(jpegwidth, sf[i]);
1827 scaledh = TJSCALED(jpegheight, sf[i]);
1828 if (scaledw <= width && scaledh <= height)
1829 break;
1830 }
1831 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001832 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001833
Chris Blumecca8c4d2019-03-01 01:09:50 -08001834 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1835 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1836 dstPlanes[0] = dstBuf;
1837 strides[0] = PAD(pw0, pad);
1838 if (jpegSubsamp == TJSAMP_GRAY) {
1839 strides[1] = strides[2] = 0;
1840 dstPlanes[1] = dstPlanes[2] = NULL;
1841 } else {
1842 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1843 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001844
Chris Blumecca8c4d2019-03-01 01:09:50 -08001845 strides[1] = strides[2] = PAD(pw1, pad);
1846 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1847 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1848 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001849
Chris Blumecca8c4d2019-03-01 01:09:50 -08001850 this->headerRead = 1;
1851 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1852 strides, height, flags);
1853
1854bailout:
1855 this->jerr.stopOnWarning = FALSE;
1856 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001857}
1858
Chris Blumecca8c4d2019-03-01 01:09:50 -08001859DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1860 unsigned long jpegSize, unsigned char *dstBuf,
1861 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001862{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001863 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001864}
1865
hbono@chromium.org98626972011-08-03 03:13:08 +00001866
1867/* Transformer */
1868
Chris Blumecca8c4d2019-03-01 01:09:50 -08001869DLLEXPORT tjhandle tjInitTransform(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001870{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001871 tjinstance *this = NULL;
1872 tjhandle handle = NULL;
1873
1874 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1875 snprintf(errStr, JMSG_LENGTH_MAX,
1876 "tjInitTransform(): Memory allocation failure");
1877 return NULL;
1878 }
1879 MEMZERO(this, sizeof(tjinstance));
1880 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1881 handle = _tjInitCompress(this);
1882 if (!handle) return NULL;
1883 handle = _tjInitDecompress(this);
1884 return handle;
hbono@chromium.org98626972011-08-03 03:13:08 +00001885}
1886
1887
Chris Blumecca8c4d2019-03-01 01:09:50 -08001888DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1889 unsigned long jpegSize, int n,
1890 unsigned char **dstBufs, unsigned long *dstSizes,
1891 tjtransform *t, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001892{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001893 jpeg_transform_info *xinfo = NULL;
1894 jvirt_barray_ptr *srccoefs, *dstcoefs;
Jonathan Wright24e31052021-04-26 12:10:48 +01001895 int retval = 0, alloc = 1, i, jpegSubsamp, saveMarkers = 0;
1896 struct my_progress_mgr progress;
hbono@chromium.org98626972011-08-03 03:13:08 +00001897
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001898 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001899 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1900 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001901 THROW("tjTransform(): Instance has not been initialized for transformation");
hbono@chromium.org98626972011-08-03 03:13:08 +00001902
Chris Blumecca8c4d2019-03-01 01:09:50 -08001903 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1904 dstSizes == NULL || t == NULL || flags < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001905 THROW("tjTransform(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001906
Chris Blumecca8c4d2019-03-01 01:09:50 -08001907#ifndef NO_PUTENV
1908 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1909 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1910 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1911#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001912
Jonathan Wright24e31052021-04-26 12:10:48 +01001913 if (flags & TJFLAG_LIMITSCANS) {
1914 MEMZERO(&progress, sizeof(struct my_progress_mgr));
1915 progress.pub.progress_monitor = my_progress_monitor;
1916 progress.this = this;
1917 dinfo->progress = &progress.pub;
1918 } else
1919 dinfo->progress = NULL;
1920
Chris Blumecca8c4d2019-03-01 01:09:50 -08001921 if ((xinfo =
1922 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001923 THROW("tjTransform(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001924 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
hbono@chromium.org98626972011-08-03 03:13:08 +00001925
Chris Blumecca8c4d2019-03-01 01:09:50 -08001926 if (setjmp(this->jerr.setjmp_buffer)) {
1927 /* If we get here, the JPEG code has signaled an error. */
1928 retval = -1; goto bailout;
1929 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001930
Chris Blumecca8c4d2019-03-01 01:09:50 -08001931 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
hbono@chromium.org98626972011-08-03 03:13:08 +00001932
Chris Blumecca8c4d2019-03-01 01:09:50 -08001933 for (i = 0; i < n; i++) {
1934 xinfo[i].transform = xformtypes[t[i].op];
1935 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1936 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1937 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1938 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1939 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1940 else xinfo[i].slow_hflip = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001941
Chris Blumecca8c4d2019-03-01 01:09:50 -08001942 if (xinfo[i].crop) {
1943 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1944 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1945 if (t[i].r.w != 0) {
1946 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1947 } else
1948 xinfo[i].crop_width = JCROP_UNSET;
1949 if (t[i].r.h != 0) {
1950 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1951 } else
1952 xinfo[i].crop_height = JCROP_UNSET;
1953 }
1954 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1955 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001956
Chris Blumecca8c4d2019-03-01 01:09:50 -08001957 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1958 jpeg_read_header(dinfo, TRUE);
1959 jpegSubsamp = getSubsamp(dinfo);
1960 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001961 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
hbono@chromium.org98626972011-08-03 03:13:08 +00001962
Chris Blumecca8c4d2019-03-01 01:09:50 -08001963 for (i = 0; i < n; i++) {
1964 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001965 THROW("tjTransform(): Transform is not perfect");
hbono@chromium.org98626972011-08-03 03:13:08 +00001966
Chris Blumecca8c4d2019-03-01 01:09:50 -08001967 if (xinfo[i].crop) {
1968 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1969 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001970 snprintf(this->errStr, JMSG_LENGTH_MAX,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001971 "To crop this JPEG image, x must be a multiple of %d\n"
1972 "and y must be a multiple of %d.\n",
1973 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001974 this->isInstanceError = TRUE;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001975 retval = -1; goto bailout;
1976 }
1977 }
1978 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001979
Chris Blumecca8c4d2019-03-01 01:09:50 -08001980 srccoefs = jpeg_read_coefficients(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001981
Chris Blumecca8c4d2019-03-01 01:09:50 -08001982 for (i = 0; i < n; i++) {
Jonathan Wright24e31052021-04-26 12:10:48 +01001983 int w, h;
hbono@chromium.org98626972011-08-03 03:13:08 +00001984
Chris Blumecca8c4d2019-03-01 01:09:50 -08001985 if (!xinfo[i].crop) {
1986 w = dinfo->image_width; h = dinfo->image_height;
1987 } else {
1988 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1989 }
1990 if (flags & TJFLAG_NOREALLOC) {
1991 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1992 }
1993 if (!(t[i].options & TJXOPT_NOOUTPUT))
1994 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1995 jpeg_copy_critical_parameters(dinfo, cinfo);
1996 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1997 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1998 jpeg_simple_progression(cinfo);
1999 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
2000 jpeg_write_coefficients(cinfo, dstcoefs);
2001 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
2002 JCOPYOPT_NONE : JCOPYOPT_ALL);
2003 } else
2004 jinit_c_master_control(cinfo, TRUE);
2005 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
2006 if (t[i].customFilter) {
2007 int ci, y;
2008 JDIMENSION by;
hbono@chromium.org98626972011-08-03 03:13:08 +00002009
Chris Blumecca8c4d2019-03-01 01:09:50 -08002010 for (ci = 0; ci < cinfo->num_components; ci++) {
2011 jpeg_component_info *compptr = &cinfo->comp_info[ci];
2012 tjregion arrayRegion = {
2013 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
2014 };
2015 tjregion planeRegion = {
2016 0, 0, compptr->width_in_blocks * DCTSIZE,
2017 compptr->height_in_blocks * DCTSIZE
2018 };
2019
2020 for (by = 0; by < compptr->height_in_blocks;
2021 by += compptr->v_samp_factor) {
2022 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
2023 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
2024 TRUE);
2025
2026 for (y = 0; y < compptr->v_samp_factor; y++) {
2027 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
2028 i, &t[i]) == -1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002029 THROW("tjTransform(): Error in custom filter");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002030 arrayRegion.y += DCTSIZE;
2031 }
2032 }
2033 }
2034 }
2035 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
2036 }
2037
2038 jpeg_finish_decompress(dinfo);
2039
2040bailout:
Jonathan Wright24e31052021-04-26 12:10:48 +01002041 if (cinfo->global_state > CSTATE_START) {
2042 if (alloc) (*cinfo->dest->term_destination) (cinfo);
2043 jpeg_abort_compress(cinfo);
2044 }
Chris Blumecca8c4d2019-03-01 01:09:50 -08002045 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002046 free(xinfo);
Chris Blumecca8c4d2019-03-01 01:09:50 -08002047 if (this->jerr.warning) retval = -1;
2048 this->jerr.stopOnWarning = FALSE;
2049 return retval;
2050}
2051
2052
2053DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
2054 int align, int *height, int *pixelFormat,
2055 int flags)
2056{
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002057 int retval = 0, tempc;
2058 size_t pitch;
Chris Blumecca8c4d2019-03-01 01:09:50 -08002059 tjhandle handle = NULL;
2060 tjinstance *this;
2061 j_compress_ptr cinfo = NULL;
2062 cjpeg_source_ptr src;
2063 unsigned char *dstBuf = NULL;
2064 FILE *file = NULL;
2065 boolean invert;
2066
2067 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2068 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002069 THROWG("tjLoadImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002070 if ((align & (align - 1)) != 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002071 THROWG("tjLoadImage(): Alignment must be a power of 2");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002072
2073 if ((handle = tjInitCompress()) == NULL) return NULL;
2074 this = (tjinstance *)handle;
2075 cinfo = &this->cinfo;
2076
2077 if ((file = fopen(filename, "rb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002078 THROW_UNIX("tjLoadImage(): Cannot open input file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002079
2080 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002081 THROW_UNIX("tjLoadImage(): Could not read input file")
Chris Blumecca8c4d2019-03-01 01:09:50 -08002082 else if (tempc == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002083 THROWG("tjLoadImage(): Input file contains no data");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002084
2085 if (setjmp(this->jerr.setjmp_buffer)) {
2086 /* If we get here, the JPEG code has signaled an error. */
2087 retval = -1; goto bailout;
2088 }
2089
2090 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2091 else cinfo->in_color_space = pf2cs[*pixelFormat];
2092 if (tempc == 'B') {
2093 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002094 THROWG("tjLoadImage(): Could not initialize bitmap loader");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002095 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2096 } else if (tempc == 'P') {
2097 if ((src = jinit_read_ppm(cinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002098 THROWG("tjLoadImage(): Could not initialize bitmap loader");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002099 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2100 } else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002101 THROWG("tjLoadImage(): Unsupported file type");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002102
2103 src->input_file = file;
Jonathan Wright24e31052021-04-26 12:10:48 +01002104#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2105 /* Refuse to load images larger than 1 Megapixel when fuzzing. */
2106 if (flags & TJFLAG_FUZZING)
2107 src->max_pixels = 1048576;
2108#endif
Chris Blumecca8c4d2019-03-01 01:09:50 -08002109 (*src->start_input) (cinfo, src);
2110 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
2111
2112 *width = cinfo->image_width; *height = cinfo->image_height;
2113 *pixelFormat = cs2pf[cinfo->in_color_space];
2114
2115 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002116 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2117 (unsigned long long)((size_t)-1) ||
2118 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2119 THROWG("tjLoadImage(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002120
2121 if (setjmp(this->jerr.setjmp_buffer)) {
2122 /* If we get here, the JPEG code has signaled an error. */
2123 retval = -1; goto bailout;
2124 }
2125
2126 while (cinfo->next_scanline < cinfo->image_height) {
2127 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
2128
2129 for (i = 0; i < nlines; i++) {
2130 unsigned char *dstptr;
2131 int row;
2132
2133 row = cinfo->next_scanline + i;
2134 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2135 else dstptr = &dstBuf[row * pitch];
2136 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2137 }
2138 cinfo->next_scanline += nlines;
2139 }
2140
2141 (*src->finish_input) (cinfo, src);
2142
2143bailout:
2144 if (handle) tjDestroy(handle);
2145 if (file) fclose(file);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002146 if (retval < 0) { free(dstBuf); dstBuf = NULL; }
Chris Blumecca8c4d2019-03-01 01:09:50 -08002147 return dstBuf;
2148}
2149
2150
2151DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2152 int width, int pitch, int height, int pixelFormat,
2153 int flags)
2154{
2155 int retval = 0;
2156 tjhandle handle = NULL;
2157 tjinstance *this;
2158 j_decompress_ptr dinfo = NULL;
2159 djpeg_dest_ptr dst;
2160 FILE *file = NULL;
2161 char *ptr = NULL;
2162 boolean invert;
2163
2164 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2165 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002166 THROWG("tjSaveImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002167
2168 if ((handle = tjInitDecompress()) == NULL)
2169 return -1;
2170 this = (tjinstance *)handle;
2171 dinfo = &this->dinfo;
2172
2173 if ((file = fopen(filename, "wb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002174 THROW_UNIX("tjSaveImage(): Cannot open output file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002175
2176 if (setjmp(this->jerr.setjmp_buffer)) {
2177 /* If we get here, the JPEG code has signaled an error. */
2178 retval = -1; goto bailout;
2179 }
2180
2181 this->dinfo.out_color_space = pf2cs[pixelFormat];
2182 dinfo->image_width = width; dinfo->image_height = height;
2183 dinfo->global_state = DSTATE_READY;
2184 dinfo->scale_num = dinfo->scale_denom = 1;
2185
2186 ptr = strrchr(filename, '.');
2187 if (ptr && !strcasecmp(ptr, ".bmp")) {
2188 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002189 THROWG("tjSaveImage(): Could not initialize bitmap writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002190 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2191 } else {
2192 if ((dst = jinit_write_ppm(dinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002193 THROWG("tjSaveImage(): Could not initialize PPM writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002194 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2195 }
2196
2197 dst->output_file = file;
2198 (*dst->start_output) (dinfo, dst);
2199 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
2200
2201 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2202
2203 while (dinfo->output_scanline < dinfo->output_height) {
2204 unsigned char *rowptr;
2205
2206 if (invert)
2207 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2208 else
2209 rowptr = &buffer[dinfo->output_scanline * pitch];
2210 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2211 (*dst->put_pixel_rows) (dinfo, dst, 1);
2212 dinfo->output_scanline++;
2213 }
2214
2215 (*dst->finish_output) (dinfo, dst);
2216
2217bailout:
2218 if (handle) tjDestroy(handle);
2219 if (file) fclose(file);
2220 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00002221}