blob: 3a1e3a980a39381e41b88301b68b2ec8b05ffa8f [file] [log] [blame]
DRC9b28def2011-05-21 14:37:15 +00001/*
DRC3d9c64e2019-01-01 18:57:36 -06002 * Copyright (C)2009-2019 D. R. Commander. All Rights Reserved.
DRC2e7b76b2009-04-03 12:04:24 +00003 *
DRC9b28def2011-05-21 14:37:15 +00004 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
DRC2e7b76b2009-04-03 12:04:24 +00006 *
DRC9b28def2011-05-21 14:37:15 +00007 * - 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.
DRC2e7b76b2009-04-03 12:04:24 +000027 */
28
DRCbdfcb742013-01-22 13:56:34 +000029/* TurboJPEG/LJT: this implements the TurboJPEG API using libjpeg or
30 libjpeg-turbo */
DRC2e7b76b2009-04-03 12:04:24 +000031
32#include <stdio.h>
33#include <stdlib.h>
DRC0713c1b2014-08-22 13:43:33 +000034#include <ctype.h>
DRC296c71b2011-05-25 04:12:52 +000035#include <jinclude.h>
DRCfbb67472010-11-24 04:02:37 +000036#define JPEG_INTERNALS
DRC2e7b76b2009-04-03 12:04:24 +000037#include <jpeglib.h>
38#include <jerror.h>
39#include <setjmp.h>
DRCaa745902017-11-16 18:09:07 -060040#include <errno.h>
DRC2e7b76b2009-04-03 12:04:24 +000041#include "./turbojpeg.h"
DRC29e453f2018-03-16 14:09:53 -050042#include "./tjutil.h"
DRC890f1e02011-02-26 22:02:37 +000043#include "transupp.h"
DRC418fe282013-05-07 21:17:35 +000044#include "./jpegcomp.h"
DRCaa745902017-11-16 18:09:07 -060045#include "./cdjpeg.h"
DRC2a2e4512011-01-05 22:33:24 +000046
DRC19c791c2018-03-08 10:55:20 -060047extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
48 boolean);
DRC6fa14b32015-08-13 20:06:03 -050049extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
DRC19c791c2018-03-08 10:55:20 -060050 unsigned long);
DRC9b28def2011-05-21 14:37:15 +000051
DRC293263c2018-03-17 15:14:35 -050052#define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
DRCbce58f42019-04-12 07:49:35 -050053#define IS_POW2(x) (((x) & (x - 1)) == 0)
DRC2e7b76b2009-04-03 12:04:24 +000054
55
DRC8c40ac82017-11-16 18:46:01 -060056/* Error handling (based on example in example.txt) */
DRC2e7b76b2009-04-03 12:04:24 +000057
DRC19c791c2018-03-08 10:55:20 -060058static char errStr[JMSG_LENGTH_MAX] = "No error";
DRC2e7b76b2009-04-03 12:04:24 +000059
DRC19c791c2018-03-08 10:55:20 -060060struct my_error_mgr {
61 struct jpeg_error_mgr pub;
62 jmp_buf setjmp_buffer;
63 void (*emit_message) (j_common_ptr, int);
64 boolean warning, stopOnWarning;
DRC9b28def2011-05-21 14:37:15 +000065};
66typedef struct my_error_mgr *my_error_ptr;
DRC2e7b76b2009-04-03 12:04:24 +000067
DRC696e7542018-06-12 18:49:37 -050068#define JMESSAGE(code, string) string,
69static const char *turbojpeg_message_table[] = {
70#include "cderror.h"
71 NULL
72};
73
DRC2e7b76b2009-04-03 12:04:24 +000074static void my_error_exit(j_common_ptr cinfo)
75{
DRC19c791c2018-03-08 10:55:20 -060076 my_error_ptr myerr = (my_error_ptr)cinfo->err;
77
78 (*cinfo->err->output_message) (cinfo);
79 longjmp(myerr->setjmp_buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +000080}
81
DRC9b28def2011-05-21 14:37:15 +000082/* Based on output_message() in jerror.c */
83
DRC2e7b76b2009-04-03 12:04:24 +000084static void my_output_message(j_common_ptr cinfo)
85{
DRC19c791c2018-03-08 10:55:20 -060086 (*cinfo->err->format_message) (cinfo, errStr);
DRC2e7b76b2009-04-03 12:04:24 +000087}
88
DRC1f79c7c2015-06-01 19:22:41 +000089static void my_emit_message(j_common_ptr cinfo, int msg_level)
90{
DRC19c791c2018-03-08 10:55:20 -060091 my_error_ptr myerr = (my_error_ptr)cinfo->err;
92
93 myerr->emit_message(cinfo, msg_level);
94 if (msg_level < 0) {
95 myerr->warning = TRUE;
96 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
97 }
DRC1f79c7c2015-06-01 19:22:41 +000098}
99
DRC2e7b76b2009-04-03 12:04:24 +0000100
DRC9b28def2011-05-21 14:37:15 +0000101/* Global structures, macros, etc. */
DRC2e7b76b2009-04-03 12:04:24 +0000102
DRC19c791c2018-03-08 10:55:20 -0600103enum { COMPRESS = 1, DECOMPRESS = 2 };
DRC9b28def2011-05-21 14:37:15 +0000104
DRC19c791c2018-03-08 10:55:20 -0600105typedef struct _tjinstance {
106 struct jpeg_compress_struct cinfo;
107 struct jpeg_decompress_struct dinfo;
108 struct my_error_mgr jerr;
109 int init, headerRead;
110 char errStr[JMSG_LENGTH_MAX];
111 boolean isInstanceError;
DRC9b28def2011-05-21 14:37:15 +0000112} tjinstance;
DRC2e7b76b2009-04-03 12:04:24 +0000113
DRC19c791c2018-03-08 10:55:20 -0600114static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
DRC9b28def2011-05-21 14:37:15 +0000115
DRC19c791c2018-03-08 10:55:20 -0600116static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
117 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
118 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
DRC890f1e02011-02-26 22:02:37 +0000119};
DRC9b28def2011-05-21 14:37:15 +0000120
DRC293263c2018-03-17 15:14:35 -0500121#define NUMSF 16
DRC19c791c2018-03-08 10:55:20 -0600122static const tjscalingfactor sf[NUMSF] = {
123 { 2, 1 },
124 { 15, 8 },
125 { 7, 4 },
126 { 13, 8 },
127 { 3, 2 },
128 { 11, 8 },
129 { 5, 4 },
130 { 9, 8 },
131 { 1, 1 },
132 { 7, 8 },
133 { 3, 4 },
134 { 5, 8 },
135 { 1, 2 },
136 { 3, 8 },
137 { 1, 4 },
138 { 1, 8 }
DRC109a5782011-03-01 09:53:07 +0000139};
DRC2e7b76b2009-04-03 12:04:24 +0000140
DRC19c791c2018-03-08 10:55:20 -0600141static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
142 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
143 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
144 JCS_EXT_ARGB, JCS_CMYK
DRCaa745902017-11-16 18:09:07 -0600145};
146
DRC19c791c2018-03-08 10:55:20 -0600147static int cs2pf[JPEG_NUMCS] = {
148 TJPF_UNKNOWN, TJPF_GRAY,
DRCaa745902017-11-16 18:09:07 -0600149#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600150 TJPF_RGB,
DRCaa745902017-11-16 18:09:07 -0600151#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600152 TJPF_BGR,
DRCaa745902017-11-16 18:09:07 -0600153#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600154 TJPF_RGBX,
DRCaa745902017-11-16 18:09:07 -0600155#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600156 TJPF_BGRX,
DRCaa745902017-11-16 18:09:07 -0600157#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600158 TJPF_XBGR,
DRCaa745902017-11-16 18:09:07 -0600159#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600160 TJPF_XRGB,
DRCaa745902017-11-16 18:09:07 -0600161#endif
DRC19c791c2018-03-08 10:55:20 -0600162 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
163 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
164 TJPF_UNKNOWN
DRCaa745902017-11-16 18:09:07 -0600165};
166
DRCbce58f42019-04-12 07:49:35 -0500167#define THROWG(m) { \
DRC19c791c2018-03-08 10:55:20 -0600168 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
169 retval = -1; goto bailout; \
DRCaa745902017-11-16 18:09:07 -0600170}
DRCbce58f42019-04-12 07:49:35 -0500171#define THROW_UNIX(m) { \
DRC19c791c2018-03-08 10:55:20 -0600172 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
173 retval = -1; goto bailout; \
174}
DRCbce58f42019-04-12 07:49:35 -0500175#define THROW(m) { \
DRC19c791c2018-03-08 10:55:20 -0600176 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
DRCbce58f42019-04-12 07:49:35 -0500177 this->isInstanceError = TRUE; THROWG(m) \
DRC19c791c2018-03-08 10:55:20 -0600178}
179
DRCbce58f42019-04-12 07:49:35 -0500180#define GET_INSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600181 tjinstance *this = (tjinstance *)handle; \
182 j_compress_ptr cinfo = NULL; \
183 j_decompress_ptr dinfo = NULL; \
184 \
185 if (!this) { \
186 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
187 return -1; \
188 } \
189 cinfo = &this->cinfo; dinfo = &this->dinfo; \
190 this->jerr.warning = FALSE; \
191 this->isInstanceError = FALSE;
192
DRCbce58f42019-04-12 07:49:35 -0500193#define GET_CINSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600194 tjinstance *this = (tjinstance *)handle; \
195 j_compress_ptr cinfo = NULL; \
196 \
197 if (!this) { \
198 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
199 return -1; \
200 } \
201 cinfo = &this->cinfo; \
202 this->jerr.warning = FALSE; \
203 this->isInstanceError = FALSE;
204
DRCbce58f42019-04-12 07:49:35 -0500205#define GET_DINSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600206 tjinstance *this = (tjinstance *)handle; \
207 j_decompress_ptr dinfo = NULL; \
208 \
209 if (!this) { \
210 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
211 return -1; \
212 } \
213 dinfo = &this->dinfo; \
214 this->jerr.warning = FALSE; \
215 this->isInstanceError = FALSE;
DRC2e7b76b2009-04-03 12:04:24 +0000216
DRC9b28def2011-05-21 14:37:15 +0000217static int getPixelFormat(int pixelSize, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000218{
DRC19c791c2018-03-08 10:55:20 -0600219 if (pixelSize == 1) return TJPF_GRAY;
220 if (pixelSize == 3) {
221 if (flags & TJ_BGR) return TJPF_BGR;
222 else return TJPF_RGB;
223 }
224 if (pixelSize == 4) {
225 if (flags & TJ_ALPHAFIRST) {
226 if (flags & TJ_BGR) return TJPF_XBGR;
227 else return TJPF_XRGB;
228 } else {
229 if (flags & TJ_BGR) return TJPF_BGRX;
230 else return TJPF_RGBX;
231 }
232 }
233 return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000234}
235
DRC19c791c2018-03-08 10:55:20 -0600236static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
237 int subsamp, int jpegQual, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000238{
DRC19c791c2018-03-08 10:55:20 -0600239 int retval = 0;
DRC500b5ec2019-02-17 09:06:42 -0600240#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600241 char *env = NULL;
DRC500b5ec2019-02-17 09:06:42 -0600242#endif
DRCf12bb302011-09-07 05:03:18 +0000243
DRC19c791c2018-03-08 10:55:20 -0600244 cinfo->in_color_space = pf2cs[pixelFormat];
245 cinfo->input_components = tjPixelSize[pixelFormat];
246 jpeg_set_defaults(cinfo);
DRC0713c1b2014-08-22 13:43:33 +0000247
DRCfeccdcf2015-02-23 19:19:40 +0000248#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600249 if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
250 !strcmp(env, "1"))
251 cinfo->optimize_coding = TRUE;
252 if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
253 !strcmp(env, "1"))
254 cinfo->arith_code = TRUE;
255 if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
256 int temp = -1;
257 char tempc = 0;
258
259 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
260 temp <= 65535) {
261 if (toupper(tempc) == 'B') {
262 cinfo->restart_interval = temp;
263 cinfo->restart_in_rows = 0;
264 } else
265 cinfo->restart_in_rows = temp;
266 }
267 }
DRCfeccdcf2015-02-23 19:19:40 +0000268#endif
DRC0713c1b2014-08-22 13:43:33 +0000269
DRC19c791c2018-03-08 10:55:20 -0600270 if (jpegQual >= 0) {
271 jpeg_set_quality(cinfo, jpegQual, TRUE);
272 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
273 cinfo->dct_method = JDCT_ISLOW;
274 else
275 cinfo->dct_method = JDCT_FASTEST;
276 }
277 if (subsamp == TJSAMP_GRAY)
278 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
279 else if (pixelFormat == TJPF_CMYK)
280 jpeg_set_colorspace(cinfo, JCS_YCCK);
281 else
282 jpeg_set_colorspace(cinfo, JCS_YCbCr);
DRC2e7b76b2009-04-03 12:04:24 +0000283
DRC19c791c2018-03-08 10:55:20 -0600284 if (flags & TJFLAG_PROGRESSIVE)
285 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000286#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600287 else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
288 !strcmp(env, "1"))
289 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000290#endif
DRC0713c1b2014-08-22 13:43:33 +0000291
DRC19c791c2018-03-08 10:55:20 -0600292 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
293 cinfo->comp_info[1].h_samp_factor = 1;
294 cinfo->comp_info[2].h_samp_factor = 1;
295 if (cinfo->num_components > 3)
296 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
297 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
298 cinfo->comp_info[1].v_samp_factor = 1;
299 cinfo->comp_info[2].v_samp_factor = 1;
300 if (cinfo->num_components > 3)
301 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
DRCf12bb302011-09-07 05:03:18 +0000302
DRC19c791c2018-03-08 10:55:20 -0600303 return retval;
DRC9b28def2011-05-21 14:37:15 +0000304}
305
DRC9b28def2011-05-21 14:37:15 +0000306
DRC9b49f0e2011-07-12 03:17:23 +0000307static int getSubsamp(j_decompress_ptr dinfo)
308{
DRC19c791c2018-03-08 10:55:20 -0600309 int retval = -1, i, k;
DRCea1eea42014-11-19 00:55:28 +0000310
DRC19c791c2018-03-08 10:55:20 -0600311 /* The sampling factors actually have no meaning with grayscale JPEG files,
312 and in fact it's possible to generate grayscale JPEGs with sampling
313 factors > 1 (even though those sampling factors are ignored by the
314 decompressor.) Thus, we need to treat grayscale as a special case. */
315 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
316 return TJSAMP_GRAY;
DRCea1eea42014-11-19 00:55:28 +0000317
DRC19c791c2018-03-08 10:55:20 -0600318 for (i = 0; i < NUMSUBOPT; i++) {
319 if (dinfo->num_components == pixelsize[i] ||
320 ((dinfo->jpeg_color_space == JCS_YCCK ||
321 dinfo->jpeg_color_space == JCS_CMYK) &&
322 pixelsize[i] == 3 && dinfo->num_components == 4)) {
323 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
324 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
325 int match = 0;
326
327 for (k = 1; k < dinfo->num_components; k++) {
328 int href = 1, vref = 1;
329
DRC2401e4d2018-04-26 18:01:52 -0500330 if ((dinfo->jpeg_color_space == JCS_YCCK ||
331 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600332 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
333 }
334 if (dinfo->comp_info[k].h_samp_factor == href &&
335 dinfo->comp_info[k].v_samp_factor == vref)
336 match++;
337 }
338 if (match == dinfo->num_components - 1) {
339 retval = i; break;
340 }
341 }
342 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
343 in non-standard ways. */
344 if (dinfo->comp_info[0].h_samp_factor == 2 &&
345 dinfo->comp_info[0].v_samp_factor == 2 &&
346 (i == TJSAMP_422 || i == TJSAMP_440)) {
347 int match = 0;
348
349 for (k = 1; k < dinfo->num_components; k++) {
350 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
351
DRC2401e4d2018-04-26 18:01:52 -0500352 if ((dinfo->jpeg_color_space == JCS_YCCK ||
353 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600354 href = vref = 2;
355 }
356 if (dinfo->comp_info[k].h_samp_factor == href &&
357 dinfo->comp_info[k].v_samp_factor == vref)
358 match++;
359 }
360 if (match == dinfo->num_components - 1) {
361 retval = i; break;
362 }
363 }
DRC479501b2019-01-21 13:57:55 -0600364 /* Handle 4:4:4 images whose sampling factors are specified in
365 non-standard ways. */
366 if (dinfo->comp_info[0].h_samp_factor *
367 dinfo->comp_info[0].v_samp_factor <=
368 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
369 int match = 0;
370 for (k = 1; k < dinfo->num_components; k++) {
371 if (dinfo->comp_info[i].h_samp_factor ==
372 dinfo->comp_info[0].h_samp_factor &&
373 dinfo->comp_info[i].v_samp_factor ==
374 dinfo->comp_info[0].v_samp_factor)
375 match++;
376 if (match == dinfo->num_components - 1) {
377 retval = i; break;
378 }
379 }
380 }
DRC19c791c2018-03-08 10:55:20 -0600381 }
382 }
383 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000384}
385
386
DRC9b28def2011-05-21 14:37:15 +0000387/* General API functions */
388
DRC19c791c2018-03-08 10:55:20 -0600389DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
DRCb9ab64d2017-05-11 21:02:29 -0500390{
DRC19c791c2018-03-08 10:55:20 -0600391 tjinstance *this = (tjinstance *)handle;
392
393 if (this && this->isInstanceError) {
394 this->isInstanceError = FALSE;
395 return this->errStr;
396 } else
397 return errStr;
DRCb9ab64d2017-05-11 21:02:29 -0500398}
399
400
DRC19c791c2018-03-08 10:55:20 -0600401DLLEXPORT char *tjGetErrorStr(void)
DRC9b28def2011-05-21 14:37:15 +0000402{
DRC19c791c2018-03-08 10:55:20 -0600403 return errStr;
DRC9b28def2011-05-21 14:37:15 +0000404}
405
406
DRC19c791c2018-03-08 10:55:20 -0600407DLLEXPORT int tjGetErrorCode(tjhandle handle)
DRCd4092f62017-06-27 10:54:21 -0500408{
DRC19c791c2018-03-08 10:55:20 -0600409 tjinstance *this = (tjinstance *)handle;
410
411 if (this && this->jerr.warning) return TJERR_WARNING;
412 else return TJERR_FATAL;
DRCd4092f62017-06-27 10:54:21 -0500413}
414
415
DRC19c791c2018-03-08 10:55:20 -0600416DLLEXPORT int tjDestroy(tjhandle handle)
DRC9b28def2011-05-21 14:37:15 +0000417{
DRCbce58f42019-04-12 07:49:35 -0500418 GET_INSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -0600419
420 if (setjmp(this->jerr.setjmp_buffer)) return -1;
421 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
422 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
423 free(this);
424 return 0;
DRC9b28def2011-05-21 14:37:15 +0000425}
426
427
DRC6b76f752011-05-24 16:52:47 +0000428/* These are exposed mainly because Windows can't malloc() and free() across
429 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
430 with turbojpeg.dll for compatibility reasons. However, these functions
431 can potentially be used for other purposes by different implementations. */
432
DRC19c791c2018-03-08 10:55:20 -0600433DLLEXPORT void tjFree(unsigned char *buf)
DRC6b76f752011-05-24 16:52:47 +0000434{
DRC19c791c2018-03-08 10:55:20 -0600435 if (buf) free(buf);
DRC6b76f752011-05-24 16:52:47 +0000436}
437
438
DRC19c791c2018-03-08 10:55:20 -0600439DLLEXPORT unsigned char *tjAlloc(int bytes)
DRC6b76f752011-05-24 16:52:47 +0000440{
DRC19c791c2018-03-08 10:55:20 -0600441 return (unsigned char *)malloc(bytes);
DRC6b76f752011-05-24 16:52:47 +0000442}
443
444
DRC9b28def2011-05-21 14:37:15 +0000445/* Compressor */
446
447static tjhandle _tjInitCompress(tjinstance *this)
448{
DRC19c791c2018-03-08 10:55:20 -0600449 static unsigned char buffer[1];
450 unsigned char *buf = buffer;
451 unsigned long size = 1;
DRC9b28def2011-05-21 14:37:15 +0000452
DRC19c791c2018-03-08 10:55:20 -0600453 /* This is also straight out of example.txt */
454 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
455 this->jerr.pub.error_exit = my_error_exit;
456 this->jerr.pub.output_message = my_output_message;
457 this->jerr.emit_message = this->jerr.pub.emit_message;
458 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -0500459 this->jerr.pub.addon_message_table = turbojpeg_message_table;
460 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
461 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC9b28def2011-05-21 14:37:15 +0000462
DRC19c791c2018-03-08 10:55:20 -0600463 if (setjmp(this->jerr.setjmp_buffer)) {
464 /* If we get here, the JPEG code has signaled an error. */
465 if (this) free(this);
466 return NULL;
467 }
DRC9b28def2011-05-21 14:37:15 +0000468
DRC19c791c2018-03-08 10:55:20 -0600469 jpeg_create_compress(&this->cinfo);
470 /* Make an initial call so it will create the destination manager */
471 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
DRC9b28def2011-05-21 14:37:15 +0000472
DRC19c791c2018-03-08 10:55:20 -0600473 this->init |= COMPRESS;
474 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +0000475}
476
DRC19c791c2018-03-08 10:55:20 -0600477DLLEXPORT tjhandle tjInitCompress(void)
DRC890f1e02011-02-26 22:02:37 +0000478{
DRC19c791c2018-03-08 10:55:20 -0600479 tjinstance *this = NULL;
480
481 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
482 snprintf(errStr, JMSG_LENGTH_MAX,
483 "tjInitCompress(): Memory allocation failure");
484 return NULL;
485 }
486 MEMZERO(this, sizeof(tjinstance));
487 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
488 return _tjInitCompress(this);
DRC890f1e02011-02-26 22:02:37 +0000489}
490
DRC84241602011-02-25 02:08:23 +0000491
DRC19c791c2018-03-08 10:55:20 -0600492DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000493{
DRC2a9e3bd2019-07-11 15:30:04 -0500494 unsigned long long retval = 0;
DRC19c791c2018-03-08 10:55:20 -0600495 int mcuw, mcuh, chromasf;
DRC9b49f0e2011-07-12 03:17:23 +0000496
DRC19c791c2018-03-08 10:55:20 -0600497 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
DRCbce58f42019-04-12 07:49:35 -0500498 THROWG("tjBufSize(): Invalid argument");
DRC9b49f0e2011-07-12 03:17:23 +0000499
DRC19c791c2018-03-08 10:55:20 -0600500 /* This allows for rare corner cases in which a JPEG image can actually be
501 larger than the uncompressed input (we wouldn't mention it if it hadn't
502 happened before.) */
503 mcuw = tjMCUWidth[jpegSubsamp];
504 mcuh = tjMCUHeight[jpegSubsamp];
505 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
DRC2a9e3bd2019-07-11 15:30:04 -0500506 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
507 if (retval > (unsigned long long)((unsigned long)-1))
508 THROWG("tjBufSize(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600509
510bailout:
DRC2a9e3bd2019-07-11 15:30:04 -0500511 return (unsigned long)retval;
DRC9b49f0e2011-07-12 03:17:23 +0000512}
513
DRC19c791c2018-03-08 10:55:20 -0600514DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
DRC2e7b76b2009-04-03 12:04:24 +0000515{
DRC2a9e3bd2019-07-11 15:30:04 -0500516 unsigned long long retval = 0;
DRCf3cf9732011-02-22 00:16:14 +0000517
DRC19c791c2018-03-08 10:55:20 -0600518 if (width < 1 || height < 1)
DRCbce58f42019-04-12 07:49:35 -0500519 THROWG("TJBUFSIZE(): Invalid argument");
DRCf3cf9732011-02-22 00:16:14 +0000520
DRC19c791c2018-03-08 10:55:20 -0600521 /* This allows for rare corner cases in which a JPEG image can actually be
522 larger than the uncompressed input (we wouldn't mention it if it hadn't
523 happened before.) */
DRC2a9e3bd2019-07-11 15:30:04 -0500524 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
525 if (retval > (unsigned long long)((unsigned long)-1))
526 THROWG("TJBUFSIZE(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600527
528bailout:
DRC2a9e3bd2019-07-11 15:30:04 -0500529 return (unsigned long)retval;
DRCf3cf9732011-02-22 00:16:14 +0000530}
531
DRC84241602011-02-25 02:08:23 +0000532
DRC19c791c2018-03-08 10:55:20 -0600533DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
534 int subsamp)
DRCf3cf9732011-02-22 00:16:14 +0000535{
DRC2a9e3bd2019-07-11 15:30:04 -0500536 unsigned long long retval = 0;
537 int nc, i;
DRC40dd3142014-08-17 12:23:49 +0000538
DRC19c791c2018-03-08 10:55:20 -0600539 if (subsamp < 0 || subsamp >= NUMSUBOPT)
DRCbce58f42019-04-12 07:49:35 -0500540 THROWG("tjBufSizeYUV2(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000541
DRC19c791c2018-03-08 10:55:20 -0600542 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
543 for (i = 0; i < nc; i++) {
544 int pw = tjPlaneWidth(i, width, subsamp);
545 int stride = PAD(pw, pad);
546 int ph = tjPlaneHeight(i, height, subsamp);
DRCf3cf9732011-02-22 00:16:14 +0000547
DRC19c791c2018-03-08 10:55:20 -0600548 if (pw < 0 || ph < 0) return -1;
DRC2a9e3bd2019-07-11 15:30:04 -0500549 else retval += (unsigned long long)stride * ph;
DRC19c791c2018-03-08 10:55:20 -0600550 }
DRC2a9e3bd2019-07-11 15:30:04 -0500551 if (retval > (unsigned long long)((unsigned long)-1))
552 THROWG("tjBufSizeYUV2(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600553
554bailout:
DRC2a9e3bd2019-07-11 15:30:04 -0500555 return (unsigned long)retval;
DRC2e7b76b2009-04-03 12:04:24 +0000556}
557
DRC19c791c2018-03-08 10:55:20 -0600558DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
DRCf610d612013-04-26 10:33:29 +0000559{
DRC19c791c2018-03-08 10:55:20 -0600560 return tjBufSizeYUV2(width, 4, height, subsamp);
DRCf610d612013-04-26 10:33:29 +0000561}
DRC84241602011-02-25 02:08:23 +0000562
DRC19c791c2018-03-08 10:55:20 -0600563DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000564{
DRC19c791c2018-03-08 10:55:20 -0600565 return tjBufSizeYUV(width, height, subsamp);
DRC9b49f0e2011-07-12 03:17:23 +0000566}
567
568
DRC40dd3142014-08-17 12:23:49 +0000569DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
570{
DRC19c791c2018-03-08 10:55:20 -0600571 int pw, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000572
DRC19c791c2018-03-08 10:55:20 -0600573 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
DRCbce58f42019-04-12 07:49:35 -0500574 THROWG("tjPlaneWidth(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600575 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
576 if (componentID < 0 || componentID >= nc)
DRCbce58f42019-04-12 07:49:35 -0500577 THROWG("tjPlaneWidth(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000578
DRC19c791c2018-03-08 10:55:20 -0600579 pw = PAD(width, tjMCUWidth[subsamp] / 8);
580 if (componentID == 0)
581 retval = pw;
582 else
583 retval = pw * 8 / tjMCUWidth[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000584
DRC19c791c2018-03-08 10:55:20 -0600585bailout:
586 return retval;
DRC40dd3142014-08-17 12:23:49 +0000587}
588
589
590DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
591{
DRC19c791c2018-03-08 10:55:20 -0600592 int ph, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000593
DRC19c791c2018-03-08 10:55:20 -0600594 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
DRCbce58f42019-04-12 07:49:35 -0500595 THROWG("tjPlaneHeight(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600596 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
597 if (componentID < 0 || componentID >= nc)
DRCbce58f42019-04-12 07:49:35 -0500598 THROWG("tjPlaneHeight(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000599
DRC19c791c2018-03-08 10:55:20 -0600600 ph = PAD(height, tjMCUHeight[subsamp] / 8);
601 if (componentID == 0)
602 retval = ph;
603 else
604 retval = ph * 8 / tjMCUHeight[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000605
DRC19c791c2018-03-08 10:55:20 -0600606bailout:
607 return retval;
DRC40dd3142014-08-17 12:23:49 +0000608}
609
610
DRC19c791c2018-03-08 10:55:20 -0600611DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
612 int height, int subsamp)
DRC40dd3142014-08-17 12:23:49 +0000613{
DRC2a9e3bd2019-07-11 15:30:04 -0500614 unsigned long long retval = 0;
DRC19c791c2018-03-08 10:55:20 -0600615 int pw, ph;
DRC40dd3142014-08-17 12:23:49 +0000616
DRC19c791c2018-03-08 10:55:20 -0600617 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
DRCbce58f42019-04-12 07:49:35 -0500618 THROWG("tjPlaneSizeYUV(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000619
DRC19c791c2018-03-08 10:55:20 -0600620 pw = tjPlaneWidth(componentID, width, subsamp);
621 ph = tjPlaneHeight(componentID, height, subsamp);
622 if (pw < 0 || ph < 0) return -1;
DRC40dd3142014-08-17 12:23:49 +0000623
DRC19c791c2018-03-08 10:55:20 -0600624 if (stride == 0) stride = pw;
625 else stride = abs(stride);
DRC40dd3142014-08-17 12:23:49 +0000626
DRC2a9e3bd2019-07-11 15:30:04 -0500627 retval = (unsigned long long)stride * (ph - 1) + pw;
628 if (retval > (unsigned long long)((unsigned long)-1))
629 THROWG("tjPlaneSizeYUV(): Image is too large");
DRC40dd3142014-08-17 12:23:49 +0000630
DRC19c791c2018-03-08 10:55:20 -0600631bailout:
DRC2a9e3bd2019-07-11 15:30:04 -0500632 return (unsigned long)retval;
DRC40dd3142014-08-17 12:23:49 +0000633}
634
635
DRC19c791c2018-03-08 10:55:20 -0600636DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
637 int width, int pitch, int height, int pixelFormat,
638 unsigned char **jpegBuf, unsigned long *jpegSize,
639 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000640{
DRC19c791c2018-03-08 10:55:20 -0600641 int i, retval = 0, alloc = 1;
642 JSAMPROW *row_pointer = NULL;
DRC9b28def2011-05-21 14:37:15 +0000643
DRCbce58f42019-04-12 07:49:35 -0500644 GET_CINSTANCE(handle)
DRC19c791c2018-03-08 10:55:20 -0600645 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
646 if ((this->init & COMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -0500647 THROW("tjCompress2(): Instance has not been initialized for compression");
DRC9b28def2011-05-21 14:37:15 +0000648
DRC19c791c2018-03-08 10:55:20 -0600649 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
650 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
651 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
652 jpegQual < 0 || jpegQual > 100)
DRCbce58f42019-04-12 07:49:35 -0500653 THROW("tjCompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +0000654
DRC19c791c2018-03-08 10:55:20 -0600655 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC9b28def2011-05-21 14:37:15 +0000656
DRC19c791c2018-03-08 10:55:20 -0600657 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
DRCbce58f42019-04-12 07:49:35 -0500658 THROW("tjCompress2(): Memory allocation failure");
DRCd4c41fe2017-03-18 12:56:36 -0500659
DRC19c791c2018-03-08 10:55:20 -0600660 if (setjmp(this->jerr.setjmp_buffer)) {
661 /* If we get here, the JPEG code has signaled an error. */
662 retval = -1; goto bailout;
663 }
DRCd4c41fe2017-03-18 12:56:36 -0500664
DRC19c791c2018-03-08 10:55:20 -0600665 cinfo->image_width = width;
666 cinfo->image_height = height;
DRC9b28def2011-05-21 14:37:15 +0000667
DRCbd96b302018-03-17 00:06:10 -0500668#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600669 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
670 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
671 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500672#endif
DRC9b28def2011-05-21 14:37:15 +0000673
DRC19c791c2018-03-08 10:55:20 -0600674 if (flags & TJFLAG_NOREALLOC) {
675 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
676 }
677 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
678 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
679 return -1;
DRC9b28def2011-05-21 14:37:15 +0000680
DRC19c791c2018-03-08 10:55:20 -0600681 jpeg_start_compress(cinfo, TRUE);
682 for (i = 0; i < height; i++) {
683 if (flags & TJFLAG_BOTTOMUP)
DRC2a9e3bd2019-07-11 15:30:04 -0500684 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600685 else
DRC2a9e3bd2019-07-11 15:30:04 -0500686 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600687 }
688 while (cinfo->next_scanline < cinfo->image_height)
689 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
690 cinfo->image_height - cinfo->next_scanline);
691 jpeg_finish_compress(cinfo);
DRC9b28def2011-05-21 14:37:15 +0000692
DRC19c791c2018-03-08 10:55:20 -0600693bailout:
694 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
695 if (row_pointer) free(row_pointer);
696 if (this->jerr.warning) retval = -1;
697 this->jerr.stopOnWarning = FALSE;
698 return retval;
DRC9b28def2011-05-21 14:37:15 +0000699}
700
DRC19c791c2018-03-08 10:55:20 -0600701DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
702 int pitch, int height, int pixelSize,
703 unsigned char *jpegBuf, unsigned long *jpegSize,
704 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000705{
DRC19c791c2018-03-08 10:55:20 -0600706 int retval = 0;
707 unsigned long size;
708
709 if (flags & TJ_YUV) {
710 size = tjBufSizeYUV(width, height, jpegSubsamp);
711 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
712 getPixelFormat(pixelSize, flags), jpegBuf,
713 jpegSubsamp, flags);
714 } else {
715 retval = tjCompress2(handle, srcBuf, width, pitch, height,
716 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
717 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
718 }
719 *jpegSize = size;
720 return retval;
DRC9b28def2011-05-21 14:37:15 +0000721}
722
723
DRC19c791c2018-03-08 10:55:20 -0600724DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
725 int width, int pitch, int height,
726 int pixelFormat, unsigned char **dstPlanes,
727 int *strides, int subsamp, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000728{
DRC19c791c2018-03-08 10:55:20 -0600729 JSAMPROW *row_pointer = NULL;
730 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
731 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
732 JSAMPROW *outbuf[MAX_COMPONENTS];
733 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
734 JSAMPLE *ptr;
735 jpeg_component_info *compptr;
DRC2e7b76b2009-04-03 12:04:24 +0000736
DRCbce58f42019-04-12 07:49:35 -0500737 GET_CINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -0600738 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000739
DRC19c791c2018-03-08 10:55:20 -0600740 for (i = 0; i < MAX_COMPONENTS; i++) {
741 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
742 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
743 }
DRCfbb67472010-11-24 04:02:37 +0000744
DRC19c791c2018-03-08 10:55:20 -0600745 if ((this->init & COMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -0500746 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
DRCe2f8e692013-10-30 22:21:06 +0000747
DRC19c791c2018-03-08 10:55:20 -0600748 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
749 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
750 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
DRCbce58f42019-04-12 07:49:35 -0500751 THROW("tjEncodeYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600752 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
DRCbce58f42019-04-12 07:49:35 -0500753 THROW("tjEncodeYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000754
DRC19c791c2018-03-08 10:55:20 -0600755 if (pixelFormat == TJPF_CMYK)
DRCbce58f42019-04-12 07:49:35 -0500756 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
DRCcd7c3e62013-08-23 02:49:25 +0000757
DRC19c791c2018-03-08 10:55:20 -0600758 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC2e7b76b2009-04-03 12:04:24 +0000759
DRC19c791c2018-03-08 10:55:20 -0600760 if (setjmp(this->jerr.setjmp_buffer)) {
761 /* If we get here, the JPEG code has signaled an error. */
762 retval = -1; goto bailout;
763 }
DRCd4c41fe2017-03-18 12:56:36 -0500764
DRC19c791c2018-03-08 10:55:20 -0600765 cinfo->image_width = width;
766 cinfo->image_height = height;
DRC2e7b76b2009-04-03 12:04:24 +0000767
DRCbd96b302018-03-17 00:06:10 -0500768#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600769 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
770 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
771 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500772#endif
DRC0c6a2712010-02-22 08:34:44 +0000773
DRC19c791c2018-03-08 10:55:20 -0600774 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000775
DRC19c791c2018-03-08 10:55:20 -0600776 /* Execute only the parts of jpeg_start_compress() that we need. If we
777 were to call the whole jpeg_start_compress() function, then it would try
778 to write the file headers, which could overflow the output buffer if the
779 YUV image were very small. */
780 if (cinfo->global_state != CSTATE_START)
DRCbce58f42019-04-12 07:49:35 -0500781 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
DRC19c791c2018-03-08 10:55:20 -0600782 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
783 jinit_c_master_control(cinfo, FALSE);
784 jinit_color_converter(cinfo);
785 jinit_downsampler(cinfo);
786 (*cinfo->cconvert->start_pass) (cinfo);
DRC38c99702014-02-11 09:45:18 +0000787
DRC19c791c2018-03-08 10:55:20 -0600788 pw0 = PAD(width, cinfo->max_h_samp_factor);
789 ph0 = PAD(height, cinfo->max_v_samp_factor);
DRC2e7b76b2009-04-03 12:04:24 +0000790
DRC19c791c2018-03-08 10:55:20 -0600791 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
DRCbce58f42019-04-12 07:49:35 -0500792 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600793 for (i = 0; i < height; i++) {
794 if (flags & TJFLAG_BOTTOMUP)
DRC2a9e3bd2019-07-11 15:30:04 -0500795 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600796 else
DRC2a9e3bd2019-07-11 15:30:04 -0500797 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600798 }
799 if (height < ph0)
800 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRCfbb67472010-11-24 04:02:37 +0000801
DRC19c791c2018-03-08 10:55:20 -0600802 for (i = 0; i < cinfo->num_components; i++) {
803 compptr = &cinfo->comp_info[i];
804 _tmpbuf[i] = (JSAMPLE *)malloc(
805 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
806 compptr->h_samp_factor, 32) *
807 cinfo->max_v_samp_factor + 32);
808 if (!_tmpbuf[i])
DRCbce58f42019-04-12 07:49:35 -0500809 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600810 tmpbuf[i] =
811 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
812 if (!tmpbuf[i])
DRCbce58f42019-04-12 07:49:35 -0500813 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600814 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
815 unsigned char *_tmpbuf_aligned =
816 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRCfbb67472010-11-24 04:02:37 +0000817
DRC19c791c2018-03-08 10:55:20 -0600818 tmpbuf[i][row] = &_tmpbuf_aligned[
819 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
820 compptr->h_samp_factor, 32) * row];
821 }
822 _tmpbuf2[i] =
823 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
824 compptr->v_samp_factor + 32);
825 if (!_tmpbuf2[i])
DRCbce58f42019-04-12 07:49:35 -0500826 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600827 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
828 if (!tmpbuf2[i])
DRCbce58f42019-04-12 07:49:35 -0500829 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600830 for (row = 0; row < compptr->v_samp_factor; row++) {
831 unsigned char *_tmpbuf2_aligned =
832 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
DRCd4c41fe2017-03-18 12:56:36 -0500833
DRC19c791c2018-03-08 10:55:20 -0600834 tmpbuf2[i][row] =
835 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
836 }
837 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
838 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
839 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
840 if (!outbuf[i])
DRCbce58f42019-04-12 07:49:35 -0500841 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600842 ptr = dstPlanes[i];
843 for (row = 0; row < ph[i]; row++) {
844 outbuf[i][row] = ptr;
845 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
846 }
847 }
DRC2e7b76b2009-04-03 12:04:24 +0000848
DRC19c791c2018-03-08 10:55:20 -0600849 if (setjmp(this->jerr.setjmp_buffer)) {
850 /* If we get here, the JPEG code has signaled an error. */
851 retval = -1; goto bailout;
852 }
853
854 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
855 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
856 cinfo->max_v_samp_factor);
857 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
858 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
859 i++, compptr++)
860 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
861 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
862 compptr->v_samp_factor, pw[i]);
863 }
864 cinfo->next_scanline += height;
865 jpeg_abort_compress(cinfo);
866
867bailout:
868 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
869 if (row_pointer) free(row_pointer);
870 for (i = 0; i < MAX_COMPONENTS; i++) {
871 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
872 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
873 if (tmpbuf2[i] != NULL) free(tmpbuf2[i]);
874 if (_tmpbuf2[i] != NULL) free(_tmpbuf2[i]);
875 if (outbuf[i] != NULL) free(outbuf[i]);
876 }
877 if (this->jerr.warning) retval = -1;
878 this->jerr.stopOnWarning = FALSE;
879 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000880}
881
DRC19c791c2018-03-08 10:55:20 -0600882DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
883 int width, int pitch, int height, int pixelFormat,
884 unsigned char *dstBuf, int pad, int subsamp,
885 int flags)
DRCaecea382014-08-11 18:05:41 +0000886{
DRC19c791c2018-03-08 10:55:20 -0600887 unsigned char *dstPlanes[3];
888 int pw0, ph0, strides[3], retval = -1;
889 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +0000890
DRCbce58f42019-04-12 07:49:35 -0500891 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -0600892 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -0500893
DRCbce58f42019-04-12 07:49:35 -0500894 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
895 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
896 THROW("tjEncodeYUV3(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +0000897
DRC19c791c2018-03-08 10:55:20 -0600898 pw0 = tjPlaneWidth(0, width, subsamp);
899 ph0 = tjPlaneHeight(0, height, subsamp);
900 dstPlanes[0] = dstBuf;
901 strides[0] = PAD(pw0, pad);
902 if (subsamp == TJSAMP_GRAY) {
903 strides[1] = strides[2] = 0;
904 dstPlanes[1] = dstPlanes[2] = NULL;
905 } else {
906 int pw1 = tjPlaneWidth(1, width, subsamp);
907 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +0000908
DRC19c791c2018-03-08 10:55:20 -0600909 strides[1] = strides[2] = PAD(pw1, pad);
910 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
911 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
912 }
DRCaecea382014-08-11 18:05:41 +0000913
DRC19c791c2018-03-08 10:55:20 -0600914 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
915 dstPlanes, strides, subsamp, flags);
916
917bailout:
918 return retval;
DRCaecea382014-08-11 18:05:41 +0000919}
920
DRC19c791c2018-03-08 10:55:20 -0600921DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
922 int pitch, int height, int pixelFormat,
923 unsigned char *dstBuf, int subsamp, int flags)
DRCf610d612013-04-26 10:33:29 +0000924{
DRC19c791c2018-03-08 10:55:20 -0600925 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
926 dstBuf, 4, subsamp, flags);
DRCf610d612013-04-26 10:33:29 +0000927}
928
DRC19c791c2018-03-08 10:55:20 -0600929DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
930 int pitch, int height, int pixelSize,
931 unsigned char *dstBuf, int subsamp, int flags)
DRC84241602011-02-25 02:08:23 +0000932{
DRC19c791c2018-03-08 10:55:20 -0600933 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
934 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
935 flags);
DRC84241602011-02-25 02:08:23 +0000936}
937
938
DRC19c791c2018-03-08 10:55:20 -0600939DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
940 const unsigned char **srcPlanes,
941 int width, const int *strides,
942 int height, int subsamp,
943 unsigned char **jpegBuf,
944 unsigned long *jpegSize, int jpegQual,
945 int flags)
DRC910a3572013-10-30 23:02:57 +0000946{
DRC19c791c2018-03-08 10:55:20 -0600947 int i, row, retval = 0, alloc = 1;
948 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
949 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
950 JSAMPLE *_tmpbuf = NULL, *ptr;
951 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
DRC910a3572013-10-30 23:02:57 +0000952
DRCbce58f42019-04-12 07:49:35 -0500953 GET_CINSTANCE(handle)
DRC19c791c2018-03-08 10:55:20 -0600954 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000955
DRC19c791c2018-03-08 10:55:20 -0600956 for (i = 0; i < MAX_COMPONENTS; i++) {
957 tmpbuf[i] = NULL; inbuf[i] = NULL;
958 }
DRC910a3572013-10-30 23:02:57 +0000959
DRC19c791c2018-03-08 10:55:20 -0600960 if ((this->init & COMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -0500961 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
DRC910a3572013-10-30 23:02:57 +0000962
DRC19c791c2018-03-08 10:55:20 -0600963 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
964 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
965 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
DRCbce58f42019-04-12 07:49:35 -0500966 THROW("tjCompressFromYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600967 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
DRCbce58f42019-04-12 07:49:35 -0500968 THROW("tjCompressFromYUVPlanes(): Invalid argument");
DRC910a3572013-10-30 23:02:57 +0000969
DRC19c791c2018-03-08 10:55:20 -0600970 if (setjmp(this->jerr.setjmp_buffer)) {
971 /* If we get here, the JPEG code has signaled an error. */
972 retval = -1; goto bailout;
973 }
DRC910a3572013-10-30 23:02:57 +0000974
DRC19c791c2018-03-08 10:55:20 -0600975 cinfo->image_width = width;
976 cinfo->image_height = height;
DRC910a3572013-10-30 23:02:57 +0000977
DRCbd96b302018-03-17 00:06:10 -0500978#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600979 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
980 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
981 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500982#endif
DRC910a3572013-10-30 23:02:57 +0000983
DRC19c791c2018-03-08 10:55:20 -0600984 if (flags & TJFLAG_NOREALLOC) {
985 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
986 }
987 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
988 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
989 return -1;
990 cinfo->raw_data_in = TRUE;
DRC910a3572013-10-30 23:02:57 +0000991
DRC19c791c2018-03-08 10:55:20 -0600992 jpeg_start_compress(cinfo, TRUE);
993 for (i = 0; i < cinfo->num_components; i++) {
994 jpeg_component_info *compptr = &cinfo->comp_info[i];
995 int ih;
DRC910a3572013-10-30 23:02:57 +0000996
DRC19c791c2018-03-08 10:55:20 -0600997 iw[i] = compptr->width_in_blocks * DCTSIZE;
998 ih = compptr->height_in_blocks * DCTSIZE;
999 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1000 compptr->h_samp_factor / cinfo->max_h_samp_factor;
1001 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1002 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1003 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1004 th[i] = compptr->v_samp_factor * DCTSIZE;
1005 tmpbufsize += iw[i] * th[i];
1006 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001007 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001008 ptr = (JSAMPLE *)srcPlanes[i];
1009 for (row = 0; row < ph[i]; row++) {
1010 inbuf[i][row] = ptr;
1011 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1012 }
1013 }
1014 if (usetmpbuf) {
1015 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001016 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001017 ptr = _tmpbuf;
1018 for (i = 0; i < cinfo->num_components; i++) {
1019 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001020 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001021 for (row = 0; row < th[i]; row++) {
1022 tmpbuf[i][row] = ptr;
1023 ptr += iw[i];
1024 }
1025 }
1026 }
DRCd4c41fe2017-03-18 12:56:36 -05001027
DRC19c791c2018-03-08 10:55:20 -06001028 if (setjmp(this->jerr.setjmp_buffer)) {
1029 /* If we get here, the JPEG code has signaled an error. */
1030 retval = -1; goto bailout;
1031 }
DRC910a3572013-10-30 23:02:57 +00001032
DRC19c791c2018-03-08 10:55:20 -06001033 for (row = 0; row < (int)cinfo->image_height;
1034 row += cinfo->max_v_samp_factor * DCTSIZE) {
1035 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1036 int crow[MAX_COMPONENTS];
1037
1038 for (i = 0; i < cinfo->num_components; i++) {
1039 jpeg_component_info *compptr = &cinfo->comp_info[i];
1040
1041 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1042 if (usetmpbuf) {
1043 int j, k;
1044
1045 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1046 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1047 /* Duplicate last sample in row to fill out MCU */
1048 for (k = pw[i]; k < iw[i]; k++)
1049 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1050 }
1051 /* Duplicate last row to fill out MCU */
1052 for (j = ph[i] - crow[i]; j < th[i]; j++)
1053 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1054 yuvptr[i] = tmpbuf[i];
1055 } else
1056 yuvptr[i] = &inbuf[i][crow[i]];
1057 }
1058 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1059 }
1060 jpeg_finish_compress(cinfo);
1061
1062bailout:
1063 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1064 for (i = 0; i < MAX_COMPONENTS; i++) {
1065 if (tmpbuf[i]) free(tmpbuf[i]);
1066 if (inbuf[i]) free(inbuf[i]);
1067 }
1068 if (_tmpbuf) free(_tmpbuf);
1069 if (this->jerr.warning) retval = -1;
1070 this->jerr.stopOnWarning = FALSE;
1071 return retval;
DRC910a3572013-10-30 23:02:57 +00001072}
1073
DRC19c791c2018-03-08 10:55:20 -06001074DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1075 int width, int pad, int height, int subsamp,
1076 unsigned char **jpegBuf,
1077 unsigned long *jpegSize, int jpegQual,
1078 int flags)
DRCaecea382014-08-11 18:05:41 +00001079{
DRC19c791c2018-03-08 10:55:20 -06001080 const unsigned char *srcPlanes[3];
1081 int pw0, ph0, strides[3], retval = -1;
1082 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +00001083
DRCbce58f42019-04-12 07:49:35 -05001084 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -06001085 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001086
DRC19c791c2018-03-08 10:55:20 -06001087 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1088 subsamp >= NUMSUBOPT)
DRCbce58f42019-04-12 07:49:35 -05001089 THROW("tjCompressFromYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001090
DRC19c791c2018-03-08 10:55:20 -06001091 pw0 = tjPlaneWidth(0, width, subsamp);
1092 ph0 = tjPlaneHeight(0, height, subsamp);
1093 srcPlanes[0] = srcBuf;
1094 strides[0] = PAD(pw0, pad);
1095 if (subsamp == TJSAMP_GRAY) {
1096 strides[1] = strides[2] = 0;
1097 srcPlanes[1] = srcPlanes[2] = NULL;
1098 } else {
1099 int pw1 = tjPlaneWidth(1, width, subsamp);
1100 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001101
DRC19c791c2018-03-08 10:55:20 -06001102 strides[1] = strides[2] = PAD(pw1, pad);
1103 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1104 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1105 }
DRCaecea382014-08-11 18:05:41 +00001106
DRC19c791c2018-03-08 10:55:20 -06001107 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1108 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1109
1110bailout:
1111 return retval;
DRCaecea382014-08-11 18:05:41 +00001112}
1113
DRC910a3572013-10-30 23:02:57 +00001114
DRC9b28def2011-05-21 14:37:15 +00001115/* Decompressor */
DRC2e7b76b2009-04-03 12:04:24 +00001116
DRC9b28def2011-05-21 14:37:15 +00001117static tjhandle _tjInitDecompress(tjinstance *this)
DRC2e7b76b2009-04-03 12:04:24 +00001118{
DRC19c791c2018-03-08 10:55:20 -06001119 static unsigned char buffer[1];
DRC2e7b76b2009-04-03 12:04:24 +00001120
DRC19c791c2018-03-08 10:55:20 -06001121 /* This is also straight out of example.txt */
1122 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1123 this->jerr.pub.error_exit = my_error_exit;
1124 this->jerr.pub.output_message = my_output_message;
1125 this->jerr.emit_message = this->jerr.pub.emit_message;
1126 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -05001127 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1128 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1129 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC2e7b76b2009-04-03 12:04:24 +00001130
DRC19c791c2018-03-08 10:55:20 -06001131 if (setjmp(this->jerr.setjmp_buffer)) {
1132 /* If we get here, the JPEG code has signaled an error. */
1133 if (this) free(this);
1134 return NULL;
1135 }
DRC2e7b76b2009-04-03 12:04:24 +00001136
DRC19c791c2018-03-08 10:55:20 -06001137 jpeg_create_decompress(&this->dinfo);
1138 /* Make an initial call so it will create the source manager */
1139 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +00001140
DRC19c791c2018-03-08 10:55:20 -06001141 this->init |= DECOMPRESS;
1142 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +00001143}
1144
DRC19c791c2018-03-08 10:55:20 -06001145DLLEXPORT tjhandle tjInitDecompress(void)
DRC890f1e02011-02-26 22:02:37 +00001146{
DRC19c791c2018-03-08 10:55:20 -06001147 tjinstance *this;
1148
1149 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1150 snprintf(errStr, JMSG_LENGTH_MAX,
1151 "tjInitDecompress(): Memory allocation failure");
1152 return NULL;
1153 }
1154 MEMZERO(this, sizeof(tjinstance));
1155 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1156 return _tjInitDecompress(this);
DRC890f1e02011-02-26 22:02:37 +00001157}
1158
DRC2e7b76b2009-04-03 12:04:24 +00001159
DRC19c791c2018-03-08 10:55:20 -06001160DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1161 const unsigned char *jpegBuf,
1162 unsigned long jpegSize, int *width,
1163 int *height, int *jpegSubsamp,
1164 int *jpegColorspace)
DRC1fe80f82010-12-14 01:21:29 +00001165{
DRC19c791c2018-03-08 10:55:20 -06001166 int retval = 0;
DRC1fe80f82010-12-14 01:21:29 +00001167
DRCbce58f42019-04-12 07:49:35 -05001168 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001169 if ((this->init & DECOMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -05001170 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
DRC1fe80f82010-12-14 01:21:29 +00001171
DRC19c791c2018-03-08 10:55:20 -06001172 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1173 jpegSubsamp == NULL || jpegColorspace == NULL)
DRCbce58f42019-04-12 07:49:35 -05001174 THROW("tjDecompressHeader3(): Invalid argument");
DRC1fe80f82010-12-14 01:21:29 +00001175
DRC19c791c2018-03-08 10:55:20 -06001176 if (setjmp(this->jerr.setjmp_buffer)) {
1177 /* If we get here, the JPEG code has signaled an error. */
1178 return -1;
1179 }
DRC1fe80f82010-12-14 01:21:29 +00001180
DRC19c791c2018-03-08 10:55:20 -06001181 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1182 jpeg_read_header(dinfo, TRUE);
DRC1fe80f82010-12-14 01:21:29 +00001183
DRC19c791c2018-03-08 10:55:20 -06001184 *width = dinfo->image_width;
1185 *height = dinfo->image_height;
1186 *jpegSubsamp = getSubsamp(dinfo);
1187 switch (dinfo->jpeg_color_space) {
1188 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1189 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1190 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1191 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1192 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1193 default: *jpegColorspace = -1; break;
1194 }
DRC1fe80f82010-12-14 01:21:29 +00001195
DRC19c791c2018-03-08 10:55:20 -06001196 jpeg_abort_decompress(dinfo);
DRC1fe80f82010-12-14 01:21:29 +00001197
DRC19c791c2018-03-08 10:55:20 -06001198 if (*jpegSubsamp < 0)
DRCbce58f42019-04-12 07:49:35 -05001199 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
DRC19c791c2018-03-08 10:55:20 -06001200 if (*jpegColorspace < 0)
DRCbce58f42019-04-12 07:49:35 -05001201 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
DRC19c791c2018-03-08 10:55:20 -06001202 if (*width < 1 || *height < 1)
DRCbce58f42019-04-12 07:49:35 -05001203 THROW("tjDecompressHeader3(): Invalid data returned in header");
DRC91e86ba2011-02-15 05:24:08 +00001204
DRC19c791c2018-03-08 10:55:20 -06001205bailout:
1206 if (this->jerr.warning) retval = -1;
1207 return retval;
DRC91e86ba2011-02-15 05:24:08 +00001208}
1209
DRC19c791c2018-03-08 10:55:20 -06001210DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1211 unsigned long jpegSize, int *width,
1212 int *height, int *jpegSubsamp)
DRCcd7c3e62013-08-23 02:49:25 +00001213{
DRC19c791c2018-03-08 10:55:20 -06001214 int jpegColorspace;
1215
1216 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1217 jpegSubsamp, &jpegColorspace);
DRCcd7c3e62013-08-23 02:49:25 +00001218}
1219
DRC19c791c2018-03-08 10:55:20 -06001220DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1221 unsigned long jpegSize, int *width,
1222 int *height)
DRC91e86ba2011-02-15 05:24:08 +00001223{
DRC19c791c2018-03-08 10:55:20 -06001224 int jpegSubsamp;
1225
1226 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1227 &jpegSubsamp);
DRC1fe80f82010-12-14 01:21:29 +00001228}
1229
1230
DRC19c791c2018-03-08 10:55:20 -06001231DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
DRCb28fc572011-02-22 06:41:29 +00001232{
DRC19c791c2018-03-08 10:55:20 -06001233 if (numscalingfactors == NULL) {
1234 snprintf(errStr, JMSG_LENGTH_MAX,
1235 "tjGetScalingFactors(): Invalid argument");
1236 return NULL;
1237 }
DRCb28fc572011-02-22 06:41:29 +00001238
DRC19c791c2018-03-08 10:55:20 -06001239 *numscalingfactors = NUMSF;
1240 return (tjscalingfactor *)sf;
DRCb28fc572011-02-22 06:41:29 +00001241}
1242
1243
DRC19c791c2018-03-08 10:55:20 -06001244DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1245 unsigned long jpegSize, unsigned char *dstBuf,
1246 int width, int pitch, int height, int pixelFormat,
1247 int flags)
DRC2e7b76b2009-04-03 12:04:24 +00001248{
DRC19c791c2018-03-08 10:55:20 -06001249 JSAMPROW *row_pointer = NULL;
1250 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
DRC2e7b76b2009-04-03 12:04:24 +00001251
DRCbce58f42019-04-12 07:49:35 -05001252 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001253 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1254 if ((this->init & DECOMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -05001255 THROW("tjDecompress2(): Instance has not been initialized for decompression");
DRC9b28def2011-05-21 14:37:15 +00001256
DRC19c791c2018-03-08 10:55:20 -06001257 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1258 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCbce58f42019-04-12 07:49:35 -05001259 THROW("tjDecompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001260
DRCbd96b302018-03-17 00:06:10 -05001261#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001262 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1263 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1264 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001265#endif
DRC9b28def2011-05-21 14:37:15 +00001266
DRC19c791c2018-03-08 10:55:20 -06001267 if (setjmp(this->jerr.setjmp_buffer)) {
1268 /* If we get here, the JPEG code has signaled an error. */
1269 retval = -1; goto bailout;
1270 }
DRC9b28def2011-05-21 14:37:15 +00001271
DRC19c791c2018-03-08 10:55:20 -06001272 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1273 jpeg_read_header(dinfo, TRUE);
1274 this->dinfo.out_color_space = pf2cs[pixelFormat];
1275 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1276 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
DRC9b28def2011-05-21 14:37:15 +00001277
DRC19c791c2018-03-08 10:55:20 -06001278 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1279 if (width == 0) width = jpegwidth;
1280 if (height == 0) height = jpegheight;
1281 for (i = 0; i < NUMSF; i++) {
1282 scaledw = TJSCALED(jpegwidth, sf[i]);
1283 scaledh = TJSCALED(jpegheight, sf[i]);
1284 if (scaledw <= width && scaledh <= height)
1285 break;
1286 }
1287 if (i >= NUMSF)
DRCbce58f42019-04-12 07:49:35 -05001288 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
DRC19c791c2018-03-08 10:55:20 -06001289 width = scaledw; height = scaledh;
1290 dinfo->scale_num = sf[i].num;
1291 dinfo->scale_denom = sf[i].denom;
DRC9b28def2011-05-21 14:37:15 +00001292
DRC19c791c2018-03-08 10:55:20 -06001293 jpeg_start_decompress(dinfo);
1294 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRCafc06922012-03-23 19:47:57 +00001295
DRC19c791c2018-03-08 10:55:20 -06001296 if ((row_pointer =
1297 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001298 THROW("tjDecompress2(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001299 if (setjmp(this->jerr.setjmp_buffer)) {
1300 /* If we get here, the JPEG code has signaled an error. */
1301 retval = -1; goto bailout;
1302 }
1303 for (i = 0; i < (int)dinfo->output_height; i++) {
1304 if (flags & TJFLAG_BOTTOMUP)
DRC2a9e3bd2019-07-11 15:30:04 -05001305 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001306 else
DRC2a9e3bd2019-07-11 15:30:04 -05001307 row_pointer[i] = &dstBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001308 }
1309 while (dinfo->output_scanline < dinfo->output_height)
1310 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1311 dinfo->output_height - dinfo->output_scanline);
1312 jpeg_finish_decompress(dinfo);
DRC9b28def2011-05-21 14:37:15 +00001313
DRC19c791c2018-03-08 10:55:20 -06001314bailout:
1315 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1316 if (row_pointer) free(row_pointer);
1317 if (this->jerr.warning) retval = -1;
1318 this->jerr.stopOnWarning = FALSE;
1319 return retval;
DRC9b28def2011-05-21 14:37:15 +00001320}
1321
DRC19c791c2018-03-08 10:55:20 -06001322DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1323 unsigned long jpegSize, unsigned char *dstBuf,
1324 int width, int pitch, int height, int pixelSize,
1325 int flags)
DRC9b28def2011-05-21 14:37:15 +00001326{
DRC19c791c2018-03-08 10:55:20 -06001327 if (flags & TJ_YUV)
1328 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1329 else
1330 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1331 height, getPixelFormat(pixelSize, flags), flags);
DRC9b28def2011-05-21 14:37:15 +00001332}
1333
1334
DRC34dca052014-02-28 09:17:14 +00001335static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
DRC19c791c2018-03-08 10:55:20 -06001336 int pixelFormat, int subsamp, int flags)
DRC34dca052014-02-28 09:17:14 +00001337{
DRC19c791c2018-03-08 10:55:20 -06001338 int i;
DRC895fd6d2014-02-28 09:35:34 +00001339
DRC19c791c2018-03-08 10:55:20 -06001340 dinfo->scale_num = dinfo->scale_denom = 1;
DRC34dca052014-02-28 09:17:14 +00001341
DRC19c791c2018-03-08 10:55:20 -06001342 if (subsamp == TJSAMP_GRAY) {
1343 dinfo->num_components = dinfo->comps_in_scan = 1;
1344 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1345 } else {
1346 dinfo->num_components = dinfo->comps_in_scan = 3;
1347 dinfo->jpeg_color_space = JCS_YCbCr;
1348 }
DRC34dca052014-02-28 09:17:14 +00001349
DRC19c791c2018-03-08 10:55:20 -06001350 dinfo->comp_info = (jpeg_component_info *)
1351 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1352 dinfo->num_components *
1353 sizeof(jpeg_component_info));
DRC34dca052014-02-28 09:17:14 +00001354
DRC19c791c2018-03-08 10:55:20 -06001355 for (i = 0; i < dinfo->num_components; i++) {
1356 jpeg_component_info *compptr = &dinfo->comp_info[i];
DRC34dca052014-02-28 09:17:14 +00001357
DRC19c791c2018-03-08 10:55:20 -06001358 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1359 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1360 compptr->component_index = i;
1361 compptr->component_id = i + 1;
1362 compptr->quant_tbl_no = compptr->dc_tbl_no =
1363 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1364 dinfo->cur_comp_info[i] = compptr;
1365 }
1366 dinfo->data_precision = 8;
1367 for (i = 0; i < 2; i++) {
1368 if (dinfo->quant_tbl_ptrs[i] == NULL)
1369 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1370 }
1371
1372 return 0;
DRC34dca052014-02-28 09:17:14 +00001373}
1374
1375
DRC6399d0a2019-04-23 14:10:04 -05001376static int my_read_markers(j_decompress_ptr dinfo)
DRC34dca052014-02-28 09:17:14 +00001377{
DRC19c791c2018-03-08 10:55:20 -06001378 return JPEG_REACHED_SOS;
DRC34dca052014-02-28 09:17:14 +00001379}
1380
DRC6399d0a2019-04-23 14:10:04 -05001381static void my_reset_marker_reader(j_decompress_ptr dinfo)
DRC34dca052014-02-28 09:17:14 +00001382{
1383}
1384
DRC19c791c2018-03-08 10:55:20 -06001385DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1386 const unsigned char **srcPlanes,
1387 const int *strides, int subsamp,
1388 unsigned char *dstBuf, int width, int pitch,
1389 int height, int pixelFormat, int flags)
DRC34dca052014-02-28 09:17:14 +00001390{
DRC19c791c2018-03-08 10:55:20 -06001391 JSAMPROW *row_pointer = NULL;
1392 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1393 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1394 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1395 JSAMPLE *ptr;
1396 jpeg_component_info *compptr;
1397 int (*old_read_markers) (j_decompress_ptr);
1398 void (*old_reset_marker_reader) (j_decompress_ptr);
DRC34dca052014-02-28 09:17:14 +00001399
DRCbce58f42019-04-12 07:49:35 -05001400 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001401 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRC34dca052014-02-28 09:17:14 +00001402
DRC19c791c2018-03-08 10:55:20 -06001403 for (i = 0; i < MAX_COMPONENTS; i++) {
1404 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1405 }
DRC34dca052014-02-28 09:17:14 +00001406
DRC19c791c2018-03-08 10:55:20 -06001407 if ((this->init & DECOMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -05001408 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
DRC34dca052014-02-28 09:17:14 +00001409
DRC19c791c2018-03-08 10:55:20 -06001410 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1411 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1412 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCbce58f42019-04-12 07:49:35 -05001413 THROW("tjDecodeYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -06001414 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
DRCbce58f42019-04-12 07:49:35 -05001415 THROW("tjDecodeYUVPlanes(): Invalid argument");
DRC34dca052014-02-28 09:17:14 +00001416
DRC19c791c2018-03-08 10:55:20 -06001417 if (setjmp(this->jerr.setjmp_buffer)) {
1418 /* If we get here, the JPEG code has signaled an error. */
1419 retval = -1; goto bailout;
1420 }
DRC34dca052014-02-28 09:17:14 +00001421
DRC19c791c2018-03-08 10:55:20 -06001422 if (pixelFormat == TJPF_CMYK)
DRCbce58f42019-04-12 07:49:35 -05001423 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
DRC34dca052014-02-28 09:17:14 +00001424
DRC19c791c2018-03-08 10:55:20 -06001425 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1426 dinfo->image_width = width;
1427 dinfo->image_height = height;
DRC34dca052014-02-28 09:17:14 +00001428
DRCbd96b302018-03-17 00:06:10 -05001429#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001430 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1431 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1432 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001433#endif
DRC34dca052014-02-28 09:17:14 +00001434
DRCded5a502019-08-15 13:24:25 -05001435 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1436 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1437 dinfo->Se = DCTSIZE2 - 1;
DRC19c791c2018-03-08 10:55:20 -06001438 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1439 retval = -1; goto bailout;
1440 }
1441 old_read_markers = dinfo->marker->read_markers;
1442 dinfo->marker->read_markers = my_read_markers;
1443 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1444 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1445 jpeg_read_header(dinfo, TRUE);
1446 dinfo->marker->read_markers = old_read_markers;
1447 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
DRC34dca052014-02-28 09:17:14 +00001448
DRC19c791c2018-03-08 10:55:20 -06001449 this->dinfo.out_color_space = pf2cs[pixelFormat];
1450 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1451 dinfo->do_fancy_upsampling = FALSE;
1452 dinfo->Se = DCTSIZE2 - 1;
1453 jinit_master_decompress(dinfo);
1454 (*dinfo->upsample->start_pass) (dinfo);
DRC34dca052014-02-28 09:17:14 +00001455
DRC19c791c2018-03-08 10:55:20 -06001456 pw0 = PAD(width, dinfo->max_h_samp_factor);
1457 ph0 = PAD(height, dinfo->max_v_samp_factor);
DRC34dca052014-02-28 09:17:14 +00001458
DRC19c791c2018-03-08 10:55:20 -06001459 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRC34dca052014-02-28 09:17:14 +00001460
DRC19c791c2018-03-08 10:55:20 -06001461 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001462 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001463 for (i = 0; i < height; i++) {
1464 if (flags & TJFLAG_BOTTOMUP)
DRC2a9e3bd2019-07-11 15:30:04 -05001465 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001466 else
DRC2a9e3bd2019-07-11 15:30:04 -05001467 row_pointer[i] = &dstBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001468 }
1469 if (height < ph0)
1470 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRC34dca052014-02-28 09:17:14 +00001471
DRC19c791c2018-03-08 10:55:20 -06001472 for (i = 0; i < dinfo->num_components; i++) {
1473 compptr = &dinfo->comp_info[i];
1474 _tmpbuf[i] =
1475 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1476 compptr->v_samp_factor + 32);
1477 if (!_tmpbuf[i])
DRCbce58f42019-04-12 07:49:35 -05001478 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001479 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1480 if (!tmpbuf[i])
DRCbce58f42019-04-12 07:49:35 -05001481 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001482 for (row = 0; row < compptr->v_samp_factor; row++) {
1483 unsigned char *_tmpbuf_aligned =
1484 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRC34dca052014-02-28 09:17:14 +00001485
DRC19c791c2018-03-08 10:55:20 -06001486 tmpbuf[i][row] =
1487 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1488 }
1489 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1490 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1491 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1492 if (!inbuf[i])
DRCbce58f42019-04-12 07:49:35 -05001493 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001494 ptr = (JSAMPLE *)srcPlanes[i];
1495 for (row = 0; row < ph[i]; row++) {
1496 inbuf[i][row] = ptr;
1497 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1498 }
1499 }
DRCd4c41fe2017-03-18 12:56:36 -05001500
DRC19c791c2018-03-08 10:55:20 -06001501 if (setjmp(this->jerr.setjmp_buffer)) {
1502 /* If we get here, the JPEG code has signaled an error. */
1503 retval = -1; goto bailout;
1504 }
DRC34dca052014-02-28 09:17:14 +00001505
DRC19c791c2018-03-08 10:55:20 -06001506 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1507 JDIMENSION inrow = 0, outrow = 0;
1508
1509 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1510 i++, compptr++)
1511 jcopy_sample_rows(inbuf[i],
1512 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1513 compptr->v_samp_factor, pw[i]);
1514 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1515 dinfo->max_v_samp_factor, &row_pointer[row],
1516 &outrow, dinfo->max_v_samp_factor);
1517 }
1518 jpeg_abort_decompress(dinfo);
1519
1520bailout:
1521 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1522 if (row_pointer) free(row_pointer);
1523 for (i = 0; i < MAX_COMPONENTS; i++) {
1524 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
1525 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
1526 if (inbuf[i] != NULL) free(inbuf[i]);
1527 }
1528 if (this->jerr.warning) retval = -1;
1529 this->jerr.stopOnWarning = FALSE;
1530 return retval;
DRC34dca052014-02-28 09:17:14 +00001531}
1532
DRC19c791c2018-03-08 10:55:20 -06001533DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1534 int pad, int subsamp, unsigned char *dstBuf,
1535 int width, int pitch, int height, int pixelFormat,
1536 int flags)
DRCaecea382014-08-11 18:05:41 +00001537{
DRC19c791c2018-03-08 10:55:20 -06001538 const unsigned char *srcPlanes[3];
1539 int pw0, ph0, strides[3], retval = -1;
1540 tjinstance *this = (tjinstance *)handle;
DRC34dca052014-02-28 09:17:14 +00001541
DRCbce58f42019-04-12 07:49:35 -05001542 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -06001543 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001544
DRCbce58f42019-04-12 07:49:35 -05001545 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
DRC19c791c2018-03-08 10:55:20 -06001546 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
DRCbce58f42019-04-12 07:49:35 -05001547 THROW("tjDecodeYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001548
DRC19c791c2018-03-08 10:55:20 -06001549 pw0 = tjPlaneWidth(0, width, subsamp);
1550 ph0 = tjPlaneHeight(0, height, subsamp);
1551 srcPlanes[0] = srcBuf;
1552 strides[0] = PAD(pw0, pad);
1553 if (subsamp == TJSAMP_GRAY) {
1554 strides[1] = strides[2] = 0;
1555 srcPlanes[1] = srcPlanes[2] = NULL;
1556 } else {
1557 int pw1 = tjPlaneWidth(1, width, subsamp);
1558 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001559
DRC19c791c2018-03-08 10:55:20 -06001560 strides[1] = strides[2] = PAD(pw1, pad);
1561 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1562 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1563 }
DRCaecea382014-08-11 18:05:41 +00001564
DRC19c791c2018-03-08 10:55:20 -06001565 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1566 pitch, height, pixelFormat, flags);
1567
1568bailout:
1569 return retval;
DRCaecea382014-08-11 18:05:41 +00001570}
1571
DRC19c791c2018-03-08 10:55:20 -06001572DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1573 const unsigned char *jpegBuf,
1574 unsigned long jpegSize,
1575 unsigned char **dstPlanes, int width,
1576 int *strides, int height, int flags)
DRC9b28def2011-05-21 14:37:15 +00001577{
DRC19c791c2018-03-08 10:55:20 -06001578 int i, sfi, row, retval = 0;
1579 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1580 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1581 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1582 JSAMPLE *_tmpbuf = NULL, *ptr;
1583 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1584 int dctsize;
DRC9b28def2011-05-21 14:37:15 +00001585
DRCbce58f42019-04-12 07:49:35 -05001586 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001587 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +00001588
DRC19c791c2018-03-08 10:55:20 -06001589 for (i = 0; i < MAX_COMPONENTS; i++) {
1590 tmpbuf[i] = NULL; outbuf[i] = NULL;
1591 }
DRC9e17f7d2010-12-10 04:59:13 +00001592
DRC19c791c2018-03-08 10:55:20 -06001593 if ((this->init & DECOMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -05001594 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
DRCe2f8e692013-10-30 22:21:06 +00001595
DRC19c791c2018-03-08 10:55:20 -06001596 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1597 width < 0 || height < 0)
DRCbce58f42019-04-12 07:49:35 -05001598 THROW("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001599
DRCbd96b302018-03-17 00:06:10 -05001600#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001601 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1602 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1603 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001604#endif
DRC0c6a2712010-02-22 08:34:44 +00001605
DRC19c791c2018-03-08 10:55:20 -06001606 if (setjmp(this->jerr.setjmp_buffer)) {
1607 /* If we get here, the JPEG code has signaled an error. */
1608 retval = -1; goto bailout;
1609 }
DRC2e7b76b2009-04-03 12:04:24 +00001610
DRC19c791c2018-03-08 10:55:20 -06001611 if (!this->headerRead) {
1612 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1613 jpeg_read_header(dinfo, TRUE);
1614 }
1615 this->headerRead = 0;
1616 jpegSubsamp = getSubsamp(dinfo);
1617 if (jpegSubsamp < 0)
DRCbce58f42019-04-12 07:49:35 -05001618 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001619
DRC19c791c2018-03-08 10:55:20 -06001620 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
DRCbce58f42019-04-12 07:49:35 -05001621 THROW("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001622
DRC19c791c2018-03-08 10:55:20 -06001623 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1624 if (width == 0) width = jpegwidth;
1625 if (height == 0) height = jpegheight;
1626 for (i = 0; i < NUMSF; i++) {
1627 scaledw = TJSCALED(jpegwidth, sf[i]);
1628 scaledh = TJSCALED(jpegheight, sf[i]);
1629 if (scaledw <= width && scaledh <= height)
1630 break;
1631 }
1632 if (i >= NUMSF)
DRCbce58f42019-04-12 07:49:35 -05001633 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
DRC19c791c2018-03-08 10:55:20 -06001634 if (dinfo->num_components > 3)
DRCbce58f42019-04-12 07:49:35 -05001635 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
DRCcd7c3e62013-08-23 02:49:25 +00001636
DRC19c791c2018-03-08 10:55:20 -06001637 width = scaledw; height = scaledh;
1638 dinfo->scale_num = sf[i].num;
1639 dinfo->scale_denom = sf[i].denom;
1640 sfi = i;
1641 jpeg_calc_output_dimensions(dinfo);
DRCf610d612013-04-26 10:33:29 +00001642
DRC19c791c2018-03-08 10:55:20 -06001643 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
DRC418fe282013-05-07 21:17:35 +00001644
DRC19c791c2018-03-08 10:55:20 -06001645 for (i = 0; i < dinfo->num_components; i++) {
1646 jpeg_component_info *compptr = &dinfo->comp_info[i];
1647 int ih;
DRC9e17f7d2010-12-10 04:59:13 +00001648
DRC19c791c2018-03-08 10:55:20 -06001649 iw[i] = compptr->width_in_blocks * dctsize;
1650 ih = compptr->height_in_blocks * dctsize;
1651 pw[i] = PAD(dinfo->output_width, dinfo->max_h_samp_factor) *
1652 compptr->h_samp_factor / dinfo->max_h_samp_factor;
1653 ph[i] = PAD(dinfo->output_height, dinfo->max_v_samp_factor) *
1654 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1655 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1656 th[i] = compptr->v_samp_factor * dctsize;
1657 tmpbufsize += iw[i] * th[i];
1658 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001659 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001660 ptr = dstPlanes[i];
1661 for (row = 0; row < ph[i]; row++) {
1662 outbuf[i][row] = ptr;
1663 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1664 }
1665 }
1666 if (usetmpbuf) {
1667 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001668 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001669 ptr = _tmpbuf;
1670 for (i = 0; i < dinfo->num_components; i++) {
1671 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001672 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001673 for (row = 0; row < th[i]; row++) {
1674 tmpbuf[i][row] = ptr;
1675 ptr += iw[i];
1676 }
1677 }
1678 }
DRCd4c41fe2017-03-18 12:56:36 -05001679
DRC19c791c2018-03-08 10:55:20 -06001680 if (setjmp(this->jerr.setjmp_buffer)) {
1681 /* If we get here, the JPEG code has signaled an error. */
1682 retval = -1; goto bailout;
1683 }
DRC9b28def2011-05-21 14:37:15 +00001684
DRC19c791c2018-03-08 10:55:20 -06001685 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1686 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1687 dinfo->raw_data_out = TRUE;
DRC2e7b76b2009-04-03 12:04:24 +00001688
DRC19c791c2018-03-08 10:55:20 -06001689 jpeg_start_decompress(dinfo);
1690 for (row = 0; row < (int)dinfo->output_height;
1691 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1692 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1693 int crow[MAX_COMPONENTS];
1694
1695 for (i = 0; i < dinfo->num_components; i++) {
1696 jpeg_component_info *compptr = &dinfo->comp_info[i];
1697
1698 if (jpegSubsamp == TJ_420) {
1699 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1700 to be clever and use the IDCT to perform upsampling on the U and V
1701 planes. For instance, if the output image is to be scaled by 1/2
1702 relative to the JPEG image, then the scaling factor and upsampling
1703 effectively cancel each other, so a normal 8x8 IDCT can be used.
1704 However, this is not desirable when using the decompress-to-YUV
1705 functionality in TurboJPEG, since we want to output the U and V
1706 planes in their subsampled form. Thus, we have to override some
1707 internal libjpeg parameters to force it to use the "scaled" IDCT
1708 functions on the U and V planes. */
1709 compptr->_DCT_scaled_size = dctsize;
1710 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1711 sf[sfi].num / sf[sfi].denom *
1712 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1713 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1714 }
1715 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1716 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1717 else yuvptr[i] = &outbuf[i][crow[i]];
1718 }
1719 jpeg_read_raw_data(dinfo, yuvptr,
1720 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1721 if (usetmpbuf) {
1722 int j;
1723
1724 for (i = 0; i < dinfo->num_components; i++) {
1725 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1726 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1727 }
1728 }
1729 }
1730 }
1731 jpeg_finish_decompress(dinfo);
1732
1733bailout:
1734 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1735 for (i = 0; i < MAX_COMPONENTS; i++) {
1736 if (tmpbuf[i]) free(tmpbuf[i]);
1737 if (outbuf[i]) free(outbuf[i]);
1738 }
1739 if (_tmpbuf) free(_tmpbuf);
1740 if (this->jerr.warning) retval = -1;
1741 this->jerr.stopOnWarning = FALSE;
1742 return retval;
DRC2e7b76b2009-04-03 12:04:24 +00001743}
1744
DRC19c791c2018-03-08 10:55:20 -06001745DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1746 unsigned long jpegSize, unsigned char *dstBuf,
1747 int width, int pad, int height, int flags)
DRCaecea382014-08-11 18:05:41 +00001748{
DRC19c791c2018-03-08 10:55:20 -06001749 unsigned char *dstPlanes[3];
1750 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1751 int i, jpegwidth, jpegheight, scaledw, scaledh;
DRCaecea382014-08-11 18:05:41 +00001752
DRCbce58f42019-04-12 07:49:35 -05001753 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001754 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCaecea382014-08-11 18:05:41 +00001755
DRC19c791c2018-03-08 10:55:20 -06001756 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
DRCbce58f42019-04-12 07:49:35 -05001757 pad < 1 || !IS_POW2(pad) || height < 0)
1758 THROW("tjDecompressToYUV2(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001759
DRC19c791c2018-03-08 10:55:20 -06001760 if (setjmp(this->jerr.setjmp_buffer)) {
1761 /* If we get here, the JPEG code has signaled an error. */
1762 return -1;
1763 }
DRCdec79952016-04-20 11:27:42 -05001764
DRC19c791c2018-03-08 10:55:20 -06001765 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1766 jpeg_read_header(dinfo, TRUE);
1767 jpegSubsamp = getSubsamp(dinfo);
1768 if (jpegSubsamp < 0)
DRCbce58f42019-04-12 07:49:35 -05001769 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001770
DRC19c791c2018-03-08 10:55:20 -06001771 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1772 if (width == 0) width = jpegwidth;
1773 if (height == 0) height = jpegheight;
DRCaecea382014-08-11 18:05:41 +00001774
DRC19c791c2018-03-08 10:55:20 -06001775 for (i = 0; i < NUMSF; i++) {
1776 scaledw = TJSCALED(jpegwidth, sf[i]);
1777 scaledh = TJSCALED(jpegheight, sf[i]);
1778 if (scaledw <= width && scaledh <= height)
1779 break;
1780 }
1781 if (i >= NUMSF)
DRCbce58f42019-04-12 07:49:35 -05001782 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
DRCaecea382014-08-11 18:05:41 +00001783
DRC19c791c2018-03-08 10:55:20 -06001784 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1785 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1786 dstPlanes[0] = dstBuf;
1787 strides[0] = PAD(pw0, pad);
1788 if (jpegSubsamp == TJSAMP_GRAY) {
1789 strides[1] = strides[2] = 0;
1790 dstPlanes[1] = dstPlanes[2] = NULL;
1791 } else {
1792 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1793 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
DRCaecea382014-08-11 18:05:41 +00001794
DRC19c791c2018-03-08 10:55:20 -06001795 strides[1] = strides[2] = PAD(pw1, pad);
1796 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1797 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1798 }
DRCaecea382014-08-11 18:05:41 +00001799
DRC19c791c2018-03-08 10:55:20 -06001800 this->headerRead = 1;
1801 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1802 strides, height, flags);
DRCaecea382014-08-11 18:05:41 +00001803
DRC19c791c2018-03-08 10:55:20 -06001804bailout:
1805 this->jerr.stopOnWarning = FALSE;
1806 return retval;
DRCaecea382014-08-11 18:05:41 +00001807}
1808
DRC19c791c2018-03-08 10:55:20 -06001809DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1810 unsigned long jpegSize, unsigned char *dstBuf,
1811 int flags)
DRCf610d612013-04-26 10:33:29 +00001812{
DRC19c791c2018-03-08 10:55:20 -06001813 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
DRCf610d612013-04-26 10:33:29 +00001814}
1815
DRC2e7b76b2009-04-03 12:04:24 +00001816
DRC9b28def2011-05-21 14:37:15 +00001817/* Transformer */
DRC890f1e02011-02-26 22:02:37 +00001818
DRC19c791c2018-03-08 10:55:20 -06001819DLLEXPORT tjhandle tjInitTransform(void)
DRC890f1e02011-02-26 22:02:37 +00001820{
DRC19c791c2018-03-08 10:55:20 -06001821 tjinstance *this = NULL;
1822 tjhandle handle = NULL;
1823
1824 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1825 snprintf(errStr, JMSG_LENGTH_MAX,
1826 "tjInitTransform(): Memory allocation failure");
1827 return NULL;
1828 }
1829 MEMZERO(this, sizeof(tjinstance));
1830 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1831 handle = _tjInitCompress(this);
1832 if (!handle) return NULL;
1833 handle = _tjInitDecompress(this);
1834 return handle;
DRC890f1e02011-02-26 22:02:37 +00001835}
1836
1837
DRC19c791c2018-03-08 10:55:20 -06001838DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1839 unsigned long jpegSize, int n,
1840 unsigned char **dstBufs, unsigned long *dstSizes,
1841 tjtransform *t, int flags)
DRC890f1e02011-02-26 22:02:37 +00001842{
DRC19c791c2018-03-08 10:55:20 -06001843 jpeg_transform_info *xinfo = NULL;
1844 jvirt_barray_ptr *srccoefs, *dstcoefs;
1845 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
DRC890f1e02011-02-26 22:02:37 +00001846
DRCbce58f42019-04-12 07:49:35 -05001847 GET_INSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001848 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1849 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
DRCbce58f42019-04-12 07:49:35 -05001850 THROW("tjTransform(): Instance has not been initialized for transformation");
DRC890f1e02011-02-26 22:02:37 +00001851
DRC19c791c2018-03-08 10:55:20 -06001852 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1853 dstSizes == NULL || t == NULL || flags < 0)
DRCbce58f42019-04-12 07:49:35 -05001854 THROW("tjTransform(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001855
DRCbd96b302018-03-17 00:06:10 -05001856#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001857 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1858 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1859 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001860#endif
DRC890f1e02011-02-26 22:02:37 +00001861
DRC19c791c2018-03-08 10:55:20 -06001862 if ((xinfo =
1863 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05001864 THROW("tjTransform(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001865 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
DRCd4c41fe2017-03-18 12:56:36 -05001866
DRC19c791c2018-03-08 10:55:20 -06001867 if (setjmp(this->jerr.setjmp_buffer)) {
1868 /* If we get here, the JPEG code has signaled an error. */
1869 retval = -1; goto bailout;
1870 }
DRC890f1e02011-02-26 22:02:37 +00001871
DRC19c791c2018-03-08 10:55:20 -06001872 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
DRC890f1e02011-02-26 22:02:37 +00001873
DRC19c791c2018-03-08 10:55:20 -06001874 for (i = 0; i < n; i++) {
1875 xinfo[i].transform = xformtypes[t[i].op];
1876 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1877 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1878 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1879 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1880 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1881 else xinfo[i].slow_hflip = 0;
DRC0a325192011-03-02 09:22:41 +00001882
DRC19c791c2018-03-08 10:55:20 -06001883 if (xinfo[i].crop) {
1884 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1885 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1886 if (t[i].r.w != 0) {
1887 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1888 } else
1889 xinfo[i].crop_width = JCROP_UNSET;
1890 if (t[i].r.h != 0) {
1891 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1892 } else
1893 xinfo[i].crop_height = JCROP_UNSET;
1894 }
1895 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1896 }
DRC890f1e02011-02-26 22:02:37 +00001897
DRC19c791c2018-03-08 10:55:20 -06001898 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1899 jpeg_read_header(dinfo, TRUE);
1900 jpegSubsamp = getSubsamp(dinfo);
1901 if (jpegSubsamp < 0)
DRCbce58f42019-04-12 07:49:35 -05001902 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
DRC890f1e02011-02-26 22:02:37 +00001903
DRC19c791c2018-03-08 10:55:20 -06001904 for (i = 0; i < n; i++) {
1905 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
DRCbce58f42019-04-12 07:49:35 -05001906 THROW("tjTransform(): Transform is not perfect");
DRC890f1e02011-02-26 22:02:37 +00001907
DRC19c791c2018-03-08 10:55:20 -06001908 if (xinfo[i].crop) {
1909 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1910 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
1911 snprintf(errStr, JMSG_LENGTH_MAX,
1912 "To crop this JPEG image, x must be a multiple of %d\n"
1913 "and y must be a multiple of %d.\n",
1914 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
1915 retval = -1; goto bailout;
1916 }
1917 }
1918 }
DRC890f1e02011-02-26 22:02:37 +00001919
DRC19c791c2018-03-08 10:55:20 -06001920 srccoefs = jpeg_read_coefficients(dinfo);
DRC890f1e02011-02-26 22:02:37 +00001921
DRC19c791c2018-03-08 10:55:20 -06001922 for (i = 0; i < n; i++) {
1923 int w, h, alloc = 1;
DRC0a325192011-03-02 09:22:41 +00001924
DRC19c791c2018-03-08 10:55:20 -06001925 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;
DRC890f1e02011-02-26 22:02:37 +00001949
DRC19c791c2018-03-08 10:55:20 -06001950 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)
DRCbce58f42019-04-12 07:49:35 -05001969 THROW("tjTransform(): Error in custom filter");
DRC19c791c2018-03-08 10:55:20 -06001970 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);
1983 if (xinfo) free(xinfo);
1984 if (this->jerr.warning) retval = -1;
1985 this->jerr.stopOnWarning = FALSE;
1986 return retval;
DRC890f1e02011-02-26 22:02:37 +00001987}
DRCaa745902017-11-16 18:09:07 -06001988
1989
DRC19c791c2018-03-08 10:55:20 -06001990DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1991 int align, int *height, int *pixelFormat,
1992 int flags)
DRCaa745902017-11-16 18:09:07 -06001993{
DRC3d9c64e2019-01-01 18:57:36 -06001994 int retval = 0, tempc;
1995 size_t pitch;
DRC19c791c2018-03-08 10:55:20 -06001996 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;
DRCaa745902017-11-16 18:09:07 -06002003
DRC19c791c2018-03-08 10:55:20 -06002004 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2005 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
DRCbce58f42019-04-12 07:49:35 -05002006 THROWG("tjLoadImage(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -06002007 if ((align & (align - 1)) != 0)
DRCbce58f42019-04-12 07:49:35 -05002008 THROWG("tjLoadImage(): Alignment must be a power of 2");
DRCaa745902017-11-16 18:09:07 -06002009
DRC19c791c2018-03-08 10:55:20 -06002010 if ((handle = tjInitCompress()) == NULL) return NULL;
2011 this = (tjinstance *)handle;
2012 cinfo = &this->cinfo;
DRCaa745902017-11-16 18:09:07 -06002013
DRC19c791c2018-03-08 10:55:20 -06002014 if ((file = fopen(filename, "rb")) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002015 THROW_UNIX("tjLoadImage(): Cannot open input file");
DRCaa745902017-11-16 18:09:07 -06002016
DRC19c791c2018-03-08 10:55:20 -06002017 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
DRCbce58f42019-04-12 07:49:35 -05002018 THROW_UNIX("tjLoadImage(): Could not read input file")
DRC19c791c2018-03-08 10:55:20 -06002019 else if (tempc == EOF)
DRCbce58f42019-04-12 07:49:35 -05002020 THROWG("tjLoadImage(): Input file contains no data");
DRCaa745902017-11-16 18:09:07 -06002021
DRC19c791c2018-03-08 10:55:20 -06002022 if (setjmp(this->jerr.setjmp_buffer)) {
2023 /* If we get here, the JPEG code has signaled an error. */
2024 retval = -1; goto bailout;
2025 }
DRCaa745902017-11-16 18:09:07 -06002026
DRC19c791c2018-03-08 10:55:20 -06002027 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)
DRCbce58f42019-04-12 07:49:35 -05002031 THROWG("tjLoadImage(): Could not initialize bitmap loader");
DRC19c791c2018-03-08 10:55:20 -06002032 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2033 } else if (tempc == 'P') {
2034 if ((src = jinit_read_ppm(cinfo)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002035 THROWG("tjLoadImage(): Could not initialize bitmap loader");
DRC19c791c2018-03-08 10:55:20 -06002036 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2037 } else
DRCbce58f42019-04-12 07:49:35 -05002038 THROWG("tjLoadImage(): Unsupported file type");
DRCaa745902017-11-16 18:09:07 -06002039
DRC19c791c2018-03-08 10:55:20 -06002040 src->input_file = file;
2041 (*src->start_input) (cinfo, src);
2042 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
DRCaa745902017-11-16 18:09:07 -06002043
DRC19c791c2018-03-08 10:55:20 -06002044 *width = cinfo->image_width; *height = cinfo->image_height;
2045 *pixelFormat = cs2pf[cinfo->in_color_space];
DRCaa745902017-11-16 18:09:07 -06002046
DRC19c791c2018-03-08 10:55:20 -06002047 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
DRC3d9c64e2019-01-01 18:57:36 -06002048 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2049 (unsigned long long)((size_t)-1) ||
2050 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002051 THROWG("tjLoadImage(): Memory allocation failure");
DRCaa745902017-11-16 18:09:07 -06002052
DRC19c791c2018-03-08 10:55:20 -06002053 if (setjmp(this->jerr.setjmp_buffer)) {
2054 /* If we get here, the JPEG code has signaled an error. */
2055 retval = -1; goto bailout;
2056 }
DRCaa745902017-11-16 18:09:07 -06002057
DRC19c791c2018-03-08 10:55:20 -06002058 while (cinfo->next_scanline < cinfo->image_height) {
2059 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
DRCaa745902017-11-16 18:09:07 -06002060
DRC19c791c2018-03-08 10:55:20 -06002061 for (i = 0; i < nlines; i++) {
2062 unsigned char *dstptr;
2063 int row;
DRCaa745902017-11-16 18:09:07 -06002064
DRC19c791c2018-03-08 10:55:20 -06002065 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);
2078 if (retval < 0 && dstBuf) { free(dstBuf); dstBuf = NULL; }
2079 return dstBuf;
DRCaa745902017-11-16 18:09:07 -06002080}
2081
2082
DRC19c791c2018-03-08 10:55:20 -06002083DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2084 int width, int pitch, int height, int pixelFormat,
2085 int flags)
DRCaa745902017-11-16 18:09:07 -06002086{
DRC19c791c2018-03-08 10:55:20 -06002087 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;
DRCaa745902017-11-16 18:09:07 -06002095
DRC19c791c2018-03-08 10:55:20 -06002096 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2097 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCbce58f42019-04-12 07:49:35 -05002098 THROWG("tjSaveImage(): Invalid argument");
DRCaa745902017-11-16 18:09:07 -06002099
DRC19c791c2018-03-08 10:55:20 -06002100 if ((handle = tjInitDecompress()) == NULL)
2101 return -1;
2102 this = (tjinstance *)handle;
2103 dinfo = &this->dinfo;
DRCaa745902017-11-16 18:09:07 -06002104
DRC19c791c2018-03-08 10:55:20 -06002105 if ((file = fopen(filename, "wb")) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002106 THROW_UNIX("tjSaveImage(): Cannot open output file");
DRCaa745902017-11-16 18:09:07 -06002107
DRC19c791c2018-03-08 10:55:20 -06002108 if (setjmp(this->jerr.setjmp_buffer)) {
2109 /* If we get here, the JPEG code has signaled an error. */
2110 retval = -1; goto bailout;
2111 }
DRCaa745902017-11-16 18:09:07 -06002112
DRC19c791c2018-03-08 10:55:20 -06002113 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;
DRCaa745902017-11-16 18:09:07 -06002117
DRC19c791c2018-03-08 10:55:20 -06002118 ptr = strrchr(filename, '.');
2119 if (ptr && !strcasecmp(ptr, ".bmp")) {
2120 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002121 THROWG("tjSaveImage(): Could not initialize bitmap writer");
DRC19c791c2018-03-08 10:55:20 -06002122 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2123 } else {
2124 if ((dst = jinit_write_ppm(dinfo)) == NULL)
DRCbce58f42019-04-12 07:49:35 -05002125 THROWG("tjSaveImage(): Could not initialize PPM writer");
DRC19c791c2018-03-08 10:55:20 -06002126 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2127 }
DRCaa745902017-11-16 18:09:07 -06002128
DRC19c791c2018-03-08 10:55:20 -06002129 dst->output_file = file;
2130 (*dst->start_output) (dinfo, dst);
2131 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
DRCaa745902017-11-16 18:09:07 -06002132
DRC19c791c2018-03-08 10:55:20 -06002133 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRCaa745902017-11-16 18:09:07 -06002134
DRC19c791c2018-03-08 10:55:20 -06002135 while (dinfo->output_scanline < dinfo->output_height) {
2136 unsigned char *rowptr;
DRCaa745902017-11-16 18:09:07 -06002137
DRC19c791c2018-03-08 10:55:20 -06002138 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 }
DRCaa745902017-11-16 18:09:07 -06002146
DRC19c791c2018-03-08 10:55:20 -06002147 (*dst->finish_output) (dinfo, dst);
2148
2149bailout:
2150 if (handle) tjDestroy(handle);
2151 if (file) fclose(file);
2152 return retval;
DRCaa745902017-11-16 18:09:07 -06002153}