blob: e63d1134c427b272dc6079f679d76a69dbf94ca3 [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
Chris Blumecca8c4d2019-03-01 01:09:50 -0800237static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
238 int subsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000239{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800240 int retval = 0;
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.orgc6beb742011-11-29 05:16:26 +0000303
Chris Blumecca8c4d2019-03-01 01:09:50 -0800304 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000305}
306
307
308static int getSubsamp(j_decompress_ptr dinfo)
309{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800310 int retval = -1, i, k;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400311
Chris Blumecca8c4d2019-03-01 01:09:50 -0800312 /* The sampling factors actually have no meaning with grayscale JPEG files,
313 and in fact it's possible to generate grayscale JPEGs with sampling
314 factors > 1 (even though those sampling factors are ignored by the
315 decompressor.) Thus, we need to treat grayscale as a special case. */
316 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
317 return TJSAMP_GRAY;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400318
Chris Blumecca8c4d2019-03-01 01:09:50 -0800319 for (i = 0; i < NUMSUBOPT; i++) {
320 if (dinfo->num_components == pixelsize[i] ||
321 ((dinfo->jpeg_color_space == JCS_YCCK ||
322 dinfo->jpeg_color_space == JCS_CMYK) &&
323 pixelsize[i] == 3 && dinfo->num_components == 4)) {
324 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
325 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
326 int match = 0;
327
328 for (k = 1; k < dinfo->num_components; k++) {
329 int href = 1, vref = 1;
330
331 if ((dinfo->jpeg_color_space == JCS_YCCK ||
332 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
333 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
334 }
335 if (dinfo->comp_info[k].h_samp_factor == href &&
336 dinfo->comp_info[k].v_samp_factor == vref)
337 match++;
338 }
339 if (match == dinfo->num_components - 1) {
340 retval = i; break;
341 }
342 }
343 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
344 in non-standard ways. */
345 if (dinfo->comp_info[0].h_samp_factor == 2 &&
346 dinfo->comp_info[0].v_samp_factor == 2 &&
347 (i == TJSAMP_422 || i == TJSAMP_440)) {
348 int match = 0;
349
350 for (k = 1; k < dinfo->num_components; k++) {
351 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
352
353 if ((dinfo->jpeg_color_space == JCS_YCCK ||
354 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
355 href = vref = 2;
356 }
357 if (dinfo->comp_info[k].h_samp_factor == href &&
358 dinfo->comp_info[k].v_samp_factor == vref)
359 match++;
360 }
361 if (match == dinfo->num_components - 1) {
362 retval = i; break;
363 }
364 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100365 /* Handle 4:4:4 images whose sampling factors are specified in
366 non-standard ways. */
367 if (dinfo->comp_info[0].h_samp_factor *
368 dinfo->comp_info[0].v_samp_factor <=
369 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
370 int match = 0;
371 for (k = 1; k < dinfo->num_components; k++) {
372 if (dinfo->comp_info[k].h_samp_factor ==
373 dinfo->comp_info[0].h_samp_factor &&
374 dinfo->comp_info[k].v_samp_factor ==
375 dinfo->comp_info[0].v_samp_factor)
376 match++;
377 if (match == dinfo->num_components - 1) {
378 retval = i; break;
379 }
380 }
381 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800382 }
383 }
384 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000385}
386
387
388/* General API functions */
389
Chris Blumecca8c4d2019-03-01 01:09:50 -0800390DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
hbono@chromium.org98626972011-08-03 03:13:08 +0000391{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800392 tjinstance *this = (tjinstance *)handle;
393
394 if (this && this->isInstanceError) {
395 this->isInstanceError = FALSE;
396 return this->errStr;
397 } else
398 return errStr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000399}
400
401
Chris Blumecca8c4d2019-03-01 01:09:50 -0800402DLLEXPORT char *tjGetErrorStr(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000403{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800404 return errStr;
405}
406
407
408DLLEXPORT int tjGetErrorCode(tjhandle handle)
409{
410 tjinstance *this = (tjinstance *)handle;
411
412 if (this && this->jerr.warning) return TJERR_WARNING;
413 else return TJERR_FATAL;
414}
415
416
417DLLEXPORT int tjDestroy(tjhandle handle)
418{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100419 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800420
421 if (setjmp(this->jerr.setjmp_buffer)) return -1;
422 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
423 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
424 free(this);
425 return 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000426}
427
428
429/* These are exposed mainly because Windows can't malloc() and free() across
430 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
431 with turbojpeg.dll for compatibility reasons. However, these functions
432 can potentially be used for other purposes by different implementations. */
433
Chris Blumecca8c4d2019-03-01 01:09:50 -0800434DLLEXPORT void tjFree(unsigned char *buf)
hbono@chromium.org98626972011-08-03 03:13:08 +0000435{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100436 free(buf);
hbono@chromium.org98626972011-08-03 03:13:08 +0000437}
438
439
Chris Blumecca8c4d2019-03-01 01:09:50 -0800440DLLEXPORT unsigned char *tjAlloc(int bytes)
hbono@chromium.org98626972011-08-03 03:13:08 +0000441{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800442 return (unsigned char *)malloc(bytes);
hbono@chromium.org98626972011-08-03 03:13:08 +0000443}
444
445
446/* Compressor */
447
448static tjhandle _tjInitCompress(tjinstance *this)
449{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800450 static unsigned char buffer[1];
451 unsigned char *buf = buffer;
452 unsigned long size = 1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000453
Chris Blumecca8c4d2019-03-01 01:09:50 -0800454 /* This is also straight out of example.txt */
455 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
456 this->jerr.pub.error_exit = my_error_exit;
457 this->jerr.pub.output_message = my_output_message;
458 this->jerr.emit_message = this->jerr.pub.emit_message;
459 this->jerr.pub.emit_message = my_emit_message;
460 this->jerr.pub.addon_message_table = turbojpeg_message_table;
461 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
462 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000463
Chris Blumecca8c4d2019-03-01 01:09:50 -0800464 if (setjmp(this->jerr.setjmp_buffer)) {
465 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100466 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800467 return NULL;
468 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000469
Chris Blumecca8c4d2019-03-01 01:09:50 -0800470 jpeg_create_compress(&this->cinfo);
471 /* Make an initial call so it will create the destination manager */
472 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
hbono@chromium.org98626972011-08-03 03:13:08 +0000473
Chris Blumecca8c4d2019-03-01 01:09:50 -0800474 this->init |= COMPRESS;
475 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +0000476}
477
Chris Blumecca8c4d2019-03-01 01:09:50 -0800478DLLEXPORT tjhandle tjInitCompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +0000479{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800480 tjinstance *this = NULL;
481
482 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
483 snprintf(errStr, JMSG_LENGTH_MAX,
484 "tjInitCompress(): Memory allocation failure");
485 return NULL;
486 }
487 MEMZERO(this, sizeof(tjinstance));
488 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
489 return _tjInitCompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +0000490}
491
492
Chris Blumecca8c4d2019-03-01 01:09:50 -0800493DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000494{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100495 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800496 int mcuw, mcuh, chromasf;
hbono@chromium.org98626972011-08-03 03:13:08 +0000497
Chris Blumecca8c4d2019-03-01 01:09:50 -0800498 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100499 THROWG("tjBufSize(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000500
Chris Blumecca8c4d2019-03-01 01:09:50 -0800501 /* This allows for rare corner cases in which a JPEG image can actually be
502 larger than the uncompressed input (we wouldn't mention it if it hadn't
503 happened before.) */
504 mcuw = tjMCUWidth[jpegSubsamp];
505 mcuh = tjMCUHeight[jpegSubsamp];
506 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100507 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
508 if (retval > (unsigned long long)((unsigned long)-1))
509 THROWG("tjBufSize(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800510
511bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100512 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000513}
514
Chris Blumecca8c4d2019-03-01 01:09:50 -0800515DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
hbono@chromium.org98626972011-08-03 03:13:08 +0000516{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100517 unsigned long long retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +0000518
Chris Blumecca8c4d2019-03-01 01:09:50 -0800519 if (width < 1 || height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100520 THROWG("TJBUFSIZE(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000521
Chris Blumecca8c4d2019-03-01 01:09:50 -0800522 /* This allows for rare corner cases in which a JPEG image can actually be
523 larger than the uncompressed input (we wouldn't mention it if it hadn't
524 happened before.) */
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100525 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
526 if (retval > (unsigned long long)((unsigned long)-1))
527 THROWG("TJBUFSIZE(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800528
529bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100530 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000531}
532
533
Chris Blumecca8c4d2019-03-01 01:09:50 -0800534DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
535 int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000536{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100537 unsigned long long retval = 0;
538 int nc, i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400539
Chris Blumecca8c4d2019-03-01 01:09:50 -0800540 if (subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100541 THROWG("tjBufSizeYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400542
Chris Blumecca8c4d2019-03-01 01:09:50 -0800543 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
544 for (i = 0; i < nc; i++) {
545 int pw = tjPlaneWidth(i, width, subsamp);
546 int stride = PAD(pw, pad);
547 int ph = tjPlaneHeight(i, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000548
Chris Blumecca8c4d2019-03-01 01:09:50 -0800549 if (pw < 0 || ph < 0) return -1;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100550 else retval += (unsigned long long)stride * ph;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800551 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100552 if (retval > (unsigned long long)((unsigned long)-1))
553 THROWG("tjBufSizeYUV2(): Image is too large");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800554
555bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100556 return (unsigned long)retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000557}
558
Chris Blumecca8c4d2019-03-01 01:09:50 -0800559DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400560{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800561 return tjBufSizeYUV2(width, 4, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400562}
hbono@chromium.org98626972011-08-03 03:13:08 +0000563
Chris Blumecca8c4d2019-03-01 01:09:50 -0800564DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
hbono@chromium.org98626972011-08-03 03:13:08 +0000565{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800566 return tjBufSizeYUV(width, height, subsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +0000567}
568
569
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400570DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
571{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800572 int pw, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400573
Chris Blumecca8c4d2019-03-01 01:09:50 -0800574 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100575 THROWG("tjPlaneWidth(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800576 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
577 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100578 THROWG("tjPlaneWidth(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400579
Chris Blumecca8c4d2019-03-01 01:09:50 -0800580 pw = PAD(width, tjMCUWidth[subsamp] / 8);
581 if (componentID == 0)
582 retval = pw;
583 else
584 retval = pw * 8 / tjMCUWidth[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400585
Chris Blumecca8c4d2019-03-01 01:09:50 -0800586bailout:
587 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400588}
589
590
591DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
592{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800593 int ph, nc, retval = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400594
Chris Blumecca8c4d2019-03-01 01:09:50 -0800595 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100596 THROWG("tjPlaneHeight(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800597 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
598 if (componentID < 0 || componentID >= nc)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100599 THROWG("tjPlaneHeight(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400600
Chris Blumecca8c4d2019-03-01 01:09:50 -0800601 ph = PAD(height, tjMCUHeight[subsamp] / 8);
602 if (componentID == 0)
603 retval = ph;
604 else
605 retval = ph * 8 / tjMCUHeight[subsamp];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400606
Chris Blumecca8c4d2019-03-01 01:09:50 -0800607bailout:
608 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400609}
610
611
Chris Blumecca8c4d2019-03-01 01:09:50 -0800612DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
613 int height, int subsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400614{
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100615 unsigned long long retval = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800616 int pw, ph;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400617
Chris Blumecca8c4d2019-03-01 01:09:50 -0800618 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100619 THROWG("tjPlaneSizeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400620
Chris Blumecca8c4d2019-03-01 01:09:50 -0800621 pw = tjPlaneWidth(componentID, width, subsamp);
622 ph = tjPlaneHeight(componentID, height, subsamp);
623 if (pw < 0 || ph < 0) return -1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400624
Chris Blumecca8c4d2019-03-01 01:09:50 -0800625 if (stride == 0) stride = pw;
626 else stride = abs(stride);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400627
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100628 retval = (unsigned long long)stride * (ph - 1) + pw;
629 if (retval > (unsigned long long)((unsigned long)-1))
630 THROWG("tjPlaneSizeYUV(): Image is too large");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400631
Chris Blumecca8c4d2019-03-01 01:09:50 -0800632bailout:
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100633 return (unsigned long)retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400634}
635
636
Chris Blumecca8c4d2019-03-01 01:09:50 -0800637DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
638 int width, int pitch, int height, int pixelFormat,
639 unsigned char **jpegBuf, unsigned long *jpegSize,
640 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000641{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800642 int i, retval = 0, alloc = 1;
643 JSAMPROW *row_pointer = NULL;
hbono@chromium.org98626972011-08-03 03:13:08 +0000644
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100645 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800646 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
647 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100648 THROW("tjCompress2(): Instance has not been initialized for compression");
hbono@chromium.org98626972011-08-03 03:13:08 +0000649
Chris Blumecca8c4d2019-03-01 01:09:50 -0800650 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
651 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
652 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
653 jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100654 THROW("tjCompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000655
Chris Blumecca8c4d2019-03-01 01:09:50 -0800656 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +0000657
Chris Blumecca8c4d2019-03-01 01:09:50 -0800658 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100659 THROW("tjCompress2(): Memory allocation failure");
hbono@chromium.org98626972011-08-03 03:13:08 +0000660
Chris Blumecca8c4d2019-03-01 01:09:50 -0800661 if (setjmp(this->jerr.setjmp_buffer)) {
662 /* If we get here, the JPEG code has signaled an error. */
663 retval = -1; goto bailout;
664 }
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000665
Chris Blumecca8c4d2019-03-01 01:09:50 -0800666 cinfo->image_width = width;
667 cinfo->image_height = height;
hbono@chromium.org98626972011-08-03 03:13:08 +0000668
Chris Blumecca8c4d2019-03-01 01:09:50 -0800669#ifndef NO_PUTENV
670 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
671 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
672 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
673#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000674
Chris Blumecca8c4d2019-03-01 01:09:50 -0800675 if (flags & TJFLAG_NOREALLOC) {
676 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
677 }
678 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
679 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
680 return -1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000681
Chris Blumecca8c4d2019-03-01 01:09:50 -0800682 jpeg_start_compress(cinfo, TRUE);
683 for (i = 0; i < height; i++) {
684 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100685 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800686 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100687 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800688 }
689 while (cinfo->next_scanline < cinfo->image_height)
690 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
691 cinfo->image_height - cinfo->next_scanline);
692 jpeg_finish_compress(cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000693
Chris Blumecca8c4d2019-03-01 01:09:50 -0800694bailout:
695 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100696 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800697 if (this->jerr.warning) retval = -1;
698 this->jerr.stopOnWarning = FALSE;
699 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000700}
701
Chris Blumecca8c4d2019-03-01 01:09:50 -0800702DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
703 int pitch, int height, int pixelSize,
704 unsigned char *jpegBuf, unsigned long *jpegSize,
705 int jpegSubsamp, int jpegQual, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000706{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800707 int retval = 0;
708 unsigned long size;
709
710 if (flags & TJ_YUV) {
711 size = tjBufSizeYUV(width, height, jpegSubsamp);
712 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
713 getPixelFormat(pixelSize, flags), jpegBuf,
714 jpegSubsamp, flags);
715 } else {
716 retval = tjCompress2(handle, srcBuf, width, pitch, height,
717 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
718 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
719 }
720 *jpegSize = size;
721 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000722}
723
724
Chris Blumecca8c4d2019-03-01 01:09:50 -0800725DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
726 int width, int pitch, int height,
727 int pixelFormat, unsigned char **dstPlanes,
728 int *strides, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000729{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800730 JSAMPROW *row_pointer = NULL;
731 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
732 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
733 JSAMPROW *outbuf[MAX_COMPONENTS];
734 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
735 JSAMPLE *ptr;
736 jpeg_component_info *compptr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000737
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100738 GET_CINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800739 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000740
Chris Blumecca8c4d2019-03-01 01:09:50 -0800741 for (i = 0; i < MAX_COMPONENTS; i++) {
742 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
743 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
744 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000745
Chris Blumecca8c4d2019-03-01 01:09:50 -0800746 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100747 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000748
Chris Blumecca8c4d2019-03-01 01:09:50 -0800749 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
750 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
751 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100752 THROW("tjEncodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800753 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100754 THROW("tjEncodeYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +0000755
Chris Blumecca8c4d2019-03-01 01:09:50 -0800756 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100757 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
hbono@chromium.org98626972011-08-03 03:13:08 +0000758
Chris Blumecca8c4d2019-03-01 01:09:50 -0800759 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400760
Chris Blumecca8c4d2019-03-01 01:09:50 -0800761 if (setjmp(this->jerr.setjmp_buffer)) {
762 /* If we get here, the JPEG code has signaled an error. */
763 retval = -1; goto bailout;
764 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000765
Chris Blumecca8c4d2019-03-01 01:09:50 -0800766 cinfo->image_width = width;
767 cinfo->image_height = height;
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +0000768
Chris Blumecca8c4d2019-03-01 01:09:50 -0800769#ifndef NO_PUTENV
770 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
771 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
772 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
773#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000774
Chris Blumecca8c4d2019-03-01 01:09:50 -0800775 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
hbono@chromium.org98626972011-08-03 03:13:08 +0000776
Chris Blumecca8c4d2019-03-01 01:09:50 -0800777 /* Execute only the parts of jpeg_start_compress() that we need. If we
778 were to call the whole jpeg_start_compress() function, then it would try
779 to write the file headers, which could overflow the output buffer if the
780 YUV image were very small. */
781 if (cinfo->global_state != CSTATE_START)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100782 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800783 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
784 jinit_c_master_control(cinfo, FALSE);
785 jinit_color_converter(cinfo);
786 jinit_downsampler(cinfo);
787 (*cinfo->cconvert->start_pass) (cinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +0000788
Chris Blumecca8c4d2019-03-01 01:09:50 -0800789 pw0 = PAD(width, cinfo->max_h_samp_factor);
790 ph0 = PAD(height, cinfo->max_v_samp_factor);
noel@chromium.org3395bcc2014-04-14 06:56:00 +0000791
Chris Blumecca8c4d2019-03-01 01:09:50 -0800792 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100793 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800794 for (i = 0; i < height; i++) {
795 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100796 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800797 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100798 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800799 }
800 if (height < ph0)
801 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
hbono@chromium.org98626972011-08-03 03:13:08 +0000802
Chris Blumecca8c4d2019-03-01 01:09:50 -0800803 for (i = 0; i < cinfo->num_components; i++) {
804 compptr = &cinfo->comp_info[i];
805 _tmpbuf[i] = (JSAMPLE *)malloc(
806 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
807 compptr->h_samp_factor, 32) *
808 cinfo->max_v_samp_factor + 32);
809 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100810 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800811 tmpbuf[i] =
812 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
813 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100814 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800815 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
816 unsigned char *_tmpbuf_aligned =
817 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000818
Chris Blumecca8c4d2019-03-01 01:09:50 -0800819 tmpbuf[i][row] = &_tmpbuf_aligned[
820 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
821 compptr->h_samp_factor, 32) * row];
822 }
823 _tmpbuf2[i] =
824 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
825 compptr->v_samp_factor + 32);
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 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
829 if (!tmpbuf2[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100830 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800831 for (row = 0; row < compptr->v_samp_factor; row++) {
832 unsigned char *_tmpbuf2_aligned =
833 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
hbono@chromium.org98626972011-08-03 03:13:08 +0000834
Chris Blumecca8c4d2019-03-01 01:09:50 -0800835 tmpbuf2[i][row] =
836 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
837 }
838 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
839 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
840 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
841 if (!outbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100842 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800843 ptr = dstPlanes[i];
844 for (row = 0; row < ph[i]; row++) {
845 outbuf[i][row] = ptr;
846 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
847 }
848 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000849
Chris Blumecca8c4d2019-03-01 01:09:50 -0800850 if (setjmp(this->jerr.setjmp_buffer)) {
851 /* If we get here, the JPEG code has signaled an error. */
852 retval = -1; goto bailout;
853 }
854
855 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
856 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
857 cinfo->max_v_samp_factor);
858 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
859 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
860 i++, compptr++)
861 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
862 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
863 compptr->v_samp_factor, pw[i]);
864 }
865 cinfo->next_scanline += height;
866 jpeg_abort_compress(cinfo);
867
868bailout:
869 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100870 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800871 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100872 free(tmpbuf[i]);
873 free(_tmpbuf[i]);
874 free(tmpbuf2[i]);
875 free(_tmpbuf2[i]);
876 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800877 }
878 if (this->jerr.warning) retval = -1;
879 this->jerr.stopOnWarning = FALSE;
880 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +0000881}
882
Chris Blumecca8c4d2019-03-01 01:09:50 -0800883DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
884 int width, int pitch, int height, int pixelFormat,
885 unsigned char *dstBuf, int pad, int subsamp,
886 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400887{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800888 unsigned char *dstPlanes[3];
889 int pw0, ph0, strides[3], retval = -1;
890 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400891
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100892 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800893 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400894
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100895 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
896 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
897 THROW("tjEncodeYUV3(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400898
Chris Blumecca8c4d2019-03-01 01:09:50 -0800899 pw0 = tjPlaneWidth(0, width, subsamp);
900 ph0 = tjPlaneHeight(0, height, subsamp);
901 dstPlanes[0] = dstBuf;
902 strides[0] = PAD(pw0, pad);
903 if (subsamp == TJSAMP_GRAY) {
904 strides[1] = strides[2] = 0;
905 dstPlanes[1] = dstPlanes[2] = NULL;
906 } else {
907 int pw1 = tjPlaneWidth(1, width, subsamp);
908 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400909
Chris Blumecca8c4d2019-03-01 01:09:50 -0800910 strides[1] = strides[2] = PAD(pw1, pad);
911 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
912 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
913 }
914
915 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
916 dstPlanes, strides, subsamp, flags);
917
918bailout:
919 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400920}
921
Chris Blumecca8c4d2019-03-01 01:09:50 -0800922DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
923 int pitch, int height, int pixelFormat,
924 unsigned char *dstBuf, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400925{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800926 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
927 dstBuf, 4, subsamp, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400928}
929
Chris Blumecca8c4d2019-03-01 01:09:50 -0800930DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
931 int pitch, int height, int pixelSize,
932 unsigned char *dstBuf, int subsamp, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +0000933{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800934 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
935 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
936 flags);
hbono@chromium.org98626972011-08-03 03:13:08 +0000937}
938
939
Chris Blumecca8c4d2019-03-01 01:09:50 -0800940DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
941 const unsigned char **srcPlanes,
942 int width, const int *strides,
943 int height, int subsamp,
944 unsigned char **jpegBuf,
945 unsigned long *jpegSize, int jpegQual,
946 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400947{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800948 int i, row, retval = 0, alloc = 1;
949 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
950 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
951 JSAMPLE *_tmpbuf = NULL, *ptr;
952 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400953
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100954 GET_CINSTANCE(handle)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800955 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400956
Chris Blumecca8c4d2019-03-01 01:09:50 -0800957 for (i = 0; i < MAX_COMPONENTS; i++) {
958 tmpbuf[i] = NULL; inbuf[i] = NULL;
959 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400960
Chris Blumecca8c4d2019-03-01 01:09:50 -0800961 if ((this->init & COMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100962 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400963
Chris Blumecca8c4d2019-03-01 01:09:50 -0800964 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
965 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
966 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100967 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -0800968 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100969 THROW("tjCompressFromYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400970
Chris Blumecca8c4d2019-03-01 01:09:50 -0800971 if (setjmp(this->jerr.setjmp_buffer)) {
972 /* If we get here, the JPEG code has signaled an error. */
973 retval = -1; goto bailout;
974 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400975
Chris Blumecca8c4d2019-03-01 01:09:50 -0800976 cinfo->image_width = width;
977 cinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400978
Chris Blumecca8c4d2019-03-01 01:09:50 -0800979#ifndef NO_PUTENV
980 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
981 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
982 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
983#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400984
Chris Blumecca8c4d2019-03-01 01:09:50 -0800985 if (flags & TJFLAG_NOREALLOC) {
986 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
987 }
988 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
989 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
990 return -1;
991 cinfo->raw_data_in = TRUE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400992
Chris Blumecca8c4d2019-03-01 01:09:50 -0800993 jpeg_start_compress(cinfo, TRUE);
994 for (i = 0; i < cinfo->num_components; i++) {
995 jpeg_component_info *compptr = &cinfo->comp_info[i];
996 int ih;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400997
Chris Blumecca8c4d2019-03-01 01:09:50 -0800998 iw[i] = compptr->width_in_blocks * DCTSIZE;
999 ih = compptr->height_in_blocks * DCTSIZE;
1000 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1001 compptr->h_samp_factor / cinfo->max_h_samp_factor;
1002 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1003 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1004 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1005 th[i] = compptr->v_samp_factor * DCTSIZE;
1006 tmpbufsize += iw[i] * th[i];
1007 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001008 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001009 ptr = (JSAMPLE *)srcPlanes[i];
1010 for (row = 0; row < ph[i]; row++) {
1011 inbuf[i][row] = ptr;
1012 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1013 }
1014 }
1015 if (usetmpbuf) {
1016 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001017 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001018 ptr = _tmpbuf;
1019 for (i = 0; i < cinfo->num_components; i++) {
1020 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001021 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001022 for (row = 0; row < th[i]; row++) {
1023 tmpbuf[i][row] = ptr;
1024 ptr += iw[i];
1025 }
1026 }
1027 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001028
Chris Blumecca8c4d2019-03-01 01:09:50 -08001029 if (setjmp(this->jerr.setjmp_buffer)) {
1030 /* If we get here, the JPEG code has signaled an error. */
1031 retval = -1; goto bailout;
1032 }
1033
1034 for (row = 0; row < (int)cinfo->image_height;
1035 row += cinfo->max_v_samp_factor * DCTSIZE) {
1036 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1037 int crow[MAX_COMPONENTS];
1038
1039 for (i = 0; i < cinfo->num_components; i++) {
1040 jpeg_component_info *compptr = &cinfo->comp_info[i];
1041
1042 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1043 if (usetmpbuf) {
1044 int j, k;
1045
1046 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1047 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1048 /* Duplicate last sample in row to fill out MCU */
1049 for (k = pw[i]; k < iw[i]; k++)
1050 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1051 }
1052 /* Duplicate last row to fill out MCU */
1053 for (j = ph[i] - crow[i]; j < th[i]; j++)
1054 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1055 yuvptr[i] = tmpbuf[i];
1056 } else
1057 yuvptr[i] = &inbuf[i][crow[i]];
1058 }
1059 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1060 }
1061 jpeg_finish_compress(cinfo);
1062
1063bailout:
1064 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1065 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001066 free(tmpbuf[i]);
1067 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001068 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001069 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001070 if (this->jerr.warning) retval = -1;
1071 this->jerr.stopOnWarning = FALSE;
1072 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001073}
1074
Chris Blumecca8c4d2019-03-01 01:09:50 -08001075DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1076 int width, int pad, int height, int subsamp,
1077 unsigned char **jpegBuf,
1078 unsigned long *jpegSize, int jpegQual,
1079 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001080{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001081 const unsigned char *srcPlanes[3];
1082 int pw0, ph0, strides[3], retval = -1;
1083 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001084
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001085 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001086 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001087
Chris Blumecca8c4d2019-03-01 01:09:50 -08001088 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1089 subsamp >= NUMSUBOPT)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001090 THROW("tjCompressFromYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001091
Chris Blumecca8c4d2019-03-01 01:09:50 -08001092 pw0 = tjPlaneWidth(0, width, subsamp);
1093 ph0 = tjPlaneHeight(0, height, subsamp);
1094 srcPlanes[0] = srcBuf;
1095 strides[0] = PAD(pw0, pad);
1096 if (subsamp == TJSAMP_GRAY) {
1097 strides[1] = strides[2] = 0;
1098 srcPlanes[1] = srcPlanes[2] = NULL;
1099 } else {
1100 int pw1 = tjPlaneWidth(1, width, subsamp);
1101 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001102
Chris Blumecca8c4d2019-03-01 01:09:50 -08001103 strides[1] = strides[2] = PAD(pw1, pad);
1104 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1105 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1106 }
1107
1108 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1109 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1110
1111bailout:
1112 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001113}
1114
1115
hbono@chromium.org98626972011-08-03 03:13:08 +00001116/* Decompressor */
1117
1118static tjhandle _tjInitDecompress(tjinstance *this)
1119{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001120 static unsigned char buffer[1];
hbono@chromium.org98626972011-08-03 03:13:08 +00001121
Chris Blumecca8c4d2019-03-01 01:09:50 -08001122 /* This is also straight out of example.txt */
1123 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1124 this->jerr.pub.error_exit = my_error_exit;
1125 this->jerr.pub.output_message = my_output_message;
1126 this->jerr.emit_message = this->jerr.pub.emit_message;
1127 this->jerr.pub.emit_message = my_emit_message;
1128 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1129 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1130 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001131
Chris Blumecca8c4d2019-03-01 01:09:50 -08001132 if (setjmp(this->jerr.setjmp_buffer)) {
1133 /* If we get here, the JPEG code has signaled an error. */
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001134 free(this);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001135 return NULL;
1136 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001137
Chris Blumecca8c4d2019-03-01 01:09:50 -08001138 jpeg_create_decompress(&this->dinfo);
1139 /* Make an initial call so it will create the source manager */
1140 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
hbono@chromium.org98626972011-08-03 03:13:08 +00001141
Chris Blumecca8c4d2019-03-01 01:09:50 -08001142 this->init |= DECOMPRESS;
1143 return (tjhandle)this;
hbono@chromium.org98626972011-08-03 03:13:08 +00001144}
1145
Chris Blumecca8c4d2019-03-01 01:09:50 -08001146DLLEXPORT tjhandle tjInitDecompress(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001147{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001148 tjinstance *this;
1149
1150 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1151 snprintf(errStr, JMSG_LENGTH_MAX,
1152 "tjInitDecompress(): Memory allocation failure");
1153 return NULL;
1154 }
1155 MEMZERO(this, sizeof(tjinstance));
1156 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1157 return _tjInitDecompress(this);
hbono@chromium.org98626972011-08-03 03:13:08 +00001158}
1159
1160
Chris Blumecca8c4d2019-03-01 01:09:50 -08001161DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1162 const unsigned char *jpegBuf,
1163 unsigned long jpegSize, int *width,
1164 int *height, int *jpegSubsamp,
1165 int *jpegColorspace)
hbono@chromium.org98626972011-08-03 03:13:08 +00001166{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001167 int retval = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001168
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001169 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001170 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001171 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001172
Chris Blumecca8c4d2019-03-01 01:09:50 -08001173 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1174 jpegSubsamp == NULL || jpegColorspace == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001175 THROW("tjDecompressHeader3(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001176
Chris Blumecca8c4d2019-03-01 01:09:50 -08001177 if (setjmp(this->jerr.setjmp_buffer)) {
1178 /* If we get here, the JPEG code has signaled an error. */
1179 return -1;
1180 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001181
Chris Blumecca8c4d2019-03-01 01:09:50 -08001182 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1183 jpeg_read_header(dinfo, TRUE);
hbono@chromium.org98626972011-08-03 03:13:08 +00001184
Chris Blumecca8c4d2019-03-01 01:09:50 -08001185 *width = dinfo->image_width;
1186 *height = dinfo->image_height;
1187 *jpegSubsamp = getSubsamp(dinfo);
1188 switch (dinfo->jpeg_color_space) {
1189 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1190 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1191 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1192 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1193 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1194 default: *jpegColorspace = -1; break;
1195 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001196
Chris Blumecca8c4d2019-03-01 01:09:50 -08001197 jpeg_abort_decompress(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001198
Chris Blumecca8c4d2019-03-01 01:09:50 -08001199 if (*jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001200 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001201 if (*jpegColorspace < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001202 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001203 if (*width < 1 || *height < 1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001204 THROW("tjDecompressHeader3(): Invalid data returned in header");
hbono@chromium.org98626972011-08-03 03:13:08 +00001205
Chris Blumecca8c4d2019-03-01 01:09:50 -08001206bailout:
1207 if (this->jerr.warning) retval = -1;
1208 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001209}
1210
Chris Blumecca8c4d2019-03-01 01:09:50 -08001211DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1212 unsigned long jpegSize, int *width,
1213 int *height, int *jpegSubsamp)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001214{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001215 int jpegColorspace;
1216
1217 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1218 jpegSubsamp, &jpegColorspace);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001219}
1220
Chris Blumecca8c4d2019-03-01 01:09:50 -08001221DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1222 unsigned long jpegSize, int *width,
1223 int *height)
hbono@chromium.org98626972011-08-03 03:13:08 +00001224{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001225 int jpegSubsamp;
1226
1227 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1228 &jpegSubsamp);
hbono@chromium.org98626972011-08-03 03:13:08 +00001229}
1230
1231
Chris Blumecca8c4d2019-03-01 01:09:50 -08001232DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
hbono@chromium.org98626972011-08-03 03:13:08 +00001233{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001234 if (numscalingfactors == NULL) {
1235 snprintf(errStr, JMSG_LENGTH_MAX,
1236 "tjGetScalingFactors(): Invalid argument");
1237 return NULL;
1238 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001239
Chris Blumecca8c4d2019-03-01 01:09:50 -08001240 *numscalingfactors = NUMSF;
1241 return (tjscalingfactor *)sf;
hbono@chromium.org98626972011-08-03 03:13:08 +00001242}
1243
1244
Chris Blumecca8c4d2019-03-01 01:09:50 -08001245DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1246 unsigned long jpegSize, unsigned char *dstBuf,
1247 int width, int pitch, int height, int pixelFormat,
1248 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001249{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001250 JSAMPROW *row_pointer = NULL;
1251 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
hbono@chromium.org98626972011-08-03 03:13:08 +00001252
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001253 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001254 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1255 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001256 THROW("tjDecompress2(): Instance has not been initialized for decompression");
hbono@chromium.org98626972011-08-03 03:13:08 +00001257
Chris Blumecca8c4d2019-03-01 01:09:50 -08001258 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1259 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001260 THROW("tjDecompress2(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001261
Chris Blumecca8c4d2019-03-01 01:09:50 -08001262#ifndef NO_PUTENV
1263 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1264 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1265 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1266#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001267
Chris Blumecca8c4d2019-03-01 01:09:50 -08001268 if (setjmp(this->jerr.setjmp_buffer)) {
1269 /* If we get here, the JPEG code has signaled an error. */
1270 retval = -1; goto bailout;
1271 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001272
Chris Blumecca8c4d2019-03-01 01:09:50 -08001273 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1274 jpeg_read_header(dinfo, TRUE);
1275 this->dinfo.out_color_space = pf2cs[pixelFormat];
1276 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1277 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001278
Chris Blumecca8c4d2019-03-01 01:09:50 -08001279 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1280 if (width == 0) width = jpegwidth;
1281 if (height == 0) height = jpegheight;
1282 for (i = 0; i < NUMSF; i++) {
1283 scaledw = TJSCALED(jpegwidth, sf[i]);
1284 scaledh = TJSCALED(jpegheight, sf[i]);
1285 if (scaledw <= width && scaledh <= height)
1286 break;
1287 }
1288 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001289 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001290 width = scaledw; height = scaledh;
1291 dinfo->scale_num = sf[i].num;
1292 dinfo->scale_denom = sf[i].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001293
Chris Blumecca8c4d2019-03-01 01:09:50 -08001294 jpeg_start_decompress(dinfo);
1295 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
hbono@chromium.org98626972011-08-03 03:13:08 +00001296
Chris Blumecca8c4d2019-03-01 01:09:50 -08001297 if ((row_pointer =
1298 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001299 THROW("tjDecompress2(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001300 if (setjmp(this->jerr.setjmp_buffer)) {
1301 /* If we get here, the JPEG code has signaled an error. */
1302 retval = -1; goto bailout;
1303 }
1304 for (i = 0; i < (int)dinfo->output_height; i++) {
1305 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001306 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001307 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001308 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001309 }
1310 while (dinfo->output_scanline < dinfo->output_height)
1311 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1312 dinfo->output_height - dinfo->output_scanline);
1313 jpeg_finish_decompress(dinfo);
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +00001314
Chris Blumecca8c4d2019-03-01 01:09:50 -08001315bailout:
1316 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001317 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001318 if (this->jerr.warning) retval = -1;
1319 this->jerr.stopOnWarning = FALSE;
1320 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001321}
1322
Chris Blumecca8c4d2019-03-01 01:09:50 -08001323DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1324 unsigned long jpegSize, unsigned char *dstBuf,
1325 int width, int pitch, int height, int pixelSize,
1326 int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001327{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001328 if (flags & TJ_YUV)
1329 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1330 else
1331 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1332 height, getPixelFormat(pixelSize, flags), flags);
hbono@chromium.org98626972011-08-03 03:13:08 +00001333}
1334
1335
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001336static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001337 int pixelFormat, int subsamp, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001338{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001339 int i;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001340
Chris Blumecca8c4d2019-03-01 01:09:50 -08001341 dinfo->scale_num = dinfo->scale_denom = 1;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001342
Chris Blumecca8c4d2019-03-01 01:09:50 -08001343 if (subsamp == TJSAMP_GRAY) {
1344 dinfo->num_components = dinfo->comps_in_scan = 1;
1345 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1346 } else {
1347 dinfo->num_components = dinfo->comps_in_scan = 3;
1348 dinfo->jpeg_color_space = JCS_YCbCr;
1349 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001350
Chris Blumecca8c4d2019-03-01 01:09:50 -08001351 dinfo->comp_info = (jpeg_component_info *)
1352 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1353 dinfo->num_components *
1354 sizeof(jpeg_component_info));
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001355
Chris Blumecca8c4d2019-03-01 01:09:50 -08001356 for (i = 0; i < dinfo->num_components; i++) {
1357 jpeg_component_info *compptr = &dinfo->comp_info[i];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001358
Chris Blumecca8c4d2019-03-01 01:09:50 -08001359 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1360 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1361 compptr->component_index = i;
1362 compptr->component_id = i + 1;
1363 compptr->quant_tbl_no = compptr->dc_tbl_no =
1364 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1365 dinfo->cur_comp_info[i] = compptr;
1366 }
1367 dinfo->data_precision = 8;
1368 for (i = 0; i < 2; i++) {
1369 if (dinfo->quant_tbl_ptrs[i] == NULL)
1370 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1371 }
1372
1373 return 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001374}
1375
1376
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001377static int my_read_markers(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001378{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001379 return JPEG_REACHED_SOS;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001380}
1381
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001382static void my_reset_marker_reader(j_decompress_ptr dinfo)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001383{
1384}
1385
Chris Blumecca8c4d2019-03-01 01:09:50 -08001386DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1387 const unsigned char **srcPlanes,
1388 const int *strides, int subsamp,
1389 unsigned char *dstBuf, int width, int pitch,
1390 int height, int pixelFormat, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001391{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001392 JSAMPROW *row_pointer = NULL;
1393 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1394 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1395 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1396 JSAMPLE *ptr;
1397 jpeg_component_info *compptr;
1398 int (*old_read_markers) (j_decompress_ptr);
1399 void (*old_reset_marker_reader) (j_decompress_ptr);
hbono@chromium.org98626972011-08-03 03:13:08 +00001400
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001401 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001402 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001403
Chris Blumecca8c4d2019-03-01 01:09:50 -08001404 for (i = 0; i < MAX_COMPONENTS; i++) {
1405 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1406 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001407
Chris Blumecca8c4d2019-03-01 01:09:50 -08001408 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001409 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001410
Chris Blumecca8c4d2019-03-01 01:09:50 -08001411 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1412 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1413 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001414 THROW("tjDecodeYUVPlanes(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001415 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001416 THROW("tjDecodeYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001417
Chris Blumecca8c4d2019-03-01 01:09:50 -08001418 if (setjmp(this->jerr.setjmp_buffer)) {
1419 /* If we get here, the JPEG code has signaled an error. */
1420 retval = -1; goto bailout;
1421 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001422
Chris Blumecca8c4d2019-03-01 01:09:50 -08001423 if (pixelFormat == TJPF_CMYK)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001424 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001425
Chris Blumecca8c4d2019-03-01 01:09:50 -08001426 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1427 dinfo->image_width = width;
1428 dinfo->image_height = height;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001429
Chris Blumecca8c4d2019-03-01 01:09:50 -08001430#ifndef NO_PUTENV
1431 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1432 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1433 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1434#endif
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001435
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001436 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1437 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1438 dinfo->Se = DCTSIZE2 - 1;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001439 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1440 retval = -1; goto bailout;
1441 }
1442 old_read_markers = dinfo->marker->read_markers;
1443 dinfo->marker->read_markers = my_read_markers;
1444 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1445 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1446 jpeg_read_header(dinfo, TRUE);
1447 dinfo->marker->read_markers = old_read_markers;
1448 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001449
Chris Blumecca8c4d2019-03-01 01:09:50 -08001450 this->dinfo.out_color_space = pf2cs[pixelFormat];
1451 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1452 dinfo->do_fancy_upsampling = FALSE;
1453 dinfo->Se = DCTSIZE2 - 1;
1454 jinit_master_decompress(dinfo);
1455 (*dinfo->upsample->start_pass) (dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001456
Chris Blumecca8c4d2019-03-01 01:09:50 -08001457 pw0 = PAD(width, dinfo->max_h_samp_factor);
1458 ph0 = PAD(height, dinfo->max_v_samp_factor);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001459
Chris Blumecca8c4d2019-03-01 01:09:50 -08001460 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001461
Chris Blumecca8c4d2019-03-01 01:09:50 -08001462 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001463 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001464 for (i = 0; i < height; i++) {
1465 if (flags & TJFLAG_BOTTOMUP)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001466 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001467 else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001468 row_pointer[i] = &dstBuf[i * (size_t)pitch];
Chris Blumecca8c4d2019-03-01 01:09:50 -08001469 }
1470 if (height < ph0)
1471 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001472
Chris Blumecca8c4d2019-03-01 01:09:50 -08001473 for (i = 0; i < dinfo->num_components; i++) {
1474 compptr = &dinfo->comp_info[i];
1475 _tmpbuf[i] =
1476 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1477 compptr->v_samp_factor + 32);
1478 if (!_tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001479 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001480 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1481 if (!tmpbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001482 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001483 for (row = 0; row < compptr->v_samp_factor; row++) {
1484 unsigned char *_tmpbuf_aligned =
1485 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001486
Chris Blumecca8c4d2019-03-01 01:09:50 -08001487 tmpbuf[i][row] =
1488 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1489 }
1490 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1491 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1492 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1493 if (!inbuf[i])
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001494 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001495 ptr = (JSAMPLE *)srcPlanes[i];
1496 for (row = 0; row < ph[i]; row++) {
1497 inbuf[i][row] = ptr;
1498 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1499 }
1500 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001501
Chris Blumecca8c4d2019-03-01 01:09:50 -08001502 if (setjmp(this->jerr.setjmp_buffer)) {
1503 /* If we get here, the JPEG code has signaled an error. */
1504 retval = -1; goto bailout;
1505 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001506
Chris Blumecca8c4d2019-03-01 01:09:50 -08001507 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1508 JDIMENSION inrow = 0, outrow = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001509
Chris Blumecca8c4d2019-03-01 01:09:50 -08001510 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1511 i++, compptr++)
1512 jcopy_sample_rows(inbuf[i],
1513 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1514 compptr->v_samp_factor, pw[i]);
1515 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1516 dinfo->max_v_samp_factor, &row_pointer[row],
1517 &outrow, dinfo->max_v_samp_factor);
1518 }
1519 jpeg_abort_decompress(dinfo);
1520
1521bailout:
1522 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001523 free(row_pointer);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001524 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001525 free(tmpbuf[i]);
1526 free(_tmpbuf[i]);
1527 free(inbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001528 }
1529 if (this->jerr.warning) retval = -1;
1530 this->jerr.stopOnWarning = FALSE;
1531 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001532}
1533
Chris Blumecca8c4d2019-03-01 01:09:50 -08001534DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1535 int pad, int subsamp, unsigned char *dstBuf,
1536 int width, int pitch, int height, int pixelFormat,
1537 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001538{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001539 const unsigned char *srcPlanes[3];
1540 int pw0, ph0, strides[3], retval = -1;
1541 tjinstance *this = (tjinstance *)handle;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001542
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001543 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001544 this->isInstanceError = FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001545
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001546 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
Chris Blumecca8c4d2019-03-01 01:09:50 -08001547 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001548 THROW("tjDecodeYUV(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001549
Chris Blumecca8c4d2019-03-01 01:09:50 -08001550 pw0 = tjPlaneWidth(0, width, subsamp);
1551 ph0 = tjPlaneHeight(0, height, subsamp);
1552 srcPlanes[0] = srcBuf;
1553 strides[0] = PAD(pw0, pad);
1554 if (subsamp == TJSAMP_GRAY) {
1555 strides[1] = strides[2] = 0;
1556 srcPlanes[1] = srcPlanes[2] = NULL;
1557 } else {
1558 int pw1 = tjPlaneWidth(1, width, subsamp);
1559 int ph1 = tjPlaneHeight(1, height, subsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001560
Chris Blumecca8c4d2019-03-01 01:09:50 -08001561 strides[1] = strides[2] = PAD(pw1, pad);
1562 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1563 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1564 }
1565
1566 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1567 pitch, height, pixelFormat, flags);
1568
1569bailout:
1570 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001571}
1572
Chris Blumecca8c4d2019-03-01 01:09:50 -08001573DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1574 const unsigned char *jpegBuf,
1575 unsigned long jpegSize,
1576 unsigned char **dstPlanes, int width,
1577 int *strides, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001578{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001579 int i, sfi, row, retval = 0;
1580 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1581 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1582 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1583 JSAMPLE *_tmpbuf = NULL, *ptr;
1584 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1585 int dctsize;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001586
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001587 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001588 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +00001589
Chris Blumecca8c4d2019-03-01 01:09:50 -08001590 for (i = 0; i < MAX_COMPONENTS; i++) {
1591 tmpbuf[i] = NULL; outbuf[i] = NULL;
1592 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001593
Chris Blumecca8c4d2019-03-01 01:09:50 -08001594 if ((this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001595 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
noel@chromium.org3395bcc2014-04-14 06:56:00 +00001596
Chris Blumecca8c4d2019-03-01 01:09:50 -08001597 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1598 width < 0 || height < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001599 THROW("tjDecompressToYUVPlanes(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001600
Chris Blumecca8c4d2019-03-01 01:09:50 -08001601#ifndef NO_PUTENV
1602 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1603 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1604 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1605#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001606
Chris Blumecca8c4d2019-03-01 01:09:50 -08001607 if (setjmp(this->jerr.setjmp_buffer)) {
1608 /* If we get here, the JPEG code has signaled an error. */
1609 retval = -1; goto bailout;
1610 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001611
Chris Blumecca8c4d2019-03-01 01:09:50 -08001612 if (!this->headerRead) {
1613 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1614 jpeg_read_header(dinfo, TRUE);
1615 }
1616 this->headerRead = 0;
1617 jpegSubsamp = getSubsamp(dinfo);
1618 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001619 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001620
Chris Blumecca8c4d2019-03-01 01:09:50 -08001621 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001622 THROW("tjDecompressToYUVPlanes(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001623
Chris Blumecca8c4d2019-03-01 01:09:50 -08001624 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1625 if (width == 0) width = jpegwidth;
1626 if (height == 0) height = jpegheight;
1627 for (i = 0; i < NUMSF; i++) {
1628 scaledw = TJSCALED(jpegwidth, sf[i]);
1629 scaledh = TJSCALED(jpegheight, sf[i]);
1630 if (scaledw <= width && scaledh <= height)
1631 break;
1632 }
1633 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001634 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001635 if (dinfo->num_components > 3)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001636 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001637
Chris Blumecca8c4d2019-03-01 01:09:50 -08001638 width = scaledw; height = scaledh;
1639 dinfo->scale_num = sf[i].num;
1640 dinfo->scale_denom = sf[i].denom;
1641 sfi = i;
1642 jpeg_calc_output_dimensions(dinfo);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001643
Chris Blumecca8c4d2019-03-01 01:09:50 -08001644 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
hbono@chromium.org98626972011-08-03 03:13:08 +00001645
Chris Blumecca8c4d2019-03-01 01:09:50 -08001646 for (i = 0; i < dinfo->num_components; i++) {
1647 jpeg_component_info *compptr = &dinfo->comp_info[i];
1648 int ih;
hbono@chromium.org98626972011-08-03 03:13:08 +00001649
Chris Blumecca8c4d2019-03-01 01:09:50 -08001650 iw[i] = compptr->width_in_blocks * dctsize;
1651 ih = compptr->height_in_blocks * dctsize;
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001652 pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp);
1653 ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001654 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1655 th[i] = compptr->v_samp_factor * dctsize;
1656 tmpbufsize += iw[i] * th[i];
1657 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001658 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001659 ptr = dstPlanes[i];
1660 for (row = 0; row < ph[i]; row++) {
1661 outbuf[i][row] = ptr;
1662 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1663 }
1664 }
1665 if (usetmpbuf) {
1666 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001667 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001668 ptr = _tmpbuf;
1669 for (i = 0; i < dinfo->num_components; i++) {
1670 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001671 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001672 for (row = 0; row < th[i]; row++) {
1673 tmpbuf[i][row] = ptr;
1674 ptr += iw[i];
1675 }
1676 }
1677 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001678
Chris Blumecca8c4d2019-03-01 01:09:50 -08001679 if (setjmp(this->jerr.setjmp_buffer)) {
1680 /* If we get here, the JPEG code has signaled an error. */
1681 retval = -1; goto bailout;
1682 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001683
Chris Blumecca8c4d2019-03-01 01:09:50 -08001684 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1685 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1686 dinfo->raw_data_out = TRUE;
1687
1688 jpeg_start_decompress(dinfo);
1689 for (row = 0; row < (int)dinfo->output_height;
1690 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1691 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1692 int crow[MAX_COMPONENTS];
1693
1694 for (i = 0; i < dinfo->num_components; i++) {
1695 jpeg_component_info *compptr = &dinfo->comp_info[i];
1696
1697 if (jpegSubsamp == TJ_420) {
1698 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1699 to be clever and use the IDCT to perform upsampling on the U and V
1700 planes. For instance, if the output image is to be scaled by 1/2
1701 relative to the JPEG image, then the scaling factor and upsampling
1702 effectively cancel each other, so a normal 8x8 IDCT can be used.
1703 However, this is not desirable when using the decompress-to-YUV
1704 functionality in TurboJPEG, since we want to output the U and V
1705 planes in their subsampled form. Thus, we have to override some
1706 internal libjpeg parameters to force it to use the "scaled" IDCT
1707 functions on the U and V planes. */
1708 compptr->_DCT_scaled_size = dctsize;
1709 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1710 sf[sfi].num / sf[sfi].denom *
1711 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1712 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1713 }
1714 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1715 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1716 else yuvptr[i] = &outbuf[i][crow[i]];
1717 }
1718 jpeg_read_raw_data(dinfo, yuvptr,
1719 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1720 if (usetmpbuf) {
1721 int j;
1722
1723 for (i = 0; i < dinfo->num_components; i++) {
1724 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1725 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1726 }
1727 }
1728 }
1729 }
1730 jpeg_finish_decompress(dinfo);
1731
1732bailout:
1733 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1734 for (i = 0; i < MAX_COMPONENTS; i++) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001735 free(tmpbuf[i]);
1736 free(outbuf[i]);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001737 }
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001738 free(_tmpbuf);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001739 if (this->jerr.warning) retval = -1;
1740 this->jerr.stopOnWarning = FALSE;
1741 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00001742}
1743
Chris Blumecca8c4d2019-03-01 01:09:50 -08001744DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1745 unsigned long jpegSize, unsigned char *dstBuf,
1746 int width, int pad, int height, int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001747{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001748 unsigned char *dstPlanes[3];
1749 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1750 int i, jpegwidth, jpegheight, scaledw, scaledh;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001751
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001752 GET_DINSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001753 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001754
Chris Blumecca8c4d2019-03-01 01:09:50 -08001755 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001756 pad < 1 || !IS_POW2(pad) || height < 0)
1757 THROW("tjDecompressToYUV2(): Invalid argument");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001758
Chris Blumecca8c4d2019-03-01 01:09:50 -08001759 if (setjmp(this->jerr.setjmp_buffer)) {
1760 /* If we get here, the JPEG code has signaled an error. */
1761 return -1;
1762 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001763
Chris Blumecca8c4d2019-03-01 01:09:50 -08001764 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1765 jpeg_read_header(dinfo, TRUE);
1766 jpegSubsamp = getSubsamp(dinfo);
1767 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001768 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001769
Chris Blumecca8c4d2019-03-01 01:09:50 -08001770 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1771 if (width == 0) width = jpegwidth;
1772 if (height == 0) height = jpegheight;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001773
Chris Blumecca8c4d2019-03-01 01:09:50 -08001774 for (i = 0; i < NUMSF; i++) {
1775 scaledw = TJSCALED(jpegwidth, sf[i]);
1776 scaledh = TJSCALED(jpegheight, sf[i]);
1777 if (scaledw <= width && scaledh <= height)
1778 break;
1779 }
1780 if (i >= NUMSF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001781 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001782
Chris Blumecca8c4d2019-03-01 01:09:50 -08001783 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1784 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1785 dstPlanes[0] = dstBuf;
1786 strides[0] = PAD(pw0, pad);
1787 if (jpegSubsamp == TJSAMP_GRAY) {
1788 strides[1] = strides[2] = 0;
1789 dstPlanes[1] = dstPlanes[2] = NULL;
1790 } else {
1791 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1792 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001793
Chris Blumecca8c4d2019-03-01 01:09:50 -08001794 strides[1] = strides[2] = PAD(pw1, pad);
1795 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1796 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1797 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001798
Chris Blumecca8c4d2019-03-01 01:09:50 -08001799 this->headerRead = 1;
1800 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1801 strides, height, flags);
1802
1803bailout:
1804 this->jerr.stopOnWarning = FALSE;
1805 return retval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001806}
1807
Chris Blumecca8c4d2019-03-01 01:09:50 -08001808DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1809 unsigned long jpegSize, unsigned char *dstBuf,
1810 int flags)
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001811{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001812 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
Tom Hudson0d47d2d2016-05-04 13:22:56 -04001813}
1814
hbono@chromium.org98626972011-08-03 03:13:08 +00001815
1816/* Transformer */
1817
Chris Blumecca8c4d2019-03-01 01:09:50 -08001818DLLEXPORT tjhandle tjInitTransform(void)
hbono@chromium.org98626972011-08-03 03:13:08 +00001819{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001820 tjinstance *this = NULL;
1821 tjhandle handle = NULL;
1822
1823 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1824 snprintf(errStr, JMSG_LENGTH_MAX,
1825 "tjInitTransform(): Memory allocation failure");
1826 return NULL;
1827 }
1828 MEMZERO(this, sizeof(tjinstance));
1829 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1830 handle = _tjInitCompress(this);
1831 if (!handle) return NULL;
1832 handle = _tjInitDecompress(this);
1833 return handle;
hbono@chromium.org98626972011-08-03 03:13:08 +00001834}
1835
1836
Chris Blumecca8c4d2019-03-01 01:09:50 -08001837DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1838 unsigned long jpegSize, int n,
1839 unsigned char **dstBufs, unsigned long *dstSizes,
1840 tjtransform *t, int flags)
hbono@chromium.org98626972011-08-03 03:13:08 +00001841{
Chris Blumecca8c4d2019-03-01 01:09:50 -08001842 jpeg_transform_info *xinfo = NULL;
1843 jvirt_barray_ptr *srccoefs, *dstcoefs;
1844 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001845
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001846 GET_INSTANCE(handle);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001847 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1848 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001849 THROW("tjTransform(): Instance has not been initialized for transformation");
hbono@chromium.org98626972011-08-03 03:13:08 +00001850
Chris Blumecca8c4d2019-03-01 01:09:50 -08001851 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1852 dstSizes == NULL || t == NULL || flags < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001853 THROW("tjTransform(): Invalid argument");
hbono@chromium.org98626972011-08-03 03:13:08 +00001854
Chris Blumecca8c4d2019-03-01 01:09:50 -08001855#ifndef NO_PUTENV
1856 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1857 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1858 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
1859#endif
hbono@chromium.org98626972011-08-03 03:13:08 +00001860
Chris Blumecca8c4d2019-03-01 01:09:50 -08001861 if ((xinfo =
1862 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001863 THROW("tjTransform(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001864 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
hbono@chromium.org98626972011-08-03 03:13:08 +00001865
Chris Blumecca8c4d2019-03-01 01:09:50 -08001866 if (setjmp(this->jerr.setjmp_buffer)) {
1867 /* If we get here, the JPEG code has signaled an error. */
1868 retval = -1; goto bailout;
1869 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001870
Chris Blumecca8c4d2019-03-01 01:09:50 -08001871 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
hbono@chromium.org98626972011-08-03 03:13:08 +00001872
Chris Blumecca8c4d2019-03-01 01:09:50 -08001873 for (i = 0; i < n; i++) {
1874 xinfo[i].transform = xformtypes[t[i].op];
1875 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1876 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1877 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1878 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1879 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1880 else xinfo[i].slow_hflip = 0;
hbono@chromium.org98626972011-08-03 03:13:08 +00001881
Chris Blumecca8c4d2019-03-01 01:09:50 -08001882 if (xinfo[i].crop) {
1883 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1884 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1885 if (t[i].r.w != 0) {
1886 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1887 } else
1888 xinfo[i].crop_width = JCROP_UNSET;
1889 if (t[i].r.h != 0) {
1890 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1891 } else
1892 xinfo[i].crop_height = JCROP_UNSET;
1893 }
1894 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1895 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001896
Chris Blumecca8c4d2019-03-01 01:09:50 -08001897 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1898 jpeg_read_header(dinfo, TRUE);
1899 jpegSubsamp = getSubsamp(dinfo);
1900 if (jpegSubsamp < 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001901 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
hbono@chromium.org98626972011-08-03 03:13:08 +00001902
Chris Blumecca8c4d2019-03-01 01:09:50 -08001903 for (i = 0; i < n; i++) {
1904 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001905 THROW("tjTransform(): Transform is not perfect");
hbono@chromium.org98626972011-08-03 03:13:08 +00001906
Chris Blumecca8c4d2019-03-01 01:09:50 -08001907 if (xinfo[i].crop) {
1908 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1909 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001910 snprintf(this->errStr, JMSG_LENGTH_MAX,
Chris Blumecca8c4d2019-03-01 01:09:50 -08001911 "To crop this JPEG image, x must be a multiple of %d\n"
1912 "and y must be a multiple of %d.\n",
1913 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001914 this->isInstanceError = TRUE;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001915 retval = -1; goto bailout;
1916 }
1917 }
1918 }
hbono@chromium.org98626972011-08-03 03:13:08 +00001919
Chris Blumecca8c4d2019-03-01 01:09:50 -08001920 srccoefs = jpeg_read_coefficients(dinfo);
hbono@chromium.org98626972011-08-03 03:13:08 +00001921
Chris Blumecca8c4d2019-03-01 01:09:50 -08001922 for (i = 0; i < n; i++) {
1923 int w, h, alloc = 1;
hbono@chromium.org98626972011-08-03 03:13:08 +00001924
Chris Blumecca8c4d2019-03-01 01:09:50 -08001925 if (!xinfo[i].crop) {
1926 w = dinfo->image_width; h = dinfo->image_height;
1927 } else {
1928 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1929 }
1930 if (flags & TJFLAG_NOREALLOC) {
1931 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1932 }
1933 if (!(t[i].options & TJXOPT_NOOUTPUT))
1934 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1935 jpeg_copy_critical_parameters(dinfo, cinfo);
1936 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1937 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1938 jpeg_simple_progression(cinfo);
1939 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1940 jpeg_write_coefficients(cinfo, dstcoefs);
1941 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1942 JCOPYOPT_NONE : JCOPYOPT_ALL);
1943 } else
1944 jinit_c_master_control(cinfo, TRUE);
1945 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1946 if (t[i].customFilter) {
1947 int ci, y;
1948 JDIMENSION by;
hbono@chromium.org98626972011-08-03 03:13:08 +00001949
Chris Blumecca8c4d2019-03-01 01:09:50 -08001950 for (ci = 0; ci < cinfo->num_components; ci++) {
1951 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1952 tjregion arrayRegion = {
1953 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1954 };
1955 tjregion planeRegion = {
1956 0, 0, compptr->width_in_blocks * DCTSIZE,
1957 compptr->height_in_blocks * DCTSIZE
1958 };
1959
1960 for (by = 0; by < compptr->height_in_blocks;
1961 by += compptr->v_samp_factor) {
1962 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1963 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1964 TRUE);
1965
1966 for (y = 0; y < compptr->v_samp_factor; y++) {
1967 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1968 i, &t[i]) == -1)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001969 THROW("tjTransform(): Error in custom filter");
Chris Blumecca8c4d2019-03-01 01:09:50 -08001970 arrayRegion.y += DCTSIZE;
1971 }
1972 }
1973 }
1974 }
1975 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1976 }
1977
1978 jpeg_finish_decompress(dinfo);
1979
1980bailout:
1981 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1982 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001983 free(xinfo);
Chris Blumecca8c4d2019-03-01 01:09:50 -08001984 if (this->jerr.warning) retval = -1;
1985 this->jerr.stopOnWarning = FALSE;
1986 return retval;
1987}
1988
1989
1990DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1991 int align, int *height, int *pixelFormat,
1992 int flags)
1993{
Jonathan Wrightdb870df2020-08-05 11:42:22 +01001994 int retval = 0, tempc;
1995 size_t pitch;
Chris Blumecca8c4d2019-03-01 01:09:50 -08001996 tjhandle handle = NULL;
1997 tjinstance *this;
1998 j_compress_ptr cinfo = NULL;
1999 cjpeg_source_ptr src;
2000 unsigned char *dstBuf = NULL;
2001 FILE *file = NULL;
2002 boolean invert;
2003
2004 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2005 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002006 THROWG("tjLoadImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002007 if ((align & (align - 1)) != 0)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002008 THROWG("tjLoadImage(): Alignment must be a power of 2");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002009
2010 if ((handle = tjInitCompress()) == NULL) return NULL;
2011 this = (tjinstance *)handle;
2012 cinfo = &this->cinfo;
2013
2014 if ((file = fopen(filename, "rb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002015 THROW_UNIX("tjLoadImage(): Cannot open input file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002016
2017 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002018 THROW_UNIX("tjLoadImage(): Could not read input file")
Chris Blumecca8c4d2019-03-01 01:09:50 -08002019 else if (tempc == EOF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002020 THROWG("tjLoadImage(): Input file contains no data");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002021
2022 if (setjmp(this->jerr.setjmp_buffer)) {
2023 /* If we get here, the JPEG code has signaled an error. */
2024 retval = -1; goto bailout;
2025 }
2026
2027 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2028 else cinfo->in_color_space = pf2cs[*pixelFormat];
2029 if (tempc == 'B') {
2030 if ((src = jinit_read_bmp(cinfo, FALSE)) == 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 if (tempc == 'P') {
2034 if ((src = jinit_read_ppm(cinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002035 THROWG("tjLoadImage(): Could not initialize bitmap loader");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002036 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2037 } else
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002038 THROWG("tjLoadImage(): Unsupported file type");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002039
2040 src->input_file = file;
2041 (*src->start_input) (cinfo, src);
2042 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
2043
2044 *width = cinfo->image_width; *height = cinfo->image_height;
2045 *pixelFormat = cs2pf[cinfo->in_color_space];
2046
2047 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002048 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2049 (unsigned long long)((size_t)-1) ||
2050 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2051 THROWG("tjLoadImage(): Memory allocation failure");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002052
2053 if (setjmp(this->jerr.setjmp_buffer)) {
2054 /* If we get here, the JPEG code has signaled an error. */
2055 retval = -1; goto bailout;
2056 }
2057
2058 while (cinfo->next_scanline < cinfo->image_height) {
2059 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
2060
2061 for (i = 0; i < nlines; i++) {
2062 unsigned char *dstptr;
2063 int row;
2064
2065 row = cinfo->next_scanline + i;
2066 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2067 else dstptr = &dstBuf[row * pitch];
2068 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2069 }
2070 cinfo->next_scanline += nlines;
2071 }
2072
2073 (*src->finish_input) (cinfo, src);
2074
2075bailout:
2076 if (handle) tjDestroy(handle);
2077 if (file) fclose(file);
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002078 if (retval < 0) { free(dstBuf); dstBuf = NULL; }
Chris Blumecca8c4d2019-03-01 01:09:50 -08002079 return dstBuf;
2080}
2081
2082
2083DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2084 int width, int pitch, int height, int pixelFormat,
2085 int flags)
2086{
2087 int retval = 0;
2088 tjhandle handle = NULL;
2089 tjinstance *this;
2090 j_decompress_ptr dinfo = NULL;
2091 djpeg_dest_ptr dst;
2092 FILE *file = NULL;
2093 char *ptr = NULL;
2094 boolean invert;
2095
2096 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2097 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002098 THROWG("tjSaveImage(): Invalid argument");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002099
2100 if ((handle = tjInitDecompress()) == NULL)
2101 return -1;
2102 this = (tjinstance *)handle;
2103 dinfo = &this->dinfo;
2104
2105 if ((file = fopen(filename, "wb")) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002106 THROW_UNIX("tjSaveImage(): Cannot open output file");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002107
2108 if (setjmp(this->jerr.setjmp_buffer)) {
2109 /* If we get here, the JPEG code has signaled an error. */
2110 retval = -1; goto bailout;
2111 }
2112
2113 this->dinfo.out_color_space = pf2cs[pixelFormat];
2114 dinfo->image_width = width; dinfo->image_height = height;
2115 dinfo->global_state = DSTATE_READY;
2116 dinfo->scale_num = dinfo->scale_denom = 1;
2117
2118 ptr = strrchr(filename, '.');
2119 if (ptr && !strcasecmp(ptr, ".bmp")) {
2120 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002121 THROWG("tjSaveImage(): Could not initialize bitmap writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002122 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2123 } else {
2124 if ((dst = jinit_write_ppm(dinfo)) == NULL)
Jonathan Wrightdb870df2020-08-05 11:42:22 +01002125 THROWG("tjSaveImage(): Could not initialize PPM writer");
Chris Blumecca8c4d2019-03-01 01:09:50 -08002126 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2127 }
2128
2129 dst->output_file = file;
2130 (*dst->start_output) (dinfo, dst);
2131 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
2132
2133 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2134
2135 while (dinfo->output_scanline < dinfo->output_height) {
2136 unsigned char *rowptr;
2137
2138 if (invert)
2139 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2140 else
2141 rowptr = &buffer[dinfo->output_scanline * pitch];
2142 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2143 (*dst->put_pixel_rows) (dinfo, dst, 1);
2144 dinfo->output_scanline++;
2145 }
2146
2147 (*dst->finish_output) (dinfo, dst);
2148
2149bailout:
2150 if (handle) tjDestroy(handle);
2151 if (file) fclose(file);
2152 return retval;
hbono@chromium.org98626972011-08-03 03:13:08 +00002153}