blob: 90a9ce6a0be81955a3d73341760467d713e53747 [file] [log] [blame]
DRC9b28def2011-05-21 14:37:15 +00001/*
DRC19c791c2018-03-08 10:55:20 -06002 * Copyright (C)2009-2018 D. R. Commander. All Rights Reserved.
DRC2e7b76b2009-04-03 12:04:24 +00003 *
DRC9b28def2011-05-21 14:37:15 +00004 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
DRC2e7b76b2009-04-03 12:04:24 +00006 *
DRC9b28def2011-05-21 14:37:15 +00007 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of the libjpeg-turbo Project nor the names of its
13 * contributors may be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
DRC2e7b76b2009-04-03 12:04:24 +000027 */
28
DRCbdfcb742013-01-22 13:56:34 +000029/* TurboJPEG/LJT: this implements the TurboJPEG API using libjpeg or
30 libjpeg-turbo */
DRC2e7b76b2009-04-03 12:04:24 +000031
32#include <stdio.h>
33#include <stdlib.h>
DRC0713c1b2014-08-22 13:43:33 +000034#include <ctype.h>
DRC296c71b2011-05-25 04:12:52 +000035#include <jinclude.h>
DRCfbb67472010-11-24 04:02:37 +000036#define JPEG_INTERNALS
DRC2e7b76b2009-04-03 12:04:24 +000037#include <jpeglib.h>
38#include <jerror.h>
39#include <setjmp.h>
DRCaa745902017-11-16 18:09:07 -060040#include <errno.h>
DRC2e7b76b2009-04-03 12:04:24 +000041#include "./turbojpeg.h"
DRC29e453f2018-03-16 14:09:53 -050042#include "./tjutil.h"
DRC890f1e02011-02-26 22:02:37 +000043#include "transupp.h"
DRC418fe282013-05-07 21:17:35 +000044#include "./jpegcomp.h"
DRCaa745902017-11-16 18:09:07 -060045#include "./cdjpeg.h"
DRC2a2e4512011-01-05 22:33:24 +000046
DRC19c791c2018-03-08 10:55:20 -060047extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
48 boolean);
DRC6fa14b32015-08-13 20:06:03 -050049extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
DRC19c791c2018-03-08 10:55:20 -060050 unsigned long);
DRC9b28def2011-05-21 14:37:15 +000051
DRC293263c2018-03-17 15:14:35 -050052#define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
53#define isPow2(x) (((x) & (x - 1)) == 0)
DRC2e7b76b2009-04-03 12:04:24 +000054
55
DRC8c40ac82017-11-16 18:46:01 -060056/* Error handling (based on example in example.txt) */
DRC2e7b76b2009-04-03 12:04:24 +000057
DRC19c791c2018-03-08 10:55:20 -060058static char errStr[JMSG_LENGTH_MAX] = "No error";
DRC2e7b76b2009-04-03 12:04:24 +000059
DRC19c791c2018-03-08 10:55:20 -060060struct my_error_mgr {
61 struct jpeg_error_mgr pub;
62 jmp_buf setjmp_buffer;
63 void (*emit_message) (j_common_ptr, int);
64 boolean warning, stopOnWarning;
DRC9b28def2011-05-21 14:37:15 +000065};
66typedef struct my_error_mgr *my_error_ptr;
DRC2e7b76b2009-04-03 12:04:24 +000067
DRC696e7542018-06-12 18:49:37 -050068#define JMESSAGE(code, string) string,
69static const char *turbojpeg_message_table[] = {
70#include "cderror.h"
71 NULL
72};
73
DRC2e7b76b2009-04-03 12:04:24 +000074static void my_error_exit(j_common_ptr cinfo)
75{
DRC19c791c2018-03-08 10:55:20 -060076 my_error_ptr myerr = (my_error_ptr)cinfo->err;
77
78 (*cinfo->err->output_message) (cinfo);
79 longjmp(myerr->setjmp_buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +000080}
81
DRC9b28def2011-05-21 14:37:15 +000082/* Based on output_message() in jerror.c */
83
DRC2e7b76b2009-04-03 12:04:24 +000084static void my_output_message(j_common_ptr cinfo)
85{
DRC19c791c2018-03-08 10:55:20 -060086 (*cinfo->err->format_message) (cinfo, errStr);
DRC2e7b76b2009-04-03 12:04:24 +000087}
88
DRC1f79c7c2015-06-01 19:22:41 +000089static void my_emit_message(j_common_ptr cinfo, int msg_level)
90{
DRC19c791c2018-03-08 10:55:20 -060091 my_error_ptr myerr = (my_error_ptr)cinfo->err;
92
93 myerr->emit_message(cinfo, msg_level);
94 if (msg_level < 0) {
95 myerr->warning = TRUE;
96 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
97 }
DRC1f79c7c2015-06-01 19:22:41 +000098}
99
DRC2e7b76b2009-04-03 12:04:24 +0000100
DRC9b28def2011-05-21 14:37:15 +0000101/* Global structures, macros, etc. */
DRC2e7b76b2009-04-03 12:04:24 +0000102
DRC19c791c2018-03-08 10:55:20 -0600103enum { COMPRESS = 1, DECOMPRESS = 2 };
DRC9b28def2011-05-21 14:37:15 +0000104
DRC19c791c2018-03-08 10:55:20 -0600105typedef struct _tjinstance {
106 struct jpeg_compress_struct cinfo;
107 struct jpeg_decompress_struct dinfo;
108 struct my_error_mgr jerr;
109 int init, headerRead;
110 char errStr[JMSG_LENGTH_MAX];
111 boolean isInstanceError;
DRC9b28def2011-05-21 14:37:15 +0000112} tjinstance;
DRC2e7b76b2009-04-03 12:04:24 +0000113
DRC19c791c2018-03-08 10:55:20 -0600114static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
DRC9b28def2011-05-21 14:37:15 +0000115
DRC19c791c2018-03-08 10:55:20 -0600116static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
117 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
118 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
DRC890f1e02011-02-26 22:02:37 +0000119};
DRC9b28def2011-05-21 14:37:15 +0000120
DRC293263c2018-03-17 15:14:35 -0500121#define NUMSF 16
DRC19c791c2018-03-08 10:55:20 -0600122static const tjscalingfactor sf[NUMSF] = {
123 { 2, 1 },
124 { 15, 8 },
125 { 7, 4 },
126 { 13, 8 },
127 { 3, 2 },
128 { 11, 8 },
129 { 5, 4 },
130 { 9, 8 },
131 { 1, 1 },
132 { 7, 8 },
133 { 3, 4 },
134 { 5, 8 },
135 { 1, 2 },
136 { 3, 8 },
137 { 1, 4 },
138 { 1, 8 }
DRC109a5782011-03-01 09:53:07 +0000139};
DRC2e7b76b2009-04-03 12:04:24 +0000140
DRC19c791c2018-03-08 10:55:20 -0600141static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
142 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
143 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
144 JCS_EXT_ARGB, JCS_CMYK
DRCaa745902017-11-16 18:09:07 -0600145};
146
DRC19c791c2018-03-08 10:55:20 -0600147static int cs2pf[JPEG_NUMCS] = {
148 TJPF_UNKNOWN, TJPF_GRAY,
DRCaa745902017-11-16 18:09:07 -0600149#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600150 TJPF_RGB,
DRCaa745902017-11-16 18:09:07 -0600151#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600152 TJPF_BGR,
DRCaa745902017-11-16 18:09:07 -0600153#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600154 TJPF_RGBX,
DRCaa745902017-11-16 18:09:07 -0600155#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600156 TJPF_BGRX,
DRCaa745902017-11-16 18:09:07 -0600157#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600158 TJPF_XBGR,
DRCaa745902017-11-16 18:09:07 -0600159#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600160 TJPF_XRGB,
DRCaa745902017-11-16 18:09:07 -0600161#endif
DRC19c791c2018-03-08 10:55:20 -0600162 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
163 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
164 TJPF_UNKNOWN
DRCaa745902017-11-16 18:09:07 -0600165};
166
DRC19c791c2018-03-08 10:55:20 -0600167#define _throwg(m) { \
168 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
169 retval = -1; goto bailout; \
DRCaa745902017-11-16 18:09:07 -0600170}
DRC19c791c2018-03-08 10:55:20 -0600171#define _throwunix(m) { \
172 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
173 retval = -1; goto bailout; \
174}
175#define _throw(m) { \
176 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
DRC58cb10e2018-03-31 13:51:31 -0500177 this->isInstanceError = TRUE; _throwg(m) \
DRC19c791c2018-03-08 10:55:20 -0600178}
179
180#define getinstance(handle) \
181 tjinstance *this = (tjinstance *)handle; \
182 j_compress_ptr cinfo = NULL; \
183 j_decompress_ptr dinfo = NULL; \
184 \
185 if (!this) { \
186 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
187 return -1; \
188 } \
189 cinfo = &this->cinfo; dinfo = &this->dinfo; \
190 this->jerr.warning = FALSE; \
191 this->isInstanceError = FALSE;
192
193#define getcinstance(handle) \
194 tjinstance *this = (tjinstance *)handle; \
195 j_compress_ptr cinfo = NULL; \
196 \
197 if (!this) { \
198 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
199 return -1; \
200 } \
201 cinfo = &this->cinfo; \
202 this->jerr.warning = FALSE; \
203 this->isInstanceError = FALSE;
204
205#define getdinstance(handle) \
206 tjinstance *this = (tjinstance *)handle; \
207 j_decompress_ptr dinfo = NULL; \
208 \
209 if (!this) { \
210 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
211 return -1; \
212 } \
213 dinfo = &this->dinfo; \
214 this->jerr.warning = FALSE; \
215 this->isInstanceError = FALSE;
DRC2e7b76b2009-04-03 12:04:24 +0000216
DRC9b28def2011-05-21 14:37:15 +0000217static int getPixelFormat(int pixelSize, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000218{
DRC19c791c2018-03-08 10:55:20 -0600219 if (pixelSize == 1) return TJPF_GRAY;
220 if (pixelSize == 3) {
221 if (flags & TJ_BGR) return TJPF_BGR;
222 else return TJPF_RGB;
223 }
224 if (pixelSize == 4) {
225 if (flags & TJ_ALPHAFIRST) {
226 if (flags & TJ_BGR) return TJPF_XBGR;
227 else return TJPF_XRGB;
228 } else {
229 if (flags & TJ_BGR) return TJPF_BGRX;
230 else return TJPF_RGBX;
231 }
232 }
233 return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000234}
235
DRC19c791c2018-03-08 10:55:20 -0600236static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
237 int subsamp, int jpegQual, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000238{
DRC19c791c2018-03-08 10:55:20 -0600239 int retval = 0;
240 char *env = NULL;
DRCf12bb302011-09-07 05:03:18 +0000241
DRC19c791c2018-03-08 10:55:20 -0600242 cinfo->in_color_space = pf2cs[pixelFormat];
243 cinfo->input_components = tjPixelSize[pixelFormat];
244 jpeg_set_defaults(cinfo);
DRC0713c1b2014-08-22 13:43:33 +0000245
DRCfeccdcf2015-02-23 19:19:40 +0000246#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600247 if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
248 !strcmp(env, "1"))
249 cinfo->optimize_coding = TRUE;
250 if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
251 !strcmp(env, "1"))
252 cinfo->arith_code = TRUE;
253 if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
254 int temp = -1;
255 char tempc = 0;
256
257 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
258 temp <= 65535) {
259 if (toupper(tempc) == 'B') {
260 cinfo->restart_interval = temp;
261 cinfo->restart_in_rows = 0;
262 } else
263 cinfo->restart_in_rows = temp;
264 }
265 }
DRCfeccdcf2015-02-23 19:19:40 +0000266#endif
DRC0713c1b2014-08-22 13:43:33 +0000267
DRC19c791c2018-03-08 10:55:20 -0600268 if (jpegQual >= 0) {
269 jpeg_set_quality(cinfo, jpegQual, TRUE);
270 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
271 cinfo->dct_method = JDCT_ISLOW;
272 else
273 cinfo->dct_method = JDCT_FASTEST;
274 }
275 if (subsamp == TJSAMP_GRAY)
276 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
277 else if (pixelFormat == TJPF_CMYK)
278 jpeg_set_colorspace(cinfo, JCS_YCCK);
279 else
280 jpeg_set_colorspace(cinfo, JCS_YCbCr);
DRC2e7b76b2009-04-03 12:04:24 +0000281
DRC19c791c2018-03-08 10:55:20 -0600282 if (flags & TJFLAG_PROGRESSIVE)
283 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000284#ifndef NO_GETENV
DRC19c791c2018-03-08 10:55:20 -0600285 else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
286 !strcmp(env, "1"))
287 jpeg_simple_progression(cinfo);
DRCfeccdcf2015-02-23 19:19:40 +0000288#endif
DRC0713c1b2014-08-22 13:43:33 +0000289
DRC19c791c2018-03-08 10:55:20 -0600290 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
291 cinfo->comp_info[1].h_samp_factor = 1;
292 cinfo->comp_info[2].h_samp_factor = 1;
293 if (cinfo->num_components > 3)
294 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
295 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
296 cinfo->comp_info[1].v_samp_factor = 1;
297 cinfo->comp_info[2].v_samp_factor = 1;
298 if (cinfo->num_components > 3)
299 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
DRCf12bb302011-09-07 05:03:18 +0000300
DRC19c791c2018-03-08 10:55:20 -0600301 return retval;
DRC9b28def2011-05-21 14:37:15 +0000302}
303
DRC9b28def2011-05-21 14:37:15 +0000304
DRC9b49f0e2011-07-12 03:17:23 +0000305static int getSubsamp(j_decompress_ptr dinfo)
306{
DRC19c791c2018-03-08 10:55:20 -0600307 int retval = -1, i, k;
DRCea1eea42014-11-19 00:55:28 +0000308
DRC19c791c2018-03-08 10:55:20 -0600309 /* The sampling factors actually have no meaning with grayscale JPEG files,
310 and in fact it's possible to generate grayscale JPEGs with sampling
311 factors > 1 (even though those sampling factors are ignored by the
312 decompressor.) Thus, we need to treat grayscale as a special case. */
313 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
314 return TJSAMP_GRAY;
DRCea1eea42014-11-19 00:55:28 +0000315
DRC19c791c2018-03-08 10:55:20 -0600316 for (i = 0; i < NUMSUBOPT; i++) {
317 if (dinfo->num_components == pixelsize[i] ||
318 ((dinfo->jpeg_color_space == JCS_YCCK ||
319 dinfo->jpeg_color_space == JCS_CMYK) &&
320 pixelsize[i] == 3 && dinfo->num_components == 4)) {
321 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
322 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
323 int match = 0;
324
325 for (k = 1; k < dinfo->num_components; k++) {
326 int href = 1, vref = 1;
327
DRC2401e4d2018-04-26 18:01:52 -0500328 if ((dinfo->jpeg_color_space == JCS_YCCK ||
329 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600330 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
331 }
332 if (dinfo->comp_info[k].h_samp_factor == href &&
333 dinfo->comp_info[k].v_samp_factor == vref)
334 match++;
335 }
336 if (match == dinfo->num_components - 1) {
337 retval = i; break;
338 }
339 }
340 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
341 in non-standard ways. */
342 if (dinfo->comp_info[0].h_samp_factor == 2 &&
343 dinfo->comp_info[0].v_samp_factor == 2 &&
344 (i == TJSAMP_422 || i == TJSAMP_440)) {
345 int match = 0;
346
347 for (k = 1; k < dinfo->num_components; k++) {
348 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
349
DRC2401e4d2018-04-26 18:01:52 -0500350 if ((dinfo->jpeg_color_space == JCS_YCCK ||
351 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
DRC19c791c2018-03-08 10:55:20 -0600352 href = vref = 2;
353 }
354 if (dinfo->comp_info[k].h_samp_factor == href &&
355 dinfo->comp_info[k].v_samp_factor == vref)
356 match++;
357 }
358 if (match == dinfo->num_components - 1) {
359 retval = i; break;
360 }
361 }
362 }
363 }
364 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000365}
366
367
DRC9b28def2011-05-21 14:37:15 +0000368/* General API functions */
369
DRC19c791c2018-03-08 10:55:20 -0600370DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
DRCb9ab64d2017-05-11 21:02:29 -0500371{
DRC19c791c2018-03-08 10:55:20 -0600372 tjinstance *this = (tjinstance *)handle;
373
374 if (this && this->isInstanceError) {
375 this->isInstanceError = FALSE;
376 return this->errStr;
377 } else
378 return errStr;
DRCb9ab64d2017-05-11 21:02:29 -0500379}
380
381
DRC19c791c2018-03-08 10:55:20 -0600382DLLEXPORT char *tjGetErrorStr(void)
DRC9b28def2011-05-21 14:37:15 +0000383{
DRC19c791c2018-03-08 10:55:20 -0600384 return errStr;
DRC9b28def2011-05-21 14:37:15 +0000385}
386
387
DRC19c791c2018-03-08 10:55:20 -0600388DLLEXPORT int tjGetErrorCode(tjhandle handle)
DRCd4092f62017-06-27 10:54:21 -0500389{
DRC19c791c2018-03-08 10:55:20 -0600390 tjinstance *this = (tjinstance *)handle;
391
392 if (this && this->jerr.warning) return TJERR_WARNING;
393 else return TJERR_FATAL;
DRCd4092f62017-06-27 10:54:21 -0500394}
395
396
DRC19c791c2018-03-08 10:55:20 -0600397DLLEXPORT int tjDestroy(tjhandle handle)
DRC9b28def2011-05-21 14:37:15 +0000398{
DRC19c791c2018-03-08 10:55:20 -0600399 getinstance(handle);
400
401 if (setjmp(this->jerr.setjmp_buffer)) return -1;
402 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
403 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
404 free(this);
405 return 0;
DRC9b28def2011-05-21 14:37:15 +0000406}
407
408
DRC6b76f752011-05-24 16:52:47 +0000409/* These are exposed mainly because Windows can't malloc() and free() across
410 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
411 with turbojpeg.dll for compatibility reasons. However, these functions
412 can potentially be used for other purposes by different implementations. */
413
DRC19c791c2018-03-08 10:55:20 -0600414DLLEXPORT void tjFree(unsigned char *buf)
DRC6b76f752011-05-24 16:52:47 +0000415{
DRC19c791c2018-03-08 10:55:20 -0600416 if (buf) free(buf);
DRC6b76f752011-05-24 16:52:47 +0000417}
418
419
DRC19c791c2018-03-08 10:55:20 -0600420DLLEXPORT unsigned char *tjAlloc(int bytes)
DRC6b76f752011-05-24 16:52:47 +0000421{
DRC19c791c2018-03-08 10:55:20 -0600422 return (unsigned char *)malloc(bytes);
DRC6b76f752011-05-24 16:52:47 +0000423}
424
425
DRC9b28def2011-05-21 14:37:15 +0000426/* Compressor */
427
428static tjhandle _tjInitCompress(tjinstance *this)
429{
DRC19c791c2018-03-08 10:55:20 -0600430 static unsigned char buffer[1];
431 unsigned char *buf = buffer;
432 unsigned long size = 1;
DRC9b28def2011-05-21 14:37:15 +0000433
DRC19c791c2018-03-08 10:55:20 -0600434 /* This is also straight out of example.txt */
435 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
436 this->jerr.pub.error_exit = my_error_exit;
437 this->jerr.pub.output_message = my_output_message;
438 this->jerr.emit_message = this->jerr.pub.emit_message;
439 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -0500440 this->jerr.pub.addon_message_table = turbojpeg_message_table;
441 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
442 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC9b28def2011-05-21 14:37:15 +0000443
DRC19c791c2018-03-08 10:55:20 -0600444 if (setjmp(this->jerr.setjmp_buffer)) {
445 /* If we get here, the JPEG code has signaled an error. */
446 if (this) free(this);
447 return NULL;
448 }
DRC9b28def2011-05-21 14:37:15 +0000449
DRC19c791c2018-03-08 10:55:20 -0600450 jpeg_create_compress(&this->cinfo);
451 /* Make an initial call so it will create the destination manager */
452 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
DRC9b28def2011-05-21 14:37:15 +0000453
DRC19c791c2018-03-08 10:55:20 -0600454 this->init |= COMPRESS;
455 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +0000456}
457
DRC19c791c2018-03-08 10:55:20 -0600458DLLEXPORT tjhandle tjInitCompress(void)
DRC890f1e02011-02-26 22:02:37 +0000459{
DRC19c791c2018-03-08 10:55:20 -0600460 tjinstance *this = NULL;
461
462 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
463 snprintf(errStr, JMSG_LENGTH_MAX,
464 "tjInitCompress(): Memory allocation failure");
465 return NULL;
466 }
467 MEMZERO(this, sizeof(tjinstance));
468 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
469 return _tjInitCompress(this);
DRC890f1e02011-02-26 22:02:37 +0000470}
471
DRC84241602011-02-25 02:08:23 +0000472
DRC19c791c2018-03-08 10:55:20 -0600473DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000474{
DRC19c791c2018-03-08 10:55:20 -0600475 unsigned long retval = 0;
476 int mcuw, mcuh, chromasf;
DRC9b49f0e2011-07-12 03:17:23 +0000477
DRC19c791c2018-03-08 10:55:20 -0600478 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
479 _throwg("tjBufSize(): Invalid argument");
DRC9b49f0e2011-07-12 03:17:23 +0000480
DRC19c791c2018-03-08 10:55:20 -0600481 /* This allows for rare corner cases in which a JPEG image can actually be
482 larger than the uncompressed input (we wouldn't mention it if it hadn't
483 happened before.) */
484 mcuw = tjMCUWidth[jpegSubsamp];
485 mcuh = tjMCUHeight[jpegSubsamp];
486 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
487 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
488
489bailout:
490 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000491}
492
DRC19c791c2018-03-08 10:55:20 -0600493DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
DRC2e7b76b2009-04-03 12:04:24 +0000494{
DRC19c791c2018-03-08 10:55:20 -0600495 unsigned long retval = 0;
DRCf3cf9732011-02-22 00:16:14 +0000496
DRC19c791c2018-03-08 10:55:20 -0600497 if (width < 1 || height < 1)
498 _throwg("TJBUFSIZE(): Invalid argument");
DRCf3cf9732011-02-22 00:16:14 +0000499
DRC19c791c2018-03-08 10:55:20 -0600500 /* This allows for rare corner cases in which a JPEG image can actually be
501 larger than the uncompressed input (we wouldn't mention it if it hadn't
502 happened before.) */
503 retval = PAD(width, 16) * PAD(height, 16) * 6 + 2048;
504
505bailout:
506 return retval;
DRCf3cf9732011-02-22 00:16:14 +0000507}
508
DRC84241602011-02-25 02:08:23 +0000509
DRC19c791c2018-03-08 10:55:20 -0600510DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
511 int subsamp)
DRCf3cf9732011-02-22 00:16:14 +0000512{
DRC19c791c2018-03-08 10:55:20 -0600513 int retval = 0, nc, i;
DRC40dd3142014-08-17 12:23:49 +0000514
DRC19c791c2018-03-08 10:55:20 -0600515 if (subsamp < 0 || subsamp >= NUMSUBOPT)
516 _throwg("tjBufSizeYUV2(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000517
DRC19c791c2018-03-08 10:55:20 -0600518 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
519 for (i = 0; i < nc; i++) {
520 int pw = tjPlaneWidth(i, width, subsamp);
521 int stride = PAD(pw, pad);
522 int ph = tjPlaneHeight(i, height, subsamp);
DRCf3cf9732011-02-22 00:16:14 +0000523
DRC19c791c2018-03-08 10:55:20 -0600524 if (pw < 0 || ph < 0) return -1;
525 else retval += stride * ph;
526 }
527
528bailout:
529 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000530}
531
DRC19c791c2018-03-08 10:55:20 -0600532DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
DRCf610d612013-04-26 10:33:29 +0000533{
DRC19c791c2018-03-08 10:55:20 -0600534 return tjBufSizeYUV2(width, 4, height, subsamp);
DRCf610d612013-04-26 10:33:29 +0000535}
DRC84241602011-02-25 02:08:23 +0000536
DRC19c791c2018-03-08 10:55:20 -0600537DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000538{
DRC19c791c2018-03-08 10:55:20 -0600539 return tjBufSizeYUV(width, height, subsamp);
DRC9b49f0e2011-07-12 03:17:23 +0000540}
541
542
DRC40dd3142014-08-17 12:23:49 +0000543DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
544{
DRC19c791c2018-03-08 10:55:20 -0600545 int pw, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000546
DRC19c791c2018-03-08 10:55:20 -0600547 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
548 _throwg("tjPlaneWidth(): Invalid argument");
549 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
550 if (componentID < 0 || componentID >= nc)
551 _throwg("tjPlaneWidth(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000552
DRC19c791c2018-03-08 10:55:20 -0600553 pw = PAD(width, tjMCUWidth[subsamp] / 8);
554 if (componentID == 0)
555 retval = pw;
556 else
557 retval = pw * 8 / tjMCUWidth[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000558
DRC19c791c2018-03-08 10:55:20 -0600559bailout:
560 return retval;
DRC40dd3142014-08-17 12:23:49 +0000561}
562
563
564DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
565{
DRC19c791c2018-03-08 10:55:20 -0600566 int ph, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000567
DRC19c791c2018-03-08 10:55:20 -0600568 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
569 _throwg("tjPlaneHeight(): Invalid argument");
570 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
571 if (componentID < 0 || componentID >= nc)
572 _throwg("tjPlaneHeight(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000573
DRC19c791c2018-03-08 10:55:20 -0600574 ph = PAD(height, tjMCUHeight[subsamp] / 8);
575 if (componentID == 0)
576 retval = ph;
577 else
578 retval = ph * 8 / tjMCUHeight[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000579
DRC19c791c2018-03-08 10:55:20 -0600580bailout:
581 return retval;
DRC40dd3142014-08-17 12:23:49 +0000582}
583
584
DRC19c791c2018-03-08 10:55:20 -0600585DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
586 int height, int subsamp)
DRC40dd3142014-08-17 12:23:49 +0000587{
DRC19c791c2018-03-08 10:55:20 -0600588 unsigned long retval = 0;
589 int pw, ph;
DRC40dd3142014-08-17 12:23:49 +0000590
DRC19c791c2018-03-08 10:55:20 -0600591 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
592 _throwg("tjPlaneSizeYUV(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000593
DRC19c791c2018-03-08 10:55:20 -0600594 pw = tjPlaneWidth(componentID, width, subsamp);
595 ph = tjPlaneHeight(componentID, height, subsamp);
596 if (pw < 0 || ph < 0) return -1;
DRC40dd3142014-08-17 12:23:49 +0000597
DRC19c791c2018-03-08 10:55:20 -0600598 if (stride == 0) stride = pw;
599 else stride = abs(stride);
DRC40dd3142014-08-17 12:23:49 +0000600
DRC19c791c2018-03-08 10:55:20 -0600601 retval = stride * (ph - 1) + pw;
DRC40dd3142014-08-17 12:23:49 +0000602
DRC19c791c2018-03-08 10:55:20 -0600603bailout:
604 return retval;
DRC40dd3142014-08-17 12:23:49 +0000605}
606
607
DRC19c791c2018-03-08 10:55:20 -0600608DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
609 int width, int pitch, int height, int pixelFormat,
610 unsigned char **jpegBuf, unsigned long *jpegSize,
611 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000612{
DRC19c791c2018-03-08 10:55:20 -0600613 int i, retval = 0, alloc = 1;
614 JSAMPROW *row_pointer = NULL;
DRC9b28def2011-05-21 14:37:15 +0000615
DRC19c791c2018-03-08 10:55:20 -0600616 getcinstance(handle)
617 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
618 if ((this->init & COMPRESS) == 0)
619 _throw("tjCompress2(): Instance has not been initialized for compression");
DRC9b28def2011-05-21 14:37:15 +0000620
DRC19c791c2018-03-08 10:55:20 -0600621 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
622 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
623 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
624 jpegQual < 0 || jpegQual > 100)
625 _throw("tjCompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +0000626
DRC19c791c2018-03-08 10:55:20 -0600627 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC9b28def2011-05-21 14:37:15 +0000628
DRC19c791c2018-03-08 10:55:20 -0600629 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
630 _throw("tjCompress2(): Memory allocation failure");
DRCd4c41fe2017-03-18 12:56:36 -0500631
DRC19c791c2018-03-08 10:55:20 -0600632 if (setjmp(this->jerr.setjmp_buffer)) {
633 /* If we get here, the JPEG code has signaled an error. */
634 retval = -1; goto bailout;
635 }
DRCd4c41fe2017-03-18 12:56:36 -0500636
DRC19c791c2018-03-08 10:55:20 -0600637 cinfo->image_width = width;
638 cinfo->image_height = height;
DRC9b28def2011-05-21 14:37:15 +0000639
DRCbd96b302018-03-17 00:06:10 -0500640#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600641 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
642 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
643 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500644#endif
DRC9b28def2011-05-21 14:37:15 +0000645
DRC19c791c2018-03-08 10:55:20 -0600646 if (flags & TJFLAG_NOREALLOC) {
647 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
648 }
649 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
650 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
651 return -1;
DRC9b28def2011-05-21 14:37:15 +0000652
DRC19c791c2018-03-08 10:55:20 -0600653 jpeg_start_compress(cinfo, TRUE);
654 for (i = 0; i < height; i++) {
655 if (flags & TJFLAG_BOTTOMUP)
656 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
657 else
658 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
659 }
660 while (cinfo->next_scanline < cinfo->image_height)
661 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
662 cinfo->image_height - cinfo->next_scanline);
663 jpeg_finish_compress(cinfo);
DRC9b28def2011-05-21 14:37:15 +0000664
DRC19c791c2018-03-08 10:55:20 -0600665bailout:
666 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
667 if (row_pointer) free(row_pointer);
668 if (this->jerr.warning) retval = -1;
669 this->jerr.stopOnWarning = FALSE;
670 return retval;
DRC9b28def2011-05-21 14:37:15 +0000671}
672
DRC19c791c2018-03-08 10:55:20 -0600673DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
674 int pitch, int height, int pixelSize,
675 unsigned char *jpegBuf, unsigned long *jpegSize,
676 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000677{
DRC19c791c2018-03-08 10:55:20 -0600678 int retval = 0;
679 unsigned long size;
680
681 if (flags & TJ_YUV) {
682 size = tjBufSizeYUV(width, height, jpegSubsamp);
683 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
684 getPixelFormat(pixelSize, flags), jpegBuf,
685 jpegSubsamp, flags);
686 } else {
687 retval = tjCompress2(handle, srcBuf, width, pitch, height,
688 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
689 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
690 }
691 *jpegSize = size;
692 return retval;
DRC9b28def2011-05-21 14:37:15 +0000693}
694
695
DRC19c791c2018-03-08 10:55:20 -0600696DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
697 int width, int pitch, int height,
698 int pixelFormat, unsigned char **dstPlanes,
699 int *strides, int subsamp, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000700{
DRC19c791c2018-03-08 10:55:20 -0600701 JSAMPROW *row_pointer = NULL;
702 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
703 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
704 JSAMPROW *outbuf[MAX_COMPONENTS];
705 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
706 JSAMPLE *ptr;
707 jpeg_component_info *compptr;
DRC2e7b76b2009-04-03 12:04:24 +0000708
DRC19c791c2018-03-08 10:55:20 -0600709 getcinstance(handle);
710 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000711
DRC19c791c2018-03-08 10:55:20 -0600712 for (i = 0; i < MAX_COMPONENTS; i++) {
713 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
714 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
715 }
DRCfbb67472010-11-24 04:02:37 +0000716
DRC19c791c2018-03-08 10:55:20 -0600717 if ((this->init & COMPRESS) == 0)
718 _throw("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
DRCe2f8e692013-10-30 22:21:06 +0000719
DRC19c791c2018-03-08 10:55:20 -0600720 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
721 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
722 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
723 _throw("tjEncodeYUVPlanes(): Invalid argument");
724 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
725 _throw("tjEncodeYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000726
DRC19c791c2018-03-08 10:55:20 -0600727 if (pixelFormat == TJPF_CMYK)
728 _throw("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
DRCcd7c3e62013-08-23 02:49:25 +0000729
DRC19c791c2018-03-08 10:55:20 -0600730 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC2e7b76b2009-04-03 12:04:24 +0000731
DRC19c791c2018-03-08 10:55:20 -0600732 if (setjmp(this->jerr.setjmp_buffer)) {
733 /* If we get here, the JPEG code has signaled an error. */
734 retval = -1; goto bailout;
735 }
DRCd4c41fe2017-03-18 12:56:36 -0500736
DRC19c791c2018-03-08 10:55:20 -0600737 cinfo->image_width = width;
738 cinfo->image_height = height;
DRC2e7b76b2009-04-03 12:04:24 +0000739
DRCbd96b302018-03-17 00:06:10 -0500740#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600741 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
742 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
743 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500744#endif
DRC0c6a2712010-02-22 08:34:44 +0000745
DRC19c791c2018-03-08 10:55:20 -0600746 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000747
DRC19c791c2018-03-08 10:55:20 -0600748 /* Execute only the parts of jpeg_start_compress() that we need. If we
749 were to call the whole jpeg_start_compress() function, then it would try
750 to write the file headers, which could overflow the output buffer if the
751 YUV image were very small. */
752 if (cinfo->global_state != CSTATE_START)
753 _throw("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
754 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
755 jinit_c_master_control(cinfo, FALSE);
756 jinit_color_converter(cinfo);
757 jinit_downsampler(cinfo);
758 (*cinfo->cconvert->start_pass) (cinfo);
DRC38c99702014-02-11 09:45:18 +0000759
DRC19c791c2018-03-08 10:55:20 -0600760 pw0 = PAD(width, cinfo->max_h_samp_factor);
761 ph0 = PAD(height, cinfo->max_v_samp_factor);
DRC2e7b76b2009-04-03 12:04:24 +0000762
DRC19c791c2018-03-08 10:55:20 -0600763 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
764 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
765 for (i = 0; i < height; i++) {
766 if (flags & TJFLAG_BOTTOMUP)
767 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
768 else
769 row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
770 }
771 if (height < ph0)
772 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRCfbb67472010-11-24 04:02:37 +0000773
DRC19c791c2018-03-08 10:55:20 -0600774 for (i = 0; i < cinfo->num_components; i++) {
775 compptr = &cinfo->comp_info[i];
776 _tmpbuf[i] = (JSAMPLE *)malloc(
777 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
778 compptr->h_samp_factor, 32) *
779 cinfo->max_v_samp_factor + 32);
780 if (!_tmpbuf[i])
781 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
782 tmpbuf[i] =
783 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
784 if (!tmpbuf[i])
785 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
786 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
787 unsigned char *_tmpbuf_aligned =
788 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRCfbb67472010-11-24 04:02:37 +0000789
DRC19c791c2018-03-08 10:55:20 -0600790 tmpbuf[i][row] = &_tmpbuf_aligned[
791 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
792 compptr->h_samp_factor, 32) * row];
793 }
794 _tmpbuf2[i] =
795 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
796 compptr->v_samp_factor + 32);
797 if (!_tmpbuf2[i])
798 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
799 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
800 if (!tmpbuf2[i])
801 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
802 for (row = 0; row < compptr->v_samp_factor; row++) {
803 unsigned char *_tmpbuf2_aligned =
804 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
DRCd4c41fe2017-03-18 12:56:36 -0500805
DRC19c791c2018-03-08 10:55:20 -0600806 tmpbuf2[i][row] =
807 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
808 }
809 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
810 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
811 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
812 if (!outbuf[i])
813 _throw("tjEncodeYUVPlanes(): Memory allocation failure");
814 ptr = dstPlanes[i];
815 for (row = 0; row < ph[i]; row++) {
816 outbuf[i][row] = ptr;
817 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
818 }
819 }
DRC2e7b76b2009-04-03 12:04:24 +0000820
DRC19c791c2018-03-08 10:55:20 -0600821 if (setjmp(this->jerr.setjmp_buffer)) {
822 /* If we get here, the JPEG code has signaled an error. */
823 retval = -1; goto bailout;
824 }
825
826 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
827 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
828 cinfo->max_v_samp_factor);
829 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
830 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
831 i++, compptr++)
832 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
833 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
834 compptr->v_samp_factor, pw[i]);
835 }
836 cinfo->next_scanline += height;
837 jpeg_abort_compress(cinfo);
838
839bailout:
840 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
841 if (row_pointer) free(row_pointer);
842 for (i = 0; i < MAX_COMPONENTS; i++) {
843 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
844 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
845 if (tmpbuf2[i] != NULL) free(tmpbuf2[i]);
846 if (_tmpbuf2[i] != NULL) free(_tmpbuf2[i]);
847 if (outbuf[i] != NULL) free(outbuf[i]);
848 }
849 if (this->jerr.warning) retval = -1;
850 this->jerr.stopOnWarning = FALSE;
851 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000852}
853
DRC19c791c2018-03-08 10:55:20 -0600854DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
855 int width, int pitch, int height, int pixelFormat,
856 unsigned char *dstBuf, int pad, int subsamp,
857 int flags)
DRCaecea382014-08-11 18:05:41 +0000858{
DRC19c791c2018-03-08 10:55:20 -0600859 unsigned char *dstPlanes[3];
860 int pw0, ph0, strides[3], retval = -1;
861 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +0000862
DRC19c791c2018-03-08 10:55:20 -0600863 if (!this) _throwg("tjEncodeYUV3(): Invalid handle");
864 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -0500865
DRC19c791c2018-03-08 10:55:20 -0600866 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 || !isPow2(pad) ||
867 subsamp < 0 || subsamp >= NUMSUBOPT)
868 _throw("tjEncodeYUV3(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +0000869
DRC19c791c2018-03-08 10:55:20 -0600870 pw0 = tjPlaneWidth(0, width, subsamp);
871 ph0 = tjPlaneHeight(0, height, subsamp);
872 dstPlanes[0] = dstBuf;
873 strides[0] = PAD(pw0, pad);
874 if (subsamp == TJSAMP_GRAY) {
875 strides[1] = strides[2] = 0;
876 dstPlanes[1] = dstPlanes[2] = NULL;
877 } else {
878 int pw1 = tjPlaneWidth(1, width, subsamp);
879 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +0000880
DRC19c791c2018-03-08 10:55:20 -0600881 strides[1] = strides[2] = PAD(pw1, pad);
882 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
883 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
884 }
DRCaecea382014-08-11 18:05:41 +0000885
DRC19c791c2018-03-08 10:55:20 -0600886 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
887 dstPlanes, strides, subsamp, flags);
888
889bailout:
890 return retval;
DRCaecea382014-08-11 18:05:41 +0000891}
892
DRC19c791c2018-03-08 10:55:20 -0600893DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
894 int pitch, int height, int pixelFormat,
895 unsigned char *dstBuf, int subsamp, int flags)
DRCf610d612013-04-26 10:33:29 +0000896{
DRC19c791c2018-03-08 10:55:20 -0600897 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
898 dstBuf, 4, subsamp, flags);
DRCf610d612013-04-26 10:33:29 +0000899}
900
DRC19c791c2018-03-08 10:55:20 -0600901DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
902 int pitch, int height, int pixelSize,
903 unsigned char *dstBuf, int subsamp, int flags)
DRC84241602011-02-25 02:08:23 +0000904{
DRC19c791c2018-03-08 10:55:20 -0600905 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
906 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
907 flags);
DRC84241602011-02-25 02:08:23 +0000908}
909
910
DRC19c791c2018-03-08 10:55:20 -0600911DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
912 const unsigned char **srcPlanes,
913 int width, const int *strides,
914 int height, int subsamp,
915 unsigned char **jpegBuf,
916 unsigned long *jpegSize, int jpegQual,
917 int flags)
DRC910a3572013-10-30 23:02:57 +0000918{
DRC19c791c2018-03-08 10:55:20 -0600919 int i, row, retval = 0, alloc = 1;
920 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
921 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
922 JSAMPLE *_tmpbuf = NULL, *ptr;
923 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
DRC910a3572013-10-30 23:02:57 +0000924
DRC19c791c2018-03-08 10:55:20 -0600925 getcinstance(handle)
926 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000927
DRC19c791c2018-03-08 10:55:20 -0600928 for (i = 0; i < MAX_COMPONENTS; i++) {
929 tmpbuf[i] = NULL; inbuf[i] = NULL;
930 }
DRC910a3572013-10-30 23:02:57 +0000931
DRC19c791c2018-03-08 10:55:20 -0600932 if ((this->init & COMPRESS) == 0)
933 _throw("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
DRC910a3572013-10-30 23:02:57 +0000934
DRC19c791c2018-03-08 10:55:20 -0600935 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
936 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
937 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
938 _throw("tjCompressFromYUVPlanes(): Invalid argument");
939 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
940 _throw("tjCompressFromYUVPlanes(): Invalid argument");
DRC910a3572013-10-30 23:02:57 +0000941
DRC19c791c2018-03-08 10:55:20 -0600942 if (setjmp(this->jerr.setjmp_buffer)) {
943 /* If we get here, the JPEG code has signaled an error. */
944 retval = -1; goto bailout;
945 }
DRC910a3572013-10-30 23:02:57 +0000946
DRC19c791c2018-03-08 10:55:20 -0600947 cinfo->image_width = width;
948 cinfo->image_height = height;
DRC910a3572013-10-30 23:02:57 +0000949
DRCbd96b302018-03-17 00:06:10 -0500950#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600951 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
952 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
953 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500954#endif
DRC910a3572013-10-30 23:02:57 +0000955
DRC19c791c2018-03-08 10:55:20 -0600956 if (flags & TJFLAG_NOREALLOC) {
957 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
958 }
959 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
960 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
961 return -1;
962 cinfo->raw_data_in = TRUE;
DRC910a3572013-10-30 23:02:57 +0000963
DRC19c791c2018-03-08 10:55:20 -0600964 jpeg_start_compress(cinfo, TRUE);
965 for (i = 0; i < cinfo->num_components; i++) {
966 jpeg_component_info *compptr = &cinfo->comp_info[i];
967 int ih;
DRC910a3572013-10-30 23:02:57 +0000968
DRC19c791c2018-03-08 10:55:20 -0600969 iw[i] = compptr->width_in_blocks * DCTSIZE;
970 ih = compptr->height_in_blocks * DCTSIZE;
971 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
972 compptr->h_samp_factor / cinfo->max_h_samp_factor;
973 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
974 compptr->v_samp_factor / cinfo->max_v_samp_factor;
975 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
976 th[i] = compptr->v_samp_factor * DCTSIZE;
977 tmpbufsize += iw[i] * th[i];
978 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
979 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
980 ptr = (JSAMPLE *)srcPlanes[i];
981 for (row = 0; row < ph[i]; row++) {
982 inbuf[i][row] = ptr;
983 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
984 }
985 }
986 if (usetmpbuf) {
987 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
988 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
989 ptr = _tmpbuf;
990 for (i = 0; i < cinfo->num_components; i++) {
991 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
992 _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
993 for (row = 0; row < th[i]; row++) {
994 tmpbuf[i][row] = ptr;
995 ptr += iw[i];
996 }
997 }
998 }
DRCd4c41fe2017-03-18 12:56:36 -0500999
DRC19c791c2018-03-08 10:55:20 -06001000 if (setjmp(this->jerr.setjmp_buffer)) {
1001 /* If we get here, the JPEG code has signaled an error. */
1002 retval = -1; goto bailout;
1003 }
DRC910a3572013-10-30 23:02:57 +00001004
DRC19c791c2018-03-08 10:55:20 -06001005 for (row = 0; row < (int)cinfo->image_height;
1006 row += cinfo->max_v_samp_factor * DCTSIZE) {
1007 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1008 int crow[MAX_COMPONENTS];
1009
1010 for (i = 0; i < cinfo->num_components; i++) {
1011 jpeg_component_info *compptr = &cinfo->comp_info[i];
1012
1013 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1014 if (usetmpbuf) {
1015 int j, k;
1016
1017 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1018 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1019 /* Duplicate last sample in row to fill out MCU */
1020 for (k = pw[i]; k < iw[i]; k++)
1021 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1022 }
1023 /* Duplicate last row to fill out MCU */
1024 for (j = ph[i] - crow[i]; j < th[i]; j++)
1025 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1026 yuvptr[i] = tmpbuf[i];
1027 } else
1028 yuvptr[i] = &inbuf[i][crow[i]];
1029 }
1030 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1031 }
1032 jpeg_finish_compress(cinfo);
1033
1034bailout:
1035 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1036 for (i = 0; i < MAX_COMPONENTS; i++) {
1037 if (tmpbuf[i]) free(tmpbuf[i]);
1038 if (inbuf[i]) free(inbuf[i]);
1039 }
1040 if (_tmpbuf) free(_tmpbuf);
1041 if (this->jerr.warning) retval = -1;
1042 this->jerr.stopOnWarning = FALSE;
1043 return retval;
DRC910a3572013-10-30 23:02:57 +00001044}
1045
DRC19c791c2018-03-08 10:55:20 -06001046DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1047 int width, int pad, int height, int subsamp,
1048 unsigned char **jpegBuf,
1049 unsigned long *jpegSize, int jpegQual,
1050 int flags)
DRCaecea382014-08-11 18:05:41 +00001051{
DRC19c791c2018-03-08 10:55:20 -06001052 const unsigned char *srcPlanes[3];
1053 int pw0, ph0, strides[3], retval = -1;
1054 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +00001055
DRC19c791c2018-03-08 10:55:20 -06001056 if (!this) _throwg("tjCompressFromYUV(): Invalid handle");
1057 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001058
DRC19c791c2018-03-08 10:55:20 -06001059 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1060 subsamp >= NUMSUBOPT)
1061 _throw("tjCompressFromYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001062
DRC19c791c2018-03-08 10:55:20 -06001063 pw0 = tjPlaneWidth(0, width, subsamp);
1064 ph0 = tjPlaneHeight(0, height, subsamp);
1065 srcPlanes[0] = srcBuf;
1066 strides[0] = PAD(pw0, pad);
1067 if (subsamp == TJSAMP_GRAY) {
1068 strides[1] = strides[2] = 0;
1069 srcPlanes[1] = srcPlanes[2] = NULL;
1070 } else {
1071 int pw1 = tjPlaneWidth(1, width, subsamp);
1072 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001073
DRC19c791c2018-03-08 10:55:20 -06001074 strides[1] = strides[2] = PAD(pw1, pad);
1075 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1076 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1077 }
DRCaecea382014-08-11 18:05:41 +00001078
DRC19c791c2018-03-08 10:55:20 -06001079 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1080 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1081
1082bailout:
1083 return retval;
DRCaecea382014-08-11 18:05:41 +00001084}
1085
DRC910a3572013-10-30 23:02:57 +00001086
DRC9b28def2011-05-21 14:37:15 +00001087/* Decompressor */
DRC2e7b76b2009-04-03 12:04:24 +00001088
DRC9b28def2011-05-21 14:37:15 +00001089static tjhandle _tjInitDecompress(tjinstance *this)
DRC2e7b76b2009-04-03 12:04:24 +00001090{
DRC19c791c2018-03-08 10:55:20 -06001091 static unsigned char buffer[1];
DRC2e7b76b2009-04-03 12:04:24 +00001092
DRC19c791c2018-03-08 10:55:20 -06001093 /* This is also straight out of example.txt */
1094 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1095 this->jerr.pub.error_exit = my_error_exit;
1096 this->jerr.pub.output_message = my_output_message;
1097 this->jerr.emit_message = this->jerr.pub.emit_message;
1098 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -05001099 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1100 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1101 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC2e7b76b2009-04-03 12:04:24 +00001102
DRC19c791c2018-03-08 10:55:20 -06001103 if (setjmp(this->jerr.setjmp_buffer)) {
1104 /* If we get here, the JPEG code has signaled an error. */
1105 if (this) free(this);
1106 return NULL;
1107 }
DRC2e7b76b2009-04-03 12:04:24 +00001108
DRC19c791c2018-03-08 10:55:20 -06001109 jpeg_create_decompress(&this->dinfo);
1110 /* Make an initial call so it will create the source manager */
1111 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +00001112
DRC19c791c2018-03-08 10:55:20 -06001113 this->init |= DECOMPRESS;
1114 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +00001115}
1116
DRC19c791c2018-03-08 10:55:20 -06001117DLLEXPORT tjhandle tjInitDecompress(void)
DRC890f1e02011-02-26 22:02:37 +00001118{
DRC19c791c2018-03-08 10:55:20 -06001119 tjinstance *this;
1120
1121 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1122 snprintf(errStr, JMSG_LENGTH_MAX,
1123 "tjInitDecompress(): Memory allocation failure");
1124 return NULL;
1125 }
1126 MEMZERO(this, sizeof(tjinstance));
1127 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1128 return _tjInitDecompress(this);
DRC890f1e02011-02-26 22:02:37 +00001129}
1130
DRC2e7b76b2009-04-03 12:04:24 +00001131
DRC19c791c2018-03-08 10:55:20 -06001132DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1133 const unsigned char *jpegBuf,
1134 unsigned long jpegSize, int *width,
1135 int *height, int *jpegSubsamp,
1136 int *jpegColorspace)
DRC1fe80f82010-12-14 01:21:29 +00001137{
DRC19c791c2018-03-08 10:55:20 -06001138 int retval = 0;
DRC1fe80f82010-12-14 01:21:29 +00001139
DRC19c791c2018-03-08 10:55:20 -06001140 getdinstance(handle);
1141 if ((this->init & DECOMPRESS) == 0)
1142 _throw("tjDecompressHeader3(): Instance has not been initialized for decompression");
DRC1fe80f82010-12-14 01:21:29 +00001143
DRC19c791c2018-03-08 10:55:20 -06001144 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1145 jpegSubsamp == NULL || jpegColorspace == NULL)
1146 _throw("tjDecompressHeader3(): Invalid argument");
DRC1fe80f82010-12-14 01:21:29 +00001147
DRC19c791c2018-03-08 10:55:20 -06001148 if (setjmp(this->jerr.setjmp_buffer)) {
1149 /* If we get here, the JPEG code has signaled an error. */
1150 return -1;
1151 }
DRC1fe80f82010-12-14 01:21:29 +00001152
DRC19c791c2018-03-08 10:55:20 -06001153 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1154 jpeg_read_header(dinfo, TRUE);
DRC1fe80f82010-12-14 01:21:29 +00001155
DRC19c791c2018-03-08 10:55:20 -06001156 *width = dinfo->image_width;
1157 *height = dinfo->image_height;
1158 *jpegSubsamp = getSubsamp(dinfo);
1159 switch (dinfo->jpeg_color_space) {
1160 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1161 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1162 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1163 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1164 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1165 default: *jpegColorspace = -1; break;
1166 }
DRC1fe80f82010-12-14 01:21:29 +00001167
DRC19c791c2018-03-08 10:55:20 -06001168 jpeg_abort_decompress(dinfo);
DRC1fe80f82010-12-14 01:21:29 +00001169
DRC19c791c2018-03-08 10:55:20 -06001170 if (*jpegSubsamp < 0)
1171 _throw("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
1172 if (*jpegColorspace < 0)
1173 _throw("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
1174 if (*width < 1 || *height < 1)
1175 _throw("tjDecompressHeader3(): Invalid data returned in header");
DRC91e86ba2011-02-15 05:24:08 +00001176
DRC19c791c2018-03-08 10:55:20 -06001177bailout:
1178 if (this->jerr.warning) retval = -1;
1179 return retval;
DRC91e86ba2011-02-15 05:24:08 +00001180}
1181
DRC19c791c2018-03-08 10:55:20 -06001182DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1183 unsigned long jpegSize, int *width,
1184 int *height, int *jpegSubsamp)
DRCcd7c3e62013-08-23 02:49:25 +00001185{
DRC19c791c2018-03-08 10:55:20 -06001186 int jpegColorspace;
1187
1188 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1189 jpegSubsamp, &jpegColorspace);
DRCcd7c3e62013-08-23 02:49:25 +00001190}
1191
DRC19c791c2018-03-08 10:55:20 -06001192DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1193 unsigned long jpegSize, int *width,
1194 int *height)
DRC91e86ba2011-02-15 05:24:08 +00001195{
DRC19c791c2018-03-08 10:55:20 -06001196 int jpegSubsamp;
1197
1198 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1199 &jpegSubsamp);
DRC1fe80f82010-12-14 01:21:29 +00001200}
1201
1202
DRC19c791c2018-03-08 10:55:20 -06001203DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
DRCb28fc572011-02-22 06:41:29 +00001204{
DRC19c791c2018-03-08 10:55:20 -06001205 if (numscalingfactors == NULL) {
1206 snprintf(errStr, JMSG_LENGTH_MAX,
1207 "tjGetScalingFactors(): Invalid argument");
1208 return NULL;
1209 }
DRCb28fc572011-02-22 06:41:29 +00001210
DRC19c791c2018-03-08 10:55:20 -06001211 *numscalingfactors = NUMSF;
1212 return (tjscalingfactor *)sf;
DRCb28fc572011-02-22 06:41:29 +00001213}
1214
1215
DRC19c791c2018-03-08 10:55:20 -06001216DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1217 unsigned long jpegSize, unsigned char *dstBuf,
1218 int width, int pitch, int height, int pixelFormat,
1219 int flags)
DRC2e7b76b2009-04-03 12:04:24 +00001220{
DRC19c791c2018-03-08 10:55:20 -06001221 JSAMPROW *row_pointer = NULL;
1222 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
DRC2e7b76b2009-04-03 12:04:24 +00001223
DRC19c791c2018-03-08 10:55:20 -06001224 getdinstance(handle);
1225 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1226 if ((this->init & DECOMPRESS) == 0)
1227 _throw("tjDecompress2(): Instance has not been initialized for decompression");
DRC9b28def2011-05-21 14:37:15 +00001228
DRC19c791c2018-03-08 10:55:20 -06001229 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1230 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1231 _throw("tjDecompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001232
DRCbd96b302018-03-17 00:06:10 -05001233#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001234 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1235 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1236 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001237#endif
DRC9b28def2011-05-21 14:37:15 +00001238
DRC19c791c2018-03-08 10:55:20 -06001239 if (setjmp(this->jerr.setjmp_buffer)) {
1240 /* If we get here, the JPEG code has signaled an error. */
1241 retval = -1; goto bailout;
1242 }
DRC9b28def2011-05-21 14:37:15 +00001243
DRC19c791c2018-03-08 10:55:20 -06001244 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1245 jpeg_read_header(dinfo, TRUE);
1246 this->dinfo.out_color_space = pf2cs[pixelFormat];
1247 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1248 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
DRC9b28def2011-05-21 14:37:15 +00001249
DRC19c791c2018-03-08 10:55:20 -06001250 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1251 if (width == 0) width = jpegwidth;
1252 if (height == 0) height = jpegheight;
1253 for (i = 0; i < NUMSF; i++) {
1254 scaledw = TJSCALED(jpegwidth, sf[i]);
1255 scaledh = TJSCALED(jpegheight, sf[i]);
1256 if (scaledw <= width && scaledh <= height)
1257 break;
1258 }
1259 if (i >= NUMSF)
1260 _throw("tjDecompress2(): Could not scale down to desired image dimensions");
1261 width = scaledw; height = scaledh;
1262 dinfo->scale_num = sf[i].num;
1263 dinfo->scale_denom = sf[i].denom;
DRC9b28def2011-05-21 14:37:15 +00001264
DRC19c791c2018-03-08 10:55:20 -06001265 jpeg_start_decompress(dinfo);
1266 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRCafc06922012-03-23 19:47:57 +00001267
DRC19c791c2018-03-08 10:55:20 -06001268 if ((row_pointer =
1269 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
1270 _throw("tjDecompress2(): Memory allocation failure");
1271 if (setjmp(this->jerr.setjmp_buffer)) {
1272 /* If we get here, the JPEG code has signaled an error. */
1273 retval = -1; goto bailout;
1274 }
1275 for (i = 0; i < (int)dinfo->output_height; i++) {
1276 if (flags & TJFLAG_BOTTOMUP)
1277 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * pitch];
1278 else
1279 row_pointer[i] = &dstBuf[i * pitch];
1280 }
1281 while (dinfo->output_scanline < dinfo->output_height)
1282 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1283 dinfo->output_height - dinfo->output_scanline);
1284 jpeg_finish_decompress(dinfo);
DRC9b28def2011-05-21 14:37:15 +00001285
DRC19c791c2018-03-08 10:55:20 -06001286bailout:
1287 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1288 if (row_pointer) free(row_pointer);
1289 if (this->jerr.warning) retval = -1;
1290 this->jerr.stopOnWarning = FALSE;
1291 return retval;
DRC9b28def2011-05-21 14:37:15 +00001292}
1293
DRC19c791c2018-03-08 10:55:20 -06001294DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1295 unsigned long jpegSize, unsigned char *dstBuf,
1296 int width, int pitch, int height, int pixelSize,
1297 int flags)
DRC9b28def2011-05-21 14:37:15 +00001298{
DRC19c791c2018-03-08 10:55:20 -06001299 if (flags & TJ_YUV)
1300 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1301 else
1302 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1303 height, getPixelFormat(pixelSize, flags), flags);
DRC9b28def2011-05-21 14:37:15 +00001304}
1305
1306
DRC34dca052014-02-28 09:17:14 +00001307static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
DRC19c791c2018-03-08 10:55:20 -06001308 int pixelFormat, int subsamp, int flags)
DRC34dca052014-02-28 09:17:14 +00001309{
DRC19c791c2018-03-08 10:55:20 -06001310 int i;
DRC895fd6d2014-02-28 09:35:34 +00001311
DRC19c791c2018-03-08 10:55:20 -06001312 dinfo->scale_num = dinfo->scale_denom = 1;
DRC34dca052014-02-28 09:17:14 +00001313
DRC19c791c2018-03-08 10:55:20 -06001314 if (subsamp == TJSAMP_GRAY) {
1315 dinfo->num_components = dinfo->comps_in_scan = 1;
1316 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1317 } else {
1318 dinfo->num_components = dinfo->comps_in_scan = 3;
1319 dinfo->jpeg_color_space = JCS_YCbCr;
1320 }
DRC34dca052014-02-28 09:17:14 +00001321
DRC19c791c2018-03-08 10:55:20 -06001322 dinfo->comp_info = (jpeg_component_info *)
1323 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1324 dinfo->num_components *
1325 sizeof(jpeg_component_info));
DRC34dca052014-02-28 09:17:14 +00001326
DRC19c791c2018-03-08 10:55:20 -06001327 for (i = 0; i < dinfo->num_components; i++) {
1328 jpeg_component_info *compptr = &dinfo->comp_info[i];
DRC34dca052014-02-28 09:17:14 +00001329
DRC19c791c2018-03-08 10:55:20 -06001330 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1331 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1332 compptr->component_index = i;
1333 compptr->component_id = i + 1;
1334 compptr->quant_tbl_no = compptr->dc_tbl_no =
1335 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1336 dinfo->cur_comp_info[i] = compptr;
1337 }
1338 dinfo->data_precision = 8;
1339 for (i = 0; i < 2; i++) {
1340 if (dinfo->quant_tbl_ptrs[i] == NULL)
1341 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1342 }
1343
1344 return 0;
DRC34dca052014-02-28 09:17:14 +00001345}
1346
1347
1348int my_read_markers(j_decompress_ptr dinfo)
1349{
DRC19c791c2018-03-08 10:55:20 -06001350 return JPEG_REACHED_SOS;
DRC34dca052014-02-28 09:17:14 +00001351}
1352
1353void my_reset_marker_reader(j_decompress_ptr dinfo)
1354{
1355}
1356
DRC19c791c2018-03-08 10:55:20 -06001357DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1358 const unsigned char **srcPlanes,
1359 const int *strides, int subsamp,
1360 unsigned char *dstBuf, int width, int pitch,
1361 int height, int pixelFormat, int flags)
DRC34dca052014-02-28 09:17:14 +00001362{
DRC19c791c2018-03-08 10:55:20 -06001363 JSAMPROW *row_pointer = NULL;
1364 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1365 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1366 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1367 JSAMPLE *ptr;
1368 jpeg_component_info *compptr;
1369 int (*old_read_markers) (j_decompress_ptr);
1370 void (*old_reset_marker_reader) (j_decompress_ptr);
DRC34dca052014-02-28 09:17:14 +00001371
DRC19c791c2018-03-08 10:55:20 -06001372 getdinstance(handle);
1373 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRC34dca052014-02-28 09:17:14 +00001374
DRC19c791c2018-03-08 10:55:20 -06001375 for (i = 0; i < MAX_COMPONENTS; i++) {
1376 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1377 }
DRC34dca052014-02-28 09:17:14 +00001378
DRC19c791c2018-03-08 10:55:20 -06001379 if ((this->init & DECOMPRESS) == 0)
1380 _throw("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
DRC34dca052014-02-28 09:17:14 +00001381
DRC19c791c2018-03-08 10:55:20 -06001382 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1383 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1384 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1385 _throw("tjDecodeYUVPlanes(): Invalid argument");
1386 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1387 _throw("tjDecodeYUVPlanes(): Invalid argument");
DRC34dca052014-02-28 09:17:14 +00001388
DRC19c791c2018-03-08 10:55:20 -06001389 if (setjmp(this->jerr.setjmp_buffer)) {
1390 /* If we get here, the JPEG code has signaled an error. */
1391 retval = -1; goto bailout;
1392 }
DRC34dca052014-02-28 09:17:14 +00001393
DRC19c791c2018-03-08 10:55:20 -06001394 if (pixelFormat == TJPF_CMYK)
1395 _throw("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
DRC34dca052014-02-28 09:17:14 +00001396
DRC19c791c2018-03-08 10:55:20 -06001397 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1398 dinfo->image_width = width;
1399 dinfo->image_height = height;
DRC34dca052014-02-28 09:17:14 +00001400
DRCbd96b302018-03-17 00:06:10 -05001401#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001402 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1403 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1404 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001405#endif
DRC34dca052014-02-28 09:17:14 +00001406
DRC19c791c2018-03-08 10:55:20 -06001407 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1408 retval = -1; goto bailout;
1409 }
1410 old_read_markers = dinfo->marker->read_markers;
1411 dinfo->marker->read_markers = my_read_markers;
1412 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1413 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1414 jpeg_read_header(dinfo, TRUE);
1415 dinfo->marker->read_markers = old_read_markers;
1416 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
DRC34dca052014-02-28 09:17:14 +00001417
DRC19c791c2018-03-08 10:55:20 -06001418 this->dinfo.out_color_space = pf2cs[pixelFormat];
1419 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1420 dinfo->do_fancy_upsampling = FALSE;
1421 dinfo->Se = DCTSIZE2 - 1;
1422 jinit_master_decompress(dinfo);
1423 (*dinfo->upsample->start_pass) (dinfo);
DRC34dca052014-02-28 09:17:14 +00001424
DRC19c791c2018-03-08 10:55:20 -06001425 pw0 = PAD(width, dinfo->max_h_samp_factor);
1426 ph0 = PAD(height, dinfo->max_v_samp_factor);
DRC34dca052014-02-28 09:17:14 +00001427
DRC19c791c2018-03-08 10:55:20 -06001428 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRC34dca052014-02-28 09:17:14 +00001429
DRC19c791c2018-03-08 10:55:20 -06001430 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1431 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1432 for (i = 0; i < height; i++) {
1433 if (flags & TJFLAG_BOTTOMUP)
1434 row_pointer[i] = &dstBuf[(height - i - 1) * pitch];
1435 else
1436 row_pointer[i] = &dstBuf[i * pitch];
1437 }
1438 if (height < ph0)
1439 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRC34dca052014-02-28 09:17:14 +00001440
DRC19c791c2018-03-08 10:55:20 -06001441 for (i = 0; i < dinfo->num_components; i++) {
1442 compptr = &dinfo->comp_info[i];
1443 _tmpbuf[i] =
1444 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1445 compptr->v_samp_factor + 32);
1446 if (!_tmpbuf[i])
1447 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1448 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1449 if (!tmpbuf[i])
1450 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1451 for (row = 0; row < compptr->v_samp_factor; row++) {
1452 unsigned char *_tmpbuf_aligned =
1453 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRC34dca052014-02-28 09:17:14 +00001454
DRC19c791c2018-03-08 10:55:20 -06001455 tmpbuf[i][row] =
1456 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1457 }
1458 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1459 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1460 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1461 if (!inbuf[i])
1462 _throw("tjDecodeYUVPlanes(): Memory allocation failure");
1463 ptr = (JSAMPLE *)srcPlanes[i];
1464 for (row = 0; row < ph[i]; row++) {
1465 inbuf[i][row] = ptr;
1466 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1467 }
1468 }
DRCd4c41fe2017-03-18 12:56:36 -05001469
DRC19c791c2018-03-08 10:55:20 -06001470 if (setjmp(this->jerr.setjmp_buffer)) {
1471 /* If we get here, the JPEG code has signaled an error. */
1472 retval = -1; goto bailout;
1473 }
DRC34dca052014-02-28 09:17:14 +00001474
DRC19c791c2018-03-08 10:55:20 -06001475 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1476 JDIMENSION inrow = 0, outrow = 0;
1477
1478 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1479 i++, compptr++)
1480 jcopy_sample_rows(inbuf[i],
1481 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1482 compptr->v_samp_factor, pw[i]);
1483 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1484 dinfo->max_v_samp_factor, &row_pointer[row],
1485 &outrow, dinfo->max_v_samp_factor);
1486 }
1487 jpeg_abort_decompress(dinfo);
1488
1489bailout:
1490 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1491 if (row_pointer) free(row_pointer);
1492 for (i = 0; i < MAX_COMPONENTS; i++) {
1493 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
1494 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
1495 if (inbuf[i] != NULL) free(inbuf[i]);
1496 }
1497 if (this->jerr.warning) retval = -1;
1498 this->jerr.stopOnWarning = FALSE;
1499 return retval;
DRC34dca052014-02-28 09:17:14 +00001500}
1501
DRC19c791c2018-03-08 10:55:20 -06001502DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1503 int pad, int subsamp, unsigned char *dstBuf,
1504 int width, int pitch, int height, int pixelFormat,
1505 int flags)
DRCaecea382014-08-11 18:05:41 +00001506{
DRC19c791c2018-03-08 10:55:20 -06001507 const unsigned char *srcPlanes[3];
1508 int pw0, ph0, strides[3], retval = -1;
1509 tjinstance *this = (tjinstance *)handle;
DRC34dca052014-02-28 09:17:14 +00001510
DRC19c791c2018-03-08 10:55:20 -06001511 if (!this) _throwg("tjDecodeYUV(): Invalid handle");
1512 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001513
DRC19c791c2018-03-08 10:55:20 -06001514 if (srcBuf == NULL || pad < 0 || !isPow2(pad) || subsamp < 0 ||
1515 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
1516 _throw("tjDecodeYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001517
DRC19c791c2018-03-08 10:55:20 -06001518 pw0 = tjPlaneWidth(0, width, subsamp);
1519 ph0 = tjPlaneHeight(0, height, subsamp);
1520 srcPlanes[0] = srcBuf;
1521 strides[0] = PAD(pw0, pad);
1522 if (subsamp == TJSAMP_GRAY) {
1523 strides[1] = strides[2] = 0;
1524 srcPlanes[1] = srcPlanes[2] = NULL;
1525 } else {
1526 int pw1 = tjPlaneWidth(1, width, subsamp);
1527 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001528
DRC19c791c2018-03-08 10:55:20 -06001529 strides[1] = strides[2] = PAD(pw1, pad);
1530 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1531 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1532 }
DRCaecea382014-08-11 18:05:41 +00001533
DRC19c791c2018-03-08 10:55:20 -06001534 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1535 pitch, height, pixelFormat, flags);
1536
1537bailout:
1538 return retval;
DRCaecea382014-08-11 18:05:41 +00001539}
1540
DRC19c791c2018-03-08 10:55:20 -06001541DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1542 const unsigned char *jpegBuf,
1543 unsigned long jpegSize,
1544 unsigned char **dstPlanes, int width,
1545 int *strides, int height, int flags)
DRC9b28def2011-05-21 14:37:15 +00001546{
DRC19c791c2018-03-08 10:55:20 -06001547 int i, sfi, row, retval = 0;
1548 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1549 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1550 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1551 JSAMPLE *_tmpbuf = NULL, *ptr;
1552 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1553 int dctsize;
DRC9b28def2011-05-21 14:37:15 +00001554
DRC19c791c2018-03-08 10:55:20 -06001555 getdinstance(handle);
1556 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +00001557
DRC19c791c2018-03-08 10:55:20 -06001558 for (i = 0; i < MAX_COMPONENTS; i++) {
1559 tmpbuf[i] = NULL; outbuf[i] = NULL;
1560 }
DRC9e17f7d2010-12-10 04:59:13 +00001561
DRC19c791c2018-03-08 10:55:20 -06001562 if ((this->init & DECOMPRESS) == 0)
1563 _throw("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
DRCe2f8e692013-10-30 22:21:06 +00001564
DRC19c791c2018-03-08 10:55:20 -06001565 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1566 width < 0 || height < 0)
1567 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001568
DRCbd96b302018-03-17 00:06:10 -05001569#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001570 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1571 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1572 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001573#endif
DRC0c6a2712010-02-22 08:34:44 +00001574
DRC19c791c2018-03-08 10:55:20 -06001575 if (setjmp(this->jerr.setjmp_buffer)) {
1576 /* If we get here, the JPEG code has signaled an error. */
1577 retval = -1; goto bailout;
1578 }
DRC2e7b76b2009-04-03 12:04:24 +00001579
DRC19c791c2018-03-08 10:55:20 -06001580 if (!this->headerRead) {
1581 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1582 jpeg_read_header(dinfo, TRUE);
1583 }
1584 this->headerRead = 0;
1585 jpegSubsamp = getSubsamp(dinfo);
1586 if (jpegSubsamp < 0)
1587 _throw("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001588
DRC19c791c2018-03-08 10:55:20 -06001589 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1590 _throw("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001591
DRC19c791c2018-03-08 10:55:20 -06001592 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1593 if (width == 0) width = jpegwidth;
1594 if (height == 0) height = jpegheight;
1595 for (i = 0; i < NUMSF; i++) {
1596 scaledw = TJSCALED(jpegwidth, sf[i]);
1597 scaledh = TJSCALED(jpegheight, sf[i]);
1598 if (scaledw <= width && scaledh <= height)
1599 break;
1600 }
1601 if (i >= NUMSF)
1602 _throw("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
1603 if (dinfo->num_components > 3)
1604 _throw("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
DRCcd7c3e62013-08-23 02:49:25 +00001605
DRC19c791c2018-03-08 10:55:20 -06001606 width = scaledw; height = scaledh;
1607 dinfo->scale_num = sf[i].num;
1608 dinfo->scale_denom = sf[i].denom;
1609 sfi = i;
1610 jpeg_calc_output_dimensions(dinfo);
DRCf610d612013-04-26 10:33:29 +00001611
DRC19c791c2018-03-08 10:55:20 -06001612 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
DRC418fe282013-05-07 21:17:35 +00001613
DRC19c791c2018-03-08 10:55:20 -06001614 for (i = 0; i < dinfo->num_components; i++) {
1615 jpeg_component_info *compptr = &dinfo->comp_info[i];
1616 int ih;
DRC9e17f7d2010-12-10 04:59:13 +00001617
DRC19c791c2018-03-08 10:55:20 -06001618 iw[i] = compptr->width_in_blocks * dctsize;
1619 ih = compptr->height_in_blocks * dctsize;
1620 pw[i] = PAD(dinfo->output_width, dinfo->max_h_samp_factor) *
1621 compptr->h_samp_factor / dinfo->max_h_samp_factor;
1622 ph[i] = PAD(dinfo->output_height, dinfo->max_v_samp_factor) *
1623 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1624 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1625 th[i] = compptr->v_samp_factor * dctsize;
1626 tmpbufsize += iw[i] * th[i];
1627 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1628 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1629 ptr = dstPlanes[i];
1630 for (row = 0; row < ph[i]; row++) {
1631 outbuf[i][row] = ptr;
1632 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1633 }
1634 }
1635 if (usetmpbuf) {
1636 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1637 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1638 ptr = _tmpbuf;
1639 for (i = 0; i < dinfo->num_components; i++) {
1640 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1641 _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
1642 for (row = 0; row < th[i]; row++) {
1643 tmpbuf[i][row] = ptr;
1644 ptr += iw[i];
1645 }
1646 }
1647 }
DRCd4c41fe2017-03-18 12:56:36 -05001648
DRC19c791c2018-03-08 10:55:20 -06001649 if (setjmp(this->jerr.setjmp_buffer)) {
1650 /* If we get here, the JPEG code has signaled an error. */
1651 retval = -1; goto bailout;
1652 }
DRC9b28def2011-05-21 14:37:15 +00001653
DRC19c791c2018-03-08 10:55:20 -06001654 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1655 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1656 dinfo->raw_data_out = TRUE;
DRC2e7b76b2009-04-03 12:04:24 +00001657
DRC19c791c2018-03-08 10:55:20 -06001658 jpeg_start_decompress(dinfo);
1659 for (row = 0; row < (int)dinfo->output_height;
1660 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1661 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1662 int crow[MAX_COMPONENTS];
1663
1664 for (i = 0; i < dinfo->num_components; i++) {
1665 jpeg_component_info *compptr = &dinfo->comp_info[i];
1666
1667 if (jpegSubsamp == TJ_420) {
1668 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1669 to be clever and use the IDCT to perform upsampling on the U and V
1670 planes. For instance, if the output image is to be scaled by 1/2
1671 relative to the JPEG image, then the scaling factor and upsampling
1672 effectively cancel each other, so a normal 8x8 IDCT can be used.
1673 However, this is not desirable when using the decompress-to-YUV
1674 functionality in TurboJPEG, since we want to output the U and V
1675 planes in their subsampled form. Thus, we have to override some
1676 internal libjpeg parameters to force it to use the "scaled" IDCT
1677 functions on the U and V planes. */
1678 compptr->_DCT_scaled_size = dctsize;
1679 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1680 sf[sfi].num / sf[sfi].denom *
1681 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1682 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1683 }
1684 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1685 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1686 else yuvptr[i] = &outbuf[i][crow[i]];
1687 }
1688 jpeg_read_raw_data(dinfo, yuvptr,
1689 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1690 if (usetmpbuf) {
1691 int j;
1692
1693 for (i = 0; i < dinfo->num_components; i++) {
1694 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1695 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1696 }
1697 }
1698 }
1699 }
1700 jpeg_finish_decompress(dinfo);
1701
1702bailout:
1703 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1704 for (i = 0; i < MAX_COMPONENTS; i++) {
1705 if (tmpbuf[i]) free(tmpbuf[i]);
1706 if (outbuf[i]) free(outbuf[i]);
1707 }
1708 if (_tmpbuf) free(_tmpbuf);
1709 if (this->jerr.warning) retval = -1;
1710 this->jerr.stopOnWarning = FALSE;
1711 return retval;
DRC2e7b76b2009-04-03 12:04:24 +00001712}
1713
DRC19c791c2018-03-08 10:55:20 -06001714DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1715 unsigned long jpegSize, unsigned char *dstBuf,
1716 int width, int pad, int height, int flags)
DRCaecea382014-08-11 18:05:41 +00001717{
DRC19c791c2018-03-08 10:55:20 -06001718 unsigned char *dstPlanes[3];
1719 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1720 int i, jpegwidth, jpegheight, scaledw, scaledh;
DRCaecea382014-08-11 18:05:41 +00001721
DRC19c791c2018-03-08 10:55:20 -06001722 getdinstance(handle);
1723 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCaecea382014-08-11 18:05:41 +00001724
DRC19c791c2018-03-08 10:55:20 -06001725 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1726 pad < 1 || !isPow2(pad) || height < 0)
1727 _throw("tjDecompressToYUV2(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001728
DRC19c791c2018-03-08 10:55:20 -06001729 if (setjmp(this->jerr.setjmp_buffer)) {
1730 /* If we get here, the JPEG code has signaled an error. */
1731 return -1;
1732 }
DRCdec79952016-04-20 11:27:42 -05001733
DRC19c791c2018-03-08 10:55:20 -06001734 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1735 jpeg_read_header(dinfo, TRUE);
1736 jpegSubsamp = getSubsamp(dinfo);
1737 if (jpegSubsamp < 0)
1738 _throw("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001739
DRC19c791c2018-03-08 10:55:20 -06001740 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1741 if (width == 0) width = jpegwidth;
1742 if (height == 0) height = jpegheight;
DRCaecea382014-08-11 18:05:41 +00001743
DRC19c791c2018-03-08 10:55:20 -06001744 for (i = 0; i < NUMSF; i++) {
1745 scaledw = TJSCALED(jpegwidth, sf[i]);
1746 scaledh = TJSCALED(jpegheight, sf[i]);
1747 if (scaledw <= width && scaledh <= height)
1748 break;
1749 }
1750 if (i >= NUMSF)
1751 _throw("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
DRCaecea382014-08-11 18:05:41 +00001752
DRC19c791c2018-03-08 10:55:20 -06001753 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1754 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1755 dstPlanes[0] = dstBuf;
1756 strides[0] = PAD(pw0, pad);
1757 if (jpegSubsamp == TJSAMP_GRAY) {
1758 strides[1] = strides[2] = 0;
1759 dstPlanes[1] = dstPlanes[2] = NULL;
1760 } else {
1761 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1762 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
DRCaecea382014-08-11 18:05:41 +00001763
DRC19c791c2018-03-08 10:55:20 -06001764 strides[1] = strides[2] = PAD(pw1, pad);
1765 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1766 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1767 }
DRCaecea382014-08-11 18:05:41 +00001768
DRC19c791c2018-03-08 10:55:20 -06001769 this->headerRead = 1;
1770 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1771 strides, height, flags);
DRCaecea382014-08-11 18:05:41 +00001772
DRC19c791c2018-03-08 10:55:20 -06001773bailout:
1774 this->jerr.stopOnWarning = FALSE;
1775 return retval;
DRCaecea382014-08-11 18:05:41 +00001776}
1777
DRC19c791c2018-03-08 10:55:20 -06001778DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1779 unsigned long jpegSize, unsigned char *dstBuf,
1780 int flags)
DRCf610d612013-04-26 10:33:29 +00001781{
DRC19c791c2018-03-08 10:55:20 -06001782 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
DRCf610d612013-04-26 10:33:29 +00001783}
1784
DRC2e7b76b2009-04-03 12:04:24 +00001785
DRC9b28def2011-05-21 14:37:15 +00001786/* Transformer */
DRC890f1e02011-02-26 22:02:37 +00001787
DRC19c791c2018-03-08 10:55:20 -06001788DLLEXPORT tjhandle tjInitTransform(void)
DRC890f1e02011-02-26 22:02:37 +00001789{
DRC19c791c2018-03-08 10:55:20 -06001790 tjinstance *this = NULL;
1791 tjhandle handle = NULL;
1792
1793 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1794 snprintf(errStr, JMSG_LENGTH_MAX,
1795 "tjInitTransform(): Memory allocation failure");
1796 return NULL;
1797 }
1798 MEMZERO(this, sizeof(tjinstance));
1799 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1800 handle = _tjInitCompress(this);
1801 if (!handle) return NULL;
1802 handle = _tjInitDecompress(this);
1803 return handle;
DRC890f1e02011-02-26 22:02:37 +00001804}
1805
1806
DRC19c791c2018-03-08 10:55:20 -06001807DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1808 unsigned long jpegSize, int n,
1809 unsigned char **dstBufs, unsigned long *dstSizes,
1810 tjtransform *t, int flags)
DRC890f1e02011-02-26 22:02:37 +00001811{
DRC19c791c2018-03-08 10:55:20 -06001812 jpeg_transform_info *xinfo = NULL;
1813 jvirt_barray_ptr *srccoefs, *dstcoefs;
1814 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
DRC890f1e02011-02-26 22:02:37 +00001815
DRC19c791c2018-03-08 10:55:20 -06001816 getinstance(handle);
1817 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1818 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
1819 _throw("tjTransform(): Instance has not been initialized for transformation");
DRC890f1e02011-02-26 22:02:37 +00001820
DRC19c791c2018-03-08 10:55:20 -06001821 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1822 dstSizes == NULL || t == NULL || flags < 0)
1823 _throw("tjTransform(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001824
DRCbd96b302018-03-17 00:06:10 -05001825#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001826 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1827 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1828 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001829#endif
DRC890f1e02011-02-26 22:02:37 +00001830
DRC19c791c2018-03-08 10:55:20 -06001831 if ((xinfo =
1832 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
1833 _throw("tjTransform(): Memory allocation failure");
1834 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
DRCd4c41fe2017-03-18 12:56:36 -05001835
DRC19c791c2018-03-08 10:55:20 -06001836 if (setjmp(this->jerr.setjmp_buffer)) {
1837 /* If we get here, the JPEG code has signaled an error. */
1838 retval = -1; goto bailout;
1839 }
DRC890f1e02011-02-26 22:02:37 +00001840
DRC19c791c2018-03-08 10:55:20 -06001841 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
DRC890f1e02011-02-26 22:02:37 +00001842
DRC19c791c2018-03-08 10:55:20 -06001843 for (i = 0; i < n; i++) {
1844 xinfo[i].transform = xformtypes[t[i].op];
1845 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1846 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1847 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1848 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1849 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1850 else xinfo[i].slow_hflip = 0;
DRC0a325192011-03-02 09:22:41 +00001851
DRC19c791c2018-03-08 10:55:20 -06001852 if (xinfo[i].crop) {
1853 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1854 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1855 if (t[i].r.w != 0) {
1856 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1857 } else
1858 xinfo[i].crop_width = JCROP_UNSET;
1859 if (t[i].r.h != 0) {
1860 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1861 } else
1862 xinfo[i].crop_height = JCROP_UNSET;
1863 }
1864 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1865 }
DRC890f1e02011-02-26 22:02:37 +00001866
DRC19c791c2018-03-08 10:55:20 -06001867 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1868 jpeg_read_header(dinfo, TRUE);
1869 jpegSubsamp = getSubsamp(dinfo);
1870 if (jpegSubsamp < 0)
1871 _throw("tjTransform(): Could not determine subsampling type for JPEG image");
DRC890f1e02011-02-26 22:02:37 +00001872
DRC19c791c2018-03-08 10:55:20 -06001873 for (i = 0; i < n; i++) {
1874 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
1875 _throw("tjTransform(): Transform is not perfect");
DRC890f1e02011-02-26 22:02:37 +00001876
DRC19c791c2018-03-08 10:55:20 -06001877 if (xinfo[i].crop) {
1878 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1879 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
1880 snprintf(errStr, JMSG_LENGTH_MAX,
1881 "To crop this JPEG image, x must be a multiple of %d\n"
1882 "and y must be a multiple of %d.\n",
1883 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
1884 retval = -1; goto bailout;
1885 }
1886 }
1887 }
DRC890f1e02011-02-26 22:02:37 +00001888
DRC19c791c2018-03-08 10:55:20 -06001889 srccoefs = jpeg_read_coefficients(dinfo);
DRC890f1e02011-02-26 22:02:37 +00001890
DRC19c791c2018-03-08 10:55:20 -06001891 for (i = 0; i < n; i++) {
1892 int w, h, alloc = 1;
DRC0a325192011-03-02 09:22:41 +00001893
DRC19c791c2018-03-08 10:55:20 -06001894 if (!xinfo[i].crop) {
1895 w = dinfo->image_width; h = dinfo->image_height;
1896 } else {
1897 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1898 }
1899 if (flags & TJFLAG_NOREALLOC) {
1900 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1901 }
1902 if (!(t[i].options & TJXOPT_NOOUTPUT))
1903 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1904 jpeg_copy_critical_parameters(dinfo, cinfo);
1905 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1906 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1907 jpeg_simple_progression(cinfo);
1908 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1909 jpeg_write_coefficients(cinfo, dstcoefs);
1910 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1911 JCOPYOPT_NONE : JCOPYOPT_ALL);
1912 } else
1913 jinit_c_master_control(cinfo, TRUE);
1914 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1915 if (t[i].customFilter) {
1916 int ci, y;
1917 JDIMENSION by;
DRC890f1e02011-02-26 22:02:37 +00001918
DRC19c791c2018-03-08 10:55:20 -06001919 for (ci = 0; ci < cinfo->num_components; ci++) {
1920 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1921 tjregion arrayRegion = {
1922 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1923 };
1924 tjregion planeRegion = {
1925 0, 0, compptr->width_in_blocks * DCTSIZE,
1926 compptr->height_in_blocks * DCTSIZE
1927 };
1928
1929 for (by = 0; by < compptr->height_in_blocks;
1930 by += compptr->v_samp_factor) {
1931 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1932 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1933 TRUE);
1934
1935 for (y = 0; y < compptr->v_samp_factor; y++) {
1936 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1937 i, &t[i]) == -1)
1938 _throw("tjTransform(): Error in custom filter");
1939 arrayRegion.y += DCTSIZE;
1940 }
1941 }
1942 }
1943 }
1944 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1945 }
1946
1947 jpeg_finish_decompress(dinfo);
1948
1949bailout:
1950 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1951 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1952 if (xinfo) free(xinfo);
1953 if (this->jerr.warning) retval = -1;
1954 this->jerr.stopOnWarning = FALSE;
1955 return retval;
DRC890f1e02011-02-26 22:02:37 +00001956}
DRCaa745902017-11-16 18:09:07 -06001957
1958
DRC19c791c2018-03-08 10:55:20 -06001959DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1960 int align, int *height, int *pixelFormat,
1961 int flags)
DRCaa745902017-11-16 18:09:07 -06001962{
DRC19c791c2018-03-08 10:55:20 -06001963 int retval = 0, tempc, pitch;
1964 tjhandle handle = NULL;
1965 tjinstance *this;
1966 j_compress_ptr cinfo = NULL;
1967 cjpeg_source_ptr src;
1968 unsigned char *dstBuf = NULL;
1969 FILE *file = NULL;
1970 boolean invert;
DRCaa745902017-11-16 18:09:07 -06001971
DRC19c791c2018-03-08 10:55:20 -06001972 if (!filename || !width || align < 1 || !height || !pixelFormat ||
1973 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
1974 _throwg("tjLoadImage(): Invalid argument");
1975 if ((align & (align - 1)) != 0)
1976 _throwg("tjLoadImage(): Alignment must be a power of 2");
DRCaa745902017-11-16 18:09:07 -06001977
DRC19c791c2018-03-08 10:55:20 -06001978 if ((handle = tjInitCompress()) == NULL) return NULL;
1979 this = (tjinstance *)handle;
1980 cinfo = &this->cinfo;
DRCaa745902017-11-16 18:09:07 -06001981
DRC19c791c2018-03-08 10:55:20 -06001982 if ((file = fopen(filename, "rb")) == NULL)
1983 _throwunix("tjLoadImage(): Cannot open input file");
DRCaa745902017-11-16 18:09:07 -06001984
DRC19c791c2018-03-08 10:55:20 -06001985 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
1986 _throwunix("tjLoadImage(): Could not read input file")
1987 else if (tempc == EOF)
1988 _throwg("tjLoadImage(): Input file contains no data");
DRCaa745902017-11-16 18:09:07 -06001989
DRC19c791c2018-03-08 10:55:20 -06001990 if (setjmp(this->jerr.setjmp_buffer)) {
1991 /* If we get here, the JPEG code has signaled an error. */
1992 retval = -1; goto bailout;
1993 }
DRCaa745902017-11-16 18:09:07 -06001994
DRC19c791c2018-03-08 10:55:20 -06001995 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
1996 else cinfo->in_color_space = pf2cs[*pixelFormat];
1997 if (tempc == 'B') {
1998 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
1999 _throwg("tjLoadImage(): Could not initialize bitmap loader");
2000 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2001 } else if (tempc == 'P') {
2002 if ((src = jinit_read_ppm(cinfo)) == NULL)
2003 _throwg("tjLoadImage(): Could not initialize bitmap loader");
2004 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2005 } else
2006 _throwg("tjLoadImage(): Unsupported file type");
DRCaa745902017-11-16 18:09:07 -06002007
DRC19c791c2018-03-08 10:55:20 -06002008 src->input_file = file;
2009 (*src->start_input) (cinfo, src);
2010 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
DRCaa745902017-11-16 18:09:07 -06002011
DRC19c791c2018-03-08 10:55:20 -06002012 *width = cinfo->image_width; *height = cinfo->image_height;
2013 *pixelFormat = cs2pf[cinfo->in_color_space];
DRCaa745902017-11-16 18:09:07 -06002014
DRC19c791c2018-03-08 10:55:20 -06002015 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
2016 if ((dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2017 _throwg("tjLoadImage(): Memory allocation failure");
DRCaa745902017-11-16 18:09:07 -06002018
DRC19c791c2018-03-08 10:55:20 -06002019 if (setjmp(this->jerr.setjmp_buffer)) {
2020 /* If we get here, the JPEG code has signaled an error. */
2021 retval = -1; goto bailout;
2022 }
DRCaa745902017-11-16 18:09:07 -06002023
DRC19c791c2018-03-08 10:55:20 -06002024 while (cinfo->next_scanline < cinfo->image_height) {
2025 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
DRCaa745902017-11-16 18:09:07 -06002026
DRC19c791c2018-03-08 10:55:20 -06002027 for (i = 0; i < nlines; i++) {
2028 unsigned char *dstptr;
2029 int row;
DRCaa745902017-11-16 18:09:07 -06002030
DRC19c791c2018-03-08 10:55:20 -06002031 row = cinfo->next_scanline + i;
2032 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2033 else dstptr = &dstBuf[row * pitch];
2034 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2035 }
2036 cinfo->next_scanline += nlines;
2037 }
2038
2039 (*src->finish_input) (cinfo, src);
2040
2041bailout:
2042 if (handle) tjDestroy(handle);
2043 if (file) fclose(file);
2044 if (retval < 0 && dstBuf) { free(dstBuf); dstBuf = NULL; }
2045 return dstBuf;
DRCaa745902017-11-16 18:09:07 -06002046}
2047
2048
DRC19c791c2018-03-08 10:55:20 -06002049DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2050 int width, int pitch, int height, int pixelFormat,
2051 int flags)
DRCaa745902017-11-16 18:09:07 -06002052{
DRC19c791c2018-03-08 10:55:20 -06002053 int retval = 0;
2054 tjhandle handle = NULL;
2055 tjinstance *this;
2056 j_decompress_ptr dinfo = NULL;
2057 djpeg_dest_ptr dst;
2058 FILE *file = NULL;
2059 char *ptr = NULL;
2060 boolean invert;
DRCaa745902017-11-16 18:09:07 -06002061
DRC19c791c2018-03-08 10:55:20 -06002062 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2063 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2064 _throwg("tjSaveImage(): Invalid argument");
DRCaa745902017-11-16 18:09:07 -06002065
DRC19c791c2018-03-08 10:55:20 -06002066 if ((handle = tjInitDecompress()) == NULL)
2067 return -1;
2068 this = (tjinstance *)handle;
2069 dinfo = &this->dinfo;
DRCaa745902017-11-16 18:09:07 -06002070
DRC19c791c2018-03-08 10:55:20 -06002071 if ((file = fopen(filename, "wb")) == NULL)
2072 _throwunix("tjSaveImage(): Cannot open output file");
DRCaa745902017-11-16 18:09:07 -06002073
DRC19c791c2018-03-08 10:55:20 -06002074 if (setjmp(this->jerr.setjmp_buffer)) {
2075 /* If we get here, the JPEG code has signaled an error. */
2076 retval = -1; goto bailout;
2077 }
DRCaa745902017-11-16 18:09:07 -06002078
DRC19c791c2018-03-08 10:55:20 -06002079 this->dinfo.out_color_space = pf2cs[pixelFormat];
2080 dinfo->image_width = width; dinfo->image_height = height;
2081 dinfo->global_state = DSTATE_READY;
2082 dinfo->scale_num = dinfo->scale_denom = 1;
DRCaa745902017-11-16 18:09:07 -06002083
DRC19c791c2018-03-08 10:55:20 -06002084 ptr = strrchr(filename, '.');
2085 if (ptr && !strcasecmp(ptr, ".bmp")) {
2086 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
2087 _throwg("tjSaveImage(): Could not initialize bitmap writer");
2088 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2089 } else {
2090 if ((dst = jinit_write_ppm(dinfo)) == NULL)
2091 _throwg("tjSaveImage(): Could not initialize PPM writer");
2092 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2093 }
DRCaa745902017-11-16 18:09:07 -06002094
DRC19c791c2018-03-08 10:55:20 -06002095 dst->output_file = file;
2096 (*dst->start_output) (dinfo, dst);
2097 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
DRCaa745902017-11-16 18:09:07 -06002098
DRC19c791c2018-03-08 10:55:20 -06002099 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRCaa745902017-11-16 18:09:07 -06002100
DRC19c791c2018-03-08 10:55:20 -06002101 while (dinfo->output_scanline < dinfo->output_height) {
2102 unsigned char *rowptr;
DRCaa745902017-11-16 18:09:07 -06002103
DRC19c791c2018-03-08 10:55:20 -06002104 if (invert)
2105 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2106 else
2107 rowptr = &buffer[dinfo->output_scanline * pitch];
2108 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2109 (*dst->put_pixel_rows) (dinfo, dst, 1);
2110 dinfo->output_scanline++;
2111 }
DRCaa745902017-11-16 18:09:07 -06002112
DRC19c791c2018-03-08 10:55:20 -06002113 (*dst->finish_output) (dinfo, dst);
2114
2115bailout:
2116 if (handle) tjDestroy(handle);
2117 if (file) fclose(file);
2118 return retval;
DRCaa745902017-11-16 18:09:07 -06002119}