blob: a41e8dfd9f7c6880245500477253c51ef4628872 [file] [log] [blame]
DRC9b28def2011-05-21 14:37:15 +00001/*
DRC19c791c2018-03-08 10:55:20 -06002 * Copyright (C)2009-2018 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)))
53#define isPow2(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
68static void my_error_exit(j_common_ptr cinfo)
69{
DRC19c791c2018-03-08 10:55:20 -060070 my_error_ptr myerr = (my_error_ptr)cinfo->err;
71
72 (*cinfo->err->output_message) (cinfo);
73 longjmp(myerr->setjmp_buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +000074}
75
DRC9b28def2011-05-21 14:37:15 +000076/* Based on output_message() in jerror.c */
77
DRC2e7b76b2009-04-03 12:04:24 +000078static void my_output_message(j_common_ptr cinfo)
79{
DRC19c791c2018-03-08 10:55:20 -060080 (*cinfo->err->format_message) (cinfo, errStr);
DRC2e7b76b2009-04-03 12:04:24 +000081}
82
DRC1f79c7c2015-06-01 19:22:41 +000083static void my_emit_message(j_common_ptr cinfo, int msg_level)
84{
DRC19c791c2018-03-08 10:55:20 -060085 my_error_ptr myerr = (my_error_ptr)cinfo->err;
86
87 myerr->emit_message(cinfo, msg_level);
88 if (msg_level < 0) {
89 myerr->warning = TRUE;
90 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
91 }
DRC1f79c7c2015-06-01 19:22:41 +000092}
93
DRC2e7b76b2009-04-03 12:04:24 +000094
DRC9b28def2011-05-21 14:37:15 +000095/* Global structures, macros, etc. */
DRC2e7b76b2009-04-03 12:04:24 +000096
DRC19c791c2018-03-08 10:55:20 -060097enum { COMPRESS = 1, DECOMPRESS = 2 };
DRC9b28def2011-05-21 14:37:15 +000098
DRC19c791c2018-03-08 10:55:20 -060099typedef struct _tjinstance {
100 struct jpeg_compress_struct cinfo;
101 struct jpeg_decompress_struct dinfo;
102 struct my_error_mgr jerr;
103 int init, headerRead;
104 char errStr[JMSG_LENGTH_MAX];
105 boolean isInstanceError;
DRC9b28def2011-05-21 14:37:15 +0000106} tjinstance;
DRC2e7b76b2009-04-03 12:04:24 +0000107
DRC19c791c2018-03-08 10:55:20 -0600108static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
DRC9b28def2011-05-21 14:37:15 +0000109
DRC19c791c2018-03-08 10:55:20 -0600110static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
111 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
112 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
DRC890f1e02011-02-26 22:02:37 +0000113};
DRC9b28def2011-05-21 14:37:15 +0000114
DRC293263c2018-03-17 15:14:35 -0500115#define NUMSF 16
DRC19c791c2018-03-08 10:55:20 -0600116static const tjscalingfactor sf[NUMSF] = {
117 { 2, 1 },
118 { 15, 8 },
119 { 7, 4 },
120 { 13, 8 },
121 { 3, 2 },
122 { 11, 8 },
123 { 5, 4 },
124 { 9, 8 },
125 { 1, 1 },
126 { 7, 8 },
127 { 3, 4 },
128 { 5, 8 },
129 { 1, 2 },
130 { 3, 8 },
131 { 1, 4 },
132 { 1, 8 }
DRC109a5782011-03-01 09:53:07 +0000133};
DRC2e7b76b2009-04-03 12:04:24 +0000134
DRC19c791c2018-03-08 10:55:20 -0600135static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
136 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
137 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
138 JCS_EXT_ARGB, JCS_CMYK
DRCaa745902017-11-16 18:09:07 -0600139};
140
DRC19c791c2018-03-08 10:55:20 -0600141static int cs2pf[JPEG_NUMCS] = {
142 TJPF_UNKNOWN, TJPF_GRAY,
DRCaa745902017-11-16 18:09:07 -0600143#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600144 TJPF_RGB,
DRCaa745902017-11-16 18:09:07 -0600145#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600146 TJPF_BGR,
DRCaa745902017-11-16 18:09:07 -0600147#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600148 TJPF_RGBX,
DRCaa745902017-11-16 18:09:07 -0600149#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600150 TJPF_BGRX,
DRCaa745902017-11-16 18:09:07 -0600151#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600152 TJPF_XBGR,
DRCaa745902017-11-16 18:09:07 -0600153#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600154 TJPF_XRGB,
DRCaa745902017-11-16 18:09:07 -0600155#endif
DRC19c791c2018-03-08 10:55:20 -0600156 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
157 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
158 TJPF_UNKNOWN
DRCaa745902017-11-16 18:09:07 -0600159};
160
DRC19c791c2018-03-08 10:55:20 -0600161#define _throwg(m) { \
162 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
163 retval = -1; goto bailout; \
DRCaa745902017-11-16 18:09:07 -0600164}
DRC19c791c2018-03-08 10:55:20 -0600165#define _throwunix(m) { \
166 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
167 retval = -1; goto bailout; \
168}
169#define _throw(m) { \
170 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
DRC58cb10e2018-03-31 13:51:31 -0500171 this->isInstanceError = TRUE; _throwg(m) \
DRC19c791c2018-03-08 10:55:20 -0600172}
173
174#define getinstance(handle) \
175 tjinstance *this = (tjinstance *)handle; \
176 j_compress_ptr cinfo = NULL; \
177 j_decompress_ptr dinfo = NULL; \
178 \
179 if (!this) { \
180 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
181 return -1; \
182 } \
183 cinfo = &this->cinfo; dinfo = &this->dinfo; \
184 this->jerr.warning = FALSE; \
185 this->isInstanceError = FALSE;
186
187#define getcinstance(handle) \
188 tjinstance *this = (tjinstance *)handle; \
189 j_compress_ptr cinfo = NULL; \
190 \
191 if (!this) { \
192 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
193 return -1; \
194 } \
195 cinfo = &this->cinfo; \
196 this->jerr.warning = FALSE; \
197 this->isInstanceError = FALSE;
198
199#define getdinstance(handle) \
200 tjinstance *this = (tjinstance *)handle; \
201 j_decompress_ptr dinfo = NULL; \
202 \
203 if (!this) { \
204 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
205 return -1; \
206 } \
207 dinfo = &this->dinfo; \
208 this->jerr.warning = FALSE; \
209 this->isInstanceError = FALSE;
DRC2e7b76b2009-04-03 12:04:24 +0000210
DRC9b28def2011-05-21 14:37:15 +0000211static int getPixelFormat(int pixelSize, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000212{
DRC19c791c2018-03-08 10:55:20 -0600213 if (pixelSize == 1) return TJPF_GRAY;
214 if (pixelSize == 3) {
215 if (flags & TJ_BGR) return TJPF_BGR;
216 else return TJPF_RGB;
217 }
218 if (pixelSize == 4) {
219 if (flags & TJ_ALPHAFIRST) {
220 if (flags & TJ_BGR) return TJPF_XBGR;
221 else return TJPF_XRGB;
222 } else {
223 if (flags & TJ_BGR) return TJPF_BGRX;
224 else return TJPF_RGBX;
225 }
226 }
227 return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000228}
229
DRC19c791c2018-03-08 10:55:20 -0600230static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
231 int subsamp, int jpegQual, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000232{
DRC19c791c2018-03-08 10:55:20 -0600233 int retval = 0;
234 char *env = NULL;
DRCf12bb302011-09-07 05:03:18 +0000235
DRC19c791c2018-03-08 10:55:20 -0600236 cinfo->in_color_space = pf2cs[pixelFormat];
237 cinfo->input_components = tjPixelSize[pixelFormat];
238 jpeg_set_defaults(cinfo);
DRC0713c1b2014-08-22 13:43:33 +0000239
DRCfeccdcf2015-02-23 19:19:40 +0000240#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600241 if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
242 !strcmp(env, "1"))
243 cinfo->optimize_coding = TRUE;
244 if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
245 !strcmp(env, "1"))
246 cinfo->arith_code = TRUE;
247 if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
248 int temp = -1;
249 char tempc = 0;
250
251 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
252 temp <= 65535) {
253 if (toupper(tempc) == 'B') {
254 cinfo->restart_interval = temp;
255 cinfo->restart_in_rows = 0;
256 } else
257 cinfo->restart_in_rows = temp;
258 }
259 }
DRCfeccdcf2015-02-23 19:19:40 +0000260#endif
DRC0713c1b2014-08-22 13:43:33 +0000261
DRC19c791c2018-03-08 10:55:20 -0600262 if (jpegQual >= 0) {
263 jpeg_set_quality(cinfo, jpegQual, TRUE);
264 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
265 cinfo->dct_method = JDCT_ISLOW;
266 else
267 cinfo->dct_method = JDCT_FASTEST;
268 }
269 if (subsamp == TJSAMP_GRAY)
270 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
271 else if (pixelFormat == TJPF_CMYK)
272 jpeg_set_colorspace(cinfo, JCS_YCCK);
273 else
274 jpeg_set_colorspace(cinfo, JCS_YCbCr);
DRC2e7b76b2009-04-03 12:04:24 +0000275
DRC19c791c2018-03-08 10:55:20 -0600276 if (flags & TJFLAG_PROGRESSIVE)
277 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000278#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600279 else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
280 !strcmp(env, "1"))
281 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000282#endif
DRC0713c1b2014-08-22 13:43:33 +0000283
DRC19c791c2018-03-08 10:55:20 -0600284 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
285 cinfo->comp_info[1].h_samp_factor = 1;
286 cinfo->comp_info[2].h_samp_factor = 1;
287 if (cinfo->num_components > 3)
288 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
289 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
290 cinfo->comp_info[1].v_samp_factor = 1;
291 cinfo->comp_info[2].v_samp_factor = 1;
292 if (cinfo->num_components > 3)
293 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
DRCf12bb302011-09-07 05:03:18 +0000294
DRC19c791c2018-03-08 10:55:20 -0600295 return retval;
DRC9b28def2011-05-21 14:37:15 +0000296}
297
DRC9b28def2011-05-21 14:37:15 +0000298
DRC9b49f0e2011-07-12 03:17:23 +0000299static int getSubsamp(j_decompress_ptr dinfo)
300{
DRC19c791c2018-03-08 10:55:20 -0600301 int retval = -1, i, k;
DRCea1eea42014-11-19 00:55:28 +0000302
DRC19c791c2018-03-08 10:55:20 -0600303 /* The sampling factors actually have no meaning with grayscale JPEG files,
304 and in fact it's possible to generate grayscale JPEGs with sampling
305 factors > 1 (even though those sampling factors are ignored by the
306 decompressor.) Thus, we need to treat grayscale as a special case. */
307 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
308 return TJSAMP_GRAY;
DRCea1eea42014-11-19 00:55:28 +0000309
DRC19c791c2018-03-08 10:55:20 -0600310 for (i = 0; i < NUMSUBOPT; i++) {
311 if (dinfo->num_components == pixelsize[i] ||
312 ((dinfo->jpeg_color_space == JCS_YCCK ||
313 dinfo->jpeg_color_space == JCS_CMYK) &&
314 pixelsize[i] == 3 && dinfo->num_components == 4)) {
315 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
316 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
317 int match = 0;
318
319 for (k = 1; k < dinfo->num_components; k++) {
320 int href = 1, vref = 1;
321
DRC2401e4d2018-04-26 18:01:52 -0500322 if ((dinfo->jpeg_color_space == JCS_YCCK ||
323 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600324 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
325 }
326 if (dinfo->comp_info[k].h_samp_factor == href &&
327 dinfo->comp_info[k].v_samp_factor == vref)
328 match++;
329 }
330 if (match == dinfo->num_components - 1) {
331 retval = i; break;
332 }
333 }
334 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
335 in non-standard ways. */
336 if (dinfo->comp_info[0].h_samp_factor == 2 &&
337 dinfo->comp_info[0].v_samp_factor == 2 &&
338 (i == TJSAMP_422 || i == TJSAMP_440)) {
339 int match = 0;
340
341 for (k = 1; k < dinfo->num_components; k++) {
342 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
343
DRC2401e4d2018-04-26 18:01:52 -0500344 if ((dinfo->jpeg_color_space == JCS_YCCK ||
345 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600346 href = vref = 2;
347 }
348 if (dinfo->comp_info[k].h_samp_factor == href &&
349 dinfo->comp_info[k].v_samp_factor == vref)
350 match++;
351 }
352 if (match == dinfo->num_components - 1) {
353 retval = i; break;
354 }
355 }
356 }
357 }
358 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000359}
360
361
DRC9b28def2011-05-21 14:37:15 +0000362/* General API functions */
363
DRC19c791c2018-03-08 10:55:20 -0600364DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
DRCb9ab64d2017-05-11 21:02:29 -0500365{
DRC19c791c2018-03-08 10:55:20 -0600366 tjinstance *this = (tjinstance *)handle;
367
368 if (this && this->isInstanceError) {
369 this->isInstanceError = FALSE;
370 return this->errStr;
371 } else
372 return errStr;
DRCb9ab64d2017-05-11 21:02:29 -0500373}
374
375
DRC19c791c2018-03-08 10:55:20 -0600376DLLEXPORT char *tjGetErrorStr(void)
DRC9b28def2011-05-21 14:37:15 +0000377{
DRC19c791c2018-03-08 10:55:20 -0600378 return errStr;
DRC9b28def2011-05-21 14:37:15 +0000379}
380
381
DRC19c791c2018-03-08 10:55:20 -0600382DLLEXPORT int tjGetErrorCode(tjhandle handle)
DRCd4092f62017-06-27 10:54:21 -0500383{
DRC19c791c2018-03-08 10:55:20 -0600384 tjinstance *this = (tjinstance *)handle;
385
386 if (this && this->jerr.warning) return TJERR_WARNING;
387 else return TJERR_FATAL;
DRCd4092f62017-06-27 10:54:21 -0500388}
389
390
DRC19c791c2018-03-08 10:55:20 -0600391DLLEXPORT int tjDestroy(tjhandle handle)
DRC9b28def2011-05-21 14:37:15 +0000392{
DRC19c791c2018-03-08 10:55:20 -0600393 getinstance(handle);
394
395 if (setjmp(this->jerr.setjmp_buffer)) return -1;
396 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
397 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
398 free(this);
399 return 0;
DRC9b28def2011-05-21 14:37:15 +0000400}
401
402
DRC6b76f752011-05-24 16:52:47 +0000403/* These are exposed mainly because Windows can't malloc() and free() across
404 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
405 with turbojpeg.dll for compatibility reasons. However, these functions
406 can potentially be used for other purposes by different implementations. */
407
DRC19c791c2018-03-08 10:55:20 -0600408DLLEXPORT void tjFree(unsigned char *buf)
DRC6b76f752011-05-24 16:52:47 +0000409{
DRC19c791c2018-03-08 10:55:20 -0600410 if (buf) free(buf);
DRC6b76f752011-05-24 16:52:47 +0000411}
412
413
DRC19c791c2018-03-08 10:55:20 -0600414DLLEXPORT unsigned char *tjAlloc(int bytes)
DRC6b76f752011-05-24 16:52:47 +0000415{
DRC19c791c2018-03-08 10:55:20 -0600416 return (unsigned char *)malloc(bytes);
DRC6b76f752011-05-24 16:52:47 +0000417}
418
419
DRC9b28def2011-05-21 14:37:15 +0000420/* Compressor */
421
422static tjhandle _tjInitCompress(tjinstance *this)
423{
DRC19c791c2018-03-08 10:55:20 -0600424 static unsigned char buffer[1];
425 unsigned char *buf = buffer;
426 unsigned long size = 1;
DRC9b28def2011-05-21 14:37:15 +0000427
DRC19c791c2018-03-08 10:55:20 -0600428 /* This is also straight out of example.txt */
429 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
430 this->jerr.pub.error_exit = my_error_exit;
431 this->jerr.pub.output_message = my_output_message;
432 this->jerr.emit_message = this->jerr.pub.emit_message;
433 this->jerr.pub.emit_message = my_emit_message;
DRC9b28def2011-05-21 14:37:15 +0000434
DRC19c791c2018-03-08 10:55:20 -0600435 if (setjmp(this->jerr.setjmp_buffer)) {
436 /* If we get here, the JPEG code has signaled an error. */
437 if (this) free(this);
438 return NULL;
439 }
DRC9b28def2011-05-21 14:37:15 +0000440
DRC19c791c2018-03-08 10:55:20 -0600441 jpeg_create_compress(&this->cinfo);
442 /* Make an initial call so it will create the destination manager */
443 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
DRC9b28def2011-05-21 14:37:15 +0000444
DRC19c791c2018-03-08 10:55:20 -0600445 this->init |= COMPRESS;
446 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +0000447}
448
DRC19c791c2018-03-08 10:55:20 -0600449DLLEXPORT tjhandle tjInitCompress(void)
DRC890f1e02011-02-26 22:02:37 +0000450{
DRC19c791c2018-03-08 10:55:20 -0600451 tjinstance *this = NULL;
452
453 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
454 snprintf(errStr, JMSG_LENGTH_MAX,
455 "tjInitCompress(): Memory allocation failure");
456 return NULL;
457 }
458 MEMZERO(this, sizeof(tjinstance));
459 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
460 return _tjInitCompress(this);
DRC890f1e02011-02-26 22:02:37 +0000461}
462
DRC84241602011-02-25 02:08:23 +0000463
DRC19c791c2018-03-08 10:55:20 -0600464DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000465{
DRC19c791c2018-03-08 10:55:20 -0600466 unsigned long retval = 0;
467 int mcuw, mcuh, chromasf;
DRC9b49f0e2011-07-12 03:17:23 +0000468
DRC19c791c2018-03-08 10:55:20 -0600469 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
470 _throwg("tjBufSize(): Invalid argument");
DRC9b49f0e2011-07-12 03:17:23 +0000471
DRC19c791c2018-03-08 10:55:20 -0600472 /* This allows for rare corner cases in which a JPEG image can actually be
473 larger than the uncompressed input (we wouldn't mention it if it hadn't
474 happened before.) */
475 mcuw = tjMCUWidth[jpegSubsamp];
476 mcuh = tjMCUHeight[jpegSubsamp];
477 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
478 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
479
480bailout:
481 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000482}
483
DRC19c791c2018-03-08 10:55:20 -0600484DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
DRC2e7b76b2009-04-03 12:04:24 +0000485{
DRC19c791c2018-03-08 10:55:20 -0600486 unsigned long retval = 0;
DRCf3cf9732011-02-22 00:16:14 +0000487
DRC19c791c2018-03-08 10:55:20 -0600488 if (width < 1 || height < 1)
489 _throwg("TJBUFSIZE(): Invalid argument");
DRCf3cf9732011-02-22 00:16:14 +0000490
DRC19c791c2018-03-08 10:55:20 -0600491 /* This allows for rare corner cases in which a JPEG image can actually be
492 larger than the uncompressed input (we wouldn't mention it if it hadn't
493 happened before.) */
494 retval = PAD(width, 16) * PAD(height, 16) * 6 + 2048;
495
496bailout:
497 return retval;
DRCf3cf9732011-02-22 00:16:14 +0000498}
499
DRC84241602011-02-25 02:08:23 +0000500
DRC19c791c2018-03-08 10:55:20 -0600501DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
502 int subsamp)
DRCf3cf9732011-02-22 00:16:14 +0000503{
DRC19c791c2018-03-08 10:55:20 -0600504 int retval = 0, nc, i;
DRC40dd3142014-08-17 12:23:49 +0000505
DRC19c791c2018-03-08 10:55:20 -0600506 if (subsamp < 0 || subsamp >= NUMSUBOPT)
507 _throwg("tjBufSizeYUV2(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000508
DRC19c791c2018-03-08 10:55:20 -0600509 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
510 for (i = 0; i < nc; i++) {
511 int pw = tjPlaneWidth(i, width, subsamp);
512 int stride = PAD(pw, pad);
513 int ph = tjPlaneHeight(i, height, subsamp);
DRCf3cf9732011-02-22 00:16:14 +0000514
DRC19c791c2018-03-08 10:55:20 -0600515 if (pw < 0 || ph < 0) return -1;
516 else retval += stride * ph;
517 }
518
519bailout:
520 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000521}
522
DRC19c791c2018-03-08 10:55:20 -0600523DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
DRCf610d612013-04-26 10:33:29 +0000524{
DRC19c791c2018-03-08 10:55:20 -0600525 return tjBufSizeYUV2(width, 4, height, subsamp);
DRCf610d612013-04-26 10:33:29 +0000526}
DRC84241602011-02-25 02:08:23 +0000527
DRC19c791c2018-03-08 10:55:20 -0600528DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000529{
DRC19c791c2018-03-08 10:55:20 -0600530 return tjBufSizeYUV(width, height, subsamp);
DRC9b49f0e2011-07-12 03:17:23 +0000531}
532
533
DRC40dd3142014-08-17 12:23:49 +0000534DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
535{
DRC19c791c2018-03-08 10:55:20 -0600536 int pw, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000537
DRC19c791c2018-03-08 10:55:20 -0600538 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
539 _throwg("tjPlaneWidth(): Invalid argument");
540 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
541 if (componentID < 0 || componentID >= nc)
542 _throwg("tjPlaneWidth(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000543
DRC19c791c2018-03-08 10:55:20 -0600544 pw = PAD(width, tjMCUWidth[subsamp] / 8);
545 if (componentID == 0)
546 retval = pw;
547 else
548 retval = pw * 8 / tjMCUWidth[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000549
DRC19c791c2018-03-08 10:55:20 -0600550bailout:
551 return retval;
DRC40dd3142014-08-17 12:23:49 +0000552}
553
554
555DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
556{
DRC19c791c2018-03-08 10:55:20 -0600557 int ph, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000558
DRC19c791c2018-03-08 10:55:20 -0600559 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
560 _throwg("tjPlaneHeight(): Invalid argument");
561 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
562 if (componentID < 0 || componentID >= nc)
563 _throwg("tjPlaneHeight(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000564
DRC19c791c2018-03-08 10:55:20 -0600565 ph = PAD(height, tjMCUHeight[subsamp] / 8);
566 if (componentID == 0)
567 retval = ph;
568 else
569 retval = ph * 8 / tjMCUHeight[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000570
DRC19c791c2018-03-08 10:55:20 -0600571bailout:
572 return retval;
DRC40dd3142014-08-17 12:23:49 +0000573}
574
575
DRC19c791c2018-03-08 10:55:20 -0600576DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
577 int height, int subsamp)
DRC40dd3142014-08-17 12:23:49 +0000578{
DRC19c791c2018-03-08 10:55:20 -0600579 unsigned long retval = 0;
580 int pw, ph;
DRC40dd3142014-08-17 12:23:49 +0000581
DRC19c791c2018-03-08 10:55:20 -0600582 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
583 _throwg("tjPlaneSizeYUV(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000584
DRC19c791c2018-03-08 10:55:20 -0600585 pw = tjPlaneWidth(componentID, width, subsamp);
586 ph = tjPlaneHeight(componentID, height, subsamp);
587 if (pw < 0 || ph < 0) return -1;
DRC40dd3142014-08-17 12:23:49 +0000588
DRC19c791c2018-03-08 10:55:20 -0600589 if (stride == 0) stride = pw;
590 else stride = abs(stride);
DRC40dd3142014-08-17 12:23:49 +0000591
DRC19c791c2018-03-08 10:55:20 -0600592 retval = stride * (ph - 1) + pw;
DRC40dd3142014-08-17 12:23:49 +0000593
DRC19c791c2018-03-08 10:55:20 -0600594bailout:
595 return retval;
DRC40dd3142014-08-17 12:23:49 +0000596}
597
598
DRC19c791c2018-03-08 10:55:20 -0600599DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
600 int width, int pitch, int height, int pixelFormat,
601 unsigned char **jpegBuf, unsigned long *jpegSize,
602 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000603{
DRC19c791c2018-03-08 10:55:20 -0600604 int i, retval = 0, alloc = 1;
605 JSAMPROW *row_pointer = NULL;
DRC9b28def2011-05-21 14:37:15 +0000606
DRC19c791c2018-03-08 10:55:20 -0600607 getcinstance(handle)
608 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
609 if ((this->init & COMPRESS) == 0)
610 _throw("tjCompress2(): Instance has not been initialized for compression");
DRC9b28def2011-05-21 14:37:15 +0000611
DRC19c791c2018-03-08 10:55:20 -0600612 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
613 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
614 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
615 jpegQual < 0 || jpegQual > 100)
616 _throw("tjCompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +0000617
DRC19c791c2018-03-08 10:55:20 -0600618 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC9b28def2011-05-21 14:37:15 +0000619
DRC19c791c2018-03-08 10:55:20 -0600620 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
621 _throw("tjCompress2(): Memory allocation failure");
DRCd4c41fe2017-03-18 12:56:36 -0500622
DRC19c791c2018-03-08 10:55:20 -0600623 if (setjmp(this->jerr.setjmp_buffer)) {
624 /* If we get here, the JPEG code has signaled an error. */
625 retval = -1; goto bailout;
626 }
DRCd4c41fe2017-03-18 12:56:36 -0500627
DRC19c791c2018-03-08 10:55:20 -0600628 cinfo->image_width = width;
629 cinfo->image_height = height;
DRC9b28def2011-05-21 14:37:15 +0000630
DRCbd96b302018-03-17 00:06:10 -0500631#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600632 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
633 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
634 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500635#endif
DRC9b28def2011-05-21 14:37:15 +0000636
DRC19c791c2018-03-08 10:55:20 -0600637 if (flags & TJFLAG_NOREALLOC) {
638 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
639 }
640 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
641 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
642 return -1;
DRC9b28def2011-05-21 14:37:15 +0000643
DRC19c791c2018-03-08 10:55:20 -0600644 jpeg_start_compress(cinfo, TRUE);
645 for (i = 0; i < height; i++) {
646 if (flags & TJFLAG_BOTTOMUP)
647 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
648 else
649 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
650 }
651 while (cinfo->next_scanline < cinfo->image_height)
652 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
653 cinfo->image_height - cinfo->next_scanline);
654 jpeg_finish_compress(cinfo);
DRC9b28def2011-05-21 14:37:15 +0000655
DRC19c791c2018-03-08 10:55:20 -0600656bailout:
657 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
658 if (row_pointer) free(row_pointer);
659 if (this->jerr.warning) retval = -1;
660 this->jerr.stopOnWarning = FALSE;
661 return retval;
DRC9b28def2011-05-21 14:37:15 +0000662}
663
DRC19c791c2018-03-08 10:55:20 -0600664DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
665 int pitch, int height, int pixelSize,
666 unsigned char *jpegBuf, unsigned long *jpegSize,
667 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000668{
DRC19c791c2018-03-08 10:55:20 -0600669 int retval = 0;
670 unsigned long size;
671
672 if (flags & TJ_YUV) {
673 size = tjBufSizeYUV(width, height, jpegSubsamp);
674 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
675 getPixelFormat(pixelSize, flags), jpegBuf,
676 jpegSubsamp, flags);
677 } else {
678 retval = tjCompress2(handle, srcBuf, width, pitch, height,
679 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
680 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
681 }
682 *jpegSize = size;
683 return retval;
DRC9b28def2011-05-21 14:37:15 +0000684}
685
686
DRC19c791c2018-03-08 10:55:20 -0600687DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
688 int width, int pitch, int height,
689 int pixelFormat, unsigned char **dstPlanes,
690 int *strides, int subsamp, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000691{
DRC19c791c2018-03-08 10:55:20 -0600692 JSAMPROW *row_pointer = NULL;
693 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
694 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
695 JSAMPROW *outbuf[MAX_COMPONENTS];
696 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
697 JSAMPLE *ptr;
698 jpeg_component_info *compptr;
DRC2e7b76b2009-04-03 12:04:24 +0000699
DRC19c791c2018-03-08 10:55:20 -0600700 getcinstance(handle);
701 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000702
DRC19c791c2018-03-08 10:55:20 -0600703 for (i = 0; i < MAX_COMPONENTS; i++) {
704 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
705 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
706 }
DRCfbb67472010-11-24 04:02:37 +0000707
DRC19c791c2018-03-08 10:55:20 -0600708 if ((this->init & COMPRESS) == 0)
709 _throw("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
DRCe2f8e692013-10-30 22:21:06 +0000710
DRC19c791c2018-03-08 10:55:20 -0600711 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
712 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
713 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
714 _throw("tjEncodeYUVPlanes(): Invalid argument");
715 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
716 _throw("tjEncodeYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000717
DRC19c791c2018-03-08 10:55:20 -0600718 if (pixelFormat == TJPF_CMYK)
719 _throw("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
DRCcd7c3e62013-08-23 02:49:25 +0000720
DRC19c791c2018-03-08 10:55:20 -0600721 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC2e7b76b2009-04-03 12:04:24 +0000722
DRC19c791c2018-03-08 10:55:20 -0600723 if (setjmp(this->jerr.setjmp_buffer)) {
724 /* If we get here, the JPEG code has signaled an error. */
725 retval = -1; goto bailout;
726 }
DRCd4c41fe2017-03-18 12:56:36 -0500727
DRC19c791c2018-03-08 10:55:20 -0600728 cinfo->image_width = width;
729 cinfo->image_height = height;
DRC2e7b76b2009-04-03 12:04:24 +0000730
DRCbd96b302018-03-17 00:06:10 -0500731#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600732 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
733 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
734 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500735#endif
DRC0c6a2712010-02-22 08:34:44 +0000736
DRC19c791c2018-03-08 10:55:20 -0600737 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000738
DRC19c791c2018-03-08 10:55:20 -0600739 /* Execute only the parts of jpeg_start_compress() that we need. If we
740 were to call the whole jpeg_start_compress() function, then it would try
741 to write the file headers, which could overflow the output buffer if the
742 YUV image were very small. */
743 if (cinfo->global_state != CSTATE_START)
744 _throw("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
745 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
746 jinit_c_master_control(cinfo, FALSE);
747 jinit_color_converter(cinfo);
748 jinit_downsampler(cinfo);
749 (*cinfo->cconvert->start_pass) (cinfo);
DRC38c99702014-02-11 09:45:18 +0000750
DRC19c791c2018-03-08 10:55:20 -0600751 pw0 = PAD(width, cinfo->max_h_samp_factor);
752 ph0 = PAD(height, cinfo->max_v_samp_factor);
DRC2e7b76b2009-04-03 12:04:24 +0000753
DRC19c791c2018-03-08 10:55:20 -0600754 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
755 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
756 for (i = 0; i < height; i++) {
757 if (flags & TJFLAG_BOTTOMUP)
758 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
759 else
760 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
761 }
762 if (height < ph0)
763 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRCfbb67472010-11-24 04:02:37 +0000764
DRC19c791c2018-03-08 10:55:20 -0600765 for (i = 0; i < cinfo->num_components; i++) {
766 compptr = &cinfo->comp_info[i];
767 _tmpbuf[i] = (JSAMPLE *)malloc(
768 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
769 compptr->h_samp_factor, 32) *
770 cinfo->max_v_samp_factor + 32);
771 if (!_tmpbuf[i])
772 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
773 tmpbuf[i] =
774 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
775 if (!tmpbuf[i])
776 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
777 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
778 unsigned char *_tmpbuf_aligned =
779 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRCfbb67472010-11-24 04:02:37 +0000780
DRC19c791c2018-03-08 10:55:20 -0600781 tmpbuf[i][row] = &_tmpbuf_aligned[
782 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
783 compptr->h_samp_factor, 32) * row];
784 }
785 _tmpbuf2[i] =
786 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
787 compptr->v_samp_factor + 32);
788 if (!_tmpbuf2[i])
789 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
790 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
791 if (!tmpbuf2[i])
792 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
793 for (row = 0; row < compptr->v_samp_factor; row++) {
794 unsigned char *_tmpbuf2_aligned =
795 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
DRCd4c41fe2017-03-18 12:56:36 -0500796
DRC19c791c2018-03-08 10:55:20 -0600797 tmpbuf2[i][row] =
798 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
799 }
800 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
801 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
802 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
803 if (!outbuf[i])
804 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
805 ptr = dstPlanes[i];
806 for (row = 0; row < ph[i]; row++) {
807 outbuf[i][row] = ptr;
808 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
809 }
810 }
DRC2e7b76b2009-04-03 12:04:24 +0000811
DRC19c791c2018-03-08 10:55:20 -0600812 if (setjmp(this->jerr.setjmp_buffer)) {
813 /* If we get here, the JPEG code has signaled an error. */
814 retval = -1; goto bailout;
815 }
816
817 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
818 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
819 cinfo->max_v_samp_factor);
820 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
821 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
822 i++, compptr++)
823 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
824 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
825 compptr->v_samp_factor, pw[i]);
826 }
827 cinfo->next_scanline += height;
828 jpeg_abort_compress(cinfo);
829
830bailout:
831 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
832 if (row_pointer) free(row_pointer);
833 for (i = 0; i < MAX_COMPONENTS; i++) {
834 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
835 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
836 if (tmpbuf2[i] != NULL) free(tmpbuf2[i]);
837 if (_tmpbuf2[i] != NULL) free(_tmpbuf2[i]);
838 if (outbuf[i] != NULL) free(outbuf[i]);
839 }
840 if (this->jerr.warning) retval = -1;
841 this->jerr.stopOnWarning = FALSE;
842 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000843}
844
DRC19c791c2018-03-08 10:55:20 -0600845DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
846 int width, int pitch, int height, int pixelFormat,
847 unsigned char *dstBuf, int pad, int subsamp,
848 int flags)
DRCaecea382014-08-11 18:05:41 +0000849{
DRC19c791c2018-03-08 10:55:20 -0600850 unsigned char *dstPlanes[3];
851 int pw0, ph0, strides[3], retval = -1;
852 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +0000853
DRC19c791c2018-03-08 10:55:20 -0600854 if (!this) _throwg("tjEncodeYUV3(): Invalid handle");
855 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -0500856
DRC19c791c2018-03-08 10:55:20 -0600857 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 || !isPow2(pad) ||
858 subsamp < 0 || subsamp >= NUMSUBOPT)
859 _throw("tjEncodeYUV3(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +0000860
DRC19c791c2018-03-08 10:55:20 -0600861 pw0 = tjPlaneWidth(0, width, subsamp);
862 ph0 = tjPlaneHeight(0, height, subsamp);
863 dstPlanes[0] = dstBuf;
864 strides[0] = PAD(pw0, pad);
865 if (subsamp == TJSAMP_GRAY) {
866 strides[1] = strides[2] = 0;
867 dstPlanes[1] = dstPlanes[2] = NULL;
868 } else {
869 int pw1 = tjPlaneWidth(1, width, subsamp);
870 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +0000871
DRC19c791c2018-03-08 10:55:20 -0600872 strides[1] = strides[2] = PAD(pw1, pad);
873 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
874 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
875 }
DRCaecea382014-08-11 18:05:41 +0000876
DRC19c791c2018-03-08 10:55:20 -0600877 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
878 dstPlanes, strides, subsamp, flags);
879
880bailout:
881 return retval;
DRCaecea382014-08-11 18:05:41 +0000882}
883
DRC19c791c2018-03-08 10:55:20 -0600884DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
885 int pitch, int height, int pixelFormat,
886 unsigned char *dstBuf, int subsamp, int flags)
DRCf610d612013-04-26 10:33:29 +0000887{
DRC19c791c2018-03-08 10:55:20 -0600888 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
889 dstBuf, 4, subsamp, flags);
DRCf610d612013-04-26 10:33:29 +0000890}
891
DRC19c791c2018-03-08 10:55:20 -0600892DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
893 int pitch, int height, int pixelSize,
894 unsigned char *dstBuf, int subsamp, int flags)
DRC84241602011-02-25 02:08:23 +0000895{
DRC19c791c2018-03-08 10:55:20 -0600896 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
897 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
898 flags);
DRC84241602011-02-25 02:08:23 +0000899}
900
901
DRC19c791c2018-03-08 10:55:20 -0600902DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
903 const unsigned char **srcPlanes,
904 int width, const int *strides,
905 int height, int subsamp,
906 unsigned char **jpegBuf,
907 unsigned long *jpegSize, int jpegQual,
908 int flags)
DRC910a3572013-10-30 23:02:57 +0000909{
DRC19c791c2018-03-08 10:55:20 -0600910 int i, row, retval = 0, alloc = 1;
911 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
912 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
913 JSAMPLE *_tmpbuf = NULL, *ptr;
914 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
DRC910a3572013-10-30 23:02:57 +0000915
DRC19c791c2018-03-08 10:55:20 -0600916 getcinstance(handle)
917 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000918
DRC19c791c2018-03-08 10:55:20 -0600919 for (i = 0; i < MAX_COMPONENTS; i++) {
920 tmpbuf[i] = NULL; inbuf[i] = NULL;
921 }
DRC910a3572013-10-30 23:02:57 +0000922
DRC19c791c2018-03-08 10:55:20 -0600923 if ((this->init & COMPRESS) == 0)
924 _throw("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
DRC910a3572013-10-30 23:02:57 +0000925
DRC19c791c2018-03-08 10:55:20 -0600926 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
927 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
928 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
929 _throw("tjCompressFromYUVPlanes(): Invalid argument");
930 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
931 _throw("tjCompressFromYUVPlanes(): Invalid argument");
DRC910a3572013-10-30 23:02:57 +0000932
DRC19c791c2018-03-08 10:55:20 -0600933 if (setjmp(this->jerr.setjmp_buffer)) {
934 /* If we get here, the JPEG code has signaled an error. */
935 retval = -1; goto bailout;
936 }
DRC910a3572013-10-30 23:02:57 +0000937
DRC19c791c2018-03-08 10:55:20 -0600938 cinfo->image_width = width;
939 cinfo->image_height = height;
DRC910a3572013-10-30 23:02:57 +0000940
DRCbd96b302018-03-17 00:06:10 -0500941#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600942 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
943 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
944 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500945#endif
DRC910a3572013-10-30 23:02:57 +0000946
DRC19c791c2018-03-08 10:55:20 -0600947 if (flags & TJFLAG_NOREALLOC) {
948 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
949 }
950 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
951 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
952 return -1;
953 cinfo->raw_data_in = TRUE;
DRC910a3572013-10-30 23:02:57 +0000954
DRC19c791c2018-03-08 10:55:20 -0600955 jpeg_start_compress(cinfo, TRUE);
956 for (i = 0; i < cinfo->num_components; i++) {
957 jpeg_component_info *compptr = &cinfo->comp_info[i];
958 int ih;
DRC910a3572013-10-30 23:02:57 +0000959
DRC19c791c2018-03-08 10:55:20 -0600960 iw[i] = compptr->width_in_blocks * DCTSIZE;
961 ih = compptr->height_in_blocks * DCTSIZE;
962 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
963 compptr->h_samp_factor / cinfo->max_h_samp_factor;
964 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
965 compptr->v_samp_factor / cinfo->max_v_samp_factor;
966 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
967 th[i] = compptr->v_samp_factor * DCTSIZE;
968 tmpbufsize += iw[i] * th[i];
969 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
970 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
971 ptr = (JSAMPLE *)srcPlanes[i];
972 for (row = 0; row < ph[i]; row++) {
973 inbuf[i][row] = ptr;
974 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
975 }
976 }
977 if (usetmpbuf) {
978 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
979 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
980 ptr = _tmpbuf;
981 for (i = 0; i < cinfo->num_components; i++) {
982 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
983 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
984 for (row = 0; row < th[i]; row++) {
985 tmpbuf[i][row] = ptr;
986 ptr += iw[i];
987 }
988 }
989 }
DRCd4c41fe2017-03-18 12:56:36 -0500990
DRC19c791c2018-03-08 10:55:20 -0600991 if (setjmp(this->jerr.setjmp_buffer)) {
992 /* If we get here, the JPEG code has signaled an error. */
993 retval = -1; goto bailout;
994 }
DRC910a3572013-10-30 23:02:57 +0000995
DRC19c791c2018-03-08 10:55:20 -0600996 for (row = 0; row < (int)cinfo->image_height;
997 row += cinfo->max_v_samp_factor * DCTSIZE) {
998 JSAMPARRAY yuvptr[MAX_COMPONENTS];
999 int crow[MAX_COMPONENTS];
1000
1001 for (i = 0; i < cinfo->num_components; i++) {
1002 jpeg_component_info *compptr = &cinfo->comp_info[i];
1003
1004 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1005 if (usetmpbuf) {
1006 int j, k;
1007
1008 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1009 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1010 /* Duplicate last sample in row to fill out MCU */
1011 for (k = pw[i]; k < iw[i]; k++)
1012 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1013 }
1014 /* Duplicate last row to fill out MCU */
1015 for (j = ph[i] - crow[i]; j < th[i]; j++)
1016 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1017 yuvptr[i] = tmpbuf[i];
1018 } else
1019 yuvptr[i] = &inbuf[i][crow[i]];
1020 }
1021 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1022 }
1023 jpeg_finish_compress(cinfo);
1024
1025bailout:
1026 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1027 for (i = 0; i < MAX_COMPONENTS; i++) {
1028 if (tmpbuf[i]) free(tmpbuf[i]);
1029 if (inbuf[i]) free(inbuf[i]);
1030 }
1031 if (_tmpbuf) free(_tmpbuf);
1032 if (this->jerr.warning) retval = -1;
1033 this->jerr.stopOnWarning = FALSE;
1034 return retval;
DRC910a3572013-10-30 23:02:57 +00001035}
1036
DRC19c791c2018-03-08 10:55:20 -06001037DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1038 int width, int pad, int height, int subsamp,
1039 unsigned char **jpegBuf,
1040 unsigned long *jpegSize, int jpegQual,
1041 int flags)
DRCaecea382014-08-11 18:05:41 +00001042{
DRC19c791c2018-03-08 10:55:20 -06001043 const unsigned char *srcPlanes[3];
1044 int pw0, ph0, strides[3], retval = -1;
1045 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +00001046
DRC19c791c2018-03-08 10:55:20 -06001047 if (!this) _throwg("tjCompressFromYUV(): Invalid handle");
1048 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001049
DRC19c791c2018-03-08 10:55:20 -06001050 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1051 subsamp >= NUMSUBOPT)
1052 _throw("tjCompressFromYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001053
DRC19c791c2018-03-08 10:55:20 -06001054 pw0 = tjPlaneWidth(0, width, subsamp);
1055 ph0 = tjPlaneHeight(0, height, subsamp);
1056 srcPlanes[0] = srcBuf;
1057 strides[0] = PAD(pw0, pad);
1058 if (subsamp == TJSAMP_GRAY) {
1059 strides[1] = strides[2] = 0;
1060 srcPlanes[1] = srcPlanes[2] = NULL;
1061 } else {
1062 int pw1 = tjPlaneWidth(1, width, subsamp);
1063 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001064
DRC19c791c2018-03-08 10:55:20 -06001065 strides[1] = strides[2] = PAD(pw1, pad);
1066 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1067 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1068 }
DRCaecea382014-08-11 18:05:41 +00001069
DRC19c791c2018-03-08 10:55:20 -06001070 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1071 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1072
1073bailout:
1074 return retval;
DRCaecea382014-08-11 18:05:41 +00001075}
1076
DRC910a3572013-10-30 23:02:57 +00001077
DRC9b28def2011-05-21 14:37:15 +00001078/* Decompressor */
DRC2e7b76b2009-04-03 12:04:24 +00001079
DRC9b28def2011-05-21 14:37:15 +00001080static tjhandle _tjInitDecompress(tjinstance *this)
DRC2e7b76b2009-04-03 12:04:24 +00001081{
DRC19c791c2018-03-08 10:55:20 -06001082 static unsigned char buffer[1];
DRC2e7b76b2009-04-03 12:04:24 +00001083
DRC19c791c2018-03-08 10:55:20 -06001084 /* This is also straight out of example.txt */
1085 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1086 this->jerr.pub.error_exit = my_error_exit;
1087 this->jerr.pub.output_message = my_output_message;
1088 this->jerr.emit_message = this->jerr.pub.emit_message;
1089 this->jerr.pub.emit_message = my_emit_message;
DRC2e7b76b2009-04-03 12:04:24 +00001090
DRC19c791c2018-03-08 10:55:20 -06001091 if (setjmp(this->jerr.setjmp_buffer)) {
1092 /* If we get here, the JPEG code has signaled an error. */
1093 if (this) free(this);
1094 return NULL;
1095 }
DRC2e7b76b2009-04-03 12:04:24 +00001096
DRC19c791c2018-03-08 10:55:20 -06001097 jpeg_create_decompress(&this->dinfo);
1098 /* Make an initial call so it will create the source manager */
1099 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +00001100
DRC19c791c2018-03-08 10:55:20 -06001101 this->init |= DECOMPRESS;
1102 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +00001103}
1104
DRC19c791c2018-03-08 10:55:20 -06001105DLLEXPORT tjhandle tjInitDecompress(void)
DRC890f1e02011-02-26 22:02:37 +00001106{
DRC19c791c2018-03-08 10:55:20 -06001107 tjinstance *this;
1108
1109 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1110 snprintf(errStr, JMSG_LENGTH_MAX,
1111 "tjInitDecompress(): Memory allocation failure");
1112 return NULL;
1113 }
1114 MEMZERO(this, sizeof(tjinstance));
1115 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1116 return _tjInitDecompress(this);
DRC890f1e02011-02-26 22:02:37 +00001117}
1118
DRC2e7b76b2009-04-03 12:04:24 +00001119
DRC19c791c2018-03-08 10:55:20 -06001120DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1121 const unsigned char *jpegBuf,
1122 unsigned long jpegSize, int *width,
1123 int *height, int *jpegSubsamp,
1124 int *jpegColorspace)
DRC1fe80f82010-12-14 01:21:29 +00001125{
DRC19c791c2018-03-08 10:55:20 -06001126 int retval = 0;
DRC1fe80f82010-12-14 01:21:29 +00001127
DRC19c791c2018-03-08 10:55:20 -06001128 getdinstance(handle);
1129 if ((this->init & DECOMPRESS) == 0)
1130 _throw("tjDecompressHeader3(): Instance has not been initialized for decompression");
DRC1fe80f82010-12-14 01:21:29 +00001131
DRC19c791c2018-03-08 10:55:20 -06001132 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1133 jpegSubsamp == NULL || jpegColorspace == NULL)
1134 _throw("tjDecompressHeader3(): Invalid argument");
DRC1fe80f82010-12-14 01:21:29 +00001135
DRC19c791c2018-03-08 10:55:20 -06001136 if (setjmp(this->jerr.setjmp_buffer)) {
1137 /* If we get here, the JPEG code has signaled an error. */
1138 return -1;
1139 }
DRC1fe80f82010-12-14 01:21:29 +00001140
DRC19c791c2018-03-08 10:55:20 -06001141 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1142 jpeg_read_header(dinfo, TRUE);
DRC1fe80f82010-12-14 01:21:29 +00001143
DRC19c791c2018-03-08 10:55:20 -06001144 *width = dinfo->image_width;
1145 *height = dinfo->image_height;
1146 *jpegSubsamp = getSubsamp(dinfo);
1147 switch (dinfo->jpeg_color_space) {
1148 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1149 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1150 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1151 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1152 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1153 default: *jpegColorspace = -1; break;
1154 }
DRC1fe80f82010-12-14 01:21:29 +00001155
DRC19c791c2018-03-08 10:55:20 -06001156 jpeg_abort_decompress(dinfo);
DRC1fe80f82010-12-14 01:21:29 +00001157
DRC19c791c2018-03-08 10:55:20 -06001158 if (*jpegSubsamp < 0)
1159 _throw("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
1160 if (*jpegColorspace < 0)
1161 _throw("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
1162 if (*width < 1 || *height < 1)
1163 _throw("tjDecompressHeader3(): Invalid data returned in header");
DRC91e86ba2011-02-15 05:24:08 +00001164
DRC19c791c2018-03-08 10:55:20 -06001165bailout:
1166 if (this->jerr.warning) retval = -1;
1167 return retval;
DRC91e86ba2011-02-15 05:24:08 +00001168}
1169
DRC19c791c2018-03-08 10:55:20 -06001170DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1171 unsigned long jpegSize, int *width,
1172 int *height, int *jpegSubsamp)
DRCcd7c3e62013-08-23 02:49:25 +00001173{
DRC19c791c2018-03-08 10:55:20 -06001174 int jpegColorspace;
1175
1176 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1177 jpegSubsamp, &jpegColorspace);
DRCcd7c3e62013-08-23 02:49:25 +00001178}
1179
DRC19c791c2018-03-08 10:55:20 -06001180DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1181 unsigned long jpegSize, int *width,
1182 int *height)
DRC91e86ba2011-02-15 05:24:08 +00001183{
DRC19c791c2018-03-08 10:55:20 -06001184 int jpegSubsamp;
1185
1186 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1187 &jpegSubsamp);
DRC1fe80f82010-12-14 01:21:29 +00001188}
1189
1190
DRC19c791c2018-03-08 10:55:20 -06001191DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
DRCb28fc572011-02-22 06:41:29 +00001192{
DRC19c791c2018-03-08 10:55:20 -06001193 if (numscalingfactors == NULL) {
1194 snprintf(errStr, JMSG_LENGTH_MAX,
1195 "tjGetScalingFactors(): Invalid argument");
1196 return NULL;
1197 }
DRCb28fc572011-02-22 06:41:29 +00001198
DRC19c791c2018-03-08 10:55:20 -06001199 *numscalingfactors = NUMSF;
1200 return (tjscalingfactor *)sf;
DRCb28fc572011-02-22 06:41:29 +00001201}
1202
1203
DRC19c791c2018-03-08 10:55:20 -06001204DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1205 unsigned long jpegSize, unsigned char *dstBuf,
1206 int width, int pitch, int height, int pixelFormat,
1207 int flags)
DRC2e7b76b2009-04-03 12:04:24 +00001208{
DRC19c791c2018-03-08 10:55:20 -06001209 JSAMPROW *row_pointer = NULL;
1210 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
DRC2e7b76b2009-04-03 12:04:24 +00001211
DRC19c791c2018-03-08 10:55:20 -06001212 getdinstance(handle);
1213 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1214 if ((this->init & DECOMPRESS) == 0)
1215 _throw("tjDecompress2(): Instance has not been initialized for decompression");
DRC9b28def2011-05-21 14:37:15 +00001216
DRC19c791c2018-03-08 10:55:20 -06001217 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1218 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1219 _throw("tjDecompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001220
DRCbd96b302018-03-17 00:06:10 -05001221#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001222 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1223 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1224 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001225#endif
DRC9b28def2011-05-21 14:37:15 +00001226
DRC19c791c2018-03-08 10:55:20 -06001227 if (setjmp(this->jerr.setjmp_buffer)) {
1228 /* If we get here, the JPEG code has signaled an error. */
1229 retval = -1; goto bailout;
1230 }
DRC9b28def2011-05-21 14:37:15 +00001231
DRC19c791c2018-03-08 10:55:20 -06001232 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1233 jpeg_read_header(dinfo, TRUE);
1234 this->dinfo.out_color_space = pf2cs[pixelFormat];
1235 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1236 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
DRC9b28def2011-05-21 14:37:15 +00001237
DRC19c791c2018-03-08 10:55:20 -06001238 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1239 if (width == 0) width = jpegwidth;
1240 if (height == 0) height = jpegheight;
1241 for (i = 0; i < NUMSF; i++) {
1242 scaledw = TJSCALED(jpegwidth, sf[i]);
1243 scaledh = TJSCALED(jpegheight, sf[i]);
1244 if (scaledw <= width && scaledh <= height)
1245 break;
1246 }
1247 if (i >= NUMSF)
1248 _throw("tjDecompress2(): Could not scale down to desired image dimensions");
1249 width = scaledw; height = scaledh;
1250 dinfo->scale_num = sf[i].num;
1251 dinfo->scale_denom = sf[i].denom;
DRC9b28def2011-05-21 14:37:15 +00001252
DRC19c791c2018-03-08 10:55:20 -06001253 jpeg_start_decompress(dinfo);
1254 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRCafc06922012-03-23 19:47:57 +00001255
DRC19c791c2018-03-08 10:55:20 -06001256 if ((row_pointer =
1257 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
1258 _throw("tjDecompress2(): Memory allocation failure");
1259 if (setjmp(this->jerr.setjmp_buffer)) {
1260 /* If we get here, the JPEG code has signaled an error. */
1261 retval = -1; goto bailout;
1262 }
1263 for (i = 0; i < (int)dinfo->output_height; i++) {
1264 if (flags & TJFLAG_BOTTOMUP)
1265 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * pitch];
1266 else
1267 row_pointer[i] = &dstBuf[i * pitch];
1268 }
1269 while (dinfo->output_scanline < dinfo->output_height)
1270 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1271 dinfo->output_height - dinfo->output_scanline);
1272 jpeg_finish_decompress(dinfo);
DRC9b28def2011-05-21 14:37:15 +00001273
DRC19c791c2018-03-08 10:55:20 -06001274bailout:
1275 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1276 if (row_pointer) free(row_pointer);
1277 if (this->jerr.warning) retval = -1;
1278 this->jerr.stopOnWarning = FALSE;
1279 return retval;
DRC9b28def2011-05-21 14:37:15 +00001280}
1281
DRC19c791c2018-03-08 10:55:20 -06001282DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1283 unsigned long jpegSize, unsigned char *dstBuf,
1284 int width, int pitch, int height, int pixelSize,
1285 int flags)
DRC9b28def2011-05-21 14:37:15 +00001286{
DRC19c791c2018-03-08 10:55:20 -06001287 if (flags & TJ_YUV)
1288 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1289 else
1290 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1291 height, getPixelFormat(pixelSize, flags), flags);
DRC9b28def2011-05-21 14:37:15 +00001292}
1293
1294
DRC34dca052014-02-28 09:17:14 +00001295static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
DRC19c791c2018-03-08 10:55:20 -06001296 int pixelFormat, int subsamp, int flags)
DRC34dca052014-02-28 09:17:14 +00001297{
DRC19c791c2018-03-08 10:55:20 -06001298 int i;
DRC895fd6d2014-02-28 09:35:34 +00001299
DRC19c791c2018-03-08 10:55:20 -06001300 dinfo->scale_num = dinfo->scale_denom = 1;
DRC34dca052014-02-28 09:17:14 +00001301
DRC19c791c2018-03-08 10:55:20 -06001302 if (subsamp == TJSAMP_GRAY) {
1303 dinfo->num_components = dinfo->comps_in_scan = 1;
1304 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1305 } else {
1306 dinfo->num_components = dinfo->comps_in_scan = 3;
1307 dinfo->jpeg_color_space = JCS_YCbCr;
1308 }
DRC34dca052014-02-28 09:17:14 +00001309
DRC19c791c2018-03-08 10:55:20 -06001310 dinfo->comp_info = (jpeg_component_info *)
1311 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1312 dinfo->num_components *
1313 sizeof(jpeg_component_info));
DRC34dca052014-02-28 09:17:14 +00001314
DRC19c791c2018-03-08 10:55:20 -06001315 for (i = 0; i < dinfo->num_components; i++) {
1316 jpeg_component_info *compptr = &dinfo->comp_info[i];
DRC34dca052014-02-28 09:17:14 +00001317
DRC19c791c2018-03-08 10:55:20 -06001318 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1319 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1320 compptr->component_index = i;
1321 compptr->component_id = i + 1;
1322 compptr->quant_tbl_no = compptr->dc_tbl_no =
1323 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1324 dinfo->cur_comp_info[i] = compptr;
1325 }
1326 dinfo->data_precision = 8;
1327 for (i = 0; i < 2; i++) {
1328 if (dinfo->quant_tbl_ptrs[i] == NULL)
1329 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1330 }
1331
1332 return 0;
DRC34dca052014-02-28 09:17:14 +00001333}
1334
1335
1336int my_read_markers(j_decompress_ptr dinfo)
1337{
DRC19c791c2018-03-08 10:55:20 -06001338 return JPEG_REACHED_SOS;
DRC34dca052014-02-28 09:17:14 +00001339}
1340
1341void my_reset_marker_reader(j_decompress_ptr dinfo)
1342{
1343}
1344
DRC19c791c2018-03-08 10:55:20 -06001345DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1346 const unsigned char **srcPlanes,
1347 const int *strides, int subsamp,
1348 unsigned char *dstBuf, int width, int pitch,
1349 int height, int pixelFormat, int flags)
DRC34dca052014-02-28 09:17:14 +00001350{
DRC19c791c2018-03-08 10:55:20 -06001351 JSAMPROW *row_pointer = NULL;
1352 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1353 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1354 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1355 JSAMPLE *ptr;
1356 jpeg_component_info *compptr;
1357 int (*old_read_markers) (j_decompress_ptr);
1358 void (*old_reset_marker_reader) (j_decompress_ptr);
DRC34dca052014-02-28 09:17:14 +00001359
DRC19c791c2018-03-08 10:55:20 -06001360 getdinstance(handle);
1361 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRC34dca052014-02-28 09:17:14 +00001362
DRC19c791c2018-03-08 10:55:20 -06001363 for (i = 0; i < MAX_COMPONENTS; i++) {
1364 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1365 }
DRC34dca052014-02-28 09:17:14 +00001366
DRC19c791c2018-03-08 10:55:20 -06001367 if ((this->init & DECOMPRESS) == 0)
1368 _throw("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
DRC34dca052014-02-28 09:17:14 +00001369
DRC19c791c2018-03-08 10:55:20 -06001370 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1371 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1372 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1373 _throw("tjDecodeYUVPlanes(): Invalid argument");
1374 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1375 _throw("tjDecodeYUVPlanes(): Invalid argument");
DRC34dca052014-02-28 09:17:14 +00001376
DRC19c791c2018-03-08 10:55:20 -06001377 if (setjmp(this->jerr.setjmp_buffer)) {
1378 /* If we get here, the JPEG code has signaled an error. */
1379 retval = -1; goto bailout;
1380 }
DRC34dca052014-02-28 09:17:14 +00001381
DRC19c791c2018-03-08 10:55:20 -06001382 if (pixelFormat == TJPF_CMYK)
1383 _throw("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
DRC34dca052014-02-28 09:17:14 +00001384
DRC19c791c2018-03-08 10:55:20 -06001385 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1386 dinfo->image_width = width;
1387 dinfo->image_height = height;
DRC34dca052014-02-28 09:17:14 +00001388
DRCbd96b302018-03-17 00:06:10 -05001389#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001390 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1391 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1392 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001393#endif
DRC34dca052014-02-28 09:17:14 +00001394
DRC19c791c2018-03-08 10:55:20 -06001395 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1396 retval = -1; goto bailout;
1397 }
1398 old_read_markers = dinfo->marker->read_markers;
1399 dinfo->marker->read_markers = my_read_markers;
1400 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1401 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1402 jpeg_read_header(dinfo, TRUE);
1403 dinfo->marker->read_markers = old_read_markers;
1404 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
DRC34dca052014-02-28 09:17:14 +00001405
DRC19c791c2018-03-08 10:55:20 -06001406 this->dinfo.out_color_space = pf2cs[pixelFormat];
1407 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1408 dinfo->do_fancy_upsampling = FALSE;
1409 dinfo->Se = DCTSIZE2 - 1;
1410 jinit_master_decompress(dinfo);
1411 (*dinfo->upsample->start_pass) (dinfo);
DRC34dca052014-02-28 09:17:14 +00001412
DRC19c791c2018-03-08 10:55:20 -06001413 pw0 = PAD(width, dinfo->max_h_samp_factor);
1414 ph0 = PAD(height, dinfo->max_v_samp_factor);
DRC34dca052014-02-28 09:17:14 +00001415
DRC19c791c2018-03-08 10:55:20 -06001416 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRC34dca052014-02-28 09:17:14 +00001417
DRC19c791c2018-03-08 10:55:20 -06001418 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1419 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1420 for (i = 0; i < height; i++) {
1421 if (flags & TJFLAG_BOTTOMUP)
1422 row_pointer[i] = &dstBuf[(height - i - 1) * pitch];
1423 else
1424 row_pointer[i] = &dstBuf[i * pitch];
1425 }
1426 if (height < ph0)
1427 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRC34dca052014-02-28 09:17:14 +00001428
DRC19c791c2018-03-08 10:55:20 -06001429 for (i = 0; i < dinfo->num_components; i++) {
1430 compptr = &dinfo->comp_info[i];
1431 _tmpbuf[i] =
1432 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1433 compptr->v_samp_factor + 32);
1434 if (!_tmpbuf[i])
1435 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1436 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1437 if (!tmpbuf[i])
1438 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1439 for (row = 0; row < compptr->v_samp_factor; row++) {
1440 unsigned char *_tmpbuf_aligned =
1441 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRC34dca052014-02-28 09:17:14 +00001442
DRC19c791c2018-03-08 10:55:20 -06001443 tmpbuf[i][row] =
1444 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1445 }
1446 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1447 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1448 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1449 if (!inbuf[i])
1450 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1451 ptr = (JSAMPLE *)srcPlanes[i];
1452 for (row = 0; row < ph[i]; row++) {
1453 inbuf[i][row] = ptr;
1454 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1455 }
1456 }
DRCd4c41fe2017-03-18 12:56:36 -05001457
DRC19c791c2018-03-08 10:55:20 -06001458 if (setjmp(this->jerr.setjmp_buffer)) {
1459 /* If we get here, the JPEG code has signaled an error. */
1460 retval = -1; goto bailout;
1461 }
DRC34dca052014-02-28 09:17:14 +00001462
DRC19c791c2018-03-08 10:55:20 -06001463 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1464 JDIMENSION inrow = 0, outrow = 0;
1465
1466 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1467 i++, compptr++)
1468 jcopy_sample_rows(inbuf[i],
1469 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1470 compptr->v_samp_factor, pw[i]);
1471 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1472 dinfo->max_v_samp_factor, &row_pointer[row],
1473 &outrow, dinfo->max_v_samp_factor);
1474 }
1475 jpeg_abort_decompress(dinfo);
1476
1477bailout:
1478 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1479 if (row_pointer) free(row_pointer);
1480 for (i = 0; i < MAX_COMPONENTS; i++) {
1481 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
1482 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
1483 if (inbuf[i] != NULL) free(inbuf[i]);
1484 }
1485 if (this->jerr.warning) retval = -1;
1486 this->jerr.stopOnWarning = FALSE;
1487 return retval;
DRC34dca052014-02-28 09:17:14 +00001488}
1489
DRC19c791c2018-03-08 10:55:20 -06001490DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1491 int pad, int subsamp, unsigned char *dstBuf,
1492 int width, int pitch, int height, int pixelFormat,
1493 int flags)
DRCaecea382014-08-11 18:05:41 +00001494{
DRC19c791c2018-03-08 10:55:20 -06001495 const unsigned char *srcPlanes[3];
1496 int pw0, ph0, strides[3], retval = -1;
1497 tjinstance *this = (tjinstance *)handle;
DRC34dca052014-02-28 09:17:14 +00001498
DRC19c791c2018-03-08 10:55:20 -06001499 if (!this) _throwg("tjDecodeYUV(): Invalid handle");
1500 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001501
DRC19c791c2018-03-08 10:55:20 -06001502 if (srcBuf == NULL || pad < 0 || !isPow2(pad) || subsamp < 0 ||
1503 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
1504 _throw("tjDecodeYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001505
DRC19c791c2018-03-08 10:55:20 -06001506 pw0 = tjPlaneWidth(0, width, subsamp);
1507 ph0 = tjPlaneHeight(0, height, subsamp);
1508 srcPlanes[0] = srcBuf;
1509 strides[0] = PAD(pw0, pad);
1510 if (subsamp == TJSAMP_GRAY) {
1511 strides[1] = strides[2] = 0;
1512 srcPlanes[1] = srcPlanes[2] = NULL;
1513 } else {
1514 int pw1 = tjPlaneWidth(1, width, subsamp);
1515 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001516
DRC19c791c2018-03-08 10:55:20 -06001517 strides[1] = strides[2] = PAD(pw1, pad);
1518 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1519 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1520 }
DRCaecea382014-08-11 18:05:41 +00001521
DRC19c791c2018-03-08 10:55:20 -06001522 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1523 pitch, height, pixelFormat, flags);
1524
1525bailout:
1526 return retval;
DRCaecea382014-08-11 18:05:41 +00001527}
1528
DRC19c791c2018-03-08 10:55:20 -06001529DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1530 const unsigned char *jpegBuf,
1531 unsigned long jpegSize,
1532 unsigned char **dstPlanes, int width,
1533 int *strides, int height, int flags)
DRC9b28def2011-05-21 14:37:15 +00001534{
DRC19c791c2018-03-08 10:55:20 -06001535 int i, sfi, row, retval = 0;
1536 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1537 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1538 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1539 JSAMPLE *_tmpbuf = NULL, *ptr;
1540 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1541 int dctsize;
DRC9b28def2011-05-21 14:37:15 +00001542
DRC19c791c2018-03-08 10:55:20 -06001543 getdinstance(handle);
1544 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +00001545
DRC19c791c2018-03-08 10:55:20 -06001546 for (i = 0; i < MAX_COMPONENTS; i++) {
1547 tmpbuf[i] = NULL; outbuf[i] = NULL;
1548 }
DRC9e17f7d2010-12-10 04:59:13 +00001549
DRC19c791c2018-03-08 10:55:20 -06001550 if ((this->init & DECOMPRESS) == 0)
1551 _throw("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
DRCe2f8e692013-10-30 22:21:06 +00001552
DRC19c791c2018-03-08 10:55:20 -06001553 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1554 width < 0 || height < 0)
1555 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001556
DRCbd96b302018-03-17 00:06:10 -05001557#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001558 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1559 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1560 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001561#endif
DRC0c6a2712010-02-22 08:34:44 +00001562
DRC19c791c2018-03-08 10:55:20 -06001563 if (setjmp(this->jerr.setjmp_buffer)) {
1564 /* If we get here, the JPEG code has signaled an error. */
1565 retval = -1; goto bailout;
1566 }
DRC2e7b76b2009-04-03 12:04:24 +00001567
DRC19c791c2018-03-08 10:55:20 -06001568 if (!this->headerRead) {
1569 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1570 jpeg_read_header(dinfo, TRUE);
1571 }
1572 this->headerRead = 0;
1573 jpegSubsamp = getSubsamp(dinfo);
1574 if (jpegSubsamp < 0)
1575 _throw("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001576
DRC19c791c2018-03-08 10:55:20 -06001577 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1578 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001579
DRC19c791c2018-03-08 10:55:20 -06001580 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1581 if (width == 0) width = jpegwidth;
1582 if (height == 0) height = jpegheight;
1583 for (i = 0; i < NUMSF; i++) {
1584 scaledw = TJSCALED(jpegwidth, sf[i]);
1585 scaledh = TJSCALED(jpegheight, sf[i]);
1586 if (scaledw <= width && scaledh <= height)
1587 break;
1588 }
1589 if (i >= NUMSF)
1590 _throw("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
1591 if (dinfo->num_components > 3)
1592 _throw("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
DRCcd7c3e62013-08-23 02:49:25 +00001593
DRC19c791c2018-03-08 10:55:20 -06001594 width = scaledw; height = scaledh;
1595 dinfo->scale_num = sf[i].num;
1596 dinfo->scale_denom = sf[i].denom;
1597 sfi = i;
1598 jpeg_calc_output_dimensions(dinfo);
DRCf610d612013-04-26 10:33:29 +00001599
DRC19c791c2018-03-08 10:55:20 -06001600 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
DRC418fe282013-05-07 21:17:35 +00001601
DRC19c791c2018-03-08 10:55:20 -06001602 for (i = 0; i < dinfo->num_components; i++) {
1603 jpeg_component_info *compptr = &dinfo->comp_info[i];
1604 int ih;
DRC9e17f7d2010-12-10 04:59:13 +00001605
DRC19c791c2018-03-08 10:55:20 -06001606 iw[i] = compptr->width_in_blocks * dctsize;
1607 ih = compptr->height_in_blocks * dctsize;
1608 pw[i] = PAD(dinfo->output_width, dinfo->max_h_samp_factor) *
1609 compptr->h_samp_factor / dinfo->max_h_samp_factor;
1610 ph[i] = PAD(dinfo->output_height, dinfo->max_v_samp_factor) *
1611 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1612 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1613 th[i] = compptr->v_samp_factor * dctsize;
1614 tmpbufsize += iw[i] * th[i];
1615 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1616 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1617 ptr = dstPlanes[i];
1618 for (row = 0; row < ph[i]; row++) {
1619 outbuf[i][row] = ptr;
1620 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1621 }
1622 }
1623 if (usetmpbuf) {
1624 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1625 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1626 ptr = _tmpbuf;
1627 for (i = 0; i < dinfo->num_components; i++) {
1628 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1629 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1630 for (row = 0; row < th[i]; row++) {
1631 tmpbuf[i][row] = ptr;
1632 ptr += iw[i];
1633 }
1634 }
1635 }
DRCd4c41fe2017-03-18 12:56:36 -05001636
DRC19c791c2018-03-08 10:55:20 -06001637 if (setjmp(this->jerr.setjmp_buffer)) {
1638 /* If we get here, the JPEG code has signaled an error. */
1639 retval = -1; goto bailout;
1640 }
DRC9b28def2011-05-21 14:37:15 +00001641
DRC19c791c2018-03-08 10:55:20 -06001642 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1643 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1644 dinfo->raw_data_out = TRUE;
DRC2e7b76b2009-04-03 12:04:24 +00001645
DRC19c791c2018-03-08 10:55:20 -06001646 jpeg_start_decompress(dinfo);
1647 for (row = 0; row < (int)dinfo->output_height;
1648 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1649 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1650 int crow[MAX_COMPONENTS];
1651
1652 for (i = 0; i < dinfo->num_components; i++) {
1653 jpeg_component_info *compptr = &dinfo->comp_info[i];
1654
1655 if (jpegSubsamp == TJ_420) {
1656 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1657 to be clever and use the IDCT to perform upsampling on the U and V
1658 planes. For instance, if the output image is to be scaled by 1/2
1659 relative to the JPEG image, then the scaling factor and upsampling
1660 effectively cancel each other, so a normal 8x8 IDCT can be used.
1661 However, this is not desirable when using the decompress-to-YUV
1662 functionality in TurboJPEG, since we want to output the U and V
1663 planes in their subsampled form. Thus, we have to override some
1664 internal libjpeg parameters to force it to use the "scaled" IDCT
1665 functions on the U and V planes. */
1666 compptr->_DCT_scaled_size = dctsize;
1667 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1668 sf[sfi].num / sf[sfi].denom *
1669 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1670 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1671 }
1672 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1673 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1674 else yuvptr[i] = &outbuf[i][crow[i]];
1675 }
1676 jpeg_read_raw_data(dinfo, yuvptr,
1677 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1678 if (usetmpbuf) {
1679 int j;
1680
1681 for (i = 0; i < dinfo->num_components; i++) {
1682 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1683 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1684 }
1685 }
1686 }
1687 }
1688 jpeg_finish_decompress(dinfo);
1689
1690bailout:
1691 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1692 for (i = 0; i < MAX_COMPONENTS; i++) {
1693 if (tmpbuf[i]) free(tmpbuf[i]);
1694 if (outbuf[i]) free(outbuf[i]);
1695 }
1696 if (_tmpbuf) free(_tmpbuf);
1697 if (this->jerr.warning) retval = -1;
1698 this->jerr.stopOnWarning = FALSE;
1699 return retval;
DRC2e7b76b2009-04-03 12:04:24 +00001700}
1701
DRC19c791c2018-03-08 10:55:20 -06001702DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1703 unsigned long jpegSize, unsigned char *dstBuf,
1704 int width, int pad, int height, int flags)
DRCaecea382014-08-11 18:05:41 +00001705{
DRC19c791c2018-03-08 10:55:20 -06001706 unsigned char *dstPlanes[3];
1707 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1708 int i, jpegwidth, jpegheight, scaledw, scaledh;
DRCaecea382014-08-11 18:05:41 +00001709
DRC19c791c2018-03-08 10:55:20 -06001710 getdinstance(handle);
1711 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCaecea382014-08-11 18:05:41 +00001712
DRC19c791c2018-03-08 10:55:20 -06001713 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1714 pad < 1 || !isPow2(pad) || height < 0)
1715 _throw("tjDecompressToYUV2(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001716
DRC19c791c2018-03-08 10:55:20 -06001717 if (setjmp(this->jerr.setjmp_buffer)) {
1718 /* If we get here, the JPEG code has signaled an error. */
1719 return -1;
1720 }
DRCdec79952016-04-20 11:27:42 -05001721
DRC19c791c2018-03-08 10:55:20 -06001722 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1723 jpeg_read_header(dinfo, TRUE);
1724 jpegSubsamp = getSubsamp(dinfo);
1725 if (jpegSubsamp < 0)
1726 _throw("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001727
DRC19c791c2018-03-08 10:55:20 -06001728 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1729 if (width == 0) width = jpegwidth;
1730 if (height == 0) height = jpegheight;
DRCaecea382014-08-11 18:05:41 +00001731
DRC19c791c2018-03-08 10:55:20 -06001732 for (i = 0; i < NUMSF; i++) {
1733 scaledw = TJSCALED(jpegwidth, sf[i]);
1734 scaledh = TJSCALED(jpegheight, sf[i]);
1735 if (scaledw <= width && scaledh <= height)
1736 break;
1737 }
1738 if (i >= NUMSF)
1739 _throw("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
DRCaecea382014-08-11 18:05:41 +00001740
DRC19c791c2018-03-08 10:55:20 -06001741 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1742 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1743 dstPlanes[0] = dstBuf;
1744 strides[0] = PAD(pw0, pad);
1745 if (jpegSubsamp == TJSAMP_GRAY) {
1746 strides[1] = strides[2] = 0;
1747 dstPlanes[1] = dstPlanes[2] = NULL;
1748 } else {
1749 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1750 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
DRCaecea382014-08-11 18:05:41 +00001751
DRC19c791c2018-03-08 10:55:20 -06001752 strides[1] = strides[2] = PAD(pw1, pad);
1753 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1754 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1755 }
DRCaecea382014-08-11 18:05:41 +00001756
DRC19c791c2018-03-08 10:55:20 -06001757 this->headerRead = 1;
1758 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1759 strides, height, flags);
DRCaecea382014-08-11 18:05:41 +00001760
DRC19c791c2018-03-08 10:55:20 -06001761bailout:
1762 this->jerr.stopOnWarning = FALSE;
1763 return retval;
DRCaecea382014-08-11 18:05:41 +00001764}
1765
DRC19c791c2018-03-08 10:55:20 -06001766DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1767 unsigned long jpegSize, unsigned char *dstBuf,
1768 int flags)
DRCf610d612013-04-26 10:33:29 +00001769{
DRC19c791c2018-03-08 10:55:20 -06001770 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
DRCf610d612013-04-26 10:33:29 +00001771}
1772
DRC2e7b76b2009-04-03 12:04:24 +00001773
DRC9b28def2011-05-21 14:37:15 +00001774/* Transformer */
DRC890f1e02011-02-26 22:02:37 +00001775
DRC19c791c2018-03-08 10:55:20 -06001776DLLEXPORT tjhandle tjInitTransform(void)
DRC890f1e02011-02-26 22:02:37 +00001777{
DRC19c791c2018-03-08 10:55:20 -06001778 tjinstance *this = NULL;
1779 tjhandle handle = NULL;
1780
1781 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1782 snprintf(errStr, JMSG_LENGTH_MAX,
1783 "tjInitTransform(): Memory allocation failure");
1784 return NULL;
1785 }
1786 MEMZERO(this, sizeof(tjinstance));
1787 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1788 handle = _tjInitCompress(this);
1789 if (!handle) return NULL;
1790 handle = _tjInitDecompress(this);
1791 return handle;
DRC890f1e02011-02-26 22:02:37 +00001792}
1793
1794
DRC19c791c2018-03-08 10:55:20 -06001795DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1796 unsigned long jpegSize, int n,
1797 unsigned char **dstBufs, unsigned long *dstSizes,
1798 tjtransform *t, int flags)
DRC890f1e02011-02-26 22:02:37 +00001799{
DRC19c791c2018-03-08 10:55:20 -06001800 jpeg_transform_info *xinfo = NULL;
1801 jvirt_barray_ptr *srccoefs, *dstcoefs;
1802 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
DRC890f1e02011-02-26 22:02:37 +00001803
DRC19c791c2018-03-08 10:55:20 -06001804 getinstance(handle);
1805 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1806 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
1807 _throw("tjTransform(): Instance has not been initialized for transformation");
DRC890f1e02011-02-26 22:02:37 +00001808
DRC19c791c2018-03-08 10:55:20 -06001809 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1810 dstSizes == NULL || t == NULL || flags < 0)
1811 _throw("tjTransform(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001812
DRCbd96b302018-03-17 00:06:10 -05001813#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001814 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1815 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1816 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001817#endif
DRC890f1e02011-02-26 22:02:37 +00001818
DRC19c791c2018-03-08 10:55:20 -06001819 if ((xinfo =
1820 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
1821 _throw("tjTransform(): Memory allocation failure");
1822 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
DRCd4c41fe2017-03-18 12:56:36 -05001823
DRC19c791c2018-03-08 10:55:20 -06001824 if (setjmp(this->jerr.setjmp_buffer)) {
1825 /* If we get here, the JPEG code has signaled an error. */
1826 retval = -1; goto bailout;
1827 }
DRC890f1e02011-02-26 22:02:37 +00001828
DRC19c791c2018-03-08 10:55:20 -06001829 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
DRC890f1e02011-02-26 22:02:37 +00001830
DRC19c791c2018-03-08 10:55:20 -06001831 for (i = 0; i < n; i++) {
1832 xinfo[i].transform = xformtypes[t[i].op];
1833 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1834 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1835 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1836 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1837 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1838 else xinfo[i].slow_hflip = 0;
DRC0a325192011-03-02 09:22:41 +00001839
DRC19c791c2018-03-08 10:55:20 -06001840 if (xinfo[i].crop) {
1841 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1842 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1843 if (t[i].r.w != 0) {
1844 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1845 } else
1846 xinfo[i].crop_width = JCROP_UNSET;
1847 if (t[i].r.h != 0) {
1848 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1849 } else
1850 xinfo[i].crop_height = JCROP_UNSET;
1851 }
1852 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1853 }
DRC890f1e02011-02-26 22:02:37 +00001854
DRC19c791c2018-03-08 10:55:20 -06001855 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1856 jpeg_read_header(dinfo, TRUE);
1857 jpegSubsamp = getSubsamp(dinfo);
1858 if (jpegSubsamp < 0)
1859 _throw("tjTransform(): Could not determine subsampling type for JPEG image");
DRC890f1e02011-02-26 22:02:37 +00001860
DRC19c791c2018-03-08 10:55:20 -06001861 for (i = 0; i < n; i++) {
1862 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
1863 _throw("tjTransform(): Transform is not perfect");
DRC890f1e02011-02-26 22:02:37 +00001864
DRC19c791c2018-03-08 10:55:20 -06001865 if (xinfo[i].crop) {
1866 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1867 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
1868 snprintf(errStr, JMSG_LENGTH_MAX,
1869 "To crop this JPEG image, x must be a multiple of %d\n"
1870 "and y must be a multiple of %d.\n",
1871 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
1872 retval = -1; goto bailout;
1873 }
1874 }
1875 }
DRC890f1e02011-02-26 22:02:37 +00001876
DRC19c791c2018-03-08 10:55:20 -06001877 srccoefs = jpeg_read_coefficients(dinfo);
DRC890f1e02011-02-26 22:02:37 +00001878
DRC19c791c2018-03-08 10:55:20 -06001879 for (i = 0; i < n; i++) {
1880 int w, h, alloc = 1;
DRC0a325192011-03-02 09:22:41 +00001881
DRC19c791c2018-03-08 10:55:20 -06001882 if (!xinfo[i].crop) {
1883 w = dinfo->image_width; h = dinfo->image_height;
1884 } else {
1885 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1886 }
1887 if (flags & TJFLAG_NOREALLOC) {
1888 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1889 }
1890 if (!(t[i].options & TJXOPT_NOOUTPUT))
1891 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1892 jpeg_copy_critical_parameters(dinfo, cinfo);
1893 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1894 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1895 jpeg_simple_progression(cinfo);
1896 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1897 jpeg_write_coefficients(cinfo, dstcoefs);
1898 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1899 JCOPYOPT_NONE : JCOPYOPT_ALL);
1900 } else
1901 jinit_c_master_control(cinfo, TRUE);
1902 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1903 if (t[i].customFilter) {
1904 int ci, y;
1905 JDIMENSION by;
DRC890f1e02011-02-26 22:02:37 +00001906
DRC19c791c2018-03-08 10:55:20 -06001907 for (ci = 0; ci < cinfo->num_components; ci++) {
1908 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1909 tjregion arrayRegion = {
1910 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1911 };
1912 tjregion planeRegion = {
1913 0, 0, compptr->width_in_blocks * DCTSIZE,
1914 compptr->height_in_blocks * DCTSIZE
1915 };
1916
1917 for (by = 0; by < compptr->height_in_blocks;
1918 by += compptr->v_samp_factor) {
1919 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1920 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1921 TRUE);
1922
1923 for (y = 0; y < compptr->v_samp_factor; y++) {
1924 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1925 i, &t[i]) == -1)
1926 _throw("tjTransform(): Error in custom filter");
1927 arrayRegion.y += DCTSIZE;
1928 }
1929 }
1930 }
1931 }
1932 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1933 }
1934
1935 jpeg_finish_decompress(dinfo);
1936
1937bailout:
1938 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1939 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1940 if (xinfo) free(xinfo);
1941 if (this->jerr.warning) retval = -1;
1942 this->jerr.stopOnWarning = FALSE;
1943 return retval;
DRC890f1e02011-02-26 22:02:37 +00001944}
DRCaa745902017-11-16 18:09:07 -06001945
1946
DRC19c791c2018-03-08 10:55:20 -06001947DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1948 int align, int *height, int *pixelFormat,
1949 int flags)
DRCaa745902017-11-16 18:09:07 -06001950{
DRC19c791c2018-03-08 10:55:20 -06001951 int retval = 0, tempc, pitch;
1952 tjhandle handle = NULL;
1953 tjinstance *this;
1954 j_compress_ptr cinfo = NULL;
1955 cjpeg_source_ptr src;
1956 unsigned char *dstBuf = NULL;
1957 FILE *file = NULL;
1958 boolean invert;
DRCaa745902017-11-16 18:09:07 -06001959
DRC19c791c2018-03-08 10:55:20 -06001960 if (!filename || !width || align < 1 || !height || !pixelFormat ||
1961 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
1962 _throwg("tjLoadImage(): Invalid argument");
1963 if ((align & (align - 1)) != 0)
1964 _throwg("tjLoadImage(): Alignment must be a power of 2");
DRCaa745902017-11-16 18:09:07 -06001965
DRC19c791c2018-03-08 10:55:20 -06001966 if ((handle = tjInitCompress()) == NULL) return NULL;
1967 this = (tjinstance *)handle;
1968 cinfo = &this->cinfo;
DRCaa745902017-11-16 18:09:07 -06001969
DRC19c791c2018-03-08 10:55:20 -06001970 if ((file = fopen(filename, "rb")) == NULL)
1971 _throwunix("tjLoadImage(): Cannot open input file");
DRCaa745902017-11-16 18:09:07 -06001972
DRC19c791c2018-03-08 10:55:20 -06001973 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
1974 _throwunix("tjLoadImage(): Could not read input file")
1975 else if (tempc == EOF)
1976 _throwg("tjLoadImage(): Input file contains no data");
DRCaa745902017-11-16 18:09:07 -06001977
DRC19c791c2018-03-08 10:55:20 -06001978 if (setjmp(this->jerr.setjmp_buffer)) {
1979 /* If we get here, the JPEG code has signaled an error. */
1980 retval = -1; goto bailout;
1981 }
DRCaa745902017-11-16 18:09:07 -06001982
DRC19c791c2018-03-08 10:55:20 -06001983 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
1984 else cinfo->in_color_space = pf2cs[*pixelFormat];
1985 if (tempc == 'B') {
1986 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
1987 _throwg("tjLoadImage(): Could not initialize bitmap loader");
1988 invert = (flags & TJFLAG_BOTTOMUP) == 0;
1989 } else if (tempc == 'P') {
1990 if ((src = jinit_read_ppm(cinfo)) == NULL)
1991 _throwg("tjLoadImage(): Could not initialize bitmap loader");
1992 invert = (flags & TJFLAG_BOTTOMUP) != 0;
1993 } else
1994 _throwg("tjLoadImage(): Unsupported file type");
DRCaa745902017-11-16 18:09:07 -06001995
DRC19c791c2018-03-08 10:55:20 -06001996 src->input_file = file;
1997 (*src->start_input) (cinfo, src);
1998 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
DRCaa745902017-11-16 18:09:07 -06001999
DRC19c791c2018-03-08 10:55:20 -06002000 *width = cinfo->image_width; *height = cinfo->image_height;
2001 *pixelFormat = cs2pf[cinfo->in_color_space];
DRCaa745902017-11-16 18:09:07 -06002002
DRC19c791c2018-03-08 10:55:20 -06002003 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
2004 if ((dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2005 _throwg("tjLoadImage(): Memory allocation failure");
DRCaa745902017-11-16 18:09:07 -06002006
DRC19c791c2018-03-08 10:55:20 -06002007 if (setjmp(this->jerr.setjmp_buffer)) {
2008 /* If we get here, the JPEG code has signaled an error. */
2009 retval = -1; goto bailout;
2010 }
DRCaa745902017-11-16 18:09:07 -06002011
DRC19c791c2018-03-08 10:55:20 -06002012 while (cinfo->next_scanline < cinfo->image_height) {
2013 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
DRCaa745902017-11-16 18:09:07 -06002014
DRC19c791c2018-03-08 10:55:20 -06002015 for (i = 0; i < nlines; i++) {
2016 unsigned char *dstptr;
2017 int row;
DRCaa745902017-11-16 18:09:07 -06002018
DRC19c791c2018-03-08 10:55:20 -06002019 row = cinfo->next_scanline + i;
2020 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2021 else dstptr = &dstBuf[row * pitch];
2022 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2023 }
2024 cinfo->next_scanline += nlines;
2025 }
2026
2027 (*src->finish_input) (cinfo, src);
2028
2029bailout:
2030 if (handle) tjDestroy(handle);
2031 if (file) fclose(file);
2032 if (retval < 0 && dstBuf) { free(dstBuf); dstBuf = NULL; }
2033 return dstBuf;
DRCaa745902017-11-16 18:09:07 -06002034}
2035
2036
DRC19c791c2018-03-08 10:55:20 -06002037DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2038 int width, int pitch, int height, int pixelFormat,
2039 int flags)
DRCaa745902017-11-16 18:09:07 -06002040{
DRC19c791c2018-03-08 10:55:20 -06002041 int retval = 0;
2042 tjhandle handle = NULL;
2043 tjinstance *this;
2044 j_decompress_ptr dinfo = NULL;
2045 djpeg_dest_ptr dst;
2046 FILE *file = NULL;
2047 char *ptr = NULL;
2048 boolean invert;
DRCaa745902017-11-16 18:09:07 -06002049
DRC19c791c2018-03-08 10:55:20 -06002050 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2051 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2052 _throwg("tjSaveImage(): Invalid argument");
DRCaa745902017-11-16 18:09:07 -06002053
DRC19c791c2018-03-08 10:55:20 -06002054 if ((handle = tjInitDecompress()) == NULL)
2055 return -1;
2056 this = (tjinstance *)handle;
2057 dinfo = &this->dinfo;
DRCaa745902017-11-16 18:09:07 -06002058
DRC19c791c2018-03-08 10:55:20 -06002059 if ((file = fopen(filename, "wb")) == NULL)
2060 _throwunix("tjSaveImage(): Cannot open output file");
DRCaa745902017-11-16 18:09:07 -06002061
DRC19c791c2018-03-08 10:55:20 -06002062 if (setjmp(this->jerr.setjmp_buffer)) {
2063 /* If we get here, the JPEG code has signaled an error. */
2064 retval = -1; goto bailout;
2065 }
DRCaa745902017-11-16 18:09:07 -06002066
DRC19c791c2018-03-08 10:55:20 -06002067 this->dinfo.out_color_space = pf2cs[pixelFormat];
2068 dinfo->image_width = width; dinfo->image_height = height;
2069 dinfo->global_state = DSTATE_READY;
2070 dinfo->scale_num = dinfo->scale_denom = 1;
DRCaa745902017-11-16 18:09:07 -06002071
DRC19c791c2018-03-08 10:55:20 -06002072 ptr = strrchr(filename, '.');
2073 if (ptr && !strcasecmp(ptr, ".bmp")) {
2074 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
2075 _throwg("tjSaveImage(): Could not initialize bitmap writer");
2076 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2077 } else {
2078 if ((dst = jinit_write_ppm(dinfo)) == NULL)
2079 _throwg("tjSaveImage(): Could not initialize PPM writer");
2080 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2081 }
DRCaa745902017-11-16 18:09:07 -06002082
DRC19c791c2018-03-08 10:55:20 -06002083 dst->output_file = file;
2084 (*dst->start_output) (dinfo, dst);
2085 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
DRCaa745902017-11-16 18:09:07 -06002086
DRC19c791c2018-03-08 10:55:20 -06002087 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRCaa745902017-11-16 18:09:07 -06002088
DRC19c791c2018-03-08 10:55:20 -06002089 while (dinfo->output_scanline < dinfo->output_height) {
2090 unsigned char *rowptr;
DRCaa745902017-11-16 18:09:07 -06002091
DRC19c791c2018-03-08 10:55:20 -06002092 if (invert)
2093 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2094 else
2095 rowptr = &buffer[dinfo->output_scanline * pitch];
2096 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2097 (*dst->put_pixel_rows) (dinfo, dst, 1);
2098 dinfo->output_scanline++;
2099 }
DRCaa745902017-11-16 18:09:07 -06002100
DRC19c791c2018-03-08 10:55:20 -06002101 (*dst->finish_output) (dinfo, dst);
2102
2103bailout:
2104 if (handle) tjDestroy(handle);
2105 if (file) fclose(file);
2106 return retval;
DRCaa745902017-11-16 18:09:07 -06002107}