blob: 8260555b51e74367094ee4c4956cd46e7552a8ad [file] [log] [blame]
hbono@chromium.org98626972011-08-03 03:13:08 +00001/*
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002 * Copyright (C)2009-2020 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
Chris Blumecca8c4d2019-03-01 01:09:50 -0800115static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
hbono@chromium.org98626972011-08-03 03:13:08 +0000116
Chris Blumecca8c4d2019-03-01 01:09:50 -0800117static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
118 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
119 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
hbono@chromium.org98626972011-08-03 03:13:08 +0000120};
121
Chris Blumecca8c4d2019-03-01 01:09:50 -0800122#define NUMSF 16
123static const tjscalingfactor sf[NUMSF] = {
124 { 2, 1 },
125 { 15, 8 },
126 { 7, 4 },
127 { 13, 8 },
128 { 3, 2 },
129 { 11, 8 },
130 { 5, 4 },
131 { 9, 8 },
132 { 1, 1 },
133 { 7, 8 },
134 { 3, 4 },
135 { 5, 8 },
136 { 1, 2 },
137 { 3, 8 },
138 { 1, 4 },
139 { 1, 8 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000140};
141
Chris Blumecca8c4d2019-03-01 01:09:50 -0800142static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
143 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
144 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
145 JCS_EXT_ARGB, JCS_CMYK
146};
147
148static int cs2pf[JPEG_NUMCS] = {
149 TJPF_UNKNOWN, TJPF_GRAY,
150#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
151 TJPF_RGB,
152#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
153 TJPF_BGR,
154#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
155 TJPF_RGBX,
156#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
157 TJPF_BGRX,
158#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
159 TJPF_XBGR,
160#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
161 TJPF_XRGB,
162#endif
163 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
164 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
165 TJPF_UNKNOWN
166};
167
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100168#define THROWG(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800169 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
170 retval = -1; goto bailout; \
171}
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100172#define THROW_UNIX(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800173 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
174 retval = -1; goto bailout; \
175}
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100176#define THROW(m) { \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800177 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100178 this->isInstanceError = TRUE; THROWG(m) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800179}
180
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100181#define GET_INSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800182 tjinstance *this = (tjinstance *)handle; \
183 j_compress_ptr cinfo = NULL; \
184 j_decompress_ptr dinfo = NULL; \
185 \
186 if (!this) { \
187 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
188 return -1; \
189 } \
190 cinfo = &this->cinfo; dinfo = &this->dinfo; \
191 this->jerr.warning = FALSE; \
192 this->isInstanceError = FALSE;
193
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100194#define GET_CINSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800195 tjinstance *this = (tjinstance *)handle; \
196 j_compress_ptr cinfo = NULL; \
197 \
198 if (!this) { \
199 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
200 return -1; \
201 } \
202 cinfo = &this->cinfo; \
203 this->jerr.warning = FALSE; \
204 this->isInstanceError = FALSE;
205
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100206#define GET_DINSTANCE(handle) \
Chris Blumecca8c4d2019-03-01 01:09:50 -0800207 tjinstance *this = (tjinstance *)handle; \
208 j_decompress_ptr dinfo = NULL; \
209 \
210 if (!this) { \
211 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
212 return -1; \
213 } \
214 dinfo = &this->dinfo; \
215 this->jerr.warning = FALSE; \
216 this->isInstanceError = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000217
218static int getPixelFormat(int pixelSize, int flags)
219{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800220 if (pixelSize == 1) return TJPF_GRAY;
221 if (pixelSize == 3) {
222 if (flags & TJ_BGR) return TJPF_BGR;
223 else return TJPF_RGB;
224 }
225 if (pixelSize == 4) {
226 if (flags & TJ_ALPHAFIRST) {
227 if (flags & TJ_BGR) return TJPF_XBGR;
228 else return TJPF_XRGB;
229 } else {
230 if (flags & TJ_BGR) return TJPF_BGRX;
231 else return TJPF_RGBX;
232 }
233 }
234 return -1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000235}
236
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000237static void setCompDefaults(struct jpeg_compress_struct *cinfo,
238 int pixelFormat, int subsamp, int jpegQual,
239 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000240{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100241#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800242 char *env = NULL;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100243#endif
hbono@chromium.orgc6beb742011-11-29 05:16:26 +0000244
Chris Blumecca8c4d2019-03-01 01:09:50 -0800245 cinfo->in_color_space = pf2cs[pixelFormat];
246 cinfo->input_components = tjPixelSize[pixelFormat];
247 jpeg_set_defaults(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400248
249#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800250 if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
251 !strcmp(env, "1"))
252 cinfo->optimize_coding = TRUE;
253 if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
254 !strcmp(env, "1"))
255 cinfo->arith_code = TRUE;
256 if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
257 int temp = -1;
258 char tempc = 0;
259
260 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
261 temp <= 65535) {
262 if (toupper(tempc) == 'B') {
263 cinfo->restart_interval = temp;
264 cinfo->restart_in_rows = 0;
265 } else
266 cinfo->restart_in_rows = temp;
267 }
268 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400269#endif
270
Chris Blumecca8c4d2019-03-01 01:09:50 -0800271 if (jpegQual >= 0) {
272 jpeg_set_quality(cinfo, jpegQual, TRUE);
273 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
274 cinfo->dct_method = JDCT_ISLOW;
275 else
276 cinfo->dct_method = JDCT_FASTEST;
277 }
278 if (subsamp == TJSAMP_GRAY)
279 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
280 else if (pixelFormat == TJPF_CMYK)
281 jpeg_set_colorspace(cinfo, JCS_YCCK);
282 else
283 jpeg_set_colorspace(cinfo, JCS_YCbCr);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400284
Chris Blumecca8c4d2019-03-01 01:09:50 -0800285 if (flags & TJFLAG_PROGRESSIVE)
286 jpeg_simple_progression(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400287#ifndef NO_GETENV
Chris Blumecca8c4d2019-03-01 01:09:50 -0800288 else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
289 !strcmp(env, "1"))
290 jpeg_simple_progression(cinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400291#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000292
Chris Blumecca8c4d2019-03-01 01:09:50 -0800293 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
294 cinfo->comp_info[1].h_samp_factor = 1;
295 cinfo->comp_info[2].h_samp_factor = 1;
296 if (cinfo->num_components > 3)
297 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
298 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
299 cinfo->comp_info[1].v_samp_factor = 1;
300 cinfo->comp_info[2].v_samp_factor = 1;
301 if (cinfo->num_components > 3)
302 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
hbono@chromium.org98626972011-08-03 03:13:08 +0000303}
304
305
306static int getSubsamp(j_decompress_ptr dinfo)
307{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800308 int retval = -1, i, k;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400309
Chris Blumecca8c4d2019-03-01 01:09:50 -0800310 /* The sampling factors actually have no meaning with grayscale JPEG files,
311 and in fact it's possible to generate grayscale JPEGs with sampling
312 factors > 1 (even though those sampling factors are ignored by the
313 decompressor.) Thus, we need to treat grayscale as a special case. */
314 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
315 return TJSAMP_GRAY;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400316
Chris Blumecca8c4d2019-03-01 01:09:50 -0800317 for (i = 0; i < NUMSUBOPT; i++) {
318 if (dinfo->num_components == pixelsize[i] ||
319 ((dinfo->jpeg_color_space == JCS_YCCK ||
320 dinfo->jpeg_color_space == JCS_CMYK) &&
321 pixelsize[i] == 3 && dinfo->num_components == 4)) {
322 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
323 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
324 int match = 0;
325
326 for (k = 1; k < dinfo->num_components; k++) {
327 int href = 1, vref = 1;
328
329 if ((dinfo->jpeg_color_space == JCS_YCCK ||
330 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
331 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
332 }
333 if (dinfo->comp_info[k].h_samp_factor == href &&
334 dinfo->comp_info[k].v_samp_factor == vref)
335 match++;
336 }
337 if (match == dinfo->num_components - 1) {
338 retval = i; break;
339 }
340 }
341 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
342 in non-standard ways. */
343 if (dinfo->comp_info[0].h_samp_factor == 2 &&
344 dinfo->comp_info[0].v_samp_factor == 2 &&
345 (i == TJSAMP_422 || i == TJSAMP_440)) {
346 int match = 0;
347
348 for (k = 1; k < dinfo->num_components; k++) {
349 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
350
351 if ((dinfo->jpeg_color_space == JCS_YCCK ||
352 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
353 href = vref = 2;
354 }
355 if (dinfo->comp_info[k].h_samp_factor == href &&
356 dinfo->comp_info[k].v_samp_factor == vref)
357 match++;
358 }
359 if (match == dinfo->num_components - 1) {
360 retval = i; break;
361 }
362 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100363 /* Handle 4:4:4 images whose sampling factors are specified in
364 non-standard ways. */
365 if (dinfo->comp_info[0].h_samp_factor *
366 dinfo->comp_info[0].v_samp_factor <=
367 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
368 int match = 0;
369 for (k = 1; k < dinfo->num_components; k++) {
370 if (dinfo->comp_info[k].h_samp_factor ==
371 dinfo->comp_info[0].h_samp_factor &&
372 dinfo->comp_info[k].v_samp_factor ==
373 dinfo->comp_info[0].v_samp_factor)
374 match++;
375 if (match == dinfo->num_components - 1) {
376 retval = i; break;
377 }
378 }
379 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800380 }
381 }
382 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000383}
384
385
386/* General API functions */
387
Chris Blumecca8c4d2019-03-01 01:09:50 -0800388DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
hbono@chromium.org98626972011-08-03 03:13:08 +0000389{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800390 tjinstance *this = (tjinstance *)handle;
391
392 if (this && this->isInstanceError) {
393 this->isInstanceError = FALSE;
394 return this->errStr;
395 } else
396 return errStr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000397}
398
399
Chris Blumecca8c4d2019-03-01 01:09:50 -0800400DLLEXPORT char *tjGetErrorStr(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000401{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800402 return errStr;
403}
404
405
406DLLEXPORT int tjGetErrorCode(tjhandle handle)
407{
408 tjinstance *this = (tjinstance *)handle;
409
410 if (this && this->jerr.warning) return TJERR_WARNING;
411 else return TJERR_FATAL;
412}
413
414
415DLLEXPORT int tjDestroy(tjhandle handle)
416{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100417 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800418
419 if (setjmp(this->jerr.setjmp_buffer)) return -1;
420 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
421 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
422 free(this);
423 return 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000424}
425
426
427/* These are exposed mainly because Windows can't malloc() and free() across
428 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
429 with turbojpeg.dll for compatibility reasons. However, these functions
430 can potentially be used for other purposes by different implementations. */
431
Chris Blumecca8c4d2019-03-01 01:09:50 -0800432DLLEXPORT void tjFree(unsigned char *buf)
hbono@chromium.org98626972011-08-03 03:13:08 +0000433{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100434 free(buf);
hbono@chromium.org98626972011-08-03 03:13:08 +0000435}
436
437
Chris Blumecca8c4d2019-03-01 01:09:50 -0800438DLLEXPORT unsigned char *tjAlloc(int bytes)
hbono@chromium.org98626972011-08-03 03:13:08 +0000439{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800440 return (unsigned char *)malloc(bytes);
hbono@chromium.org98626972011-08-03 03:13:08 +0000441}
442
443
444/* Compressor */
445
446static tjhandle _tjInitCompress(tjinstance *this)
447{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800448 static unsigned char buffer[1];
449 unsigned char *buf = buffer;
450 unsigned long size = 1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000451
Chris Blumecca8c4d2019-03-01 01:09:50 -0800452 /* This is also straight out of example.txt */
453 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
454 this->jerr.pub.error_exit = my_error_exit;
455 this->jerr.pub.output_message = my_output_message;
456 this->jerr.emit_message = this->jerr.pub.emit_message;
457 this->jerr.pub.emit_message = my_emit_message;
458 this->jerr.pub.addon_message_table = turbojpeg_message_table;
459 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
460 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000461
Chris Blumecca8c4d2019-03-01 01:09:50 -0800462 if (setjmp(this->jerr.setjmp_buffer)) {
463 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100464 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800465 return NULL;
466 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000467
Chris Blumecca8c4d2019-03-01 01:09:50 -0800468 jpeg_create_compress(&this->cinfo);
469 /* Make an initial call so it will create the destination manager */
470 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
hbono@chromium.org98626972011-08-03 03:13:08 +0000471
Chris Blumecca8c4d2019-03-01 01:09:50 -0800472 this->init |= COMPRESS;
473 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +0000474}
475
Chris Blumecca8c4d2019-03-01 01:09:50 -0800476DLLEXPORT tjhandle tjInitCompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000477{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800478 tjinstance *this = NULL;
479
480 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
481 snprintf(errStr, JMSG_LENGTH_MAX,
482 "tjInitCompress(): Memory allocation failure");
483 return NULL;
484 }
485 MEMZERO(this, sizeof(tjinstance));
486 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
487 return _tjInitCompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +0000488}
489
490
Chris Blumecca8c4d2019-03-01 01:09:50 -0800491DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000492{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100493 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800494 int mcuw, mcuh, chromasf;
hbono@chromium.org98626972011-08-03 03:13:08 +0000495
Chris Blumecca8c4d2019-03-01 01:09:50 -0800496 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100497 THROWG("tjBufSize(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000498
Chris Blumecca8c4d2019-03-01 01:09:50 -0800499 /* This allows for rare corner cases in which a JPEG image can actually be
500 larger than the uncompressed input (we wouldn't mention it if it hadn't
501 happened before.) */
502 mcuw = tjMCUWidth[jpegSubsamp];
503 mcuh = tjMCUHeight[jpegSubsamp];
504 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100505 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
506 if (retval > (unsigned long long)((unsigned long)-1))
507 THROWG("tjBufSize(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800508
509bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100510 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000511}
512
Chris Blumecca8c4d2019-03-01 01:09:50 -0800513DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
hbono@chromium.org98626972011-08-03 03:13:08 +0000514{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100515 unsigned long long retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000516
Chris Blumecca8c4d2019-03-01 01:09:50 -0800517 if (width < 1 || height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100518 THROWG("TJBUFSIZE(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000519
Chris Blumecca8c4d2019-03-01 01:09:50 -0800520 /* This allows for rare corner cases in which a JPEG image can actually be
521 larger than the uncompressed input (we wouldn't mention it if it hadn't
522 happened before.) */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100523 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
524 if (retval > (unsigned long long)((unsigned long)-1))
525 THROWG("TJBUFSIZE(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800526
527bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100528 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000529}
530
531
Chris Blumecca8c4d2019-03-01 01:09:50 -0800532DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
533 int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000534{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100535 unsigned long long retval = 0;
536 int nc, i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400537
Chris Blumecca8c4d2019-03-01 01:09:50 -0800538 if (subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100539 THROWG("tjBufSizeYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400540
Chris Blumecca8c4d2019-03-01 01:09:50 -0800541 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
542 for (i = 0; i < nc; i++) {
543 int pw = tjPlaneWidth(i, width, subsamp);
544 int stride = PAD(pw, pad);
545 int ph = tjPlaneHeight(i, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000546
Chris Blumecca8c4d2019-03-01 01:09:50 -0800547 if (pw < 0 || ph < 0) return -1;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100548 else retval += (unsigned long long)stride * ph;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800549 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100550 if (retval > (unsigned long long)((unsigned long)-1))
551 THROWG("tjBufSizeYUV2(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800552
553bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100554 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000555}
556
Chris Blumecca8c4d2019-03-01 01:09:50 -0800557DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400558{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800559 return tjBufSizeYUV2(width, 4, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400560}
hbono@chromium.org98626972011-08-03 03:13:08 +0000561
Chris Blumecca8c4d2019-03-01 01:09:50 -0800562DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000563{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800564 return tjBufSizeYUV(width, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000565}
566
567
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400568DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
569{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800570 int pw, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400571
Chris Blumecca8c4d2019-03-01 01:09:50 -0800572 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100573 THROWG("tjPlaneWidth(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800574 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
575 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100576 THROWG("tjPlaneWidth(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400577
Chris Blumecca8c4d2019-03-01 01:09:50 -0800578 pw = PAD(width, tjMCUWidth[subsamp] / 8);
579 if (componentID == 0)
580 retval = pw;
581 else
582 retval = pw * 8 / tjMCUWidth[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400583
Chris Blumecca8c4d2019-03-01 01:09:50 -0800584bailout:
585 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400586}
587
588
589DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
590{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800591 int ph, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400592
Chris Blumecca8c4d2019-03-01 01:09:50 -0800593 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100594 THROWG("tjPlaneHeight(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800595 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
596 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100597 THROWG("tjPlaneHeight(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400598
Chris Blumecca8c4d2019-03-01 01:09:50 -0800599 ph = PAD(height, tjMCUHeight[subsamp] / 8);
600 if (componentID == 0)
601 retval = ph;
602 else
603 retval = ph * 8 / tjMCUHeight[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400604
Chris Blumecca8c4d2019-03-01 01:09:50 -0800605bailout:
606 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400607}
608
609
Chris Blumecca8c4d2019-03-01 01:09:50 -0800610DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
611 int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400612{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100613 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800614 int pw, ph;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400615
Chris Blumecca8c4d2019-03-01 01:09:50 -0800616 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100617 THROWG("tjPlaneSizeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400618
Chris Blumecca8c4d2019-03-01 01:09:50 -0800619 pw = tjPlaneWidth(componentID, width, subsamp);
620 ph = tjPlaneHeight(componentID, height, subsamp);
621 if (pw < 0 || ph < 0) return -1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400622
Chris Blumecca8c4d2019-03-01 01:09:50 -0800623 if (stride == 0) stride = pw;
624 else stride = abs(stride);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400625
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100626 retval = (unsigned long long)stride * (ph - 1) + pw;
627 if (retval > (unsigned long long)((unsigned long)-1))
628 THROWG("tjPlaneSizeYUV(): Image is too large");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400629
Chris Blumecca8c4d2019-03-01 01:09:50 -0800630bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100631 return (unsigned long)retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400632}
633
634
Chris Blumecca8c4d2019-03-01 01:09:50 -0800635DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
636 int width, int pitch, int height, int pixelFormat,
637 unsigned char **jpegBuf, unsigned long *jpegSize,
638 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000639{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800640 int i, retval = 0, alloc = 1;
641 JSAMPROW *row_pointer = NULL;
hbono@chromium.org98626972011-08-03 03:13:08 +0000642
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100643 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800644 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
645 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100646 THROW("tjCompress2(): Instance has not been initialized for compression");
hbono@chromium.org98626972011-08-03 03:13:08 +0000647
Chris Blumecca8c4d2019-03-01 01:09:50 -0800648 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
649 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
650 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
651 jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100652 THROW("tjCompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000653
Chris Blumecca8c4d2019-03-01 01:09:50 -0800654 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +0000655
Chris Blumecca8c4d2019-03-01 01:09:50 -0800656 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100657 THROW("tjCompress2(): Memory allocation failure");
hbono@chromium.org98626972011-08-03 03:13:08 +0000658
Chris Blumecca8c4d2019-03-01 01:09:50 -0800659 if (setjmp(this->jerr.setjmp_buffer)) {
660 /* If we get here, the JPEG code has signaled an error. */
661 retval = -1; goto bailout;
662 }
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000663
Chris Blumecca8c4d2019-03-01 01:09:50 -0800664 cinfo->image_width = width;
665 cinfo->image_height = height;
hbono@chromium.org98626972011-08-03 03:13:08 +0000666
Chris Blumecca8c4d2019-03-01 01:09:50 -0800667#ifndef NO_PUTENV
668 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
669 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
670 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
671#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000672
Chris Blumecca8c4d2019-03-01 01:09:50 -0800673 if (flags & TJFLAG_NOREALLOC) {
674 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
675 }
676 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000677 setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000678
Chris Blumecca8c4d2019-03-01 01:09:50 -0800679 jpeg_start_compress(cinfo, TRUE);
680 for (i = 0; i < height; i++) {
681 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100682 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800683 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100684 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800685 }
686 while (cinfo->next_scanline < cinfo->image_height)
687 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
688 cinfo->image_height - cinfo->next_scanline);
689 jpeg_finish_compress(cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000690
Chris Blumecca8c4d2019-03-01 01:09:50 -0800691bailout:
692 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100693 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800694 if (this->jerr.warning) retval = -1;
695 this->jerr.stopOnWarning = FALSE;
696 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000697}
698
Chris Blumecca8c4d2019-03-01 01:09:50 -0800699DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
700 int pitch, int height, int pixelSize,
701 unsigned char *jpegBuf, unsigned long *jpegSize,
702 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000703{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800704 int retval = 0;
705 unsigned long size;
706
707 if (flags & TJ_YUV) {
708 size = tjBufSizeYUV(width, height, jpegSubsamp);
709 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
710 getPixelFormat(pixelSize, flags), jpegBuf,
711 jpegSubsamp, flags);
712 } else {
713 retval = tjCompress2(handle, srcBuf, width, pitch, height,
714 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
715 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
716 }
717 *jpegSize = size;
718 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000719}
720
721
Chris Blumecca8c4d2019-03-01 01:09:50 -0800722DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
723 int width, int pitch, int height,
724 int pixelFormat, unsigned char **dstPlanes,
725 int *strides, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000726{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800727 JSAMPROW *row_pointer = NULL;
728 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
729 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
730 JSAMPROW *outbuf[MAX_COMPONENTS];
731 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
732 JSAMPLE *ptr;
733 jpeg_component_info *compptr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000734
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100735 GET_CINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800736 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000737
Chris Blumecca8c4d2019-03-01 01:09:50 -0800738 for (i = 0; i < MAX_COMPONENTS; i++) {
739 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
740 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
741 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000742
Chris Blumecca8c4d2019-03-01 01:09:50 -0800743 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100744 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000745
Chris Blumecca8c4d2019-03-01 01:09:50 -0800746 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
747 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
748 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100749 THROW("tjEncodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800750 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100751 THROW("tjEncodeYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000752
Chris Blumecca8c4d2019-03-01 01:09:50 -0800753 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100754 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
hbono@chromium.org98626972011-08-03 03:13:08 +0000755
Chris Blumecca8c4d2019-03-01 01:09:50 -0800756 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400757
Chris Blumecca8c4d2019-03-01 01:09:50 -0800758 if (setjmp(this->jerr.setjmp_buffer)) {
759 /* If we get here, the JPEG code has signaled an error. */
760 retval = -1; goto bailout;
761 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000762
Chris Blumecca8c4d2019-03-01 01:09:50 -0800763 cinfo->image_width = width;
764 cinfo->image_height = height;
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000765
Chris Blumecca8c4d2019-03-01 01:09:50 -0800766#ifndef NO_PUTENV
767 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
768 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
769 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
770#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000771
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000772 setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000773
Chris Blumecca8c4d2019-03-01 01:09:50 -0800774 /* Execute only the parts of jpeg_start_compress() that we need. If we
775 were to call the whole jpeg_start_compress() function, then it would try
776 to write the file headers, which could overflow the output buffer if the
777 YUV image were very small. */
778 if (cinfo->global_state != CSTATE_START)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100779 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800780 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
781 jinit_c_master_control(cinfo, FALSE);
782 jinit_color_converter(cinfo);
783 jinit_downsampler(cinfo);
784 (*cinfo->cconvert->start_pass) (cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000785
Chris Blumecca8c4d2019-03-01 01:09:50 -0800786 pw0 = PAD(width, cinfo->max_h_samp_factor);
787 ph0 = PAD(height, cinfo->max_v_samp_factor);
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000788
Chris Blumecca8c4d2019-03-01 01:09:50 -0800789 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100790 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800791 for (i = 0; i < height; i++) {
792 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100793 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800794 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100795 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800796 }
797 if (height < ph0)
798 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
hbono@chromium.org98626972011-08-03 03:13:08 +0000799
Chris Blumecca8c4d2019-03-01 01:09:50 -0800800 for (i = 0; i < cinfo->num_components; i++) {
801 compptr = &cinfo->comp_info[i];
802 _tmpbuf[i] = (JSAMPLE *)malloc(
803 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
804 compptr->h_samp_factor, 32) *
805 cinfo->max_v_samp_factor + 32);
806 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100807 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800808 tmpbuf[i] =
809 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
810 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100811 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800812 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
813 unsigned char *_tmpbuf_aligned =
814 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000815
Chris Blumecca8c4d2019-03-01 01:09:50 -0800816 tmpbuf[i][row] = &_tmpbuf_aligned[
817 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
818 compptr->h_samp_factor, 32) * row];
819 }
820 _tmpbuf2[i] =
821 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
822 compptr->v_samp_factor + 32);
823 if (!_tmpbuf2[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100824 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800825 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
826 if (!tmpbuf2[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100827 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800828 for (row = 0; row < compptr->v_samp_factor; row++) {
829 unsigned char *_tmpbuf2_aligned =
830 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000831
Chris Blumecca8c4d2019-03-01 01:09:50 -0800832 tmpbuf2[i][row] =
833 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
834 }
835 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
836 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
837 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
838 if (!outbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100839 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800840 ptr = dstPlanes[i];
841 for (row = 0; row < ph[i]; row++) {
842 outbuf[i][row] = ptr;
843 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
844 }
845 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000846
Chris Blumecca8c4d2019-03-01 01:09:50 -0800847 if (setjmp(this->jerr.setjmp_buffer)) {
848 /* If we get here, the JPEG code has signaled an error. */
849 retval = -1; goto bailout;
850 }
851
852 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
853 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
854 cinfo->max_v_samp_factor);
855 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
856 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
857 i++, compptr++)
858 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
859 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
860 compptr->v_samp_factor, pw[i]);
861 }
862 cinfo->next_scanline += height;
863 jpeg_abort_compress(cinfo);
864
865bailout:
866 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100867 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800868 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100869 free(tmpbuf[i]);
870 free(_tmpbuf[i]);
871 free(tmpbuf2[i]);
872 free(_tmpbuf2[i]);
873 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800874 }
875 if (this->jerr.warning) retval = -1;
876 this->jerr.stopOnWarning = FALSE;
877 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000878}
879
Chris Blumecca8c4d2019-03-01 01:09:50 -0800880DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
881 int width, int pitch, int height, int pixelFormat,
882 unsigned char *dstBuf, int pad, int subsamp,
883 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400884{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800885 unsigned char *dstPlanes[3];
886 int pw0, ph0, strides[3], retval = -1;
887 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400888
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100889 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800890 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400891
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100892 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
893 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
894 THROW("tjEncodeYUV3(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400895
Chris Blumecca8c4d2019-03-01 01:09:50 -0800896 pw0 = tjPlaneWidth(0, width, subsamp);
897 ph0 = tjPlaneHeight(0, height, subsamp);
898 dstPlanes[0] = dstBuf;
899 strides[0] = PAD(pw0, pad);
900 if (subsamp == TJSAMP_GRAY) {
901 strides[1] = strides[2] = 0;
902 dstPlanes[1] = dstPlanes[2] = NULL;
903 } else {
904 int pw1 = tjPlaneWidth(1, width, subsamp);
905 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400906
Chris Blumecca8c4d2019-03-01 01:09:50 -0800907 strides[1] = strides[2] = PAD(pw1, pad);
908 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
909 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
910 }
911
912 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
913 dstPlanes, strides, subsamp, flags);
914
915bailout:
916 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400917}
918
Chris Blumecca8c4d2019-03-01 01:09:50 -0800919DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
920 int pitch, int height, int pixelFormat,
921 unsigned char *dstBuf, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400922{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800923 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
924 dstBuf, 4, subsamp, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400925}
926
Chris Blumecca8c4d2019-03-01 01:09:50 -0800927DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
928 int pitch, int height, int pixelSize,
929 unsigned char *dstBuf, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000930{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800931 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
932 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
933 flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000934}
935
936
Chris Blumecca8c4d2019-03-01 01:09:50 -0800937DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
938 const unsigned char **srcPlanes,
939 int width, const int *strides,
940 int height, int subsamp,
941 unsigned char **jpegBuf,
942 unsigned long *jpegSize, int jpegQual,
943 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400944{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800945 int i, row, retval = 0, alloc = 1;
946 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
947 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
948 JSAMPLE *_tmpbuf = NULL, *ptr;
949 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400950
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100951 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800952 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400953
Chris Blumecca8c4d2019-03-01 01:09:50 -0800954 for (i = 0; i < MAX_COMPONENTS; i++) {
955 tmpbuf[i] = NULL; inbuf[i] = NULL;
956 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400957
Chris Blumecca8c4d2019-03-01 01:09:50 -0800958 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100959 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400960
Chris Blumecca8c4d2019-03-01 01:09:50 -0800961 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
962 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
963 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100964 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800965 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100966 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400967
Chris Blumecca8c4d2019-03-01 01:09:50 -0800968 if (setjmp(this->jerr.setjmp_buffer)) {
969 /* If we get here, the JPEG code has signaled an error. */
970 retval = -1; goto bailout;
971 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400972
Chris Blumecca8c4d2019-03-01 01:09:50 -0800973 cinfo->image_width = width;
974 cinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400975
Chris Blumecca8c4d2019-03-01 01:09:50 -0800976#ifndef NO_PUTENV
977 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
978 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
979 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
980#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400981
Chris Blumecca8c4d2019-03-01 01:09:50 -0800982 if (flags & TJFLAG_NOREALLOC) {
983 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
984 }
985 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000986 setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800987 cinfo->raw_data_in = TRUE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400988
Chris Blumecca8c4d2019-03-01 01:09:50 -0800989 jpeg_start_compress(cinfo, TRUE);
990 for (i = 0; i < cinfo->num_components; i++) {
991 jpeg_component_info *compptr = &cinfo->comp_info[i];
992 int ih;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400993
Chris Blumecca8c4d2019-03-01 01:09:50 -0800994 iw[i] = compptr->width_in_blocks * DCTSIZE;
995 ih = compptr->height_in_blocks * DCTSIZE;
996 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
997 compptr->h_samp_factor / cinfo->max_h_samp_factor;
998 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
999 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1000 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1001 th[i] = compptr->v_samp_factor * DCTSIZE;
1002 tmpbufsize += iw[i] * th[i];
1003 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001004 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001005 ptr = (JSAMPLE *)srcPlanes[i];
1006 for (row = 0; row < ph[i]; row++) {
1007 inbuf[i][row] = ptr;
1008 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1009 }
1010 }
1011 if (usetmpbuf) {
1012 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001013 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001014 ptr = _tmpbuf;
1015 for (i = 0; i < cinfo->num_components; i++) {
1016 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001017 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001018 for (row = 0; row < th[i]; row++) {
1019 tmpbuf[i][row] = ptr;
1020 ptr += iw[i];
1021 }
1022 }
1023 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001024
Chris Blumecca8c4d2019-03-01 01:09:50 -08001025 if (setjmp(this->jerr.setjmp_buffer)) {
1026 /* If we get here, the JPEG code has signaled an error. */
1027 retval = -1; goto bailout;
1028 }
1029
1030 for (row = 0; row < (int)cinfo->image_height;
1031 row += cinfo->max_v_samp_factor * DCTSIZE) {
1032 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1033 int crow[MAX_COMPONENTS];
1034
1035 for (i = 0; i < cinfo->num_components; i++) {
1036 jpeg_component_info *compptr = &cinfo->comp_info[i];
1037
1038 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1039 if (usetmpbuf) {
1040 int j, k;
1041
1042 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1043 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1044 /* Duplicate last sample in row to fill out MCU */
1045 for (k = pw[i]; k < iw[i]; k++)
1046 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1047 }
1048 /* Duplicate last row to fill out MCU */
1049 for (j = ph[i] - crow[i]; j < th[i]; j++)
1050 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1051 yuvptr[i] = tmpbuf[i];
1052 } else
1053 yuvptr[i] = &inbuf[i][crow[i]];
1054 }
1055 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1056 }
1057 jpeg_finish_compress(cinfo);
1058
1059bailout:
1060 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1061 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001062 free(tmpbuf[i]);
1063 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001064 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001065 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001066 if (this->jerr.warning) retval = -1;
1067 this->jerr.stopOnWarning = FALSE;
1068 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001069}
1070
Chris Blumecca8c4d2019-03-01 01:09:50 -08001071DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1072 int width, int pad, int height, int subsamp,
1073 unsigned char **jpegBuf,
1074 unsigned long *jpegSize, int jpegQual,
1075 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001076{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001077 const unsigned char *srcPlanes[3];
1078 int pw0, ph0, strides[3], retval = -1;
1079 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001080
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001081 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001082 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001083
Chris Blumecca8c4d2019-03-01 01:09:50 -08001084 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1085 subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001086 THROW("tjCompressFromYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001087
Chris Blumecca8c4d2019-03-01 01:09:50 -08001088 pw0 = tjPlaneWidth(0, width, subsamp);
1089 ph0 = tjPlaneHeight(0, height, subsamp);
1090 srcPlanes[0] = srcBuf;
1091 strides[0] = PAD(pw0, pad);
1092 if (subsamp == TJSAMP_GRAY) {
1093 strides[1] = strides[2] = 0;
1094 srcPlanes[1] = srcPlanes[2] = NULL;
1095 } else {
1096 int pw1 = tjPlaneWidth(1, width, subsamp);
1097 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001098
Chris Blumecca8c4d2019-03-01 01:09:50 -08001099 strides[1] = strides[2] = PAD(pw1, pad);
1100 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1101 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1102 }
1103
1104 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1105 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1106
1107bailout:
1108 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001109}
1110
1111
hbono@chromium.org98626972011-08-03 03:13:08 +00001112/* Decompressor */
1113
1114static tjhandle _tjInitDecompress(tjinstance *this)
1115{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001116 static unsigned char buffer[1];
hbono@chromium.org98626972011-08-03 03:13:08 +00001117
Chris Blumecca8c4d2019-03-01 01:09:50 -08001118 /* This is also straight out of example.txt */
1119 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1120 this->jerr.pub.error_exit = my_error_exit;
1121 this->jerr.pub.output_message = my_output_message;
1122 this->jerr.emit_message = this->jerr.pub.emit_message;
1123 this->jerr.pub.emit_message = my_emit_message;
1124 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1125 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1126 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001127
Chris Blumecca8c4d2019-03-01 01:09:50 -08001128 if (setjmp(this->jerr.setjmp_buffer)) {
1129 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001130 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001131 return NULL;
1132 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001133
Chris Blumecca8c4d2019-03-01 01:09:50 -08001134 jpeg_create_decompress(&this->dinfo);
1135 /* Make an initial call so it will create the source manager */
1136 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
hbono@chromium.org98626972011-08-03 03:13:08 +00001137
Chris Blumecca8c4d2019-03-01 01:09:50 -08001138 this->init |= DECOMPRESS;
1139 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +00001140}
1141
Chris Blumecca8c4d2019-03-01 01:09:50 -08001142DLLEXPORT tjhandle tjInitDecompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001143{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001144 tjinstance *this;
1145
1146 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1147 snprintf(errStr, JMSG_LENGTH_MAX,
1148 "tjInitDecompress(): Memory allocation failure");
1149 return NULL;
1150 }
1151 MEMZERO(this, sizeof(tjinstance));
1152 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1153 return _tjInitDecompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +00001154}
1155
1156
Chris Blumecca8c4d2019-03-01 01:09:50 -08001157DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1158 const unsigned char *jpegBuf,
1159 unsigned long jpegSize, int *width,
1160 int *height, int *jpegSubsamp,
1161 int *jpegColorspace)
hbono@chromium.org98626972011-08-03 03:13:08 +00001162{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001163 int retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001164
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001165 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001166 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001167 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001168
Chris Blumecca8c4d2019-03-01 01:09:50 -08001169 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1170 jpegSubsamp == NULL || jpegColorspace == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001171 THROW("tjDecompressHeader3(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001172
Chris Blumecca8c4d2019-03-01 01:09:50 -08001173 if (setjmp(this->jerr.setjmp_buffer)) {
1174 /* If we get here, the JPEG code has signaled an error. */
1175 return -1;
1176 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001177
Chris Blumecca8c4d2019-03-01 01:09:50 -08001178 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1179 jpeg_read_header(dinfo, TRUE);
hbono@chromium.org98626972011-08-03 03:13:08 +00001180
Chris Blumecca8c4d2019-03-01 01:09:50 -08001181 *width = dinfo->image_width;
1182 *height = dinfo->image_height;
1183 *jpegSubsamp = getSubsamp(dinfo);
1184 switch (dinfo->jpeg_color_space) {
1185 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1186 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1187 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1188 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1189 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1190 default: *jpegColorspace = -1; break;
1191 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001192
Chris Blumecca8c4d2019-03-01 01:09:50 -08001193 jpeg_abort_decompress(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001194
Chris Blumecca8c4d2019-03-01 01:09:50 -08001195 if (*jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001196 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001197 if (*jpegColorspace < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001198 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001199 if (*width < 1 || *height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001200 THROW("tjDecompressHeader3(): Invalid data returned in header");
hbono@chromium.org98626972011-08-03 03:13:08 +00001201
Chris Blumecca8c4d2019-03-01 01:09:50 -08001202bailout:
1203 if (this->jerr.warning) retval = -1;
1204 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001205}
1206
Chris Blumecca8c4d2019-03-01 01:09:50 -08001207DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1208 unsigned long jpegSize, int *width,
1209 int *height, int *jpegSubsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001210{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001211 int jpegColorspace;
1212
1213 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1214 jpegSubsamp, &jpegColorspace);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001215}
1216
Chris Blumecca8c4d2019-03-01 01:09:50 -08001217DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1218 unsigned long jpegSize, int *width,
1219 int *height)
hbono@chromium.org98626972011-08-03 03:13:08 +00001220{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001221 int jpegSubsamp;
1222
1223 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1224 &jpegSubsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +00001225}
1226
1227
Chris Blumecca8c4d2019-03-01 01:09:50 -08001228DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
hbono@chromium.org98626972011-08-03 03:13:08 +00001229{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001230 if (numscalingfactors == NULL) {
1231 snprintf(errStr, JMSG_LENGTH_MAX,
1232 "tjGetScalingFactors(): Invalid argument");
1233 return NULL;
1234 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001235
Chris Blumecca8c4d2019-03-01 01:09:50 -08001236 *numscalingfactors = NUMSF;
1237 return (tjscalingfactor *)sf;
hbono@chromium.org98626972011-08-03 03:13:08 +00001238}
1239
1240
Chris Blumecca8c4d2019-03-01 01:09:50 -08001241DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1242 unsigned long jpegSize, unsigned char *dstBuf,
1243 int width, int pitch, int height, int pixelFormat,
1244 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001245{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001246 JSAMPROW *row_pointer = NULL;
1247 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
hbono@chromium.org98626972011-08-03 03:13:08 +00001248
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001249 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001250 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1251 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001252 THROW("tjDecompress2(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001253
Chris Blumecca8c4d2019-03-01 01:09:50 -08001254 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1255 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001256 THROW("tjDecompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001257
Chris Blumecca8c4d2019-03-01 01:09:50 -08001258#ifndef NO_PUTENV
1259 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1260 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1261 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1262#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001263
Chris Blumecca8c4d2019-03-01 01:09:50 -08001264 if (setjmp(this->jerr.setjmp_buffer)) {
1265 /* If we get here, the JPEG code has signaled an error. */
1266 retval = -1; goto bailout;
1267 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001268
Chris Blumecca8c4d2019-03-01 01:09:50 -08001269 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1270 jpeg_read_header(dinfo, TRUE);
1271 this->dinfo.out_color_space = pf2cs[pixelFormat];
1272 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1273 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001274
Chris Blumecca8c4d2019-03-01 01:09:50 -08001275 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1276 if (width == 0) width = jpegwidth;
1277 if (height == 0) height = jpegheight;
1278 for (i = 0; i < NUMSF; i++) {
1279 scaledw = TJSCALED(jpegwidth, sf[i]);
1280 scaledh = TJSCALED(jpegheight, sf[i]);
1281 if (scaledw <= width && scaledh <= height)
1282 break;
1283 }
1284 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001285 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001286 width = scaledw; height = scaledh;
1287 dinfo->scale_num = sf[i].num;
1288 dinfo->scale_denom = sf[i].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001289
Chris Blumecca8c4d2019-03-01 01:09:50 -08001290 jpeg_start_decompress(dinfo);
1291 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +00001292
Chris Blumecca8c4d2019-03-01 01:09:50 -08001293 if ((row_pointer =
1294 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001295 THROW("tjDecompress2(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001296 if (setjmp(this->jerr.setjmp_buffer)) {
1297 /* If we get here, the JPEG code has signaled an error. */
1298 retval = -1; goto bailout;
1299 }
1300 for (i = 0; i < (int)dinfo->output_height; i++) {
1301 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001302 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001303 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001304 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001305 }
1306 while (dinfo->output_scanline < dinfo->output_height)
1307 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1308 dinfo->output_height - dinfo->output_scanline);
1309 jpeg_finish_decompress(dinfo);
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +00001310
Chris Blumecca8c4d2019-03-01 01:09:50 -08001311bailout:
1312 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001313 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001314 if (this->jerr.warning) retval = -1;
1315 this->jerr.stopOnWarning = FALSE;
1316 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001317}
1318
Chris Blumecca8c4d2019-03-01 01:09:50 -08001319DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1320 unsigned long jpegSize, unsigned char *dstBuf,
1321 int width, int pitch, int height, int pixelSize,
1322 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001323{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001324 if (flags & TJ_YUV)
1325 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1326 else
1327 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1328 height, getPixelFormat(pixelSize, flags), flags);
hbono@chromium.org98626972011-08-03 03:13:08 +00001329}
1330
1331
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001332static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001333 int pixelFormat, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001334{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001335 int i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001336
Chris Blumecca8c4d2019-03-01 01:09:50 -08001337 dinfo->scale_num = dinfo->scale_denom = 1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001338
Chris Blumecca8c4d2019-03-01 01:09:50 -08001339 if (subsamp == TJSAMP_GRAY) {
1340 dinfo->num_components = dinfo->comps_in_scan = 1;
1341 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1342 } else {
1343 dinfo->num_components = dinfo->comps_in_scan = 3;
1344 dinfo->jpeg_color_space = JCS_YCbCr;
1345 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001346
Chris Blumecca8c4d2019-03-01 01:09:50 -08001347 dinfo->comp_info = (jpeg_component_info *)
1348 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1349 dinfo->num_components *
1350 sizeof(jpeg_component_info));
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001351
Chris Blumecca8c4d2019-03-01 01:09:50 -08001352 for (i = 0; i < dinfo->num_components; i++) {
1353 jpeg_component_info *compptr = &dinfo->comp_info[i];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001354
Chris Blumecca8c4d2019-03-01 01:09:50 -08001355 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1356 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1357 compptr->component_index = i;
1358 compptr->component_id = i + 1;
1359 compptr->quant_tbl_no = compptr->dc_tbl_no =
1360 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1361 dinfo->cur_comp_info[i] = compptr;
1362 }
1363 dinfo->data_precision = 8;
1364 for (i = 0; i < 2; i++) {
1365 if (dinfo->quant_tbl_ptrs[i] == NULL)
1366 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1367 }
1368
1369 return 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001370}
1371
1372
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001373static int my_read_markers(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001374{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001375 return JPEG_REACHED_SOS;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001376}
1377
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001378static void my_reset_marker_reader(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001379{
1380}
1381
Chris Blumecca8c4d2019-03-01 01:09:50 -08001382DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1383 const unsigned char **srcPlanes,
1384 const int *strides, int subsamp,
1385 unsigned char *dstBuf, int width, int pitch,
1386 int height, int pixelFormat, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001387{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001388 JSAMPROW *row_pointer = NULL;
1389 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1390 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1391 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1392 JSAMPLE *ptr;
1393 jpeg_component_info *compptr;
1394 int (*old_read_markers) (j_decompress_ptr);
1395 void (*old_reset_marker_reader) (j_decompress_ptr);
hbono@chromium.org98626972011-08-03 03:13:08 +00001396
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001397 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001398 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001399
Chris Blumecca8c4d2019-03-01 01:09:50 -08001400 for (i = 0; i < MAX_COMPONENTS; i++) {
1401 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1402 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001403
Chris Blumecca8c4d2019-03-01 01:09:50 -08001404 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001405 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001406
Chris Blumecca8c4d2019-03-01 01:09:50 -08001407 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1408 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1409 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001410 THROW("tjDecodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001411 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001412 THROW("tjDecodeYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001413
Chris Blumecca8c4d2019-03-01 01:09:50 -08001414 if (setjmp(this->jerr.setjmp_buffer)) {
1415 /* If we get here, the JPEG code has signaled an error. */
1416 retval = -1; goto bailout;
1417 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001418
Chris Blumecca8c4d2019-03-01 01:09:50 -08001419 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001420 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001421
Chris Blumecca8c4d2019-03-01 01:09:50 -08001422 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1423 dinfo->image_width = width;
1424 dinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001425
Chris Blumecca8c4d2019-03-01 01:09:50 -08001426#ifndef NO_PUTENV
1427 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1428 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1429 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1430#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001431
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001432 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1433 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1434 dinfo->Se = DCTSIZE2 - 1;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001435 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1436 retval = -1; goto bailout;
1437 }
1438 old_read_markers = dinfo->marker->read_markers;
1439 dinfo->marker->read_markers = my_read_markers;
1440 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1441 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1442 jpeg_read_header(dinfo, TRUE);
1443 dinfo->marker->read_markers = old_read_markers;
1444 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001445
Chris Blumecca8c4d2019-03-01 01:09:50 -08001446 this->dinfo.out_color_space = pf2cs[pixelFormat];
1447 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1448 dinfo->do_fancy_upsampling = FALSE;
1449 dinfo->Se = DCTSIZE2 - 1;
1450 jinit_master_decompress(dinfo);
1451 (*dinfo->upsample->start_pass) (dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001452
Chris Blumecca8c4d2019-03-01 01:09:50 -08001453 pw0 = PAD(width, dinfo->max_h_samp_factor);
1454 ph0 = PAD(height, dinfo->max_v_samp_factor);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001455
Chris Blumecca8c4d2019-03-01 01:09:50 -08001456 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001457
Chris Blumecca8c4d2019-03-01 01:09:50 -08001458 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001459 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001460 for (i = 0; i < height; i++) {
1461 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001462 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001463 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001464 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001465 }
1466 if (height < ph0)
1467 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001468
Chris Blumecca8c4d2019-03-01 01:09:50 -08001469 for (i = 0; i < dinfo->num_components; i++) {
1470 compptr = &dinfo->comp_info[i];
1471 _tmpbuf[i] =
1472 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1473 compptr->v_samp_factor + 32);
1474 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001475 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001476 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1477 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001478 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001479 for (row = 0; row < compptr->v_samp_factor; row++) {
1480 unsigned char *_tmpbuf_aligned =
1481 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001482
Chris Blumecca8c4d2019-03-01 01:09:50 -08001483 tmpbuf[i][row] =
1484 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1485 }
1486 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1487 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1488 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1489 if (!inbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001490 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001491 ptr = (JSAMPLE *)srcPlanes[i];
1492 for (row = 0; row < ph[i]; row++) {
1493 inbuf[i][row] = ptr;
1494 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1495 }
1496 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001497
Chris Blumecca8c4d2019-03-01 01:09:50 -08001498 if (setjmp(this->jerr.setjmp_buffer)) {
1499 /* If we get here, the JPEG code has signaled an error. */
1500 retval = -1; goto bailout;
1501 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001502
Chris Blumecca8c4d2019-03-01 01:09:50 -08001503 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1504 JDIMENSION inrow = 0, outrow = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001505
Chris Blumecca8c4d2019-03-01 01:09:50 -08001506 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1507 i++, compptr++)
1508 jcopy_sample_rows(inbuf[i],
1509 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1510 compptr->v_samp_factor, pw[i]);
1511 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1512 dinfo->max_v_samp_factor, &row_pointer[row],
1513 &outrow, dinfo->max_v_samp_factor);
1514 }
1515 jpeg_abort_decompress(dinfo);
1516
1517bailout:
1518 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001519 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001520 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001521 free(tmpbuf[i]);
1522 free(_tmpbuf[i]);
1523 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001524 }
1525 if (this->jerr.warning) retval = -1;
1526 this->jerr.stopOnWarning = FALSE;
1527 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001528}
1529
Chris Blumecca8c4d2019-03-01 01:09:50 -08001530DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1531 int pad, int subsamp, unsigned char *dstBuf,
1532 int width, int pitch, int height, int pixelFormat,
1533 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001534{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001535 const unsigned char *srcPlanes[3];
1536 int pw0, ph0, strides[3], retval = -1;
1537 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001538
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001539 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001540 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001541
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001542 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
Chris Blumecca8c4d2019-03-01 01:09:50 -08001543 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001544 THROW("tjDecodeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001545
Chris Blumecca8c4d2019-03-01 01:09:50 -08001546 pw0 = tjPlaneWidth(0, width, subsamp);
1547 ph0 = tjPlaneHeight(0, height, subsamp);
1548 srcPlanes[0] = srcBuf;
1549 strides[0] = PAD(pw0, pad);
1550 if (subsamp == TJSAMP_GRAY) {
1551 strides[1] = strides[2] = 0;
1552 srcPlanes[1] = srcPlanes[2] = NULL;
1553 } else {
1554 int pw1 = tjPlaneWidth(1, width, subsamp);
1555 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001556
Chris Blumecca8c4d2019-03-01 01:09:50 -08001557 strides[1] = strides[2] = PAD(pw1, pad);
1558 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1559 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1560 }
1561
1562 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1563 pitch, height, pixelFormat, flags);
1564
1565bailout:
1566 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001567}
1568
Chris Blumecca8c4d2019-03-01 01:09:50 -08001569DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1570 const unsigned char *jpegBuf,
1571 unsigned long jpegSize,
1572 unsigned char **dstPlanes, int width,
1573 int *strides, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001574{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001575 int i, sfi, row, retval = 0;
1576 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1577 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1578 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1579 JSAMPLE *_tmpbuf = NULL, *ptr;
1580 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1581 int dctsize;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001582
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001583 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001584 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001585
Chris Blumecca8c4d2019-03-01 01:09:50 -08001586 for (i = 0; i < MAX_COMPONENTS; i++) {
1587 tmpbuf[i] = NULL; outbuf[i] = NULL;
1588 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001589
Chris Blumecca8c4d2019-03-01 01:09:50 -08001590 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001591 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +00001592
Chris Blumecca8c4d2019-03-01 01:09:50 -08001593 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1594 width < 0 || height < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001595 THROW("tjDecompressToYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001596
Chris Blumecca8c4d2019-03-01 01:09:50 -08001597#ifndef NO_PUTENV
1598 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1599 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1600 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1601#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001602
Chris Blumecca8c4d2019-03-01 01:09:50 -08001603 if (setjmp(this->jerr.setjmp_buffer)) {
1604 /* If we get here, the JPEG code has signaled an error. */
1605 retval = -1; goto bailout;
1606 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001607
Chris Blumecca8c4d2019-03-01 01:09:50 -08001608 if (!this->headerRead) {
1609 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1610 jpeg_read_header(dinfo, TRUE);
1611 }
1612 this->headerRead = 0;
1613 jpegSubsamp = getSubsamp(dinfo);
1614 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001615 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001616
Chris Blumecca8c4d2019-03-01 01:09:50 -08001617 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001618 THROW("tjDecompressToYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001619
Chris Blumecca8c4d2019-03-01 01:09:50 -08001620 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1621 if (width == 0) width = jpegwidth;
1622 if (height == 0) height = jpegheight;
1623 for (i = 0; i < NUMSF; i++) {
1624 scaledw = TJSCALED(jpegwidth, sf[i]);
1625 scaledh = TJSCALED(jpegheight, sf[i]);
1626 if (scaledw <= width && scaledh <= height)
1627 break;
1628 }
1629 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001630 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001631 if (dinfo->num_components > 3)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001632 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001633
Chris Blumecca8c4d2019-03-01 01:09:50 -08001634 width = scaledw; height = scaledh;
1635 dinfo->scale_num = sf[i].num;
1636 dinfo->scale_denom = sf[i].denom;
1637 sfi = i;
1638 jpeg_calc_output_dimensions(dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001639
Chris Blumecca8c4d2019-03-01 01:09:50 -08001640 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001641
Chris Blumecca8c4d2019-03-01 01:09:50 -08001642 for (i = 0; i < dinfo->num_components; i++) {
1643 jpeg_component_info *compptr = &dinfo->comp_info[i];
1644 int ih;
hbono@chromium.org98626972011-08-03 03:13:08 +00001645
Chris Blumecca8c4d2019-03-01 01:09:50 -08001646 iw[i] = compptr->width_in_blocks * dctsize;
1647 ih = compptr->height_in_blocks * dctsize;
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001648 pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp);
1649 ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001650 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1651 th[i] = compptr->v_samp_factor * dctsize;
1652 tmpbufsize += iw[i] * th[i];
1653 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001654 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001655 ptr = dstPlanes[i];
1656 for (row = 0; row < ph[i]; row++) {
1657 outbuf[i][row] = ptr;
1658 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1659 }
1660 }
1661 if (usetmpbuf) {
1662 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001663 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001664 ptr = _tmpbuf;
1665 for (i = 0; i < dinfo->num_components; i++) {
1666 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001667 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001668 for (row = 0; row < th[i]; row++) {
1669 tmpbuf[i][row] = ptr;
1670 ptr += iw[i];
1671 }
1672 }
1673 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001674
Chris Blumecca8c4d2019-03-01 01:09:50 -08001675 if (setjmp(this->jerr.setjmp_buffer)) {
1676 /* If we get here, the JPEG code has signaled an error. */
1677 retval = -1; goto bailout;
1678 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001679
Chris Blumecca8c4d2019-03-01 01:09:50 -08001680 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1681 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1682 dinfo->raw_data_out = TRUE;
1683
1684 jpeg_start_decompress(dinfo);
1685 for (row = 0; row < (int)dinfo->output_height;
1686 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1687 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1688 int crow[MAX_COMPONENTS];
1689
1690 for (i = 0; i < dinfo->num_components; i++) {
1691 jpeg_component_info *compptr = &dinfo->comp_info[i];
1692
1693 if (jpegSubsamp == TJ_420) {
1694 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1695 to be clever and use the IDCT to perform upsampling on the U and V
1696 planes. For instance, if the output image is to be scaled by 1/2
1697 relative to the JPEG image, then the scaling factor and upsampling
1698 effectively cancel each other, so a normal 8x8 IDCT can be used.
1699 However, this is not desirable when using the decompress-to-YUV
1700 functionality in TurboJPEG, since we want to output the U and V
1701 planes in their subsampled form. Thus, we have to override some
1702 internal libjpeg parameters to force it to use the "scaled" IDCT
1703 functions on the U and V planes. */
1704 compptr->_DCT_scaled_size = dctsize;
1705 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1706 sf[sfi].num / sf[sfi].denom *
1707 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1708 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1709 }
1710 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1711 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1712 else yuvptr[i] = &outbuf[i][crow[i]];
1713 }
1714 jpeg_read_raw_data(dinfo, yuvptr,
1715 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1716 if (usetmpbuf) {
1717 int j;
1718
1719 for (i = 0; i < dinfo->num_components; i++) {
1720 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1721 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1722 }
1723 }
1724 }
1725 }
1726 jpeg_finish_decompress(dinfo);
1727
1728bailout:
1729 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1730 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001731 free(tmpbuf[i]);
1732 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001733 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001734 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001735 if (this->jerr.warning) retval = -1;
1736 this->jerr.stopOnWarning = FALSE;
1737 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001738}
1739
Chris Blumecca8c4d2019-03-01 01:09:50 -08001740DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1741 unsigned long jpegSize, unsigned char *dstBuf,
1742 int width, int pad, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001743{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001744 unsigned char *dstPlanes[3];
1745 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1746 int i, jpegwidth, jpegheight, scaledw, scaledh;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001747
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001748 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001749 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001750
Chris Blumecca8c4d2019-03-01 01:09:50 -08001751 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001752 pad < 1 || !IS_POW2(pad) || height < 0)
1753 THROW("tjDecompressToYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001754
Chris Blumecca8c4d2019-03-01 01:09:50 -08001755 if (setjmp(this->jerr.setjmp_buffer)) {
1756 /* If we get here, the JPEG code has signaled an error. */
1757 return -1;
1758 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001759
Chris Blumecca8c4d2019-03-01 01:09:50 -08001760 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1761 jpeg_read_header(dinfo, TRUE);
1762 jpegSubsamp = getSubsamp(dinfo);
1763 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001764 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001765
Chris Blumecca8c4d2019-03-01 01:09:50 -08001766 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1767 if (width == 0) width = jpegwidth;
1768 if (height == 0) height = jpegheight;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001769
Chris Blumecca8c4d2019-03-01 01:09:50 -08001770 for (i = 0; i < NUMSF; i++) {
1771 scaledw = TJSCALED(jpegwidth, sf[i]);
1772 scaledh = TJSCALED(jpegheight, sf[i]);
1773 if (scaledw <= width && scaledh <= height)
1774 break;
1775 }
1776 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001777 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001778
Chris Blumecca8c4d2019-03-01 01:09:50 -08001779 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1780 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1781 dstPlanes[0] = dstBuf;
1782 strides[0] = PAD(pw0, pad);
1783 if (jpegSubsamp == TJSAMP_GRAY) {
1784 strides[1] = strides[2] = 0;
1785 dstPlanes[1] = dstPlanes[2] = NULL;
1786 } else {
1787 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1788 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001789
Chris Blumecca8c4d2019-03-01 01:09:50 -08001790 strides[1] = strides[2] = PAD(pw1, pad);
1791 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1792 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1793 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001794
Chris Blumecca8c4d2019-03-01 01:09:50 -08001795 this->headerRead = 1;
1796 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1797 strides, height, flags);
1798
1799bailout:
1800 this->jerr.stopOnWarning = FALSE;
1801 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001802}
1803
Chris Blumecca8c4d2019-03-01 01:09:50 -08001804DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1805 unsigned long jpegSize, unsigned char *dstBuf,
1806 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001807{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001808 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001809}
1810
hbono@chromium.org98626972011-08-03 03:13:08 +00001811
1812/* Transformer */
1813
Chris Blumecca8c4d2019-03-01 01:09:50 -08001814DLLEXPORT tjhandle tjInitTransform(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001815{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001816 tjinstance *this = NULL;
1817 tjhandle handle = NULL;
1818
1819 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1820 snprintf(errStr, JMSG_LENGTH_MAX,
1821 "tjInitTransform(): Memory allocation failure");
1822 return NULL;
1823 }
1824 MEMZERO(this, sizeof(tjinstance));
1825 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1826 handle = _tjInitCompress(this);
1827 if (!handle) return NULL;
1828 handle = _tjInitDecompress(this);
1829 return handle;
hbono@chromium.org98626972011-08-03 03:13:08 +00001830}
1831
1832
Chris Blumecca8c4d2019-03-01 01:09:50 -08001833DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1834 unsigned long jpegSize, int n,
1835 unsigned char **dstBufs, unsigned long *dstSizes,
1836 tjtransform *t, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001837{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001838 jpeg_transform_info *xinfo = NULL;
1839 jvirt_barray_ptr *srccoefs, *dstcoefs;
1840 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001841
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001842 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001843 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1844 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001845 THROW("tjTransform(): Instance has not been initialized for transformation");
hbono@chromium.org98626972011-08-03 03:13:08 +00001846
Chris Blumecca8c4d2019-03-01 01:09:50 -08001847 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1848 dstSizes == NULL || t == NULL || flags < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001849 THROW("tjTransform(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001850
Chris Blumecca8c4d2019-03-01 01:09:50 -08001851#ifndef NO_PUTENV
1852 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1853 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1854 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1855#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001856
Chris Blumecca8c4d2019-03-01 01:09:50 -08001857 if ((xinfo =
1858 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001859 THROW("tjTransform(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001860 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
hbono@chromium.org98626972011-08-03 03:13:08 +00001861
Chris Blumecca8c4d2019-03-01 01:09:50 -08001862 if (setjmp(this->jerr.setjmp_buffer)) {
1863 /* If we get here, the JPEG code has signaled an error. */
1864 retval = -1; goto bailout;
1865 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001866
Chris Blumecca8c4d2019-03-01 01:09:50 -08001867 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
hbono@chromium.org98626972011-08-03 03:13:08 +00001868
Chris Blumecca8c4d2019-03-01 01:09:50 -08001869 for (i = 0; i < n; i++) {
1870 xinfo[i].transform = xformtypes[t[i].op];
1871 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1872 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1873 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1874 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1875 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1876 else xinfo[i].slow_hflip = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001877
Chris Blumecca8c4d2019-03-01 01:09:50 -08001878 if (xinfo[i].crop) {
1879 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1880 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1881 if (t[i].r.w != 0) {
1882 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1883 } else
1884 xinfo[i].crop_width = JCROP_UNSET;
1885 if (t[i].r.h != 0) {
1886 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1887 } else
1888 xinfo[i].crop_height = JCROP_UNSET;
1889 }
1890 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1891 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001892
Chris Blumecca8c4d2019-03-01 01:09:50 -08001893 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1894 jpeg_read_header(dinfo, TRUE);
1895 jpegSubsamp = getSubsamp(dinfo);
1896 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001897 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
hbono@chromium.org98626972011-08-03 03:13:08 +00001898
Chris Blumecca8c4d2019-03-01 01:09:50 -08001899 for (i = 0; i < n; i++) {
1900 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001901 THROW("tjTransform(): Transform is not perfect");
hbono@chromium.org98626972011-08-03 03:13:08 +00001902
Chris Blumecca8c4d2019-03-01 01:09:50 -08001903 if (xinfo[i].crop) {
1904 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1905 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001906 snprintf(this->errStr, JMSG_LENGTH_MAX,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001907 "To crop this JPEG image, x must be a multiple of %d\n"
1908 "and y must be a multiple of %d.\n",
1909 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001910 this->isInstanceError = TRUE;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001911 retval = -1; goto bailout;
1912 }
1913 }
1914 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001915
Chris Blumecca8c4d2019-03-01 01:09:50 -08001916 srccoefs = jpeg_read_coefficients(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001917
Chris Blumecca8c4d2019-03-01 01:09:50 -08001918 for (i = 0; i < n; i++) {
1919 int w, h, alloc = 1;
hbono@chromium.org98626972011-08-03 03:13:08 +00001920
Chris Blumecca8c4d2019-03-01 01:09:50 -08001921 if (!xinfo[i].crop) {
1922 w = dinfo->image_width; h = dinfo->image_height;
1923 } else {
1924 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1925 }
1926 if (flags & TJFLAG_NOREALLOC) {
1927 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1928 }
1929 if (!(t[i].options & TJXOPT_NOOUTPUT))
1930 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1931 jpeg_copy_critical_parameters(dinfo, cinfo);
1932 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1933 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1934 jpeg_simple_progression(cinfo);
1935 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1936 jpeg_write_coefficients(cinfo, dstcoefs);
1937 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1938 JCOPYOPT_NONE : JCOPYOPT_ALL);
1939 } else
1940 jinit_c_master_control(cinfo, TRUE);
1941 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1942 if (t[i].customFilter) {
1943 int ci, y;
1944 JDIMENSION by;
hbono@chromium.org98626972011-08-03 03:13:08 +00001945
Chris Blumecca8c4d2019-03-01 01:09:50 -08001946 for (ci = 0; ci < cinfo->num_components; ci++) {
1947 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1948 tjregion arrayRegion = {
1949 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1950 };
1951 tjregion planeRegion = {
1952 0, 0, compptr->width_in_blocks * DCTSIZE,
1953 compptr->height_in_blocks * DCTSIZE
1954 };
1955
1956 for (by = 0; by < compptr->height_in_blocks;
1957 by += compptr->v_samp_factor) {
1958 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1959 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1960 TRUE);
1961
1962 for (y = 0; y < compptr->v_samp_factor; y++) {
1963 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1964 i, &t[i]) == -1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001965 THROW("tjTransform(): Error in custom filter");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001966 arrayRegion.y += DCTSIZE;
1967 }
1968 }
1969 }
1970 }
1971 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1972 }
1973
1974 jpeg_finish_decompress(dinfo);
1975
1976bailout:
1977 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1978 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001979 free(xinfo);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001980 if (this->jerr.warning) retval = -1;
1981 this->jerr.stopOnWarning = FALSE;
1982 return retval;
1983}
1984
1985
1986DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1987 int align, int *height, int *pixelFormat,
1988 int flags)
1989{
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001990 int retval = 0, tempc;
1991 size_t pitch;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001992 tjhandle handle = NULL;
1993 tjinstance *this;
1994 j_compress_ptr cinfo = NULL;
1995 cjpeg_source_ptr src;
1996 unsigned char *dstBuf = NULL;
1997 FILE *file = NULL;
1998 boolean invert;
1999
2000 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2001 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002002 THROWG("tjLoadImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002003 if ((align & (align - 1)) != 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002004 THROWG("tjLoadImage(): Alignment must be a power of 2");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002005
2006 if ((handle = tjInitCompress()) == NULL) return NULL;
2007 this = (tjinstance *)handle;
2008 cinfo = &this->cinfo;
2009
2010 if ((file = fopen(filename, "rb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002011 THROW_UNIX("tjLoadImage(): Cannot open input file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002012
2013 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002014 THROW_UNIX("tjLoadImage(): Could not read input file")
Chris Blumecca8c4d2019-03-01 01:09:50 -08002015 else if (tempc == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002016 THROWG("tjLoadImage(): Input file contains no data");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002017
2018 if (setjmp(this->jerr.setjmp_buffer)) {
2019 /* If we get here, the JPEG code has signaled an error. */
2020 retval = -1; goto bailout;
2021 }
2022
2023 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2024 else cinfo->in_color_space = pf2cs[*pixelFormat];
2025 if (tempc == 'B') {
2026 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002027 THROWG("tjLoadImage(): Could not initialize bitmap loader");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002028 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2029 } else if (tempc == 'P') {
2030 if ((src = jinit_read_ppm(cinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002031 THROWG("tjLoadImage(): Could not initialize bitmap loader");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002032 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2033 } else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002034 THROWG("tjLoadImage(): Unsupported file type");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002035
2036 src->input_file = file;
2037 (*src->start_input) (cinfo, src);
2038 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
2039
2040 *width = cinfo->image_width; *height = cinfo->image_height;
2041 *pixelFormat = cs2pf[cinfo->in_color_space];
2042
2043 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002044 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2045 (unsigned long long)((size_t)-1) ||
2046 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2047 THROWG("tjLoadImage(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002048
2049 if (setjmp(this->jerr.setjmp_buffer)) {
2050 /* If we get here, the JPEG code has signaled an error. */
2051 retval = -1; goto bailout;
2052 }
2053
2054 while (cinfo->next_scanline < cinfo->image_height) {
2055 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
2056
2057 for (i = 0; i < nlines; i++) {
2058 unsigned char *dstptr;
2059 int row;
2060
2061 row = cinfo->next_scanline + i;
2062 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2063 else dstptr = &dstBuf[row * pitch];
2064 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2065 }
2066 cinfo->next_scanline += nlines;
2067 }
2068
2069 (*src->finish_input) (cinfo, src);
2070
2071bailout:
2072 if (handle) tjDestroy(handle);
2073 if (file) fclose(file);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002074 if (retval < 0) { free(dstBuf); dstBuf = NULL; }
Chris Blumecca8c4d2019-03-01 01:09:50 -08002075 return dstBuf;
2076}
2077
2078
2079DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2080 int width, int pitch, int height, int pixelFormat,
2081 int flags)
2082{
2083 int retval = 0;
2084 tjhandle handle = NULL;
2085 tjinstance *this;
2086 j_decompress_ptr dinfo = NULL;
2087 djpeg_dest_ptr dst;
2088 FILE *file = NULL;
2089 char *ptr = NULL;
2090 boolean invert;
2091
2092 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2093 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002094 THROWG("tjSaveImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002095
2096 if ((handle = tjInitDecompress()) == NULL)
2097 return -1;
2098 this = (tjinstance *)handle;
2099 dinfo = &this->dinfo;
2100
2101 if ((file = fopen(filename, "wb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002102 THROW_UNIX("tjSaveImage(): Cannot open output file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002103
2104 if (setjmp(this->jerr.setjmp_buffer)) {
2105 /* If we get here, the JPEG code has signaled an error. */
2106 retval = -1; goto bailout;
2107 }
2108
2109 this->dinfo.out_color_space = pf2cs[pixelFormat];
2110 dinfo->image_width = width; dinfo->image_height = height;
2111 dinfo->global_state = DSTATE_READY;
2112 dinfo->scale_num = dinfo->scale_denom = 1;
2113
2114 ptr = strrchr(filename, '.');
2115 if (ptr && !strcasecmp(ptr, ".bmp")) {
2116 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002117 THROWG("tjSaveImage(): Could not initialize bitmap writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002118 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2119 } else {
2120 if ((dst = jinit_write_ppm(dinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002121 THROWG("tjSaveImage(): Could not initialize PPM writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002122 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2123 }
2124
2125 dst->output_file = file;
2126 (*dst->start_output) (dinfo, dst);
2127 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
2128
2129 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2130
2131 while (dinfo->output_scanline < dinfo->output_height) {
2132 unsigned char *rowptr;
2133
2134 if (invert)
2135 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2136 else
2137 rowptr = &buffer[dinfo->output_scanline * pitch];
2138 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2139 (*dst->put_pixel_rows) (dinfo, dst, 1);
2140 dinfo->output_scanline++;
2141 }
2142
2143 (*dst->finish_output) (dinfo, dst);
2144
2145bailout:
2146 if (handle) tjDestroy(handle);
2147 if (file) fclose(file);
2148 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00002149}