blob: bb061a20ee58cef33e6a5ff60957e699fef4ae93 [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
DRC19c791c2018-03-08 10:55:20 -060052#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
DRCab2df6e2012-01-28 06:49:56 +0000115#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); \
171 this->isInstanceError = TRUE; _throwg(m); \
172}
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
322 if (dinfo->jpeg_color_space == JCS_YCCK && k == 3) {
323 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
324 }
325 if (dinfo->comp_info[k].h_samp_factor == href &&
326 dinfo->comp_info[k].v_samp_factor == vref)
327 match++;
328 }
329 if (match == dinfo->num_components - 1) {
330 retval = i; break;
331 }
332 }
333 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
334 in non-standard ways. */
335 if (dinfo->comp_info[0].h_samp_factor == 2 &&
336 dinfo->comp_info[0].v_samp_factor == 2 &&
337 (i == TJSAMP_422 || i == TJSAMP_440)) {
338 int match = 0;
339
340 for (k = 1; k < dinfo->num_components; k++) {
341 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
342
343 if (dinfo->jpeg_color_space == JCS_YCCK && k == 3) {
344 href = vref = 2;
345 }
346 if (dinfo->comp_info[k].h_samp_factor == href &&
347 dinfo->comp_info[k].v_samp_factor == vref)
348 match++;
349 }
350 if (match == dinfo->num_components - 1) {
351 retval = i; break;
352 }
353 }
354 }
355 }
356 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000357}
358
359
DRC9b28def2011-05-21 14:37:15 +0000360/* General API functions */
361
DRC19c791c2018-03-08 10:55:20 -0600362DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
DRCb9ab64d2017-05-11 21:02:29 -0500363{
DRC19c791c2018-03-08 10:55:20 -0600364 tjinstance *this = (tjinstance *)handle;
365
366 if (this && this->isInstanceError) {
367 this->isInstanceError = FALSE;
368 return this->errStr;
369 } else
370 return errStr;
DRCb9ab64d2017-05-11 21:02:29 -0500371}
372
373
DRC19c791c2018-03-08 10:55:20 -0600374DLLEXPORT char *tjGetErrorStr(void)
DRC9b28def2011-05-21 14:37:15 +0000375{
DRC19c791c2018-03-08 10:55:20 -0600376 return errStr;
DRC9b28def2011-05-21 14:37:15 +0000377}
378
379
DRC19c791c2018-03-08 10:55:20 -0600380DLLEXPORT int tjGetErrorCode(tjhandle handle)
DRCd4092f62017-06-27 10:54:21 -0500381{
DRC19c791c2018-03-08 10:55:20 -0600382 tjinstance *this = (tjinstance *)handle;
383
384 if (this && this->jerr.warning) return TJERR_WARNING;
385 else return TJERR_FATAL;
DRCd4092f62017-06-27 10:54:21 -0500386}
387
388
DRC19c791c2018-03-08 10:55:20 -0600389DLLEXPORT int tjDestroy(tjhandle handle)
DRC9b28def2011-05-21 14:37:15 +0000390{
DRC19c791c2018-03-08 10:55:20 -0600391 getinstance(handle);
392
393 if (setjmp(this->jerr.setjmp_buffer)) return -1;
394 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
395 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
396 free(this);
397 return 0;
DRC9b28def2011-05-21 14:37:15 +0000398}
399
400
DRC6b76f752011-05-24 16:52:47 +0000401/* These are exposed mainly because Windows can't malloc() and free() across
402 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
403 with turbojpeg.dll for compatibility reasons. However, these functions
404 can potentially be used for other purposes by different implementations. */
405
DRC19c791c2018-03-08 10:55:20 -0600406DLLEXPORT void tjFree(unsigned char *buf)
DRC6b76f752011-05-24 16:52:47 +0000407{
DRC19c791c2018-03-08 10:55:20 -0600408 if (buf) free(buf);
DRC6b76f752011-05-24 16:52:47 +0000409}
410
411
DRC19c791c2018-03-08 10:55:20 -0600412DLLEXPORT unsigned char *tjAlloc(int bytes)
DRC6b76f752011-05-24 16:52:47 +0000413{
DRC19c791c2018-03-08 10:55:20 -0600414 return (unsigned char *)malloc(bytes);
DRC6b76f752011-05-24 16:52:47 +0000415}
416
417
DRC9b28def2011-05-21 14:37:15 +0000418/* Compressor */
419
420static tjhandle _tjInitCompress(tjinstance *this)
421{
DRC19c791c2018-03-08 10:55:20 -0600422 static unsigned char buffer[1];
423 unsigned char *buf = buffer;
424 unsigned long size = 1;
DRC9b28def2011-05-21 14:37:15 +0000425
DRC19c791c2018-03-08 10:55:20 -0600426 /* This is also straight out of example.txt */
427 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
428 this->jerr.pub.error_exit = my_error_exit;
429 this->jerr.pub.output_message = my_output_message;
430 this->jerr.emit_message = this->jerr.pub.emit_message;
431 this->jerr.pub.emit_message = my_emit_message;
DRC9b28def2011-05-21 14:37:15 +0000432
DRC19c791c2018-03-08 10:55:20 -0600433 if (setjmp(this->jerr.setjmp_buffer)) {
434 /* If we get here, the JPEG code has signaled an error. */
435 if (this) free(this);
436 return NULL;
437 }
DRC9b28def2011-05-21 14:37:15 +0000438
DRC19c791c2018-03-08 10:55:20 -0600439 jpeg_create_compress(&this->cinfo);
440 /* Make an initial call so it will create the destination manager */
441 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
DRC9b28def2011-05-21 14:37:15 +0000442
DRC19c791c2018-03-08 10:55:20 -0600443 this->init |= COMPRESS;
444 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +0000445}
446
DRC19c791c2018-03-08 10:55:20 -0600447DLLEXPORT tjhandle tjInitCompress(void)
DRC890f1e02011-02-26 22:02:37 +0000448{
DRC19c791c2018-03-08 10:55:20 -0600449 tjinstance *this = NULL;
450
451 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
452 snprintf(errStr, JMSG_LENGTH_MAX,
453 "tjInitCompress(): Memory allocation failure");
454 return NULL;
455 }
456 MEMZERO(this, sizeof(tjinstance));
457 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
458 return _tjInitCompress(this);
DRC890f1e02011-02-26 22:02:37 +0000459}
460
DRC84241602011-02-25 02:08:23 +0000461
DRC19c791c2018-03-08 10:55:20 -0600462DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000463{
DRC19c791c2018-03-08 10:55:20 -0600464 unsigned long retval = 0;
465 int mcuw, mcuh, chromasf;
DRC9b49f0e2011-07-12 03:17:23 +0000466
DRC19c791c2018-03-08 10:55:20 -0600467 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
468 _throwg("tjBufSize(): Invalid argument");
DRC9b49f0e2011-07-12 03:17:23 +0000469
DRC19c791c2018-03-08 10:55:20 -0600470 /* This allows for rare corner cases in which a JPEG image can actually be
471 larger than the uncompressed input (we wouldn't mention it if it hadn't
472 happened before.) */
473 mcuw = tjMCUWidth[jpegSubsamp];
474 mcuh = tjMCUHeight[jpegSubsamp];
475 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
476 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
477
478bailout:
479 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000480}
481
DRC19c791c2018-03-08 10:55:20 -0600482DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
DRC2e7b76b2009-04-03 12:04:24 +0000483{
DRC19c791c2018-03-08 10:55:20 -0600484 unsigned long retval = 0;
DRCf3cf9732011-02-22 00:16:14 +0000485
DRC19c791c2018-03-08 10:55:20 -0600486 if (width < 1 || height < 1)
487 _throwg("TJBUFSIZE(): Invalid argument");
DRCf3cf9732011-02-22 00:16:14 +0000488
DRC19c791c2018-03-08 10:55:20 -0600489 /* This allows for rare corner cases in which a JPEG image can actually be
490 larger than the uncompressed input (we wouldn't mention it if it hadn't
491 happened before.) */
492 retval = PAD(width, 16) * PAD(height, 16) * 6 + 2048;
493
494bailout:
495 return retval;
DRCf3cf9732011-02-22 00:16:14 +0000496}
497
DRC84241602011-02-25 02:08:23 +0000498
DRC19c791c2018-03-08 10:55:20 -0600499DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
500 int subsamp)
DRCf3cf9732011-02-22 00:16:14 +0000501{
DRC19c791c2018-03-08 10:55:20 -0600502 int retval = 0, nc, i;
DRC40dd3142014-08-17 12:23:49 +0000503
DRC19c791c2018-03-08 10:55:20 -0600504 if (subsamp < 0 || subsamp >= NUMSUBOPT)
505 _throwg("tjBufSizeYUV2(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000506
DRC19c791c2018-03-08 10:55:20 -0600507 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
508 for (i = 0; i < nc; i++) {
509 int pw = tjPlaneWidth(i, width, subsamp);
510 int stride = PAD(pw, pad);
511 int ph = tjPlaneHeight(i, height, subsamp);
DRCf3cf9732011-02-22 00:16:14 +0000512
DRC19c791c2018-03-08 10:55:20 -0600513 if (pw < 0 || ph < 0) return -1;
514 else retval += stride * ph;
515 }
516
517bailout:
518 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000519}
520
DRC19c791c2018-03-08 10:55:20 -0600521DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
DRCf610d612013-04-26 10:33:29 +0000522{
DRC19c791c2018-03-08 10:55:20 -0600523 return tjBufSizeYUV2(width, 4, height, subsamp);
DRCf610d612013-04-26 10:33:29 +0000524}
DRC84241602011-02-25 02:08:23 +0000525
DRC19c791c2018-03-08 10:55:20 -0600526DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000527{
DRC19c791c2018-03-08 10:55:20 -0600528 return tjBufSizeYUV(width, height, subsamp);
DRC9b49f0e2011-07-12 03:17:23 +0000529}
530
531
DRC40dd3142014-08-17 12:23:49 +0000532DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
533{
DRC19c791c2018-03-08 10:55:20 -0600534 int pw, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000535
DRC19c791c2018-03-08 10:55:20 -0600536 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
537 _throwg("tjPlaneWidth(): Invalid argument");
538 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
539 if (componentID < 0 || componentID >= nc)
540 _throwg("tjPlaneWidth(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000541
DRC19c791c2018-03-08 10:55:20 -0600542 pw = PAD(width, tjMCUWidth[subsamp] / 8);
543 if (componentID == 0)
544 retval = pw;
545 else
546 retval = pw * 8 / tjMCUWidth[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000547
DRC19c791c2018-03-08 10:55:20 -0600548bailout:
549 return retval;
DRC40dd3142014-08-17 12:23:49 +0000550}
551
552
553DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
554{
DRC19c791c2018-03-08 10:55:20 -0600555 int ph, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000556
DRC19c791c2018-03-08 10:55:20 -0600557 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
558 _throwg("tjPlaneHeight(): Invalid argument");
559 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
560 if (componentID < 0 || componentID >= nc)
561 _throwg("tjPlaneHeight(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000562
DRC19c791c2018-03-08 10:55:20 -0600563 ph = PAD(height, tjMCUHeight[subsamp] / 8);
564 if (componentID == 0)
565 retval = ph;
566 else
567 retval = ph * 8 / tjMCUHeight[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000568
DRC19c791c2018-03-08 10:55:20 -0600569bailout:
570 return retval;
DRC40dd3142014-08-17 12:23:49 +0000571}
572
573
DRC19c791c2018-03-08 10:55:20 -0600574DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
575 int height, int subsamp)
DRC40dd3142014-08-17 12:23:49 +0000576{
DRC19c791c2018-03-08 10:55:20 -0600577 unsigned long retval = 0;
578 int pw, ph;
DRC40dd3142014-08-17 12:23:49 +0000579
DRC19c791c2018-03-08 10:55:20 -0600580 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
581 _throwg("tjPlaneSizeYUV(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000582
DRC19c791c2018-03-08 10:55:20 -0600583 pw = tjPlaneWidth(componentID, width, subsamp);
584 ph = tjPlaneHeight(componentID, height, subsamp);
585 if (pw < 0 || ph < 0) return -1;
DRC40dd3142014-08-17 12:23:49 +0000586
DRC19c791c2018-03-08 10:55:20 -0600587 if (stride == 0) stride = pw;
588 else stride = abs(stride);
DRC40dd3142014-08-17 12:23:49 +0000589
DRC19c791c2018-03-08 10:55:20 -0600590 retval = stride * (ph - 1) + pw;
DRC40dd3142014-08-17 12:23:49 +0000591
DRC19c791c2018-03-08 10:55:20 -0600592bailout:
593 return retval;
DRC40dd3142014-08-17 12:23:49 +0000594}
595
596
DRC19c791c2018-03-08 10:55:20 -0600597DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
598 int width, int pitch, int height, int pixelFormat,
599 unsigned char **jpegBuf, unsigned long *jpegSize,
600 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000601{
DRC19c791c2018-03-08 10:55:20 -0600602 int i, retval = 0, alloc = 1;
603 JSAMPROW *row_pointer = NULL;
DRC9b28def2011-05-21 14:37:15 +0000604
DRC19c791c2018-03-08 10:55:20 -0600605 getcinstance(handle)
606 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
607 if ((this->init & COMPRESS) == 0)
608 _throw("tjCompress2(): Instance has not been initialized for compression");
DRC9b28def2011-05-21 14:37:15 +0000609
DRC19c791c2018-03-08 10:55:20 -0600610 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
611 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
612 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
613 jpegQual < 0 || jpegQual > 100)
614 _throw("tjCompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +0000615
DRC19c791c2018-03-08 10:55:20 -0600616 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC9b28def2011-05-21 14:37:15 +0000617
DRC19c791c2018-03-08 10:55:20 -0600618 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
619 _throw("tjCompress2(): Memory allocation failure");
DRCd4c41fe2017-03-18 12:56:36 -0500620
DRC19c791c2018-03-08 10:55:20 -0600621 if (setjmp(this->jerr.setjmp_buffer)) {
622 /* If we get here, the JPEG code has signaled an error. */
623 retval = -1; goto bailout;
624 }
DRCd4c41fe2017-03-18 12:56:36 -0500625
DRC19c791c2018-03-08 10:55:20 -0600626 cinfo->image_width = width;
627 cinfo->image_height = height;
DRC9b28def2011-05-21 14:37:15 +0000628
DRC19c791c2018-03-08 10:55:20 -0600629 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
630 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
631 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC9b28def2011-05-21 14:37:15 +0000632
DRC19c791c2018-03-08 10:55:20 -0600633 if (flags & TJFLAG_NOREALLOC) {
634 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
635 }
636 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
637 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
638 return -1;
DRC9b28def2011-05-21 14:37:15 +0000639
DRC19c791c2018-03-08 10:55:20 -0600640 jpeg_start_compress(cinfo, TRUE);
641 for (i = 0; i < height; i++) {
642 if (flags & TJFLAG_BOTTOMUP)
643 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
644 else
645 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
646 }
647 while (cinfo->next_scanline < cinfo->image_height)
648 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
649 cinfo->image_height - cinfo->next_scanline);
650 jpeg_finish_compress(cinfo);
DRC9b28def2011-05-21 14:37:15 +0000651
DRC19c791c2018-03-08 10:55:20 -0600652bailout:
653 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
654 if (row_pointer) free(row_pointer);
655 if (this->jerr.warning) retval = -1;
656 this->jerr.stopOnWarning = FALSE;
657 return retval;
DRC9b28def2011-05-21 14:37:15 +0000658}
659
DRC19c791c2018-03-08 10:55:20 -0600660DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
661 int pitch, int height, int pixelSize,
662 unsigned char *jpegBuf, unsigned long *jpegSize,
663 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000664{
DRC19c791c2018-03-08 10:55:20 -0600665 int retval = 0;
666 unsigned long size;
667
668 if (flags & TJ_YUV) {
669 size = tjBufSizeYUV(width, height, jpegSubsamp);
670 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
671 getPixelFormat(pixelSize, flags), jpegBuf,
672 jpegSubsamp, flags);
673 } else {
674 retval = tjCompress2(handle, srcBuf, width, pitch, height,
675 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
676 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
677 }
678 *jpegSize = size;
679 return retval;
DRC9b28def2011-05-21 14:37:15 +0000680}
681
682
DRC19c791c2018-03-08 10:55:20 -0600683DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
684 int width, int pitch, int height,
685 int pixelFormat, unsigned char **dstPlanes,
686 int *strides, int subsamp, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000687{
DRC19c791c2018-03-08 10:55:20 -0600688 JSAMPROW *row_pointer = NULL;
689 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
690 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
691 JSAMPROW *outbuf[MAX_COMPONENTS];
692 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
693 JSAMPLE *ptr;
694 jpeg_component_info *compptr;
DRC2e7b76b2009-04-03 12:04:24 +0000695
DRC19c791c2018-03-08 10:55:20 -0600696 getcinstance(handle);
697 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000698
DRC19c791c2018-03-08 10:55:20 -0600699 for (i = 0; i < MAX_COMPONENTS; i++) {
700 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
701 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
702 }
DRCfbb67472010-11-24 04:02:37 +0000703
DRC19c791c2018-03-08 10:55:20 -0600704 if ((this->init & COMPRESS) == 0)
705 _throw("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
DRCe2f8e692013-10-30 22:21:06 +0000706
DRC19c791c2018-03-08 10:55:20 -0600707 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
708 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
709 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
710 _throw("tjEncodeYUVPlanes(): Invalid argument");
711 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
712 _throw("tjEncodeYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000713
DRC19c791c2018-03-08 10:55:20 -0600714 if (pixelFormat == TJPF_CMYK)
715 _throw("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
DRCcd7c3e62013-08-23 02:49:25 +0000716
DRC19c791c2018-03-08 10:55:20 -0600717 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC2e7b76b2009-04-03 12:04:24 +0000718
DRC19c791c2018-03-08 10:55:20 -0600719 if (setjmp(this->jerr.setjmp_buffer)) {
720 /* If we get here, the JPEG code has signaled an error. */
721 retval = -1; goto bailout;
722 }
DRCd4c41fe2017-03-18 12:56:36 -0500723
DRC19c791c2018-03-08 10:55:20 -0600724 cinfo->image_width = width;
725 cinfo->image_height = height;
DRC2e7b76b2009-04-03 12:04:24 +0000726
DRC19c791c2018-03-08 10:55:20 -0600727 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
728 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
729 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC0c6a2712010-02-22 08:34:44 +0000730
DRC19c791c2018-03-08 10:55:20 -0600731 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000732
DRC19c791c2018-03-08 10:55:20 -0600733 /* Execute only the parts of jpeg_start_compress() that we need. If we
734 were to call the whole jpeg_start_compress() function, then it would try
735 to write the file headers, which could overflow the output buffer if the
736 YUV image were very small. */
737 if (cinfo->global_state != CSTATE_START)
738 _throw("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
739 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
740 jinit_c_master_control(cinfo, FALSE);
741 jinit_color_converter(cinfo);
742 jinit_downsampler(cinfo);
743 (*cinfo->cconvert->start_pass) (cinfo);
DRC38c99702014-02-11 09:45:18 +0000744
DRC19c791c2018-03-08 10:55:20 -0600745 pw0 = PAD(width, cinfo->max_h_samp_factor);
746 ph0 = PAD(height, cinfo->max_v_samp_factor);
DRC2e7b76b2009-04-03 12:04:24 +0000747
DRC19c791c2018-03-08 10:55:20 -0600748 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
749 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
750 for (i = 0; i < height; i++) {
751 if (flags & TJFLAG_BOTTOMUP)
752 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
753 else
754 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
755 }
756 if (height < ph0)
757 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRCfbb67472010-11-24 04:02:37 +0000758
DRC19c791c2018-03-08 10:55:20 -0600759 for (i = 0; i < cinfo->num_components; i++) {
760 compptr = &cinfo->comp_info[i];
761 _tmpbuf[i] = (JSAMPLE *)malloc(
762 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
763 compptr->h_samp_factor, 32) *
764 cinfo->max_v_samp_factor + 32);
765 if (!_tmpbuf[i])
766 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
767 tmpbuf[i] =
768 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
769 if (!tmpbuf[i])
770 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
771 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
772 unsigned char *_tmpbuf_aligned =
773 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRCfbb67472010-11-24 04:02:37 +0000774
DRC19c791c2018-03-08 10:55:20 -0600775 tmpbuf[i][row] = &_tmpbuf_aligned[
776 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
777 compptr->h_samp_factor, 32) * row];
778 }
779 _tmpbuf2[i] =
780 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
781 compptr->v_samp_factor + 32);
782 if (!_tmpbuf2[i])
783 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
784 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
785 if (!tmpbuf2[i])
786 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
787 for (row = 0; row < compptr->v_samp_factor; row++) {
788 unsigned char *_tmpbuf2_aligned =
789 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
DRCd4c41fe2017-03-18 12:56:36 -0500790
DRC19c791c2018-03-08 10:55:20 -0600791 tmpbuf2[i][row] =
792 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
793 }
794 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
795 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
796 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
797 if (!outbuf[i])
798 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
799 ptr = dstPlanes[i];
800 for (row = 0; row < ph[i]; row++) {
801 outbuf[i][row] = ptr;
802 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
803 }
804 }
DRC2e7b76b2009-04-03 12:04:24 +0000805
DRC19c791c2018-03-08 10:55:20 -0600806 if (setjmp(this->jerr.setjmp_buffer)) {
807 /* If we get here, the JPEG code has signaled an error. */
808 retval = -1; goto bailout;
809 }
810
811 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
812 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
813 cinfo->max_v_samp_factor);
814 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
815 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
816 i++, compptr++)
817 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
818 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
819 compptr->v_samp_factor, pw[i]);
820 }
821 cinfo->next_scanline += height;
822 jpeg_abort_compress(cinfo);
823
824bailout:
825 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
826 if (row_pointer) free(row_pointer);
827 for (i = 0; i < MAX_COMPONENTS; i++) {
828 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
829 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
830 if (tmpbuf2[i] != NULL) free(tmpbuf2[i]);
831 if (_tmpbuf2[i] != NULL) free(_tmpbuf2[i]);
832 if (outbuf[i] != NULL) free(outbuf[i]);
833 }
834 if (this->jerr.warning) retval = -1;
835 this->jerr.stopOnWarning = FALSE;
836 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000837}
838
DRC19c791c2018-03-08 10:55:20 -0600839DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
840 int width, int pitch, int height, int pixelFormat,
841 unsigned char *dstBuf, int pad, int subsamp,
842 int flags)
DRCaecea382014-08-11 18:05:41 +0000843{
DRC19c791c2018-03-08 10:55:20 -0600844 unsigned char *dstPlanes[3];
845 int pw0, ph0, strides[3], retval = -1;
846 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +0000847
DRC19c791c2018-03-08 10:55:20 -0600848 if (!this) _throwg("tjEncodeYUV3(): Invalid handle");
849 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -0500850
DRC19c791c2018-03-08 10:55:20 -0600851 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 || !isPow2(pad) ||
852 subsamp < 0 || subsamp >= NUMSUBOPT)
853 _throw("tjEncodeYUV3(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +0000854
DRC19c791c2018-03-08 10:55:20 -0600855 pw0 = tjPlaneWidth(0, width, subsamp);
856 ph0 = tjPlaneHeight(0, height, subsamp);
857 dstPlanes[0] = dstBuf;
858 strides[0] = PAD(pw0, pad);
859 if (subsamp == TJSAMP_GRAY) {
860 strides[1] = strides[2] = 0;
861 dstPlanes[1] = dstPlanes[2] = NULL;
862 } else {
863 int pw1 = tjPlaneWidth(1, width, subsamp);
864 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +0000865
DRC19c791c2018-03-08 10:55:20 -0600866 strides[1] = strides[2] = PAD(pw1, pad);
867 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
868 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
869 }
DRCaecea382014-08-11 18:05:41 +0000870
DRC19c791c2018-03-08 10:55:20 -0600871 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
872 dstPlanes, strides, subsamp, flags);
873
874bailout:
875 return retval;
DRCaecea382014-08-11 18:05:41 +0000876}
877
DRC19c791c2018-03-08 10:55:20 -0600878DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
879 int pitch, int height, int pixelFormat,
880 unsigned char *dstBuf, int subsamp, int flags)
DRCf610d612013-04-26 10:33:29 +0000881{
DRC19c791c2018-03-08 10:55:20 -0600882 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
883 dstBuf, 4, subsamp, flags);
DRCf610d612013-04-26 10:33:29 +0000884}
885
DRC19c791c2018-03-08 10:55:20 -0600886DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
887 int pitch, int height, int pixelSize,
888 unsigned char *dstBuf, int subsamp, int flags)
DRC84241602011-02-25 02:08:23 +0000889{
DRC19c791c2018-03-08 10:55:20 -0600890 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
891 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
892 flags);
DRC84241602011-02-25 02:08:23 +0000893}
894
895
DRC19c791c2018-03-08 10:55:20 -0600896DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
897 const unsigned char **srcPlanes,
898 int width, const int *strides,
899 int height, int subsamp,
900 unsigned char **jpegBuf,
901 unsigned long *jpegSize, int jpegQual,
902 int flags)
DRC910a3572013-10-30 23:02:57 +0000903{
DRC19c791c2018-03-08 10:55:20 -0600904 int i, row, retval = 0, alloc = 1;
905 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
906 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
907 JSAMPLE *_tmpbuf = NULL, *ptr;
908 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
DRC910a3572013-10-30 23:02:57 +0000909
DRC19c791c2018-03-08 10:55:20 -0600910 getcinstance(handle)
911 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000912
DRC19c791c2018-03-08 10:55:20 -0600913 for (i = 0; i < MAX_COMPONENTS; i++) {
914 tmpbuf[i] = NULL; inbuf[i] = NULL;
915 }
DRC910a3572013-10-30 23:02:57 +0000916
DRC19c791c2018-03-08 10:55:20 -0600917 if ((this->init & COMPRESS) == 0)
918 _throw("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
DRC910a3572013-10-30 23:02:57 +0000919
DRC19c791c2018-03-08 10:55:20 -0600920 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
921 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
922 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
923 _throw("tjCompressFromYUVPlanes(): Invalid argument");
924 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
925 _throw("tjCompressFromYUVPlanes(): Invalid argument");
DRC910a3572013-10-30 23:02:57 +0000926
DRC19c791c2018-03-08 10:55:20 -0600927 if (setjmp(this->jerr.setjmp_buffer)) {
928 /* If we get here, the JPEG code has signaled an error. */
929 retval = -1; goto bailout;
930 }
DRC910a3572013-10-30 23:02:57 +0000931
DRC19c791c2018-03-08 10:55:20 -0600932 cinfo->image_width = width;
933 cinfo->image_height = height;
DRC910a3572013-10-30 23:02:57 +0000934
DRC19c791c2018-03-08 10:55:20 -0600935 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
936 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
937 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC910a3572013-10-30 23:02:57 +0000938
DRC19c791c2018-03-08 10:55:20 -0600939 if (flags & TJFLAG_NOREALLOC) {
940 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
941 }
942 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
943 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
944 return -1;
945 cinfo->raw_data_in = TRUE;
DRC910a3572013-10-30 23:02:57 +0000946
DRC19c791c2018-03-08 10:55:20 -0600947 jpeg_start_compress(cinfo, TRUE);
948 for (i = 0; i < cinfo->num_components; i++) {
949 jpeg_component_info *compptr = &cinfo->comp_info[i];
950 int ih;
DRC910a3572013-10-30 23:02:57 +0000951
DRC19c791c2018-03-08 10:55:20 -0600952 iw[i] = compptr->width_in_blocks * DCTSIZE;
953 ih = compptr->height_in_blocks * DCTSIZE;
954 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
955 compptr->h_samp_factor / cinfo->max_h_samp_factor;
956 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
957 compptr->v_samp_factor / cinfo->max_v_samp_factor;
958 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
959 th[i] = compptr->v_samp_factor * DCTSIZE;
960 tmpbufsize += iw[i] * th[i];
961 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
962 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
963 ptr = (JSAMPLE *)srcPlanes[i];
964 for (row = 0; row < ph[i]; row++) {
965 inbuf[i][row] = ptr;
966 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
967 }
968 }
969 if (usetmpbuf) {
970 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
971 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
972 ptr = _tmpbuf;
973 for (i = 0; i < cinfo->num_components; i++) {
974 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
975 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
976 for (row = 0; row < th[i]; row++) {
977 tmpbuf[i][row] = ptr;
978 ptr += iw[i];
979 }
980 }
981 }
DRCd4c41fe2017-03-18 12:56:36 -0500982
DRC19c791c2018-03-08 10:55:20 -0600983 if (setjmp(this->jerr.setjmp_buffer)) {
984 /* If we get here, the JPEG code has signaled an error. */
985 retval = -1; goto bailout;
986 }
DRC910a3572013-10-30 23:02:57 +0000987
DRC19c791c2018-03-08 10:55:20 -0600988 for (row = 0; row < (int)cinfo->image_height;
989 row += cinfo->max_v_samp_factor * DCTSIZE) {
990 JSAMPARRAY yuvptr[MAX_COMPONENTS];
991 int crow[MAX_COMPONENTS];
992
993 for (i = 0; i < cinfo->num_components; i++) {
994 jpeg_component_info *compptr = &cinfo->comp_info[i];
995
996 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
997 if (usetmpbuf) {
998 int j, k;
999
1000 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1001 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1002 /* Duplicate last sample in row to fill out MCU */
1003 for (k = pw[i]; k < iw[i]; k++)
1004 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1005 }
1006 /* Duplicate last row to fill out MCU */
1007 for (j = ph[i] - crow[i]; j < th[i]; j++)
1008 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1009 yuvptr[i] = tmpbuf[i];
1010 } else
1011 yuvptr[i] = &inbuf[i][crow[i]];
1012 }
1013 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1014 }
1015 jpeg_finish_compress(cinfo);
1016
1017bailout:
1018 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1019 for (i = 0; i < MAX_COMPONENTS; i++) {
1020 if (tmpbuf[i]) free(tmpbuf[i]);
1021 if (inbuf[i]) free(inbuf[i]);
1022 }
1023 if (_tmpbuf) free(_tmpbuf);
1024 if (this->jerr.warning) retval = -1;
1025 this->jerr.stopOnWarning = FALSE;
1026 return retval;
DRC910a3572013-10-30 23:02:57 +00001027}
1028
DRC19c791c2018-03-08 10:55:20 -06001029DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1030 int width, int pad, int height, int subsamp,
1031 unsigned char **jpegBuf,
1032 unsigned long *jpegSize, int jpegQual,
1033 int flags)
DRCaecea382014-08-11 18:05:41 +00001034{
DRC19c791c2018-03-08 10:55:20 -06001035 const unsigned char *srcPlanes[3];
1036 int pw0, ph0, strides[3], retval = -1;
1037 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +00001038
DRC19c791c2018-03-08 10:55:20 -06001039 if (!this) _throwg("tjCompressFromYUV(): Invalid handle");
1040 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001041
DRC19c791c2018-03-08 10:55:20 -06001042 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1043 subsamp >= NUMSUBOPT)
1044 _throw("tjCompressFromYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001045
DRC19c791c2018-03-08 10:55:20 -06001046 pw0 = tjPlaneWidth(0, width, subsamp);
1047 ph0 = tjPlaneHeight(0, height, subsamp);
1048 srcPlanes[0] = srcBuf;
1049 strides[0] = PAD(pw0, pad);
1050 if (subsamp == TJSAMP_GRAY) {
1051 strides[1] = strides[2] = 0;
1052 srcPlanes[1] = srcPlanes[2] = NULL;
1053 } else {
1054 int pw1 = tjPlaneWidth(1, width, subsamp);
1055 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001056
DRC19c791c2018-03-08 10:55:20 -06001057 strides[1] = strides[2] = PAD(pw1, pad);
1058 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1059 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1060 }
DRCaecea382014-08-11 18:05:41 +00001061
DRC19c791c2018-03-08 10:55:20 -06001062 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1063 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1064
1065bailout:
1066 return retval;
DRCaecea382014-08-11 18:05:41 +00001067}
1068
DRC910a3572013-10-30 23:02:57 +00001069
DRC9b28def2011-05-21 14:37:15 +00001070/* Decompressor */
DRC2e7b76b2009-04-03 12:04:24 +00001071
DRC9b28def2011-05-21 14:37:15 +00001072static tjhandle _tjInitDecompress(tjinstance *this)
DRC2e7b76b2009-04-03 12:04:24 +00001073{
DRC19c791c2018-03-08 10:55:20 -06001074 static unsigned char buffer[1];
DRC2e7b76b2009-04-03 12:04:24 +00001075
DRC19c791c2018-03-08 10:55:20 -06001076 /* This is also straight out of example.txt */
1077 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1078 this->jerr.pub.error_exit = my_error_exit;
1079 this->jerr.pub.output_message = my_output_message;
1080 this->jerr.emit_message = this->jerr.pub.emit_message;
1081 this->jerr.pub.emit_message = my_emit_message;
DRC2e7b76b2009-04-03 12:04:24 +00001082
DRC19c791c2018-03-08 10:55:20 -06001083 if (setjmp(this->jerr.setjmp_buffer)) {
1084 /* If we get here, the JPEG code has signaled an error. */
1085 if (this) free(this);
1086 return NULL;
1087 }
DRC2e7b76b2009-04-03 12:04:24 +00001088
DRC19c791c2018-03-08 10:55:20 -06001089 jpeg_create_decompress(&this->dinfo);
1090 /* Make an initial call so it will create the source manager */
1091 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +00001092
DRC19c791c2018-03-08 10:55:20 -06001093 this->init |= DECOMPRESS;
1094 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +00001095}
1096
DRC19c791c2018-03-08 10:55:20 -06001097DLLEXPORT tjhandle tjInitDecompress(void)
DRC890f1e02011-02-26 22:02:37 +00001098{
DRC19c791c2018-03-08 10:55:20 -06001099 tjinstance *this;
1100
1101 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1102 snprintf(errStr, JMSG_LENGTH_MAX,
1103 "tjInitDecompress(): Memory allocation failure");
1104 return NULL;
1105 }
1106 MEMZERO(this, sizeof(tjinstance));
1107 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1108 return _tjInitDecompress(this);
DRC890f1e02011-02-26 22:02:37 +00001109}
1110
DRC2e7b76b2009-04-03 12:04:24 +00001111
DRC19c791c2018-03-08 10:55:20 -06001112DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1113 const unsigned char *jpegBuf,
1114 unsigned long jpegSize, int *width,
1115 int *height, int *jpegSubsamp,
1116 int *jpegColorspace)
DRC1fe80f82010-12-14 01:21:29 +00001117{
DRC19c791c2018-03-08 10:55:20 -06001118 int retval = 0;
DRC1fe80f82010-12-14 01:21:29 +00001119
DRC19c791c2018-03-08 10:55:20 -06001120 getdinstance(handle);
1121 if ((this->init & DECOMPRESS) == 0)
1122 _throw("tjDecompressHeader3(): Instance has not been initialized for decompression");
DRC1fe80f82010-12-14 01:21:29 +00001123
DRC19c791c2018-03-08 10:55:20 -06001124 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1125 jpegSubsamp == NULL || jpegColorspace == NULL)
1126 _throw("tjDecompressHeader3(): Invalid argument");
DRC1fe80f82010-12-14 01:21:29 +00001127
DRC19c791c2018-03-08 10:55:20 -06001128 if (setjmp(this->jerr.setjmp_buffer)) {
1129 /* If we get here, the JPEG code has signaled an error. */
1130 return -1;
1131 }
DRC1fe80f82010-12-14 01:21:29 +00001132
DRC19c791c2018-03-08 10:55:20 -06001133 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1134 jpeg_read_header(dinfo, TRUE);
DRC1fe80f82010-12-14 01:21:29 +00001135
DRC19c791c2018-03-08 10:55:20 -06001136 *width = dinfo->image_width;
1137 *height = dinfo->image_height;
1138 *jpegSubsamp = getSubsamp(dinfo);
1139 switch (dinfo->jpeg_color_space) {
1140 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1141 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1142 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1143 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1144 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1145 default: *jpegColorspace = -1; break;
1146 }
DRC1fe80f82010-12-14 01:21:29 +00001147
DRC19c791c2018-03-08 10:55:20 -06001148 jpeg_abort_decompress(dinfo);
DRC1fe80f82010-12-14 01:21:29 +00001149
DRC19c791c2018-03-08 10:55:20 -06001150 if (*jpegSubsamp < 0)
1151 _throw("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
1152 if (*jpegColorspace < 0)
1153 _throw("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
1154 if (*width < 1 || *height < 1)
1155 _throw("tjDecompressHeader3(): Invalid data returned in header");
DRC91e86ba2011-02-15 05:24:08 +00001156
DRC19c791c2018-03-08 10:55:20 -06001157bailout:
1158 if (this->jerr.warning) retval = -1;
1159 return retval;
DRC91e86ba2011-02-15 05:24:08 +00001160}
1161
DRC19c791c2018-03-08 10:55:20 -06001162DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1163 unsigned long jpegSize, int *width,
1164 int *height, int *jpegSubsamp)
DRCcd7c3e62013-08-23 02:49:25 +00001165{
DRC19c791c2018-03-08 10:55:20 -06001166 int jpegColorspace;
1167
1168 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1169 jpegSubsamp, &jpegColorspace);
DRCcd7c3e62013-08-23 02:49:25 +00001170}
1171
DRC19c791c2018-03-08 10:55:20 -06001172DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1173 unsigned long jpegSize, int *width,
1174 int *height)
DRC91e86ba2011-02-15 05:24:08 +00001175{
DRC19c791c2018-03-08 10:55:20 -06001176 int jpegSubsamp;
1177
1178 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1179 &jpegSubsamp);
DRC1fe80f82010-12-14 01:21:29 +00001180}
1181
1182
DRC19c791c2018-03-08 10:55:20 -06001183DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
DRCb28fc572011-02-22 06:41:29 +00001184{
DRC19c791c2018-03-08 10:55:20 -06001185 if (numscalingfactors == NULL) {
1186 snprintf(errStr, JMSG_LENGTH_MAX,
1187 "tjGetScalingFactors(): Invalid argument");
1188 return NULL;
1189 }
DRCb28fc572011-02-22 06:41:29 +00001190
DRC19c791c2018-03-08 10:55:20 -06001191 *numscalingfactors = NUMSF;
1192 return (tjscalingfactor *)sf;
DRCb28fc572011-02-22 06:41:29 +00001193}
1194
1195
DRC19c791c2018-03-08 10:55:20 -06001196DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1197 unsigned long jpegSize, unsigned char *dstBuf,
1198 int width, int pitch, int height, int pixelFormat,
1199 int flags)
DRC2e7b76b2009-04-03 12:04:24 +00001200{
DRC19c791c2018-03-08 10:55:20 -06001201 JSAMPROW *row_pointer = NULL;
1202 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
DRC2e7b76b2009-04-03 12:04:24 +00001203
DRC19c791c2018-03-08 10:55:20 -06001204 getdinstance(handle);
1205 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1206 if ((this->init & DECOMPRESS) == 0)
1207 _throw("tjDecompress2(): Instance has not been initialized for decompression");
DRC9b28def2011-05-21 14:37:15 +00001208
DRC19c791c2018-03-08 10:55:20 -06001209 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1210 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1211 _throw("tjDecompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001212
DRC19c791c2018-03-08 10:55:20 -06001213 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1214 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1215 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC9b28def2011-05-21 14:37:15 +00001216
DRC19c791c2018-03-08 10:55:20 -06001217 if (setjmp(this->jerr.setjmp_buffer)) {
1218 /* If we get here, the JPEG code has signaled an error. */
1219 retval = -1; goto bailout;
1220 }
DRC9b28def2011-05-21 14:37:15 +00001221
DRC19c791c2018-03-08 10:55:20 -06001222 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1223 jpeg_read_header(dinfo, TRUE);
1224 this->dinfo.out_color_space = pf2cs[pixelFormat];
1225 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1226 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
DRC9b28def2011-05-21 14:37:15 +00001227
DRC19c791c2018-03-08 10:55:20 -06001228 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1229 if (width == 0) width = jpegwidth;
1230 if (height == 0) height = jpegheight;
1231 for (i = 0; i < NUMSF; i++) {
1232 scaledw = TJSCALED(jpegwidth, sf[i]);
1233 scaledh = TJSCALED(jpegheight, sf[i]);
1234 if (scaledw <= width && scaledh <= height)
1235 break;
1236 }
1237 if (i >= NUMSF)
1238 _throw("tjDecompress2(): Could not scale down to desired image dimensions");
1239 width = scaledw; height = scaledh;
1240 dinfo->scale_num = sf[i].num;
1241 dinfo->scale_denom = sf[i].denom;
DRC9b28def2011-05-21 14:37:15 +00001242
DRC19c791c2018-03-08 10:55:20 -06001243 jpeg_start_decompress(dinfo);
1244 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRCafc06922012-03-23 19:47:57 +00001245
DRC19c791c2018-03-08 10:55:20 -06001246 if ((row_pointer =
1247 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
1248 _throw("tjDecompress2(): Memory allocation failure");
1249 if (setjmp(this->jerr.setjmp_buffer)) {
1250 /* If we get here, the JPEG code has signaled an error. */
1251 retval = -1; goto bailout;
1252 }
1253 for (i = 0; i < (int)dinfo->output_height; i++) {
1254 if (flags & TJFLAG_BOTTOMUP)
1255 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * pitch];
1256 else
1257 row_pointer[i] = &dstBuf[i * pitch];
1258 }
1259 while (dinfo->output_scanline < dinfo->output_height)
1260 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1261 dinfo->output_height - dinfo->output_scanline);
1262 jpeg_finish_decompress(dinfo);
DRC9b28def2011-05-21 14:37:15 +00001263
DRC19c791c2018-03-08 10:55:20 -06001264bailout:
1265 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1266 if (row_pointer) free(row_pointer);
1267 if (this->jerr.warning) retval = -1;
1268 this->jerr.stopOnWarning = FALSE;
1269 return retval;
DRC9b28def2011-05-21 14:37:15 +00001270}
1271
DRC19c791c2018-03-08 10:55:20 -06001272DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1273 unsigned long jpegSize, unsigned char *dstBuf,
1274 int width, int pitch, int height, int pixelSize,
1275 int flags)
DRC9b28def2011-05-21 14:37:15 +00001276{
DRC19c791c2018-03-08 10:55:20 -06001277 if (flags & TJ_YUV)
1278 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1279 else
1280 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1281 height, getPixelFormat(pixelSize, flags), flags);
DRC9b28def2011-05-21 14:37:15 +00001282}
1283
1284
DRC34dca052014-02-28 09:17:14 +00001285static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
DRC19c791c2018-03-08 10:55:20 -06001286 int pixelFormat, int subsamp, int flags)
DRC34dca052014-02-28 09:17:14 +00001287{
DRC19c791c2018-03-08 10:55:20 -06001288 int i;
DRC895fd6d2014-02-28 09:35:34 +00001289
DRC19c791c2018-03-08 10:55:20 -06001290 dinfo->scale_num = dinfo->scale_denom = 1;
DRC34dca052014-02-28 09:17:14 +00001291
DRC19c791c2018-03-08 10:55:20 -06001292 if (subsamp == TJSAMP_GRAY) {
1293 dinfo->num_components = dinfo->comps_in_scan = 1;
1294 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1295 } else {
1296 dinfo->num_components = dinfo->comps_in_scan = 3;
1297 dinfo->jpeg_color_space = JCS_YCbCr;
1298 }
DRC34dca052014-02-28 09:17:14 +00001299
DRC19c791c2018-03-08 10:55:20 -06001300 dinfo->comp_info = (jpeg_component_info *)
1301 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1302 dinfo->num_components *
1303 sizeof(jpeg_component_info));
DRC34dca052014-02-28 09:17:14 +00001304
DRC19c791c2018-03-08 10:55:20 -06001305 for (i = 0; i < dinfo->num_components; i++) {
1306 jpeg_component_info *compptr = &dinfo->comp_info[i];
DRC34dca052014-02-28 09:17:14 +00001307
DRC19c791c2018-03-08 10:55:20 -06001308 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1309 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1310 compptr->component_index = i;
1311 compptr->component_id = i + 1;
1312 compptr->quant_tbl_no = compptr->dc_tbl_no =
1313 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1314 dinfo->cur_comp_info[i] = compptr;
1315 }
1316 dinfo->data_precision = 8;
1317 for (i = 0; i < 2; i++) {
1318 if (dinfo->quant_tbl_ptrs[i] == NULL)
1319 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1320 }
1321
1322 return 0;
DRC34dca052014-02-28 09:17:14 +00001323}
1324
1325
1326int my_read_markers(j_decompress_ptr dinfo)
1327{
DRC19c791c2018-03-08 10:55:20 -06001328 return JPEG_REACHED_SOS;
DRC34dca052014-02-28 09:17:14 +00001329}
1330
1331void my_reset_marker_reader(j_decompress_ptr dinfo)
1332{
1333}
1334
DRC19c791c2018-03-08 10:55:20 -06001335DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1336 const unsigned char **srcPlanes,
1337 const int *strides, int subsamp,
1338 unsigned char *dstBuf, int width, int pitch,
1339 int height, int pixelFormat, int flags)
DRC34dca052014-02-28 09:17:14 +00001340{
DRC19c791c2018-03-08 10:55:20 -06001341 JSAMPROW *row_pointer = NULL;
1342 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1343 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1344 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1345 JSAMPLE *ptr;
1346 jpeg_component_info *compptr;
1347 int (*old_read_markers) (j_decompress_ptr);
1348 void (*old_reset_marker_reader) (j_decompress_ptr);
DRC34dca052014-02-28 09:17:14 +00001349
DRC19c791c2018-03-08 10:55:20 -06001350 getdinstance(handle);
1351 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRC34dca052014-02-28 09:17:14 +00001352
DRC19c791c2018-03-08 10:55:20 -06001353 for (i = 0; i < MAX_COMPONENTS; i++) {
1354 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1355 }
DRC34dca052014-02-28 09:17:14 +00001356
DRC19c791c2018-03-08 10:55:20 -06001357 if ((this->init & DECOMPRESS) == 0)
1358 _throw("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
DRC34dca052014-02-28 09:17:14 +00001359
DRC19c791c2018-03-08 10:55:20 -06001360 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1361 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1362 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1363 _throw("tjDecodeYUVPlanes(): Invalid argument");
1364 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1365 _throw("tjDecodeYUVPlanes(): Invalid argument");
DRC34dca052014-02-28 09:17:14 +00001366
DRC19c791c2018-03-08 10:55:20 -06001367 if (setjmp(this->jerr.setjmp_buffer)) {
1368 /* If we get here, the JPEG code has signaled an error. */
1369 retval = -1; goto bailout;
1370 }
DRC34dca052014-02-28 09:17:14 +00001371
DRC19c791c2018-03-08 10:55:20 -06001372 if (pixelFormat == TJPF_CMYK)
1373 _throw("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
DRC34dca052014-02-28 09:17:14 +00001374
DRC19c791c2018-03-08 10:55:20 -06001375 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1376 dinfo->image_width = width;
1377 dinfo->image_height = height;
DRC34dca052014-02-28 09:17:14 +00001378
DRC19c791c2018-03-08 10:55:20 -06001379 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1380 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1381 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC34dca052014-02-28 09:17:14 +00001382
DRC19c791c2018-03-08 10:55:20 -06001383 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1384 retval = -1; goto bailout;
1385 }
1386 old_read_markers = dinfo->marker->read_markers;
1387 dinfo->marker->read_markers = my_read_markers;
1388 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1389 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1390 jpeg_read_header(dinfo, TRUE);
1391 dinfo->marker->read_markers = old_read_markers;
1392 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
DRC34dca052014-02-28 09:17:14 +00001393
DRC19c791c2018-03-08 10:55:20 -06001394 this->dinfo.out_color_space = pf2cs[pixelFormat];
1395 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1396 dinfo->do_fancy_upsampling = FALSE;
1397 dinfo->Se = DCTSIZE2 - 1;
1398 jinit_master_decompress(dinfo);
1399 (*dinfo->upsample->start_pass) (dinfo);
DRC34dca052014-02-28 09:17:14 +00001400
DRC19c791c2018-03-08 10:55:20 -06001401 pw0 = PAD(width, dinfo->max_h_samp_factor);
1402 ph0 = PAD(height, dinfo->max_v_samp_factor);
DRC34dca052014-02-28 09:17:14 +00001403
DRC19c791c2018-03-08 10:55:20 -06001404 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRC34dca052014-02-28 09:17:14 +00001405
DRC19c791c2018-03-08 10:55:20 -06001406 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1407 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1408 for (i = 0; i < height; i++) {
1409 if (flags & TJFLAG_BOTTOMUP)
1410 row_pointer[i] = &dstBuf[(height - i - 1) * pitch];
1411 else
1412 row_pointer[i] = &dstBuf[i * pitch];
1413 }
1414 if (height < ph0)
1415 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRC34dca052014-02-28 09:17:14 +00001416
DRC19c791c2018-03-08 10:55:20 -06001417 for (i = 0; i < dinfo->num_components; i++) {
1418 compptr = &dinfo->comp_info[i];
1419 _tmpbuf[i] =
1420 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1421 compptr->v_samp_factor + 32);
1422 if (!_tmpbuf[i])
1423 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1424 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1425 if (!tmpbuf[i])
1426 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1427 for (row = 0; row < compptr->v_samp_factor; row++) {
1428 unsigned char *_tmpbuf_aligned =
1429 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRC34dca052014-02-28 09:17:14 +00001430
DRC19c791c2018-03-08 10:55:20 -06001431 tmpbuf[i][row] =
1432 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1433 }
1434 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1435 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1436 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1437 if (!inbuf[i])
1438 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1439 ptr = (JSAMPLE *)srcPlanes[i];
1440 for (row = 0; row < ph[i]; row++) {
1441 inbuf[i][row] = ptr;
1442 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1443 }
1444 }
DRCd4c41fe2017-03-18 12:56:36 -05001445
DRC19c791c2018-03-08 10:55:20 -06001446 if (setjmp(this->jerr.setjmp_buffer)) {
1447 /* If we get here, the JPEG code has signaled an error. */
1448 retval = -1; goto bailout;
1449 }
DRC34dca052014-02-28 09:17:14 +00001450
DRC19c791c2018-03-08 10:55:20 -06001451 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1452 JDIMENSION inrow = 0, outrow = 0;
1453
1454 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1455 i++, compptr++)
1456 jcopy_sample_rows(inbuf[i],
1457 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1458 compptr->v_samp_factor, pw[i]);
1459 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1460 dinfo->max_v_samp_factor, &row_pointer[row],
1461 &outrow, dinfo->max_v_samp_factor);
1462 }
1463 jpeg_abort_decompress(dinfo);
1464
1465bailout:
1466 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1467 if (row_pointer) free(row_pointer);
1468 for (i = 0; i < MAX_COMPONENTS; i++) {
1469 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
1470 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
1471 if (inbuf[i] != NULL) free(inbuf[i]);
1472 }
1473 if (this->jerr.warning) retval = -1;
1474 this->jerr.stopOnWarning = FALSE;
1475 return retval;
DRC34dca052014-02-28 09:17:14 +00001476}
1477
DRC19c791c2018-03-08 10:55:20 -06001478DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1479 int pad, int subsamp, unsigned char *dstBuf,
1480 int width, int pitch, int height, int pixelFormat,
1481 int flags)
DRCaecea382014-08-11 18:05:41 +00001482{
DRC19c791c2018-03-08 10:55:20 -06001483 const unsigned char *srcPlanes[3];
1484 int pw0, ph0, strides[3], retval = -1;
1485 tjinstance *this = (tjinstance *)handle;
DRC34dca052014-02-28 09:17:14 +00001486
DRC19c791c2018-03-08 10:55:20 -06001487 if (!this) _throwg("tjDecodeYUV(): Invalid handle");
1488 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001489
DRC19c791c2018-03-08 10:55:20 -06001490 if (srcBuf == NULL || pad < 0 || !isPow2(pad) || subsamp < 0 ||
1491 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
1492 _throw("tjDecodeYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001493
DRC19c791c2018-03-08 10:55:20 -06001494 pw0 = tjPlaneWidth(0, width, subsamp);
1495 ph0 = tjPlaneHeight(0, height, subsamp);
1496 srcPlanes[0] = srcBuf;
1497 strides[0] = PAD(pw0, pad);
1498 if (subsamp == TJSAMP_GRAY) {
1499 strides[1] = strides[2] = 0;
1500 srcPlanes[1] = srcPlanes[2] = NULL;
1501 } else {
1502 int pw1 = tjPlaneWidth(1, width, subsamp);
1503 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001504
DRC19c791c2018-03-08 10:55:20 -06001505 strides[1] = strides[2] = PAD(pw1, pad);
1506 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1507 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1508 }
DRCaecea382014-08-11 18:05:41 +00001509
DRC19c791c2018-03-08 10:55:20 -06001510 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1511 pitch, height, pixelFormat, flags);
1512
1513bailout:
1514 return retval;
DRCaecea382014-08-11 18:05:41 +00001515}
1516
DRC19c791c2018-03-08 10:55:20 -06001517DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1518 const unsigned char *jpegBuf,
1519 unsigned long jpegSize,
1520 unsigned char **dstPlanes, int width,
1521 int *strides, int height, int flags)
DRC9b28def2011-05-21 14:37:15 +00001522{
DRC19c791c2018-03-08 10:55:20 -06001523 int i, sfi, row, retval = 0;
1524 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1525 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1526 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1527 JSAMPLE *_tmpbuf = NULL, *ptr;
1528 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1529 int dctsize;
DRC9b28def2011-05-21 14:37:15 +00001530
DRC19c791c2018-03-08 10:55:20 -06001531 getdinstance(handle);
1532 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +00001533
DRC19c791c2018-03-08 10:55:20 -06001534 for (i = 0; i < MAX_COMPONENTS; i++) {
1535 tmpbuf[i] = NULL; outbuf[i] = NULL;
1536 }
DRC9e17f7d2010-12-10 04:59:13 +00001537
DRC19c791c2018-03-08 10:55:20 -06001538 if ((this->init & DECOMPRESS) == 0)
1539 _throw("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
DRCe2f8e692013-10-30 22:21:06 +00001540
DRC19c791c2018-03-08 10:55:20 -06001541 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1542 width < 0 || height < 0)
1543 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001544
DRC19c791c2018-03-08 10:55:20 -06001545 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1546 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1547 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC0c6a2712010-02-22 08:34:44 +00001548
DRC19c791c2018-03-08 10:55:20 -06001549 if (setjmp(this->jerr.setjmp_buffer)) {
1550 /* If we get here, the JPEG code has signaled an error. */
1551 retval = -1; goto bailout;
1552 }
DRC2e7b76b2009-04-03 12:04:24 +00001553
DRC19c791c2018-03-08 10:55:20 -06001554 if (!this->headerRead) {
1555 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1556 jpeg_read_header(dinfo, TRUE);
1557 }
1558 this->headerRead = 0;
1559 jpegSubsamp = getSubsamp(dinfo);
1560 if (jpegSubsamp < 0)
1561 _throw("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001562
DRC19c791c2018-03-08 10:55:20 -06001563 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1564 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001565
DRC19c791c2018-03-08 10:55:20 -06001566 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1567 if (width == 0) width = jpegwidth;
1568 if (height == 0) height = jpegheight;
1569 for (i = 0; i < NUMSF; i++) {
1570 scaledw = TJSCALED(jpegwidth, sf[i]);
1571 scaledh = TJSCALED(jpegheight, sf[i]);
1572 if (scaledw <= width && scaledh <= height)
1573 break;
1574 }
1575 if (i >= NUMSF)
1576 _throw("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
1577 if (dinfo->num_components > 3)
1578 _throw("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
DRCcd7c3e62013-08-23 02:49:25 +00001579
DRC19c791c2018-03-08 10:55:20 -06001580 width = scaledw; height = scaledh;
1581 dinfo->scale_num = sf[i].num;
1582 dinfo->scale_denom = sf[i].denom;
1583 sfi = i;
1584 jpeg_calc_output_dimensions(dinfo);
DRCf610d612013-04-26 10:33:29 +00001585
DRC19c791c2018-03-08 10:55:20 -06001586 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
DRC418fe282013-05-07 21:17:35 +00001587
DRC19c791c2018-03-08 10:55:20 -06001588 for (i = 0; i < dinfo->num_components; i++) {
1589 jpeg_component_info *compptr = &dinfo->comp_info[i];
1590 int ih;
DRC9e17f7d2010-12-10 04:59:13 +00001591
DRC19c791c2018-03-08 10:55:20 -06001592 iw[i] = compptr->width_in_blocks * dctsize;
1593 ih = compptr->height_in_blocks * dctsize;
1594 pw[i] = PAD(dinfo->output_width, dinfo->max_h_samp_factor) *
1595 compptr->h_samp_factor / dinfo->max_h_samp_factor;
1596 ph[i] = PAD(dinfo->output_height, dinfo->max_v_samp_factor) *
1597 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1598 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1599 th[i] = compptr->v_samp_factor * dctsize;
1600 tmpbufsize += iw[i] * th[i];
1601 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1602 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1603 ptr = dstPlanes[i];
1604 for (row = 0; row < ph[i]; row++) {
1605 outbuf[i][row] = ptr;
1606 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1607 }
1608 }
1609 if (usetmpbuf) {
1610 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1611 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1612 ptr = _tmpbuf;
1613 for (i = 0; i < dinfo->num_components; i++) {
1614 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1615 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1616 for (row = 0; row < th[i]; row++) {
1617 tmpbuf[i][row] = ptr;
1618 ptr += iw[i];
1619 }
1620 }
1621 }
DRCd4c41fe2017-03-18 12:56:36 -05001622
DRC19c791c2018-03-08 10:55:20 -06001623 if (setjmp(this->jerr.setjmp_buffer)) {
1624 /* If we get here, the JPEG code has signaled an error. */
1625 retval = -1; goto bailout;
1626 }
DRC9b28def2011-05-21 14:37:15 +00001627
DRC19c791c2018-03-08 10:55:20 -06001628 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1629 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1630 dinfo->raw_data_out = TRUE;
DRC2e7b76b2009-04-03 12:04:24 +00001631
DRC19c791c2018-03-08 10:55:20 -06001632 jpeg_start_decompress(dinfo);
1633 for (row = 0; row < (int)dinfo->output_height;
1634 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1635 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1636 int crow[MAX_COMPONENTS];
1637
1638 for (i = 0; i < dinfo->num_components; i++) {
1639 jpeg_component_info *compptr = &dinfo->comp_info[i];
1640
1641 if (jpegSubsamp == TJ_420) {
1642 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1643 to be clever and use the IDCT to perform upsampling on the U and V
1644 planes. For instance, if the output image is to be scaled by 1/2
1645 relative to the JPEG image, then the scaling factor and upsampling
1646 effectively cancel each other, so a normal 8x8 IDCT can be used.
1647 However, this is not desirable when using the decompress-to-YUV
1648 functionality in TurboJPEG, since we want to output the U and V
1649 planes in their subsampled form. Thus, we have to override some
1650 internal libjpeg parameters to force it to use the "scaled" IDCT
1651 functions on the U and V planes. */
1652 compptr->_DCT_scaled_size = dctsize;
1653 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1654 sf[sfi].num / sf[sfi].denom *
1655 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1656 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1657 }
1658 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1659 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1660 else yuvptr[i] = &outbuf[i][crow[i]];
1661 }
1662 jpeg_read_raw_data(dinfo, yuvptr,
1663 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1664 if (usetmpbuf) {
1665 int j;
1666
1667 for (i = 0; i < dinfo->num_components; i++) {
1668 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1669 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1670 }
1671 }
1672 }
1673 }
1674 jpeg_finish_decompress(dinfo);
1675
1676bailout:
1677 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1678 for (i = 0; i < MAX_COMPONENTS; i++) {
1679 if (tmpbuf[i]) free(tmpbuf[i]);
1680 if (outbuf[i]) free(outbuf[i]);
1681 }
1682 if (_tmpbuf) free(_tmpbuf);
1683 if (this->jerr.warning) retval = -1;
1684 this->jerr.stopOnWarning = FALSE;
1685 return retval;
DRC2e7b76b2009-04-03 12:04:24 +00001686}
1687
DRC19c791c2018-03-08 10:55:20 -06001688DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1689 unsigned long jpegSize, unsigned char *dstBuf,
1690 int width, int pad, int height, int flags)
DRCaecea382014-08-11 18:05:41 +00001691{
DRC19c791c2018-03-08 10:55:20 -06001692 unsigned char *dstPlanes[3];
1693 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1694 int i, jpegwidth, jpegheight, scaledw, scaledh;
DRCaecea382014-08-11 18:05:41 +00001695
DRC19c791c2018-03-08 10:55:20 -06001696 getdinstance(handle);
1697 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCaecea382014-08-11 18:05:41 +00001698
DRC19c791c2018-03-08 10:55:20 -06001699 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1700 pad < 1 || !isPow2(pad) || height < 0)
1701 _throw("tjDecompressToYUV2(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001702
DRC19c791c2018-03-08 10:55:20 -06001703 if (setjmp(this->jerr.setjmp_buffer)) {
1704 /* If we get here, the JPEG code has signaled an error. */
1705 return -1;
1706 }
DRCdec79952016-04-20 11:27:42 -05001707
DRC19c791c2018-03-08 10:55:20 -06001708 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1709 jpeg_read_header(dinfo, TRUE);
1710 jpegSubsamp = getSubsamp(dinfo);
1711 if (jpegSubsamp < 0)
1712 _throw("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001713
DRC19c791c2018-03-08 10:55:20 -06001714 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1715 if (width == 0) width = jpegwidth;
1716 if (height == 0) height = jpegheight;
DRCaecea382014-08-11 18:05:41 +00001717
DRC19c791c2018-03-08 10:55:20 -06001718 for (i = 0; i < NUMSF; i++) {
1719 scaledw = TJSCALED(jpegwidth, sf[i]);
1720 scaledh = TJSCALED(jpegheight, sf[i]);
1721 if (scaledw <= width && scaledh <= height)
1722 break;
1723 }
1724 if (i >= NUMSF)
1725 _throw("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
DRCaecea382014-08-11 18:05:41 +00001726
DRC19c791c2018-03-08 10:55:20 -06001727 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1728 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1729 dstPlanes[0] = dstBuf;
1730 strides[0] = PAD(pw0, pad);
1731 if (jpegSubsamp == TJSAMP_GRAY) {
1732 strides[1] = strides[2] = 0;
1733 dstPlanes[1] = dstPlanes[2] = NULL;
1734 } else {
1735 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1736 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
DRCaecea382014-08-11 18:05:41 +00001737
DRC19c791c2018-03-08 10:55:20 -06001738 strides[1] = strides[2] = PAD(pw1, pad);
1739 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1740 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1741 }
DRCaecea382014-08-11 18:05:41 +00001742
DRC19c791c2018-03-08 10:55:20 -06001743 this->headerRead = 1;
1744 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1745 strides, height, flags);
DRCaecea382014-08-11 18:05:41 +00001746
DRC19c791c2018-03-08 10:55:20 -06001747bailout:
1748 this->jerr.stopOnWarning = FALSE;
1749 return retval;
DRCaecea382014-08-11 18:05:41 +00001750}
1751
DRC19c791c2018-03-08 10:55:20 -06001752DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1753 unsigned long jpegSize, unsigned char *dstBuf,
1754 int flags)
DRCf610d612013-04-26 10:33:29 +00001755{
DRC19c791c2018-03-08 10:55:20 -06001756 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
DRCf610d612013-04-26 10:33:29 +00001757}
1758
DRC2e7b76b2009-04-03 12:04:24 +00001759
DRC9b28def2011-05-21 14:37:15 +00001760/* Transformer */
DRC890f1e02011-02-26 22:02:37 +00001761
DRC19c791c2018-03-08 10:55:20 -06001762DLLEXPORT tjhandle tjInitTransform(void)
DRC890f1e02011-02-26 22:02:37 +00001763{
DRC19c791c2018-03-08 10:55:20 -06001764 tjinstance *this = NULL;
1765 tjhandle handle = NULL;
1766
1767 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1768 snprintf(errStr, JMSG_LENGTH_MAX,
1769 "tjInitTransform(): Memory allocation failure");
1770 return NULL;
1771 }
1772 MEMZERO(this, sizeof(tjinstance));
1773 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1774 handle = _tjInitCompress(this);
1775 if (!handle) return NULL;
1776 handle = _tjInitDecompress(this);
1777 return handle;
DRC890f1e02011-02-26 22:02:37 +00001778}
1779
1780
DRC19c791c2018-03-08 10:55:20 -06001781DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1782 unsigned long jpegSize, int n,
1783 unsigned char **dstBufs, unsigned long *dstSizes,
1784 tjtransform *t, int flags)
DRC890f1e02011-02-26 22:02:37 +00001785{
DRC19c791c2018-03-08 10:55:20 -06001786 jpeg_transform_info *xinfo = NULL;
1787 jvirt_barray_ptr *srccoefs, *dstcoefs;
1788 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
DRC890f1e02011-02-26 22:02:37 +00001789
DRC19c791c2018-03-08 10:55:20 -06001790 getinstance(handle);
1791 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1792 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
1793 _throw("tjTransform(): Instance has not been initialized for transformation");
DRC890f1e02011-02-26 22:02:37 +00001794
DRC19c791c2018-03-08 10:55:20 -06001795 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1796 dstSizes == NULL || t == NULL || flags < 0)
1797 _throw("tjTransform(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001798
DRC19c791c2018-03-08 10:55:20 -06001799 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1800 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1801 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRC890f1e02011-02-26 22:02:37 +00001802
DRC19c791c2018-03-08 10:55:20 -06001803 if ((xinfo =
1804 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
1805 _throw("tjTransform(): Memory allocation failure");
1806 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
DRCd4c41fe2017-03-18 12:56:36 -05001807
DRC19c791c2018-03-08 10:55:20 -06001808 if (setjmp(this->jerr.setjmp_buffer)) {
1809 /* If we get here, the JPEG code has signaled an error. */
1810 retval = -1; goto bailout;
1811 }
DRC890f1e02011-02-26 22:02:37 +00001812
DRC19c791c2018-03-08 10:55:20 -06001813 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
DRC890f1e02011-02-26 22:02:37 +00001814
DRC19c791c2018-03-08 10:55:20 -06001815 for (i = 0; i < n; i++) {
1816 xinfo[i].transform = xformtypes[t[i].op];
1817 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1818 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1819 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1820 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1821 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1822 else xinfo[i].slow_hflip = 0;
DRC0a325192011-03-02 09:22:41 +00001823
DRC19c791c2018-03-08 10:55:20 -06001824 if (xinfo[i].crop) {
1825 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1826 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1827 if (t[i].r.w != 0) {
1828 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1829 } else
1830 xinfo[i].crop_width = JCROP_UNSET;
1831 if (t[i].r.h != 0) {
1832 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1833 } else
1834 xinfo[i].crop_height = JCROP_UNSET;
1835 }
1836 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1837 }
DRC890f1e02011-02-26 22:02:37 +00001838
DRC19c791c2018-03-08 10:55:20 -06001839 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1840 jpeg_read_header(dinfo, TRUE);
1841 jpegSubsamp = getSubsamp(dinfo);
1842 if (jpegSubsamp < 0)
1843 _throw("tjTransform(): Could not determine subsampling type for JPEG image");
DRC890f1e02011-02-26 22:02:37 +00001844
DRC19c791c2018-03-08 10:55:20 -06001845 for (i = 0; i < n; i++) {
1846 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
1847 _throw("tjTransform(): Transform is not perfect");
DRC890f1e02011-02-26 22:02:37 +00001848
DRC19c791c2018-03-08 10:55:20 -06001849 if (xinfo[i].crop) {
1850 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1851 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
1852 snprintf(errStr, JMSG_LENGTH_MAX,
1853 "To crop this JPEG image, x must be a multiple of %d\n"
1854 "and y must be a multiple of %d.\n",
1855 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
1856 retval = -1; goto bailout;
1857 }
1858 }
1859 }
DRC890f1e02011-02-26 22:02:37 +00001860
DRC19c791c2018-03-08 10:55:20 -06001861 srccoefs = jpeg_read_coefficients(dinfo);
DRC890f1e02011-02-26 22:02:37 +00001862
DRC19c791c2018-03-08 10:55:20 -06001863 for (i = 0; i < n; i++) {
1864 int w, h, alloc = 1;
DRC0a325192011-03-02 09:22:41 +00001865
DRC19c791c2018-03-08 10:55:20 -06001866 if (!xinfo[i].crop) {
1867 w = dinfo->image_width; h = dinfo->image_height;
1868 } else {
1869 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1870 }
1871 if (flags & TJFLAG_NOREALLOC) {
1872 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1873 }
1874 if (!(t[i].options & TJXOPT_NOOUTPUT))
1875 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1876 jpeg_copy_critical_parameters(dinfo, cinfo);
1877 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1878 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1879 jpeg_simple_progression(cinfo);
1880 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1881 jpeg_write_coefficients(cinfo, dstcoefs);
1882 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1883 JCOPYOPT_NONE : JCOPYOPT_ALL);
1884 } else
1885 jinit_c_master_control(cinfo, TRUE);
1886 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1887 if (t[i].customFilter) {
1888 int ci, y;
1889 JDIMENSION by;
DRC890f1e02011-02-26 22:02:37 +00001890
DRC19c791c2018-03-08 10:55:20 -06001891 for (ci = 0; ci < cinfo->num_components; ci++) {
1892 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1893 tjregion arrayRegion = {
1894 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1895 };
1896 tjregion planeRegion = {
1897 0, 0, compptr->width_in_blocks * DCTSIZE,
1898 compptr->height_in_blocks * DCTSIZE
1899 };
1900
1901 for (by = 0; by < compptr->height_in_blocks;
1902 by += compptr->v_samp_factor) {
1903 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1904 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1905 TRUE);
1906
1907 for (y = 0; y < compptr->v_samp_factor; y++) {
1908 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1909 i, &t[i]) == -1)
1910 _throw("tjTransform(): Error in custom filter");
1911 arrayRegion.y += DCTSIZE;
1912 }
1913 }
1914 }
1915 }
1916 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1917 }
1918
1919 jpeg_finish_decompress(dinfo);
1920
1921bailout:
1922 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1923 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1924 if (xinfo) free(xinfo);
1925 if (this->jerr.warning) retval = -1;
1926 this->jerr.stopOnWarning = FALSE;
1927 return retval;
DRC890f1e02011-02-26 22:02:37 +00001928}
DRCaa745902017-11-16 18:09:07 -06001929
1930
DRC19c791c2018-03-08 10:55:20 -06001931DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1932 int align, int *height, int *pixelFormat,
1933 int flags)
DRCaa745902017-11-16 18:09:07 -06001934{
DRC19c791c2018-03-08 10:55:20 -06001935 int retval = 0, tempc, pitch;
1936 tjhandle handle = NULL;
1937 tjinstance *this;
1938 j_compress_ptr cinfo = NULL;
1939 cjpeg_source_ptr src;
1940 unsigned char *dstBuf = NULL;
1941 FILE *file = NULL;
1942 boolean invert;
DRCaa745902017-11-16 18:09:07 -06001943
DRC19c791c2018-03-08 10:55:20 -06001944 if (!filename || !width || align < 1 || !height || !pixelFormat ||
1945 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
1946 _throwg("tjLoadImage(): Invalid argument");
1947 if ((align & (align - 1)) != 0)
1948 _throwg("tjLoadImage(): Alignment must be a power of 2");
DRCaa745902017-11-16 18:09:07 -06001949
DRC19c791c2018-03-08 10:55:20 -06001950 if ((handle = tjInitCompress()) == NULL) return NULL;
1951 this = (tjinstance *)handle;
1952 cinfo = &this->cinfo;
DRCaa745902017-11-16 18:09:07 -06001953
DRC19c791c2018-03-08 10:55:20 -06001954 if ((file = fopen(filename, "rb")) == NULL)
1955 _throwunix("tjLoadImage(): Cannot open input file");
DRCaa745902017-11-16 18:09:07 -06001956
DRC19c791c2018-03-08 10:55:20 -06001957 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
1958 _throwunix("tjLoadImage(): Could not read input file")
1959 else if (tempc == EOF)
1960 _throwg("tjLoadImage(): Input file contains no data");
DRCaa745902017-11-16 18:09:07 -06001961
DRC19c791c2018-03-08 10:55:20 -06001962 if (setjmp(this->jerr.setjmp_buffer)) {
1963 /* If we get here, the JPEG code has signaled an error. */
1964 retval = -1; goto bailout;
1965 }
DRCaa745902017-11-16 18:09:07 -06001966
DRC19c791c2018-03-08 10:55:20 -06001967 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
1968 else cinfo->in_color_space = pf2cs[*pixelFormat];
1969 if (tempc == 'B') {
1970 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
1971 _throwg("tjLoadImage(): Could not initialize bitmap loader");
1972 invert = (flags & TJFLAG_BOTTOMUP) == 0;
1973 } else if (tempc == 'P') {
1974 if ((src = jinit_read_ppm(cinfo)) == NULL)
1975 _throwg("tjLoadImage(): Could not initialize bitmap loader");
1976 invert = (flags & TJFLAG_BOTTOMUP) != 0;
1977 } else
1978 _throwg("tjLoadImage(): Unsupported file type");
DRCaa745902017-11-16 18:09:07 -06001979
DRC19c791c2018-03-08 10:55:20 -06001980 src->input_file = file;
1981 (*src->start_input) (cinfo, src);
1982 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
DRCaa745902017-11-16 18:09:07 -06001983
DRC19c791c2018-03-08 10:55:20 -06001984 *width = cinfo->image_width; *height = cinfo->image_height;
1985 *pixelFormat = cs2pf[cinfo->in_color_space];
DRCaa745902017-11-16 18:09:07 -06001986
DRC19c791c2018-03-08 10:55:20 -06001987 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
1988 if ((dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
1989 _throwg("tjLoadImage(): Memory allocation failure");
DRCaa745902017-11-16 18:09:07 -06001990
DRC19c791c2018-03-08 10:55:20 -06001991 if (setjmp(this->jerr.setjmp_buffer)) {
1992 /* If we get here, the JPEG code has signaled an error. */
1993 retval = -1; goto bailout;
1994 }
DRCaa745902017-11-16 18:09:07 -06001995
DRC19c791c2018-03-08 10:55:20 -06001996 while (cinfo->next_scanline < cinfo->image_height) {
1997 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
DRCaa745902017-11-16 18:09:07 -06001998
DRC19c791c2018-03-08 10:55:20 -06001999 for (i = 0; i < nlines; i++) {
2000 unsigned char *dstptr;
2001 int row;
DRCaa745902017-11-16 18:09:07 -06002002
DRC19c791c2018-03-08 10:55:20 -06002003 row = cinfo->next_scanline + i;
2004 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2005 else dstptr = &dstBuf[row * pitch];
2006 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2007 }
2008 cinfo->next_scanline += nlines;
2009 }
2010
2011 (*src->finish_input) (cinfo, src);
2012
2013bailout:
2014 if (handle) tjDestroy(handle);
2015 if (file) fclose(file);
2016 if (retval < 0 && dstBuf) { free(dstBuf); dstBuf = NULL; }
2017 return dstBuf;
DRCaa745902017-11-16 18:09:07 -06002018}
2019
2020
DRC19c791c2018-03-08 10:55:20 -06002021DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2022 int width, int pitch, int height, int pixelFormat,
2023 int flags)
DRCaa745902017-11-16 18:09:07 -06002024{
DRC19c791c2018-03-08 10:55:20 -06002025 int retval = 0;
2026 tjhandle handle = NULL;
2027 tjinstance *this;
2028 j_decompress_ptr dinfo = NULL;
2029 djpeg_dest_ptr dst;
2030 FILE *file = NULL;
2031 char *ptr = NULL;
2032 boolean invert;
DRCaa745902017-11-16 18:09:07 -06002033
DRC19c791c2018-03-08 10:55:20 -06002034 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2035 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2036 _throwg("tjSaveImage(): Invalid argument");
DRCaa745902017-11-16 18:09:07 -06002037
DRC19c791c2018-03-08 10:55:20 -06002038 if ((handle = tjInitDecompress()) == NULL)
2039 return -1;
2040 this = (tjinstance *)handle;
2041 dinfo = &this->dinfo;
DRCaa745902017-11-16 18:09:07 -06002042
DRC19c791c2018-03-08 10:55:20 -06002043 if ((file = fopen(filename, "wb")) == NULL)
2044 _throwunix("tjSaveImage(): Cannot open output file");
DRCaa745902017-11-16 18:09:07 -06002045
DRC19c791c2018-03-08 10:55:20 -06002046 if (setjmp(this->jerr.setjmp_buffer)) {
2047 /* If we get here, the JPEG code has signaled an error. */
2048 retval = -1; goto bailout;
2049 }
DRCaa745902017-11-16 18:09:07 -06002050
DRC19c791c2018-03-08 10:55:20 -06002051 this->dinfo.out_color_space = pf2cs[pixelFormat];
2052 dinfo->image_width = width; dinfo->image_height = height;
2053 dinfo->global_state = DSTATE_READY;
2054 dinfo->scale_num = dinfo->scale_denom = 1;
DRCaa745902017-11-16 18:09:07 -06002055
DRC19c791c2018-03-08 10:55:20 -06002056 ptr = strrchr(filename, '.');
2057 if (ptr && !strcasecmp(ptr, ".bmp")) {
2058 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
2059 _throwg("tjSaveImage(): Could not initialize bitmap writer");
2060 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2061 } else {
2062 if ((dst = jinit_write_ppm(dinfo)) == NULL)
2063 _throwg("tjSaveImage(): Could not initialize PPM writer");
2064 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2065 }
DRCaa745902017-11-16 18:09:07 -06002066
DRC19c791c2018-03-08 10:55:20 -06002067 dst->output_file = file;
2068 (*dst->start_output) (dinfo, dst);
2069 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
DRCaa745902017-11-16 18:09:07 -06002070
DRC19c791c2018-03-08 10:55:20 -06002071 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRCaa745902017-11-16 18:09:07 -06002072
DRC19c791c2018-03-08 10:55:20 -06002073 while (dinfo->output_scanline < dinfo->output_height) {
2074 unsigned char *rowptr;
DRCaa745902017-11-16 18:09:07 -06002075
DRC19c791c2018-03-08 10:55:20 -06002076 if (invert)
2077 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2078 else
2079 rowptr = &buffer[dinfo->output_scanline * pitch];
2080 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2081 (*dst->put_pixel_rows) (dinfo, dst, 1);
2082 dinfo->output_scanline++;
2083 }
DRCaa745902017-11-16 18:09:07 -06002084
DRC19c791c2018-03-08 10:55:20 -06002085 (*dst->finish_output) (dinfo, dst);
2086
2087bailout:
2088 if (handle) tjDestroy(handle);
2089 if (file) fclose(file);
2090 return retval;
DRCaa745902017-11-16 18:09:07 -06002091}