blob: 1f99786fd6e972ea447a68153e935f9608ed06e0 [file] [log] [blame]
DRC9b28def2011-05-21 14:37:15 +00001/*
DRC3d9c64e2019-01-01 18:57:36 -06002 * Copyright (C)2009-2019 D. R. Commander. All Rights Reserved.
DRC2e7b76b2009-04-03 12:04:24 +00003 *
DRC9b28def2011-05-21 14:37:15 +00004 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
DRC2e7b76b2009-04-03 12:04:24 +00006 *
DRC9b28def2011-05-21 14:37:15 +00007 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of the libjpeg-turbo Project nor the names of its
13 * contributors may be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
DRC2e7b76b2009-04-03 12:04:24 +000027 */
28
DRCbdfcb742013-01-22 13:56:34 +000029/* TurboJPEG/LJT: this implements the TurboJPEG API using libjpeg or
30 libjpeg-turbo */
DRC2e7b76b2009-04-03 12:04:24 +000031
32#include <stdio.h>
33#include <stdlib.h>
DRC0713c1b2014-08-22 13:43:33 +000034#include <ctype.h>
DRC296c71b2011-05-25 04:12:52 +000035#include <jinclude.h>
DRCfbb67472010-11-24 04:02:37 +000036#define JPEG_INTERNALS
DRC2e7b76b2009-04-03 12:04:24 +000037#include <jpeglib.h>
38#include <jerror.h>
39#include <setjmp.h>
DRCaa745902017-11-16 18:09:07 -060040#include <errno.h>
DRC2e7b76b2009-04-03 12:04:24 +000041#include "./turbojpeg.h"
DRC29e453f2018-03-16 14:09:53 -050042#include "./tjutil.h"
DRC890f1e02011-02-26 22:02:37 +000043#include "transupp.h"
DRC418fe282013-05-07 21:17:35 +000044#include "./jpegcomp.h"
DRCaa745902017-11-16 18:09:07 -060045#include "./cdjpeg.h"
DRC2a2e4512011-01-05 22:33:24 +000046
DRC19c791c2018-03-08 10:55:20 -060047extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
48 boolean);
DRC6fa14b32015-08-13 20:06:03 -050049extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
DRC19c791c2018-03-08 10:55:20 -060050 unsigned long);
DRC9b28def2011-05-21 14:37:15 +000051
DRC293263c2018-03-17 15:14:35 -050052#define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
DRCd8b95102019-04-12 07:49:35 -050053#define IS_POW2(x) (((x) & (x - 1)) == 0)
DRC2e7b76b2009-04-03 12:04:24 +000054
55
DRC8c40ac82017-11-16 18:46:01 -060056/* Error handling (based on example in example.txt) */
DRC2e7b76b2009-04-03 12:04:24 +000057
DRC19c791c2018-03-08 10:55:20 -060058static char errStr[JMSG_LENGTH_MAX] = "No error";
DRC2e7b76b2009-04-03 12:04:24 +000059
DRC19c791c2018-03-08 10:55:20 -060060struct my_error_mgr {
61 struct jpeg_error_mgr pub;
62 jmp_buf setjmp_buffer;
63 void (*emit_message) (j_common_ptr, int);
64 boolean warning, stopOnWarning;
DRC9b28def2011-05-21 14:37:15 +000065};
66typedef struct my_error_mgr *my_error_ptr;
DRC2e7b76b2009-04-03 12:04:24 +000067
DRC696e7542018-06-12 18:49:37 -050068#define JMESSAGE(code, string) string,
69static const char *turbojpeg_message_table[] = {
70#include "cderror.h"
71 NULL
72};
73
DRC2e7b76b2009-04-03 12:04:24 +000074static void my_error_exit(j_common_ptr cinfo)
75{
DRC19c791c2018-03-08 10:55:20 -060076 my_error_ptr myerr = (my_error_ptr)cinfo->err;
77
78 (*cinfo->err->output_message) (cinfo);
79 longjmp(myerr->setjmp_buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +000080}
81
DRC9b28def2011-05-21 14:37:15 +000082/* Based on output_message() in jerror.c */
83
DRC2e7b76b2009-04-03 12:04:24 +000084static void my_output_message(j_common_ptr cinfo)
85{
DRC19c791c2018-03-08 10:55:20 -060086 (*cinfo->err->format_message) (cinfo, errStr);
DRC2e7b76b2009-04-03 12:04:24 +000087}
88
DRC1f79c7c2015-06-01 19:22:41 +000089static void my_emit_message(j_common_ptr cinfo, int msg_level)
90{
DRC19c791c2018-03-08 10:55:20 -060091 my_error_ptr myerr = (my_error_ptr)cinfo->err;
92
93 myerr->emit_message(cinfo, msg_level);
94 if (msg_level < 0) {
95 myerr->warning = TRUE;
96 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
97 }
DRC1f79c7c2015-06-01 19:22:41 +000098}
99
DRC2e7b76b2009-04-03 12:04:24 +0000100
DRC9b28def2011-05-21 14:37:15 +0000101/* Global structures, macros, etc. */
DRC2e7b76b2009-04-03 12:04:24 +0000102
DRC19c791c2018-03-08 10:55:20 -0600103enum { COMPRESS = 1, DECOMPRESS = 2 };
DRC9b28def2011-05-21 14:37:15 +0000104
DRC19c791c2018-03-08 10:55:20 -0600105typedef struct _tjinstance {
106 struct jpeg_compress_struct cinfo;
107 struct jpeg_decompress_struct dinfo;
108 struct my_error_mgr jerr;
109 int init, headerRead;
110 char errStr[JMSG_LENGTH_MAX];
111 boolean isInstanceError;
DRC9b28def2011-05-21 14:37:15 +0000112} tjinstance;
DRC2e7b76b2009-04-03 12:04:24 +0000113
DRC19c791c2018-03-08 10:55:20 -0600114static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
DRC9b28def2011-05-21 14:37:15 +0000115
DRC19c791c2018-03-08 10:55:20 -0600116static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
117 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
118 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
DRC890f1e02011-02-26 22:02:37 +0000119};
DRC9b28def2011-05-21 14:37:15 +0000120
DRC293263c2018-03-17 15:14:35 -0500121#define NUMSF 16
DRC19c791c2018-03-08 10:55:20 -0600122static const tjscalingfactor sf[NUMSF] = {
123 { 2, 1 },
124 { 15, 8 },
125 { 7, 4 },
126 { 13, 8 },
127 { 3, 2 },
128 { 11, 8 },
129 { 5, 4 },
130 { 9, 8 },
131 { 1, 1 },
132 { 7, 8 },
133 { 3, 4 },
134 { 5, 8 },
135 { 1, 2 },
136 { 3, 8 },
137 { 1, 4 },
138 { 1, 8 }
DRC109a5782011-03-01 09:53:07 +0000139};
DRC2e7b76b2009-04-03 12:04:24 +0000140
DRC19c791c2018-03-08 10:55:20 -0600141static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
142 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
143 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
144 JCS_EXT_ARGB, JCS_CMYK
DRCaa745902017-11-16 18:09:07 -0600145};
146
DRC19c791c2018-03-08 10:55:20 -0600147static int cs2pf[JPEG_NUMCS] = {
148 TJPF_UNKNOWN, TJPF_GRAY,
DRCaa745902017-11-16 18:09:07 -0600149#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600150 TJPF_RGB,
DRCaa745902017-11-16 18:09:07 -0600151#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
DRC19c791c2018-03-08 10:55:20 -0600152 TJPF_BGR,
DRCaa745902017-11-16 18:09:07 -0600153#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600154 TJPF_RGBX,
DRCaa745902017-11-16 18:09:07 -0600155#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600156 TJPF_BGRX,
DRCaa745902017-11-16 18:09:07 -0600157#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600158 TJPF_XBGR,
DRCaa745902017-11-16 18:09:07 -0600159#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
DRC19c791c2018-03-08 10:55:20 -0600160 TJPF_XRGB,
DRCaa745902017-11-16 18:09:07 -0600161#endif
DRC19c791c2018-03-08 10:55:20 -0600162 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
163 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
164 TJPF_UNKNOWN
DRCaa745902017-11-16 18:09:07 -0600165};
166
DRCd8b95102019-04-12 07:49:35 -0500167#define THROWG(m) { \
DRC19c791c2018-03-08 10:55:20 -0600168 snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
169 retval = -1; goto bailout; \
DRCaa745902017-11-16 18:09:07 -0600170}
DRCd8b95102019-04-12 07:49:35 -0500171#define THROW_UNIX(m) { \
DRC19c791c2018-03-08 10:55:20 -0600172 snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
173 retval = -1; goto bailout; \
174}
DRCd8b95102019-04-12 07:49:35 -0500175#define THROW(m) { \
DRC19c791c2018-03-08 10:55:20 -0600176 snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
DRCd8b95102019-04-12 07:49:35 -0500177 this->isInstanceError = TRUE; THROWG(m) \
DRC19c791c2018-03-08 10:55:20 -0600178}
179
DRCd8b95102019-04-12 07:49:35 -0500180#define GET_INSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600181 tjinstance *this = (tjinstance *)handle; \
182 j_compress_ptr cinfo = NULL; \
183 j_decompress_ptr dinfo = NULL; \
184 \
185 if (!this) { \
186 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
187 return -1; \
188 } \
189 cinfo = &this->cinfo; dinfo = &this->dinfo; \
190 this->jerr.warning = FALSE; \
191 this->isInstanceError = FALSE;
192
DRCd8b95102019-04-12 07:49:35 -0500193#define GET_CINSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600194 tjinstance *this = (tjinstance *)handle; \
195 j_compress_ptr cinfo = NULL; \
196 \
197 if (!this) { \
198 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
199 return -1; \
200 } \
201 cinfo = &this->cinfo; \
202 this->jerr.warning = FALSE; \
203 this->isInstanceError = FALSE;
204
DRCd8b95102019-04-12 07:49:35 -0500205#define GET_DINSTANCE(handle) \
DRC19c791c2018-03-08 10:55:20 -0600206 tjinstance *this = (tjinstance *)handle; \
207 j_decompress_ptr dinfo = NULL; \
208 \
209 if (!this) { \
210 snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
211 return -1; \
212 } \
213 dinfo = &this->dinfo; \
214 this->jerr.warning = FALSE; \
215 this->isInstanceError = FALSE;
DRC2e7b76b2009-04-03 12:04:24 +0000216
DRC9b28def2011-05-21 14:37:15 +0000217static int getPixelFormat(int pixelSize, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000218{
DRC19c791c2018-03-08 10:55:20 -0600219 if (pixelSize == 1) return TJPF_GRAY;
220 if (pixelSize == 3) {
221 if (flags & TJ_BGR) return TJPF_BGR;
222 else return TJPF_RGB;
223 }
224 if (pixelSize == 4) {
225 if (flags & TJ_ALPHAFIRST) {
226 if (flags & TJ_BGR) return TJPF_XBGR;
227 else return TJPF_XRGB;
228 } else {
229 if (flags & TJ_BGR) return TJPF_BGRX;
230 else return TJPF_RGBX;
231 }
232 }
233 return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000234}
235
DRC19c791c2018-03-08 10:55:20 -0600236static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
237 int subsamp, int jpegQual, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000238{
DRC19c791c2018-03-08 10:55:20 -0600239 int retval = 0;
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 }
DRC479501b2019-01-21 13:57:55 -0600362 /* Handle 4:4:4 images whose sampling factors are specified in
363 non-standard ways. */
364 if (dinfo->comp_info[0].h_samp_factor *
365 dinfo->comp_info[0].v_samp_factor <=
366 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
367 int match = 0;
368 for (k = 1; k < dinfo->num_components; k++) {
369 if (dinfo->comp_info[i].h_samp_factor ==
370 dinfo->comp_info[0].h_samp_factor &&
371 dinfo->comp_info[i].v_samp_factor ==
372 dinfo->comp_info[0].v_samp_factor)
373 match++;
374 if (match == dinfo->num_components - 1) {
375 retval = i; break;
376 }
377 }
378 }
DRC19c791c2018-03-08 10:55:20 -0600379 }
380 }
381 return retval;
DRC9b49f0e2011-07-12 03:17:23 +0000382}
383
384
DRC9b28def2011-05-21 14:37:15 +0000385/* General API functions */
386
DRC19c791c2018-03-08 10:55:20 -0600387DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
DRCb9ab64d2017-05-11 21:02:29 -0500388{
DRC19c791c2018-03-08 10:55:20 -0600389 tjinstance *this = (tjinstance *)handle;
390
391 if (this && this->isInstanceError) {
392 this->isInstanceError = FALSE;
393 return this->errStr;
394 } else
395 return errStr;
DRCb9ab64d2017-05-11 21:02:29 -0500396}
397
398
DRC19c791c2018-03-08 10:55:20 -0600399DLLEXPORT char *tjGetErrorStr(void)
DRC9b28def2011-05-21 14:37:15 +0000400{
DRC19c791c2018-03-08 10:55:20 -0600401 return errStr;
DRC9b28def2011-05-21 14:37:15 +0000402}
403
404
DRC19c791c2018-03-08 10:55:20 -0600405DLLEXPORT int tjGetErrorCode(tjhandle handle)
DRCd4092f62017-06-27 10:54:21 -0500406{
DRC19c791c2018-03-08 10:55:20 -0600407 tjinstance *this = (tjinstance *)handle;
408
409 if (this && this->jerr.warning) return TJERR_WARNING;
410 else return TJERR_FATAL;
DRCd4092f62017-06-27 10:54:21 -0500411}
412
413
DRC19c791c2018-03-08 10:55:20 -0600414DLLEXPORT int tjDestroy(tjhandle handle)
DRC9b28def2011-05-21 14:37:15 +0000415{
DRCd8b95102019-04-12 07:49:35 -0500416 GET_INSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -0600417
418 if (setjmp(this->jerr.setjmp_buffer)) return -1;
419 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
420 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
421 free(this);
422 return 0;
DRC9b28def2011-05-21 14:37:15 +0000423}
424
425
DRC6b76f752011-05-24 16:52:47 +0000426/* These are exposed mainly because Windows can't malloc() and free() across
427 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
428 with turbojpeg.dll for compatibility reasons. However, these functions
429 can potentially be used for other purposes by different implementations. */
430
DRC19c791c2018-03-08 10:55:20 -0600431DLLEXPORT void tjFree(unsigned char *buf)
DRC6b76f752011-05-24 16:52:47 +0000432{
DRC19c791c2018-03-08 10:55:20 -0600433 if (buf) free(buf);
DRC6b76f752011-05-24 16:52:47 +0000434}
435
436
DRC19c791c2018-03-08 10:55:20 -0600437DLLEXPORT unsigned char *tjAlloc(int bytes)
DRC6b76f752011-05-24 16:52:47 +0000438{
DRC19c791c2018-03-08 10:55:20 -0600439 return (unsigned char *)malloc(bytes);
DRC6b76f752011-05-24 16:52:47 +0000440}
441
442
DRC9b28def2011-05-21 14:37:15 +0000443/* Compressor */
444
445static tjhandle _tjInitCompress(tjinstance *this)
446{
DRC19c791c2018-03-08 10:55:20 -0600447 static unsigned char buffer[1];
448 unsigned char *buf = buffer;
449 unsigned long size = 1;
DRC9b28def2011-05-21 14:37:15 +0000450
DRC19c791c2018-03-08 10:55:20 -0600451 /* This is also straight out of example.txt */
452 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
453 this->jerr.pub.error_exit = my_error_exit;
454 this->jerr.pub.output_message = my_output_message;
455 this->jerr.emit_message = this->jerr.pub.emit_message;
456 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -0500457 this->jerr.pub.addon_message_table = turbojpeg_message_table;
458 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
459 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC9b28def2011-05-21 14:37:15 +0000460
DRC19c791c2018-03-08 10:55:20 -0600461 if (setjmp(this->jerr.setjmp_buffer)) {
462 /* If we get here, the JPEG code has signaled an error. */
463 if (this) free(this);
464 return NULL;
465 }
DRC9b28def2011-05-21 14:37:15 +0000466
DRC19c791c2018-03-08 10:55:20 -0600467 jpeg_create_compress(&this->cinfo);
468 /* Make an initial call so it will create the destination manager */
469 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
DRC9b28def2011-05-21 14:37:15 +0000470
DRC19c791c2018-03-08 10:55:20 -0600471 this->init |= COMPRESS;
472 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +0000473}
474
DRC19c791c2018-03-08 10:55:20 -0600475DLLEXPORT tjhandle tjInitCompress(void)
DRC890f1e02011-02-26 22:02:37 +0000476{
DRC19c791c2018-03-08 10:55:20 -0600477 tjinstance *this = NULL;
478
479 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
480 snprintf(errStr, JMSG_LENGTH_MAX,
481 "tjInitCompress(): Memory allocation failure");
482 return NULL;
483 }
484 MEMZERO(this, sizeof(tjinstance));
485 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
486 return _tjInitCompress(this);
DRC890f1e02011-02-26 22:02:37 +0000487}
488
DRC84241602011-02-25 02:08:23 +0000489
DRC19c791c2018-03-08 10:55:20 -0600490DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000491{
DRCd8b95102019-04-12 07:49:35 -0500492 unsigned long long retval = 0;
DRC19c791c2018-03-08 10:55:20 -0600493 int mcuw, mcuh, chromasf;
DRC9b49f0e2011-07-12 03:17:23 +0000494
DRC19c791c2018-03-08 10:55:20 -0600495 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
DRCd8b95102019-04-12 07:49:35 -0500496 THROWG("tjBufSize(): Invalid argument");
DRC9b49f0e2011-07-12 03:17:23 +0000497
DRC19c791c2018-03-08 10:55:20 -0600498 /* This allows for rare corner cases in which a JPEG image can actually be
499 larger than the uncompressed input (we wouldn't mention it if it hadn't
500 happened before.) */
501 mcuw = tjMCUWidth[jpegSubsamp];
502 mcuh = tjMCUHeight[jpegSubsamp];
503 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
DRCd8b95102019-04-12 07:49:35 -0500504 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
505 if (retval > (unsigned long long)((unsigned long)-1))
506 THROWG("tjBufSize(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600507
508bailout:
DRCd8b95102019-04-12 07:49:35 -0500509 return (unsigned long)retval;
DRC9b49f0e2011-07-12 03:17:23 +0000510}
511
DRC19c791c2018-03-08 10:55:20 -0600512DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
DRC2e7b76b2009-04-03 12:04:24 +0000513{
DRCd8b95102019-04-12 07:49:35 -0500514 unsigned long long retval = 0;
DRCf3cf9732011-02-22 00:16:14 +0000515
DRC19c791c2018-03-08 10:55:20 -0600516 if (width < 1 || height < 1)
DRCd8b95102019-04-12 07:49:35 -0500517 THROWG("TJBUFSIZE(): Invalid argument");
DRCf3cf9732011-02-22 00:16:14 +0000518
DRC19c791c2018-03-08 10:55:20 -0600519 /* This allows for rare corner cases in which a JPEG image can actually be
520 larger than the uncompressed input (we wouldn't mention it if it hadn't
521 happened before.) */
DRCd8b95102019-04-12 07:49:35 -0500522 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
523 if (retval > (unsigned long long)((unsigned long)-1))
524 THROWG("TJBUFSIZE(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600525
526bailout:
DRCd8b95102019-04-12 07:49:35 -0500527 return (unsigned long)retval;
DRCf3cf9732011-02-22 00:16:14 +0000528}
529
DRC84241602011-02-25 02:08:23 +0000530
DRC19c791c2018-03-08 10:55:20 -0600531DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
532 int subsamp)
DRCf3cf9732011-02-22 00:16:14 +0000533{
DRCd8b95102019-04-12 07:49:35 -0500534 unsigned long long retval = 0;
535 int nc, i;
DRC40dd3142014-08-17 12:23:49 +0000536
DRC19c791c2018-03-08 10:55:20 -0600537 if (subsamp < 0 || subsamp >= NUMSUBOPT)
DRCd8b95102019-04-12 07:49:35 -0500538 THROWG("tjBufSizeYUV2(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000539
DRC19c791c2018-03-08 10:55:20 -0600540 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
541 for (i = 0; i < nc; i++) {
542 int pw = tjPlaneWidth(i, width, subsamp);
543 int stride = PAD(pw, pad);
544 int ph = tjPlaneHeight(i, height, subsamp);
DRCf3cf9732011-02-22 00:16:14 +0000545
DRC19c791c2018-03-08 10:55:20 -0600546 if (pw < 0 || ph < 0) return -1;
DRCd8b95102019-04-12 07:49:35 -0500547 else retval += (unsigned long long)stride * ph;
DRC19c791c2018-03-08 10:55:20 -0600548 }
DRCd8b95102019-04-12 07:49:35 -0500549 if (retval > (unsigned long long)((unsigned long)-1))
550 THROWG("tjBufSizeYUV2(): Image is too large");
DRC19c791c2018-03-08 10:55:20 -0600551
552bailout:
DRCd8b95102019-04-12 07:49:35 -0500553 return (unsigned long)retval;
DRC2e7b76b2009-04-03 12:04:24 +0000554}
555
DRC19c791c2018-03-08 10:55:20 -0600556DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
DRCf610d612013-04-26 10:33:29 +0000557{
DRC19c791c2018-03-08 10:55:20 -0600558 return tjBufSizeYUV2(width, 4, height, subsamp);
DRCf610d612013-04-26 10:33:29 +0000559}
DRC84241602011-02-25 02:08:23 +0000560
DRC19c791c2018-03-08 10:55:20 -0600561DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
DRC9b49f0e2011-07-12 03:17:23 +0000562{
DRC19c791c2018-03-08 10:55:20 -0600563 return tjBufSizeYUV(width, height, subsamp);
DRC9b49f0e2011-07-12 03:17:23 +0000564}
565
566
DRC40dd3142014-08-17 12:23:49 +0000567DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
568{
DRC19c791c2018-03-08 10:55:20 -0600569 int pw, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000570
DRC19c791c2018-03-08 10:55:20 -0600571 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
DRCd8b95102019-04-12 07:49:35 -0500572 THROWG("tjPlaneWidth(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600573 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
574 if (componentID < 0 || componentID >= nc)
DRCd8b95102019-04-12 07:49:35 -0500575 THROWG("tjPlaneWidth(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000576
DRC19c791c2018-03-08 10:55:20 -0600577 pw = PAD(width, tjMCUWidth[subsamp] / 8);
578 if (componentID == 0)
579 retval = pw;
580 else
581 retval = pw * 8 / tjMCUWidth[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000582
DRC19c791c2018-03-08 10:55:20 -0600583bailout:
584 return retval;
DRC40dd3142014-08-17 12:23:49 +0000585}
586
587
588DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
589{
DRC19c791c2018-03-08 10:55:20 -0600590 int ph, nc, retval = 0;
DRC40dd3142014-08-17 12:23:49 +0000591
DRC19c791c2018-03-08 10:55:20 -0600592 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
DRCd8b95102019-04-12 07:49:35 -0500593 THROWG("tjPlaneHeight(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600594 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
595 if (componentID < 0 || componentID >= nc)
DRCd8b95102019-04-12 07:49:35 -0500596 THROWG("tjPlaneHeight(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000597
DRC19c791c2018-03-08 10:55:20 -0600598 ph = PAD(height, tjMCUHeight[subsamp] / 8);
599 if (componentID == 0)
600 retval = ph;
601 else
602 retval = ph * 8 / tjMCUHeight[subsamp];
DRC40dd3142014-08-17 12:23:49 +0000603
DRC19c791c2018-03-08 10:55:20 -0600604bailout:
605 return retval;
DRC40dd3142014-08-17 12:23:49 +0000606}
607
608
DRC19c791c2018-03-08 10:55:20 -0600609DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
610 int height, int subsamp)
DRC40dd3142014-08-17 12:23:49 +0000611{
DRCd8b95102019-04-12 07:49:35 -0500612 unsigned long long retval = 0;
DRC19c791c2018-03-08 10:55:20 -0600613 int pw, ph;
DRC40dd3142014-08-17 12:23:49 +0000614
DRC19c791c2018-03-08 10:55:20 -0600615 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
DRCd8b95102019-04-12 07:49:35 -0500616 THROWG("tjPlaneSizeYUV(): Invalid argument");
DRC40dd3142014-08-17 12:23:49 +0000617
DRC19c791c2018-03-08 10:55:20 -0600618 pw = tjPlaneWidth(componentID, width, subsamp);
619 ph = tjPlaneHeight(componentID, height, subsamp);
620 if (pw < 0 || ph < 0) return -1;
DRC40dd3142014-08-17 12:23:49 +0000621
DRC19c791c2018-03-08 10:55:20 -0600622 if (stride == 0) stride = pw;
623 else stride = abs(stride);
DRC40dd3142014-08-17 12:23:49 +0000624
DRCd8b95102019-04-12 07:49:35 -0500625 retval = (unsigned long long)stride * (ph - 1) + pw;
626 if (retval > (unsigned long long)((unsigned long)-1))
627 THROWG("tjPlaneSizeYUV(): Image is too large");
DRC40dd3142014-08-17 12:23:49 +0000628
DRC19c791c2018-03-08 10:55:20 -0600629bailout:
DRCd8b95102019-04-12 07:49:35 -0500630 return (unsigned long)retval;
DRC40dd3142014-08-17 12:23:49 +0000631}
632
633
DRC19c791c2018-03-08 10:55:20 -0600634DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
635 int width, int pitch, int height, int pixelFormat,
636 unsigned char **jpegBuf, unsigned long *jpegSize,
637 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000638{
DRC19c791c2018-03-08 10:55:20 -0600639 int i, retval = 0, alloc = 1;
640 JSAMPROW *row_pointer = NULL;
DRC9b28def2011-05-21 14:37:15 +0000641
DRCd8b95102019-04-12 07:49:35 -0500642 GET_CINSTANCE(handle)
DRC19c791c2018-03-08 10:55:20 -0600643 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
644 if ((this->init & COMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -0500645 THROW("tjCompress2(): Instance has not been initialized for compression");
DRC9b28def2011-05-21 14:37:15 +0000646
DRC19c791c2018-03-08 10:55:20 -0600647 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
648 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
649 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
650 jpegQual < 0 || jpegQual > 100)
DRCd8b95102019-04-12 07:49:35 -0500651 THROW("tjCompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +0000652
DRC19c791c2018-03-08 10:55:20 -0600653 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC9b28def2011-05-21 14:37:15 +0000654
DRC19c791c2018-03-08 10:55:20 -0600655 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
DRCd8b95102019-04-12 07:49:35 -0500656 THROW("tjCompress2(): Memory allocation failure");
DRCd4c41fe2017-03-18 12:56:36 -0500657
DRC19c791c2018-03-08 10:55:20 -0600658 if (setjmp(this->jerr.setjmp_buffer)) {
659 /* If we get here, the JPEG code has signaled an error. */
660 retval = -1; goto bailout;
661 }
DRCd4c41fe2017-03-18 12:56:36 -0500662
DRC19c791c2018-03-08 10:55:20 -0600663 cinfo->image_width = width;
664 cinfo->image_height = height;
DRC9b28def2011-05-21 14:37:15 +0000665
DRCbd96b302018-03-17 00:06:10 -0500666#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600667 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
668 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
669 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500670#endif
DRC9b28def2011-05-21 14:37:15 +0000671
DRC19c791c2018-03-08 10:55:20 -0600672 if (flags & TJFLAG_NOREALLOC) {
673 alloc = 0; *jpegSize = tjBufSize(width, height, jpegSubsamp);
674 }
675 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
676 if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
677 return -1;
DRC9b28def2011-05-21 14:37:15 +0000678
DRC19c791c2018-03-08 10:55:20 -0600679 jpeg_start_compress(cinfo, TRUE);
680 for (i = 0; i < height; i++) {
681 if (flags & TJFLAG_BOTTOMUP)
DRCd8b95102019-04-12 07:49:35 -0500682 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600683 else
DRCd8b95102019-04-12 07:49:35 -0500684 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600685 }
686 while (cinfo->next_scanline < cinfo->image_height)
687 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
688 cinfo->image_height - cinfo->next_scanline);
689 jpeg_finish_compress(cinfo);
DRC9b28def2011-05-21 14:37:15 +0000690
DRC19c791c2018-03-08 10:55:20 -0600691bailout:
692 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
693 if (row_pointer) free(row_pointer);
694 if (this->jerr.warning) retval = -1;
695 this->jerr.stopOnWarning = FALSE;
696 return retval;
DRC9b28def2011-05-21 14:37:15 +0000697}
698
DRC19c791c2018-03-08 10:55:20 -0600699DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
700 int pitch, int height, int pixelSize,
701 unsigned char *jpegBuf, unsigned long *jpegSize,
702 int jpegSubsamp, int jpegQual, int flags)
DRC9b28def2011-05-21 14:37:15 +0000703{
DRC19c791c2018-03-08 10:55:20 -0600704 int retval = 0;
705 unsigned long size;
706
707 if (flags & TJ_YUV) {
708 size = tjBufSizeYUV(width, height, jpegSubsamp);
709 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
710 getPixelFormat(pixelSize, flags), jpegBuf,
711 jpegSubsamp, flags);
712 } else {
713 retval = tjCompress2(handle, srcBuf, width, pitch, height,
714 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
715 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
716 }
717 *jpegSize = size;
718 return retval;
DRC9b28def2011-05-21 14:37:15 +0000719}
720
721
DRC19c791c2018-03-08 10:55:20 -0600722DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
723 int width, int pitch, int height,
724 int pixelFormat, unsigned char **dstPlanes,
725 int *strides, int subsamp, int flags)
DRC2e7b76b2009-04-03 12:04:24 +0000726{
DRC19c791c2018-03-08 10:55:20 -0600727 JSAMPROW *row_pointer = NULL;
728 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
729 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
730 JSAMPROW *outbuf[MAX_COMPONENTS];
731 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
732 JSAMPLE *ptr;
733 jpeg_component_info *compptr;
DRC2e7b76b2009-04-03 12:04:24 +0000734
DRCd8b95102019-04-12 07:49:35 -0500735 GET_CINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -0600736 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000737
DRC19c791c2018-03-08 10:55:20 -0600738 for (i = 0; i < MAX_COMPONENTS; i++) {
739 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
740 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
741 }
DRCfbb67472010-11-24 04:02:37 +0000742
DRC19c791c2018-03-08 10:55:20 -0600743 if ((this->init & COMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -0500744 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
DRCe2f8e692013-10-30 22:21:06 +0000745
DRC19c791c2018-03-08 10:55:20 -0600746 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
747 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
748 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
DRCd8b95102019-04-12 07:49:35 -0500749 THROW("tjEncodeYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600750 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
DRCd8b95102019-04-12 07:49:35 -0500751 THROW("tjEncodeYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000752
DRC19c791c2018-03-08 10:55:20 -0600753 if (pixelFormat == TJPF_CMYK)
DRCd8b95102019-04-12 07:49:35 -0500754 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
DRCcd7c3e62013-08-23 02:49:25 +0000755
DRC19c791c2018-03-08 10:55:20 -0600756 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRC2e7b76b2009-04-03 12:04:24 +0000757
DRC19c791c2018-03-08 10:55:20 -0600758 if (setjmp(this->jerr.setjmp_buffer)) {
759 /* If we get here, the JPEG code has signaled an error. */
760 retval = -1; goto bailout;
761 }
DRCd4c41fe2017-03-18 12:56:36 -0500762
DRC19c791c2018-03-08 10:55:20 -0600763 cinfo->image_width = width;
764 cinfo->image_height = height;
DRC2e7b76b2009-04-03 12:04:24 +0000765
DRCbd96b302018-03-17 00:06:10 -0500766#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600767 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
768 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
769 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500770#endif
DRC0c6a2712010-02-22 08:34:44 +0000771
DRC19c791c2018-03-08 10:55:20 -0600772 if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) return -1;
DRC2e7b76b2009-04-03 12:04:24 +0000773
DRC19c791c2018-03-08 10:55:20 -0600774 /* Execute only the parts of jpeg_start_compress() that we need. If we
775 were to call the whole jpeg_start_compress() function, then it would try
776 to write the file headers, which could overflow the output buffer if the
777 YUV image were very small. */
778 if (cinfo->global_state != CSTATE_START)
DRCd8b95102019-04-12 07:49:35 -0500779 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
DRC19c791c2018-03-08 10:55:20 -0600780 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
781 jinit_c_master_control(cinfo, FALSE);
782 jinit_color_converter(cinfo);
783 jinit_downsampler(cinfo);
784 (*cinfo->cconvert->start_pass) (cinfo);
DRC38c99702014-02-11 09:45:18 +0000785
DRC19c791c2018-03-08 10:55:20 -0600786 pw0 = PAD(width, cinfo->max_h_samp_factor);
787 ph0 = PAD(height, cinfo->max_v_samp_factor);
DRC2e7b76b2009-04-03 12:04:24 +0000788
DRC19c791c2018-03-08 10:55:20 -0600789 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
DRCd8b95102019-04-12 07:49:35 -0500790 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600791 for (i = 0; i < height; i++) {
792 if (flags & TJFLAG_BOTTOMUP)
DRCd8b95102019-04-12 07:49:35 -0500793 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600794 else
DRCd8b95102019-04-12 07:49:35 -0500795 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -0600796 }
797 if (height < ph0)
798 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRCfbb67472010-11-24 04:02:37 +0000799
DRC19c791c2018-03-08 10:55:20 -0600800 for (i = 0; i < cinfo->num_components; i++) {
801 compptr = &cinfo->comp_info[i];
802 _tmpbuf[i] = (JSAMPLE *)malloc(
803 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
804 compptr->h_samp_factor, 32) *
805 cinfo->max_v_samp_factor + 32);
806 if (!_tmpbuf[i])
DRCd8b95102019-04-12 07:49:35 -0500807 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600808 tmpbuf[i] =
809 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
810 if (!tmpbuf[i])
DRCd8b95102019-04-12 07:49:35 -0500811 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600812 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
813 unsigned char *_tmpbuf_aligned =
814 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRCfbb67472010-11-24 04:02:37 +0000815
DRC19c791c2018-03-08 10:55:20 -0600816 tmpbuf[i][row] = &_tmpbuf_aligned[
817 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
818 compptr->h_samp_factor, 32) * row];
819 }
820 _tmpbuf2[i] =
821 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
822 compptr->v_samp_factor + 32);
823 if (!_tmpbuf2[i])
DRCd8b95102019-04-12 07:49:35 -0500824 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600825 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
826 if (!tmpbuf2[i])
DRCd8b95102019-04-12 07:49:35 -0500827 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600828 for (row = 0; row < compptr->v_samp_factor; row++) {
829 unsigned char *_tmpbuf2_aligned =
830 (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
DRCd4c41fe2017-03-18 12:56:36 -0500831
DRC19c791c2018-03-08 10:55:20 -0600832 tmpbuf2[i][row] =
833 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
834 }
835 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
836 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
837 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
838 if (!outbuf[i])
DRCd8b95102019-04-12 07:49:35 -0500839 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -0600840 ptr = dstPlanes[i];
841 for (row = 0; row < ph[i]; row++) {
842 outbuf[i][row] = ptr;
843 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
844 }
845 }
DRC2e7b76b2009-04-03 12:04:24 +0000846
DRC19c791c2018-03-08 10:55:20 -0600847 if (setjmp(this->jerr.setjmp_buffer)) {
848 /* If we get here, the JPEG code has signaled an error. */
849 retval = -1; goto bailout;
850 }
851
852 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
853 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
854 cinfo->max_v_samp_factor);
855 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
856 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
857 i++, compptr++)
858 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
859 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
860 compptr->v_samp_factor, pw[i]);
861 }
862 cinfo->next_scanline += height;
863 jpeg_abort_compress(cinfo);
864
865bailout:
866 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
867 if (row_pointer) free(row_pointer);
868 for (i = 0; i < MAX_COMPONENTS; i++) {
869 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
870 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
871 if (tmpbuf2[i] != NULL) free(tmpbuf2[i]);
872 if (_tmpbuf2[i] != NULL) free(_tmpbuf2[i]);
873 if (outbuf[i] != NULL) free(outbuf[i]);
874 }
875 if (this->jerr.warning) retval = -1;
876 this->jerr.stopOnWarning = FALSE;
877 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000878}
879
DRC19c791c2018-03-08 10:55:20 -0600880DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
881 int width, int pitch, int height, int pixelFormat,
882 unsigned char *dstBuf, int pad, int subsamp,
883 int flags)
DRCaecea382014-08-11 18:05:41 +0000884{
DRC19c791c2018-03-08 10:55:20 -0600885 unsigned char *dstPlanes[3];
886 int pw0, ph0, strides[3], retval = -1;
887 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +0000888
DRCd8b95102019-04-12 07:49:35 -0500889 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -0600890 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -0500891
DRCd8b95102019-04-12 07:49:35 -0500892 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
893 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
894 THROW("tjEncodeYUV3(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +0000895
DRC19c791c2018-03-08 10:55:20 -0600896 pw0 = tjPlaneWidth(0, width, subsamp);
897 ph0 = tjPlaneHeight(0, height, subsamp);
898 dstPlanes[0] = dstBuf;
899 strides[0] = PAD(pw0, pad);
900 if (subsamp == TJSAMP_GRAY) {
901 strides[1] = strides[2] = 0;
902 dstPlanes[1] = dstPlanes[2] = NULL;
903 } else {
904 int pw1 = tjPlaneWidth(1, width, subsamp);
905 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +0000906
DRC19c791c2018-03-08 10:55:20 -0600907 strides[1] = strides[2] = PAD(pw1, pad);
908 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
909 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
910 }
DRCaecea382014-08-11 18:05:41 +0000911
DRC19c791c2018-03-08 10:55:20 -0600912 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
913 dstPlanes, strides, subsamp, flags);
914
915bailout:
916 return retval;
DRCaecea382014-08-11 18:05:41 +0000917}
918
DRC19c791c2018-03-08 10:55:20 -0600919DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
920 int pitch, int height, int pixelFormat,
921 unsigned char *dstBuf, int subsamp, int flags)
DRCf610d612013-04-26 10:33:29 +0000922{
DRC19c791c2018-03-08 10:55:20 -0600923 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
924 dstBuf, 4, subsamp, flags);
DRCf610d612013-04-26 10:33:29 +0000925}
926
DRC19c791c2018-03-08 10:55:20 -0600927DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
928 int pitch, int height, int pixelSize,
929 unsigned char *dstBuf, int subsamp, int flags)
DRC84241602011-02-25 02:08:23 +0000930{
DRC19c791c2018-03-08 10:55:20 -0600931 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
932 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
933 flags);
DRC84241602011-02-25 02:08:23 +0000934}
935
936
DRC19c791c2018-03-08 10:55:20 -0600937DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
938 const unsigned char **srcPlanes,
939 int width, const int *strides,
940 int height, int subsamp,
941 unsigned char **jpegBuf,
942 unsigned long *jpegSize, int jpegQual,
943 int flags)
DRC910a3572013-10-30 23:02:57 +0000944{
DRC19c791c2018-03-08 10:55:20 -0600945 int i, row, retval = 0, alloc = 1;
946 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
947 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
948 JSAMPLE *_tmpbuf = NULL, *ptr;
949 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
DRC910a3572013-10-30 23:02:57 +0000950
DRCd8b95102019-04-12 07:49:35 -0500951 GET_CINSTANCE(handle)
DRC19c791c2018-03-08 10:55:20 -0600952 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +0000953
DRC19c791c2018-03-08 10:55:20 -0600954 for (i = 0; i < MAX_COMPONENTS; i++) {
955 tmpbuf[i] = NULL; inbuf[i] = NULL;
956 }
DRC910a3572013-10-30 23:02:57 +0000957
DRC19c791c2018-03-08 10:55:20 -0600958 if ((this->init & COMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -0500959 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
DRC910a3572013-10-30 23:02:57 +0000960
DRC19c791c2018-03-08 10:55:20 -0600961 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
962 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
963 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
DRCd8b95102019-04-12 07:49:35 -0500964 THROW("tjCompressFromYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -0600965 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
DRCd8b95102019-04-12 07:49:35 -0500966 THROW("tjCompressFromYUVPlanes(): Invalid argument");
DRC910a3572013-10-30 23:02:57 +0000967
DRC19c791c2018-03-08 10:55:20 -0600968 if (setjmp(this->jerr.setjmp_buffer)) {
969 /* If we get here, the JPEG code has signaled an error. */
970 retval = -1; goto bailout;
971 }
DRC910a3572013-10-30 23:02:57 +0000972
DRC19c791c2018-03-08 10:55:20 -0600973 cinfo->image_width = width;
974 cinfo->image_height = height;
DRC910a3572013-10-30 23:02:57 +0000975
DRCbd96b302018-03-17 00:06:10 -0500976#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -0600977 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
978 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
979 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -0500980#endif
DRC910a3572013-10-30 23:02:57 +0000981
DRC19c791c2018-03-08 10:55:20 -0600982 if (flags & TJFLAG_NOREALLOC) {
983 alloc = 0; *jpegSize = tjBufSize(width, height, subsamp);
984 }
985 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
986 if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1)
987 return -1;
988 cinfo->raw_data_in = TRUE;
DRC910a3572013-10-30 23:02:57 +0000989
DRC19c791c2018-03-08 10:55:20 -0600990 jpeg_start_compress(cinfo, TRUE);
991 for (i = 0; i < cinfo->num_components; i++) {
992 jpeg_component_info *compptr = &cinfo->comp_info[i];
993 int ih;
DRC910a3572013-10-30 23:02:57 +0000994
DRC19c791c2018-03-08 10:55:20 -0600995 iw[i] = compptr->width_in_blocks * DCTSIZE;
996 ih = compptr->height_in_blocks * DCTSIZE;
997 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
998 compptr->h_samp_factor / cinfo->max_h_samp_factor;
999 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1000 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1001 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1002 th[i] = compptr->v_samp_factor * DCTSIZE;
1003 tmpbufsize += iw[i] * th[i];
1004 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001005 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001006 ptr = (JSAMPLE *)srcPlanes[i];
1007 for (row = 0; row < ph[i]; row++) {
1008 inbuf[i][row] = ptr;
1009 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1010 }
1011 }
1012 if (usetmpbuf) {
1013 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001014 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001015 ptr = _tmpbuf;
1016 for (i = 0; i < cinfo->num_components; i++) {
1017 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001018 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001019 for (row = 0; row < th[i]; row++) {
1020 tmpbuf[i][row] = ptr;
1021 ptr += iw[i];
1022 }
1023 }
1024 }
DRCd4c41fe2017-03-18 12:56:36 -05001025
DRC19c791c2018-03-08 10:55:20 -06001026 if (setjmp(this->jerr.setjmp_buffer)) {
1027 /* If we get here, the JPEG code has signaled an error. */
1028 retval = -1; goto bailout;
1029 }
DRC910a3572013-10-30 23:02:57 +00001030
DRC19c791c2018-03-08 10:55:20 -06001031 for (row = 0; row < (int)cinfo->image_height;
1032 row += cinfo->max_v_samp_factor * DCTSIZE) {
1033 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1034 int crow[MAX_COMPONENTS];
1035
1036 for (i = 0; i < cinfo->num_components; i++) {
1037 jpeg_component_info *compptr = &cinfo->comp_info[i];
1038
1039 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1040 if (usetmpbuf) {
1041 int j, k;
1042
1043 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1044 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1045 /* Duplicate last sample in row to fill out MCU */
1046 for (k = pw[i]; k < iw[i]; k++)
1047 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1048 }
1049 /* Duplicate last row to fill out MCU */
1050 for (j = ph[i] - crow[i]; j < th[i]; j++)
1051 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1052 yuvptr[i] = tmpbuf[i];
1053 } else
1054 yuvptr[i] = &inbuf[i][crow[i]];
1055 }
1056 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1057 }
1058 jpeg_finish_compress(cinfo);
1059
1060bailout:
1061 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1062 for (i = 0; i < MAX_COMPONENTS; i++) {
1063 if (tmpbuf[i]) free(tmpbuf[i]);
1064 if (inbuf[i]) free(inbuf[i]);
1065 }
1066 if (_tmpbuf) free(_tmpbuf);
1067 if (this->jerr.warning) retval = -1;
1068 this->jerr.stopOnWarning = FALSE;
1069 return retval;
DRC910a3572013-10-30 23:02:57 +00001070}
1071
DRC19c791c2018-03-08 10:55:20 -06001072DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1073 int width, int pad, int height, int subsamp,
1074 unsigned char **jpegBuf,
1075 unsigned long *jpegSize, int jpegQual,
1076 int flags)
DRCaecea382014-08-11 18:05:41 +00001077{
DRC19c791c2018-03-08 10:55:20 -06001078 const unsigned char *srcPlanes[3];
1079 int pw0, ph0, strides[3], retval = -1;
1080 tjinstance *this = (tjinstance *)handle;
DRCaecea382014-08-11 18:05:41 +00001081
DRCd8b95102019-04-12 07:49:35 -05001082 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -06001083 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001084
DRC19c791c2018-03-08 10:55:20 -06001085 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1086 subsamp >= NUMSUBOPT)
DRCd8b95102019-04-12 07:49:35 -05001087 THROW("tjCompressFromYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001088
DRC19c791c2018-03-08 10:55:20 -06001089 pw0 = tjPlaneWidth(0, width, subsamp);
1090 ph0 = tjPlaneHeight(0, height, subsamp);
1091 srcPlanes[0] = srcBuf;
1092 strides[0] = PAD(pw0, pad);
1093 if (subsamp == TJSAMP_GRAY) {
1094 strides[1] = strides[2] = 0;
1095 srcPlanes[1] = srcPlanes[2] = NULL;
1096 } else {
1097 int pw1 = tjPlaneWidth(1, width, subsamp);
1098 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001099
DRC19c791c2018-03-08 10:55:20 -06001100 strides[1] = strides[2] = PAD(pw1, pad);
1101 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1102 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1103 }
DRCaecea382014-08-11 18:05:41 +00001104
DRC19c791c2018-03-08 10:55:20 -06001105 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1106 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1107
1108bailout:
1109 return retval;
DRCaecea382014-08-11 18:05:41 +00001110}
1111
DRC910a3572013-10-30 23:02:57 +00001112
DRC9b28def2011-05-21 14:37:15 +00001113/* Decompressor */
DRC2e7b76b2009-04-03 12:04:24 +00001114
DRC9b28def2011-05-21 14:37:15 +00001115static tjhandle _tjInitDecompress(tjinstance *this)
DRC2e7b76b2009-04-03 12:04:24 +00001116{
DRC19c791c2018-03-08 10:55:20 -06001117 static unsigned char buffer[1];
DRC2e7b76b2009-04-03 12:04:24 +00001118
DRC19c791c2018-03-08 10:55:20 -06001119 /* This is also straight out of example.txt */
1120 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1121 this->jerr.pub.error_exit = my_error_exit;
1122 this->jerr.pub.output_message = my_output_message;
1123 this->jerr.emit_message = this->jerr.pub.emit_message;
1124 this->jerr.pub.emit_message = my_emit_message;
DRC696e7542018-06-12 18:49:37 -05001125 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1126 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1127 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
DRC2e7b76b2009-04-03 12:04:24 +00001128
DRC19c791c2018-03-08 10:55:20 -06001129 if (setjmp(this->jerr.setjmp_buffer)) {
1130 /* If we get here, the JPEG code has signaled an error. */
1131 if (this) free(this);
1132 return NULL;
1133 }
DRC2e7b76b2009-04-03 12:04:24 +00001134
DRC19c791c2018-03-08 10:55:20 -06001135 jpeg_create_decompress(&this->dinfo);
1136 /* Make an initial call so it will create the source manager */
1137 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +00001138
DRC19c791c2018-03-08 10:55:20 -06001139 this->init |= DECOMPRESS;
1140 return (tjhandle)this;
DRC2e7b76b2009-04-03 12:04:24 +00001141}
1142
DRC19c791c2018-03-08 10:55:20 -06001143DLLEXPORT tjhandle tjInitDecompress(void)
DRC890f1e02011-02-26 22:02:37 +00001144{
DRC19c791c2018-03-08 10:55:20 -06001145 tjinstance *this;
1146
1147 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1148 snprintf(errStr, JMSG_LENGTH_MAX,
1149 "tjInitDecompress(): Memory allocation failure");
1150 return NULL;
1151 }
1152 MEMZERO(this, sizeof(tjinstance));
1153 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1154 return _tjInitDecompress(this);
DRC890f1e02011-02-26 22:02:37 +00001155}
1156
DRC2e7b76b2009-04-03 12:04:24 +00001157
DRC19c791c2018-03-08 10:55:20 -06001158DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1159 const unsigned char *jpegBuf,
1160 unsigned long jpegSize, int *width,
1161 int *height, int *jpegSubsamp,
1162 int *jpegColorspace)
DRC1fe80f82010-12-14 01:21:29 +00001163{
DRC19c791c2018-03-08 10:55:20 -06001164 int retval = 0;
DRC1fe80f82010-12-14 01:21:29 +00001165
DRCd8b95102019-04-12 07:49:35 -05001166 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001167 if ((this->init & DECOMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -05001168 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
DRC1fe80f82010-12-14 01:21:29 +00001169
DRC19c791c2018-03-08 10:55:20 -06001170 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1171 jpegSubsamp == NULL || jpegColorspace == NULL)
DRCd8b95102019-04-12 07:49:35 -05001172 THROW("tjDecompressHeader3(): Invalid argument");
DRC1fe80f82010-12-14 01:21:29 +00001173
DRC19c791c2018-03-08 10:55:20 -06001174 if (setjmp(this->jerr.setjmp_buffer)) {
1175 /* If we get here, the JPEG code has signaled an error. */
1176 return -1;
1177 }
DRC1fe80f82010-12-14 01:21:29 +00001178
DRC19c791c2018-03-08 10:55:20 -06001179 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1180 jpeg_read_header(dinfo, TRUE);
DRC1fe80f82010-12-14 01:21:29 +00001181
DRC19c791c2018-03-08 10:55:20 -06001182 *width = dinfo->image_width;
1183 *height = dinfo->image_height;
1184 *jpegSubsamp = getSubsamp(dinfo);
1185 switch (dinfo->jpeg_color_space) {
1186 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1187 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1188 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1189 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1190 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1191 default: *jpegColorspace = -1; break;
1192 }
DRC1fe80f82010-12-14 01:21:29 +00001193
DRC19c791c2018-03-08 10:55:20 -06001194 jpeg_abort_decompress(dinfo);
DRC1fe80f82010-12-14 01:21:29 +00001195
DRC19c791c2018-03-08 10:55:20 -06001196 if (*jpegSubsamp < 0)
DRCd8b95102019-04-12 07:49:35 -05001197 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
DRC19c791c2018-03-08 10:55:20 -06001198 if (*jpegColorspace < 0)
DRCd8b95102019-04-12 07:49:35 -05001199 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
DRC19c791c2018-03-08 10:55:20 -06001200 if (*width < 1 || *height < 1)
DRCd8b95102019-04-12 07:49:35 -05001201 THROW("tjDecompressHeader3(): Invalid data returned in header");
DRC91e86ba2011-02-15 05:24:08 +00001202
DRC19c791c2018-03-08 10:55:20 -06001203bailout:
1204 if (this->jerr.warning) retval = -1;
1205 return retval;
DRC91e86ba2011-02-15 05:24:08 +00001206}
1207
DRC19c791c2018-03-08 10:55:20 -06001208DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1209 unsigned long jpegSize, int *width,
1210 int *height, int *jpegSubsamp)
DRCcd7c3e62013-08-23 02:49:25 +00001211{
DRC19c791c2018-03-08 10:55:20 -06001212 int jpegColorspace;
1213
1214 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1215 jpegSubsamp, &jpegColorspace);
DRCcd7c3e62013-08-23 02:49:25 +00001216}
1217
DRC19c791c2018-03-08 10:55:20 -06001218DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1219 unsigned long jpegSize, int *width,
1220 int *height)
DRC91e86ba2011-02-15 05:24:08 +00001221{
DRC19c791c2018-03-08 10:55:20 -06001222 int jpegSubsamp;
1223
1224 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1225 &jpegSubsamp);
DRC1fe80f82010-12-14 01:21:29 +00001226}
1227
1228
DRC19c791c2018-03-08 10:55:20 -06001229DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
DRCb28fc572011-02-22 06:41:29 +00001230{
DRC19c791c2018-03-08 10:55:20 -06001231 if (numscalingfactors == NULL) {
1232 snprintf(errStr, JMSG_LENGTH_MAX,
1233 "tjGetScalingFactors(): Invalid argument");
1234 return NULL;
1235 }
DRCb28fc572011-02-22 06:41:29 +00001236
DRC19c791c2018-03-08 10:55:20 -06001237 *numscalingfactors = NUMSF;
1238 return (tjscalingfactor *)sf;
DRCb28fc572011-02-22 06:41:29 +00001239}
1240
1241
DRC19c791c2018-03-08 10:55:20 -06001242DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1243 unsigned long jpegSize, unsigned char *dstBuf,
1244 int width, int pitch, int height, int pixelFormat,
1245 int flags)
DRC2e7b76b2009-04-03 12:04:24 +00001246{
DRC19c791c2018-03-08 10:55:20 -06001247 JSAMPROW *row_pointer = NULL;
1248 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
DRC2e7b76b2009-04-03 12:04:24 +00001249
DRCd8b95102019-04-12 07:49:35 -05001250 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001251 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1252 if ((this->init & DECOMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -05001253 THROW("tjDecompress2(): Instance has not been initialized for decompression");
DRC9b28def2011-05-21 14:37:15 +00001254
DRC19c791c2018-03-08 10:55:20 -06001255 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1256 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCd8b95102019-04-12 07:49:35 -05001257 THROW("tjDecompress2(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001258
DRCbd96b302018-03-17 00:06:10 -05001259#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001260 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1261 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1262 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001263#endif
DRC9b28def2011-05-21 14:37:15 +00001264
DRC19c791c2018-03-08 10:55:20 -06001265 if (setjmp(this->jerr.setjmp_buffer)) {
1266 /* If we get here, the JPEG code has signaled an error. */
1267 retval = -1; goto bailout;
1268 }
DRC9b28def2011-05-21 14:37:15 +00001269
DRC19c791c2018-03-08 10:55:20 -06001270 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1271 jpeg_read_header(dinfo, TRUE);
1272 this->dinfo.out_color_space = pf2cs[pixelFormat];
1273 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1274 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
DRC9b28def2011-05-21 14:37:15 +00001275
DRC19c791c2018-03-08 10:55:20 -06001276 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1277 if (width == 0) width = jpegwidth;
1278 if (height == 0) height = jpegheight;
1279 for (i = 0; i < NUMSF; i++) {
1280 scaledw = TJSCALED(jpegwidth, sf[i]);
1281 scaledh = TJSCALED(jpegheight, sf[i]);
1282 if (scaledw <= width && scaledh <= height)
1283 break;
1284 }
1285 if (i >= NUMSF)
DRCd8b95102019-04-12 07:49:35 -05001286 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
DRC19c791c2018-03-08 10:55:20 -06001287 width = scaledw; height = scaledh;
1288 dinfo->scale_num = sf[i].num;
1289 dinfo->scale_denom = sf[i].denom;
DRC9b28def2011-05-21 14:37:15 +00001290
DRC19c791c2018-03-08 10:55:20 -06001291 jpeg_start_decompress(dinfo);
1292 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRCafc06922012-03-23 19:47:57 +00001293
DRC19c791c2018-03-08 10:55:20 -06001294 if ((row_pointer =
1295 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001296 THROW("tjDecompress2(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001297 if (setjmp(this->jerr.setjmp_buffer)) {
1298 /* If we get here, the JPEG code has signaled an error. */
1299 retval = -1; goto bailout;
1300 }
1301 for (i = 0; i < (int)dinfo->output_height; i++) {
1302 if (flags & TJFLAG_BOTTOMUP)
DRCd8b95102019-04-12 07:49:35 -05001303 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001304 else
DRCd8b95102019-04-12 07:49:35 -05001305 row_pointer[i] = &dstBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001306 }
1307 while (dinfo->output_scanline < dinfo->output_height)
1308 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1309 dinfo->output_height - dinfo->output_scanline);
1310 jpeg_finish_decompress(dinfo);
DRC9b28def2011-05-21 14:37:15 +00001311
DRC19c791c2018-03-08 10:55:20 -06001312bailout:
1313 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1314 if (row_pointer) free(row_pointer);
1315 if (this->jerr.warning) retval = -1;
1316 this->jerr.stopOnWarning = FALSE;
1317 return retval;
DRC9b28def2011-05-21 14:37:15 +00001318}
1319
DRC19c791c2018-03-08 10:55:20 -06001320DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1321 unsigned long jpegSize, unsigned char *dstBuf,
1322 int width, int pitch, int height, int pixelSize,
1323 int flags)
DRC9b28def2011-05-21 14:37:15 +00001324{
DRC19c791c2018-03-08 10:55:20 -06001325 if (flags & TJ_YUV)
1326 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1327 else
1328 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1329 height, getPixelFormat(pixelSize, flags), flags);
DRC9b28def2011-05-21 14:37:15 +00001330}
1331
1332
DRC34dca052014-02-28 09:17:14 +00001333static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
DRC19c791c2018-03-08 10:55:20 -06001334 int pixelFormat, int subsamp, int flags)
DRC34dca052014-02-28 09:17:14 +00001335{
DRC19c791c2018-03-08 10:55:20 -06001336 int i;
DRC895fd6d2014-02-28 09:35:34 +00001337
DRC19c791c2018-03-08 10:55:20 -06001338 dinfo->scale_num = dinfo->scale_denom = 1;
DRC34dca052014-02-28 09:17:14 +00001339
DRC19c791c2018-03-08 10:55:20 -06001340 if (subsamp == TJSAMP_GRAY) {
1341 dinfo->num_components = dinfo->comps_in_scan = 1;
1342 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1343 } else {
1344 dinfo->num_components = dinfo->comps_in_scan = 3;
1345 dinfo->jpeg_color_space = JCS_YCbCr;
1346 }
DRC34dca052014-02-28 09:17:14 +00001347
DRC19c791c2018-03-08 10:55:20 -06001348 dinfo->comp_info = (jpeg_component_info *)
1349 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1350 dinfo->num_components *
1351 sizeof(jpeg_component_info));
DRC34dca052014-02-28 09:17:14 +00001352
DRC19c791c2018-03-08 10:55:20 -06001353 for (i = 0; i < dinfo->num_components; i++) {
1354 jpeg_component_info *compptr = &dinfo->comp_info[i];
DRC34dca052014-02-28 09:17:14 +00001355
DRC19c791c2018-03-08 10:55:20 -06001356 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1357 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1358 compptr->component_index = i;
1359 compptr->component_id = i + 1;
1360 compptr->quant_tbl_no = compptr->dc_tbl_no =
1361 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1362 dinfo->cur_comp_info[i] = compptr;
1363 }
1364 dinfo->data_precision = 8;
1365 for (i = 0; i < 2; i++) {
1366 if (dinfo->quant_tbl_ptrs[i] == NULL)
1367 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1368 }
1369
1370 return 0;
DRC34dca052014-02-28 09:17:14 +00001371}
1372
1373
1374int my_read_markers(j_decompress_ptr dinfo)
1375{
DRC19c791c2018-03-08 10:55:20 -06001376 return JPEG_REACHED_SOS;
DRC34dca052014-02-28 09:17:14 +00001377}
1378
1379void my_reset_marker_reader(j_decompress_ptr dinfo)
1380{
1381}
1382
DRC19c791c2018-03-08 10:55:20 -06001383DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1384 const unsigned char **srcPlanes,
1385 const int *strides, int subsamp,
1386 unsigned char *dstBuf, int width, int pitch,
1387 int height, int pixelFormat, int flags)
DRC34dca052014-02-28 09:17:14 +00001388{
DRC19c791c2018-03-08 10:55:20 -06001389 JSAMPROW *row_pointer = NULL;
1390 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1391 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1392 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1393 JSAMPLE *ptr;
1394 jpeg_component_info *compptr;
1395 int (*old_read_markers) (j_decompress_ptr);
1396 void (*old_reset_marker_reader) (j_decompress_ptr);
DRC34dca052014-02-28 09:17:14 +00001397
DRCd8b95102019-04-12 07:49:35 -05001398 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001399 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRC34dca052014-02-28 09:17:14 +00001400
DRC19c791c2018-03-08 10:55:20 -06001401 for (i = 0; i < MAX_COMPONENTS; i++) {
1402 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1403 }
DRC34dca052014-02-28 09:17:14 +00001404
DRC19c791c2018-03-08 10:55:20 -06001405 if ((this->init & DECOMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -05001406 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
DRC34dca052014-02-28 09:17:14 +00001407
DRC19c791c2018-03-08 10:55:20 -06001408 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1409 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1410 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCd8b95102019-04-12 07:49:35 -05001411 THROW("tjDecodeYUVPlanes(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -06001412 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
DRCd8b95102019-04-12 07:49:35 -05001413 THROW("tjDecodeYUVPlanes(): Invalid argument");
DRC34dca052014-02-28 09:17:14 +00001414
DRC19c791c2018-03-08 10:55:20 -06001415 if (setjmp(this->jerr.setjmp_buffer)) {
1416 /* If we get here, the JPEG code has signaled an error. */
1417 retval = -1; goto bailout;
1418 }
DRC34dca052014-02-28 09:17:14 +00001419
DRC19c791c2018-03-08 10:55:20 -06001420 if (pixelFormat == TJPF_CMYK)
DRCd8b95102019-04-12 07:49:35 -05001421 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
DRC34dca052014-02-28 09:17:14 +00001422
DRC19c791c2018-03-08 10:55:20 -06001423 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1424 dinfo->image_width = width;
1425 dinfo->image_height = height;
DRC34dca052014-02-28 09:17:14 +00001426
DRCbd96b302018-03-17 00:06:10 -05001427#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001428 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1429 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1430 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001431#endif
DRC34dca052014-02-28 09:17:14 +00001432
DRC19c791c2018-03-08 10:55:20 -06001433 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1434 retval = -1; goto bailout;
1435 }
1436 old_read_markers = dinfo->marker->read_markers;
1437 dinfo->marker->read_markers = my_read_markers;
1438 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1439 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1440 jpeg_read_header(dinfo, TRUE);
1441 dinfo->marker->read_markers = old_read_markers;
1442 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
DRC34dca052014-02-28 09:17:14 +00001443
DRC19c791c2018-03-08 10:55:20 -06001444 this->dinfo.out_color_space = pf2cs[pixelFormat];
1445 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1446 dinfo->do_fancy_upsampling = FALSE;
1447 dinfo->Se = DCTSIZE2 - 1;
1448 jinit_master_decompress(dinfo);
1449 (*dinfo->upsample->start_pass) (dinfo);
DRC34dca052014-02-28 09:17:14 +00001450
DRC19c791c2018-03-08 10:55:20 -06001451 pw0 = PAD(width, dinfo->max_h_samp_factor);
1452 ph0 = PAD(height, dinfo->max_v_samp_factor);
DRC34dca052014-02-28 09:17:14 +00001453
DRC19c791c2018-03-08 10:55:20 -06001454 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
DRC34dca052014-02-28 09:17:14 +00001455
DRC19c791c2018-03-08 10:55:20 -06001456 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001457 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001458 for (i = 0; i < height; i++) {
1459 if (flags & TJFLAG_BOTTOMUP)
DRCd8b95102019-04-12 07:49:35 -05001460 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001461 else
DRCd8b95102019-04-12 07:49:35 -05001462 row_pointer[i] = &dstBuf[i * (size_t)pitch];
DRC19c791c2018-03-08 10:55:20 -06001463 }
1464 if (height < ph0)
1465 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
DRC34dca052014-02-28 09:17:14 +00001466
DRC19c791c2018-03-08 10:55:20 -06001467 for (i = 0; i < dinfo->num_components; i++) {
1468 compptr = &dinfo->comp_info[i];
1469 _tmpbuf[i] =
1470 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1471 compptr->v_samp_factor + 32);
1472 if (!_tmpbuf[i])
DRCd8b95102019-04-12 07:49:35 -05001473 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001474 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1475 if (!tmpbuf[i])
DRCd8b95102019-04-12 07:49:35 -05001476 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001477 for (row = 0; row < compptr->v_samp_factor; row++) {
1478 unsigned char *_tmpbuf_aligned =
1479 (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
DRC34dca052014-02-28 09:17:14 +00001480
DRC19c791c2018-03-08 10:55:20 -06001481 tmpbuf[i][row] =
1482 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1483 }
1484 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1485 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1486 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1487 if (!inbuf[i])
DRCd8b95102019-04-12 07:49:35 -05001488 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001489 ptr = (JSAMPLE *)srcPlanes[i];
1490 for (row = 0; row < ph[i]; row++) {
1491 inbuf[i][row] = ptr;
1492 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1493 }
1494 }
DRCd4c41fe2017-03-18 12:56:36 -05001495
DRC19c791c2018-03-08 10:55:20 -06001496 if (setjmp(this->jerr.setjmp_buffer)) {
1497 /* If we get here, the JPEG code has signaled an error. */
1498 retval = -1; goto bailout;
1499 }
DRC34dca052014-02-28 09:17:14 +00001500
DRC19c791c2018-03-08 10:55:20 -06001501 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1502 JDIMENSION inrow = 0, outrow = 0;
1503
1504 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1505 i++, compptr++)
1506 jcopy_sample_rows(inbuf[i],
1507 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1508 compptr->v_samp_factor, pw[i]);
1509 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1510 dinfo->max_v_samp_factor, &row_pointer[row],
1511 &outrow, dinfo->max_v_samp_factor);
1512 }
1513 jpeg_abort_decompress(dinfo);
1514
1515bailout:
1516 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1517 if (row_pointer) free(row_pointer);
1518 for (i = 0; i < MAX_COMPONENTS; i++) {
1519 if (tmpbuf[i] != NULL) free(tmpbuf[i]);
1520 if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
1521 if (inbuf[i] != NULL) free(inbuf[i]);
1522 }
1523 if (this->jerr.warning) retval = -1;
1524 this->jerr.stopOnWarning = FALSE;
1525 return retval;
DRC34dca052014-02-28 09:17:14 +00001526}
1527
DRC19c791c2018-03-08 10:55:20 -06001528DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1529 int pad, int subsamp, unsigned char *dstBuf,
1530 int width, int pitch, int height, int pixelFormat,
1531 int flags)
DRCaecea382014-08-11 18:05:41 +00001532{
DRC19c791c2018-03-08 10:55:20 -06001533 const unsigned char *srcPlanes[3];
1534 int pw0, ph0, strides[3], retval = -1;
1535 tjinstance *this = (tjinstance *)handle;
DRC34dca052014-02-28 09:17:14 +00001536
DRCd8b95102019-04-12 07:49:35 -05001537 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
DRC19c791c2018-03-08 10:55:20 -06001538 this->isInstanceError = FALSE;
DRCb9ab64d2017-05-11 21:02:29 -05001539
DRCd8b95102019-04-12 07:49:35 -05001540 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
DRC19c791c2018-03-08 10:55:20 -06001541 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
DRCd8b95102019-04-12 07:49:35 -05001542 THROW("tjDecodeYUV(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001543
DRC19c791c2018-03-08 10:55:20 -06001544 pw0 = tjPlaneWidth(0, width, subsamp);
1545 ph0 = tjPlaneHeight(0, height, subsamp);
1546 srcPlanes[0] = srcBuf;
1547 strides[0] = PAD(pw0, pad);
1548 if (subsamp == TJSAMP_GRAY) {
1549 strides[1] = strides[2] = 0;
1550 srcPlanes[1] = srcPlanes[2] = NULL;
1551 } else {
1552 int pw1 = tjPlaneWidth(1, width, subsamp);
1553 int ph1 = tjPlaneHeight(1, height, subsamp);
DRCaecea382014-08-11 18:05:41 +00001554
DRC19c791c2018-03-08 10:55:20 -06001555 strides[1] = strides[2] = PAD(pw1, pad);
1556 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1557 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1558 }
DRCaecea382014-08-11 18:05:41 +00001559
DRC19c791c2018-03-08 10:55:20 -06001560 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1561 pitch, height, pixelFormat, flags);
1562
1563bailout:
1564 return retval;
DRCaecea382014-08-11 18:05:41 +00001565}
1566
DRC19c791c2018-03-08 10:55:20 -06001567DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1568 const unsigned char *jpegBuf,
1569 unsigned long jpegSize,
1570 unsigned char **dstPlanes, int width,
1571 int *strides, int height, int flags)
DRC9b28def2011-05-21 14:37:15 +00001572{
DRC19c791c2018-03-08 10:55:20 -06001573 int i, sfi, row, retval = 0;
1574 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1575 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1576 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1577 JSAMPLE *_tmpbuf = NULL, *ptr;
1578 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1579 int dctsize;
DRC9b28def2011-05-21 14:37:15 +00001580
DRCd8b95102019-04-12 07:49:35 -05001581 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001582 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCb51ee892013-10-31 05:00:19 +00001583
DRC19c791c2018-03-08 10:55:20 -06001584 for (i = 0; i < MAX_COMPONENTS; i++) {
1585 tmpbuf[i] = NULL; outbuf[i] = NULL;
1586 }
DRC9e17f7d2010-12-10 04:59:13 +00001587
DRC19c791c2018-03-08 10:55:20 -06001588 if ((this->init & DECOMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -05001589 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
DRCe2f8e692013-10-30 22:21:06 +00001590
DRC19c791c2018-03-08 10:55:20 -06001591 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1592 width < 0 || height < 0)
DRCd8b95102019-04-12 07:49:35 -05001593 THROW("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001594
DRCbd96b302018-03-17 00:06:10 -05001595#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001596 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1597 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1598 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001599#endif
DRC0c6a2712010-02-22 08:34:44 +00001600
DRC19c791c2018-03-08 10:55:20 -06001601 if (setjmp(this->jerr.setjmp_buffer)) {
1602 /* If we get here, the JPEG code has signaled an error. */
1603 retval = -1; goto bailout;
1604 }
DRC2e7b76b2009-04-03 12:04:24 +00001605
DRC19c791c2018-03-08 10:55:20 -06001606 if (!this->headerRead) {
1607 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1608 jpeg_read_header(dinfo, TRUE);
1609 }
1610 this->headerRead = 0;
1611 jpegSubsamp = getSubsamp(dinfo);
1612 if (jpegSubsamp < 0)
DRCd8b95102019-04-12 07:49:35 -05001613 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001614
DRC19c791c2018-03-08 10:55:20 -06001615 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
DRCd8b95102019-04-12 07:49:35 -05001616 THROW("tjDecompressToYUVPlanes(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +00001617
DRC19c791c2018-03-08 10:55:20 -06001618 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1619 if (width == 0) width = jpegwidth;
1620 if (height == 0) height = jpegheight;
1621 for (i = 0; i < NUMSF; i++) {
1622 scaledw = TJSCALED(jpegwidth, sf[i]);
1623 scaledh = TJSCALED(jpegheight, sf[i]);
1624 if (scaledw <= width && scaledh <= height)
1625 break;
1626 }
1627 if (i >= NUMSF)
DRCd8b95102019-04-12 07:49:35 -05001628 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
DRC19c791c2018-03-08 10:55:20 -06001629 if (dinfo->num_components > 3)
DRCd8b95102019-04-12 07:49:35 -05001630 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
DRCcd7c3e62013-08-23 02:49:25 +00001631
DRC19c791c2018-03-08 10:55:20 -06001632 width = scaledw; height = scaledh;
1633 dinfo->scale_num = sf[i].num;
1634 dinfo->scale_denom = sf[i].denom;
1635 sfi = i;
1636 jpeg_calc_output_dimensions(dinfo);
DRCf610d612013-04-26 10:33:29 +00001637
DRC19c791c2018-03-08 10:55:20 -06001638 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
DRC418fe282013-05-07 21:17:35 +00001639
DRC19c791c2018-03-08 10:55:20 -06001640 for (i = 0; i < dinfo->num_components; i++) {
1641 jpeg_component_info *compptr = &dinfo->comp_info[i];
1642 int ih;
DRC9e17f7d2010-12-10 04:59:13 +00001643
DRC19c791c2018-03-08 10:55:20 -06001644 iw[i] = compptr->width_in_blocks * dctsize;
1645 ih = compptr->height_in_blocks * dctsize;
1646 pw[i] = PAD(dinfo->output_width, dinfo->max_h_samp_factor) *
1647 compptr->h_samp_factor / dinfo->max_h_samp_factor;
1648 ph[i] = PAD(dinfo->output_height, dinfo->max_v_samp_factor) *
1649 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1650 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1651 th[i] = compptr->v_samp_factor * dctsize;
1652 tmpbufsize += iw[i] * th[i];
1653 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001654 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001655 ptr = dstPlanes[i];
1656 for (row = 0; row < ph[i]; row++) {
1657 outbuf[i][row] = ptr;
1658 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1659 }
1660 }
1661 if (usetmpbuf) {
1662 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001663 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001664 ptr = _tmpbuf;
1665 for (i = 0; i < dinfo->num_components; i++) {
1666 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001667 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001668 for (row = 0; row < th[i]; row++) {
1669 tmpbuf[i][row] = ptr;
1670 ptr += iw[i];
1671 }
1672 }
1673 }
DRCd4c41fe2017-03-18 12:56:36 -05001674
DRC19c791c2018-03-08 10:55:20 -06001675 if (setjmp(this->jerr.setjmp_buffer)) {
1676 /* If we get here, the JPEG code has signaled an error. */
1677 retval = -1; goto bailout;
1678 }
DRC9b28def2011-05-21 14:37:15 +00001679
DRC19c791c2018-03-08 10:55:20 -06001680 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1681 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1682 dinfo->raw_data_out = TRUE;
DRC2e7b76b2009-04-03 12:04:24 +00001683
DRC19c791c2018-03-08 10:55:20 -06001684 jpeg_start_decompress(dinfo);
1685 for (row = 0; row < (int)dinfo->output_height;
1686 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1687 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1688 int crow[MAX_COMPONENTS];
1689
1690 for (i = 0; i < dinfo->num_components; i++) {
1691 jpeg_component_info *compptr = &dinfo->comp_info[i];
1692
1693 if (jpegSubsamp == TJ_420) {
1694 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1695 to be clever and use the IDCT to perform upsampling on the U and V
1696 planes. For instance, if the output image is to be scaled by 1/2
1697 relative to the JPEG image, then the scaling factor and upsampling
1698 effectively cancel each other, so a normal 8x8 IDCT can be used.
1699 However, this is not desirable when using the decompress-to-YUV
1700 functionality in TurboJPEG, since we want to output the U and V
1701 planes in their subsampled form. Thus, we have to override some
1702 internal libjpeg parameters to force it to use the "scaled" IDCT
1703 functions on the U and V planes. */
1704 compptr->_DCT_scaled_size = dctsize;
1705 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1706 sf[sfi].num / sf[sfi].denom *
1707 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1708 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1709 }
1710 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1711 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1712 else yuvptr[i] = &outbuf[i][crow[i]];
1713 }
1714 jpeg_read_raw_data(dinfo, yuvptr,
1715 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1716 if (usetmpbuf) {
1717 int j;
1718
1719 for (i = 0; i < dinfo->num_components; i++) {
1720 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1721 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1722 }
1723 }
1724 }
1725 }
1726 jpeg_finish_decompress(dinfo);
1727
1728bailout:
1729 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1730 for (i = 0; i < MAX_COMPONENTS; i++) {
1731 if (tmpbuf[i]) free(tmpbuf[i]);
1732 if (outbuf[i]) free(outbuf[i]);
1733 }
1734 if (_tmpbuf) free(_tmpbuf);
1735 if (this->jerr.warning) retval = -1;
1736 this->jerr.stopOnWarning = FALSE;
1737 return retval;
DRC2e7b76b2009-04-03 12:04:24 +00001738}
1739
DRC19c791c2018-03-08 10:55:20 -06001740DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1741 unsigned long jpegSize, unsigned char *dstBuf,
1742 int width, int pad, int height, int flags)
DRCaecea382014-08-11 18:05:41 +00001743{
DRC19c791c2018-03-08 10:55:20 -06001744 unsigned char *dstPlanes[3];
1745 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1746 int i, jpegwidth, jpegheight, scaledw, scaledh;
DRCaecea382014-08-11 18:05:41 +00001747
DRCd8b95102019-04-12 07:49:35 -05001748 GET_DINSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001749 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
DRCaecea382014-08-11 18:05:41 +00001750
DRC19c791c2018-03-08 10:55:20 -06001751 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
DRCd8b95102019-04-12 07:49:35 -05001752 pad < 1 || !IS_POW2(pad) || height < 0)
1753 THROW("tjDecompressToYUV2(): Invalid argument");
DRCaecea382014-08-11 18:05:41 +00001754
DRC19c791c2018-03-08 10:55:20 -06001755 if (setjmp(this->jerr.setjmp_buffer)) {
1756 /* If we get here, the JPEG code has signaled an error. */
1757 return -1;
1758 }
DRCdec79952016-04-20 11:27:42 -05001759
DRC19c791c2018-03-08 10:55:20 -06001760 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1761 jpeg_read_header(dinfo, TRUE);
1762 jpegSubsamp = getSubsamp(dinfo);
1763 if (jpegSubsamp < 0)
DRCd8b95102019-04-12 07:49:35 -05001764 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
DRCaecea382014-08-11 18:05:41 +00001765
DRC19c791c2018-03-08 10:55:20 -06001766 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1767 if (width == 0) width = jpegwidth;
1768 if (height == 0) height = jpegheight;
DRCaecea382014-08-11 18:05:41 +00001769
DRC19c791c2018-03-08 10:55:20 -06001770 for (i = 0; i < NUMSF; i++) {
1771 scaledw = TJSCALED(jpegwidth, sf[i]);
1772 scaledh = TJSCALED(jpegheight, sf[i]);
1773 if (scaledw <= width && scaledh <= height)
1774 break;
1775 }
1776 if (i >= NUMSF)
DRCd8b95102019-04-12 07:49:35 -05001777 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
DRCaecea382014-08-11 18:05:41 +00001778
DRC19c791c2018-03-08 10:55:20 -06001779 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1780 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1781 dstPlanes[0] = dstBuf;
1782 strides[0] = PAD(pw0, pad);
1783 if (jpegSubsamp == TJSAMP_GRAY) {
1784 strides[1] = strides[2] = 0;
1785 dstPlanes[1] = dstPlanes[2] = NULL;
1786 } else {
1787 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1788 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
DRCaecea382014-08-11 18:05:41 +00001789
DRC19c791c2018-03-08 10:55:20 -06001790 strides[1] = strides[2] = PAD(pw1, pad);
1791 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1792 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1793 }
DRCaecea382014-08-11 18:05:41 +00001794
DRC19c791c2018-03-08 10:55:20 -06001795 this->headerRead = 1;
1796 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1797 strides, height, flags);
DRCaecea382014-08-11 18:05:41 +00001798
DRC19c791c2018-03-08 10:55:20 -06001799bailout:
1800 this->jerr.stopOnWarning = FALSE;
1801 return retval;
DRCaecea382014-08-11 18:05:41 +00001802}
1803
DRC19c791c2018-03-08 10:55:20 -06001804DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1805 unsigned long jpegSize, unsigned char *dstBuf,
1806 int flags)
DRCf610d612013-04-26 10:33:29 +00001807{
DRC19c791c2018-03-08 10:55:20 -06001808 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
DRCf610d612013-04-26 10:33:29 +00001809}
1810
DRC2e7b76b2009-04-03 12:04:24 +00001811
DRC9b28def2011-05-21 14:37:15 +00001812/* Transformer */
DRC890f1e02011-02-26 22:02:37 +00001813
DRC19c791c2018-03-08 10:55:20 -06001814DLLEXPORT tjhandle tjInitTransform(void)
DRC890f1e02011-02-26 22:02:37 +00001815{
DRC19c791c2018-03-08 10:55:20 -06001816 tjinstance *this = NULL;
1817 tjhandle handle = NULL;
1818
1819 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1820 snprintf(errStr, JMSG_LENGTH_MAX,
1821 "tjInitTransform(): Memory allocation failure");
1822 return NULL;
1823 }
1824 MEMZERO(this, sizeof(tjinstance));
1825 snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
1826 handle = _tjInitCompress(this);
1827 if (!handle) return NULL;
1828 handle = _tjInitDecompress(this);
1829 return handle;
DRC890f1e02011-02-26 22:02:37 +00001830}
1831
1832
DRC19c791c2018-03-08 10:55:20 -06001833DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1834 unsigned long jpegSize, int n,
1835 unsigned char **dstBufs, unsigned long *dstSizes,
1836 tjtransform *t, int flags)
DRC890f1e02011-02-26 22:02:37 +00001837{
DRC19c791c2018-03-08 10:55:20 -06001838 jpeg_transform_info *xinfo = NULL;
1839 jvirt_barray_ptr *srccoefs, *dstcoefs;
1840 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
DRC890f1e02011-02-26 22:02:37 +00001841
DRCd8b95102019-04-12 07:49:35 -05001842 GET_INSTANCE(handle);
DRC19c791c2018-03-08 10:55:20 -06001843 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1844 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
DRCd8b95102019-04-12 07:49:35 -05001845 THROW("tjTransform(): Instance has not been initialized for transformation");
DRC890f1e02011-02-26 22:02:37 +00001846
DRC19c791c2018-03-08 10:55:20 -06001847 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1848 dstSizes == NULL || t == NULL || flags < 0)
DRCd8b95102019-04-12 07:49:35 -05001849 THROW("tjTransform(): Invalid argument");
DRC9b28def2011-05-21 14:37:15 +00001850
DRCbd96b302018-03-17 00:06:10 -05001851#ifndef NO_PUTENV
DRC19c791c2018-03-08 10:55:20 -06001852 if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
1853 else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
1854 else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
DRCbd96b302018-03-17 00:06:10 -05001855#endif
DRC890f1e02011-02-26 22:02:37 +00001856
DRC19c791c2018-03-08 10:55:20 -06001857 if ((xinfo =
1858 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05001859 THROW("tjTransform(): Memory allocation failure");
DRC19c791c2018-03-08 10:55:20 -06001860 MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
DRCd4c41fe2017-03-18 12:56:36 -05001861
DRC19c791c2018-03-08 10:55:20 -06001862 if (setjmp(this->jerr.setjmp_buffer)) {
1863 /* If we get here, the JPEG code has signaled an error. */
1864 retval = -1; goto bailout;
1865 }
DRC890f1e02011-02-26 22:02:37 +00001866
DRC19c791c2018-03-08 10:55:20 -06001867 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
DRC890f1e02011-02-26 22:02:37 +00001868
DRC19c791c2018-03-08 10:55:20 -06001869 for (i = 0; i < n; i++) {
1870 xinfo[i].transform = xformtypes[t[i].op];
1871 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1872 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1873 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1874 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1875 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1876 else xinfo[i].slow_hflip = 0;
DRC0a325192011-03-02 09:22:41 +00001877
DRC19c791c2018-03-08 10:55:20 -06001878 if (xinfo[i].crop) {
1879 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1880 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1881 if (t[i].r.w != 0) {
1882 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1883 } else
1884 xinfo[i].crop_width = JCROP_UNSET;
1885 if (t[i].r.h != 0) {
1886 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1887 } else
1888 xinfo[i].crop_height = JCROP_UNSET;
1889 }
1890 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1891 }
DRC890f1e02011-02-26 22:02:37 +00001892
DRC19c791c2018-03-08 10:55:20 -06001893 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1894 jpeg_read_header(dinfo, TRUE);
1895 jpegSubsamp = getSubsamp(dinfo);
1896 if (jpegSubsamp < 0)
DRCd8b95102019-04-12 07:49:35 -05001897 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
DRC890f1e02011-02-26 22:02:37 +00001898
DRC19c791c2018-03-08 10:55:20 -06001899 for (i = 0; i < n; i++) {
1900 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
DRCd8b95102019-04-12 07:49:35 -05001901 THROW("tjTransform(): Transform is not perfect");
DRC890f1e02011-02-26 22:02:37 +00001902
DRC19c791c2018-03-08 10:55:20 -06001903 if (xinfo[i].crop) {
1904 if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
1905 (t[i].r.y % xinfo[i].iMCU_sample_height) != 0) {
1906 snprintf(errStr, JMSG_LENGTH_MAX,
1907 "To crop this JPEG image, x must be a multiple of %d\n"
1908 "and y must be a multiple of %d.\n",
1909 xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
1910 retval = -1; goto bailout;
1911 }
1912 }
1913 }
DRC890f1e02011-02-26 22:02:37 +00001914
DRC19c791c2018-03-08 10:55:20 -06001915 srccoefs = jpeg_read_coefficients(dinfo);
DRC890f1e02011-02-26 22:02:37 +00001916
DRC19c791c2018-03-08 10:55:20 -06001917 for (i = 0; i < n; i++) {
1918 int w, h, alloc = 1;
DRC0a325192011-03-02 09:22:41 +00001919
DRC19c791c2018-03-08 10:55:20 -06001920 if (!xinfo[i].crop) {
1921 w = dinfo->image_width; h = dinfo->image_height;
1922 } else {
1923 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
1924 }
1925 if (flags & TJFLAG_NOREALLOC) {
1926 alloc = 0; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
1927 }
1928 if (!(t[i].options & TJXOPT_NOOUTPUT))
1929 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
1930 jpeg_copy_critical_parameters(dinfo, cinfo);
1931 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
1932 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
1933 jpeg_simple_progression(cinfo);
1934 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
1935 jpeg_write_coefficients(cinfo, dstcoefs);
1936 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
1937 JCOPYOPT_NONE : JCOPYOPT_ALL);
1938 } else
1939 jinit_c_master_control(cinfo, TRUE);
1940 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
1941 if (t[i].customFilter) {
1942 int ci, y;
1943 JDIMENSION by;
DRC890f1e02011-02-26 22:02:37 +00001944
DRC19c791c2018-03-08 10:55:20 -06001945 for (ci = 0; ci < cinfo->num_components; ci++) {
1946 jpeg_component_info *compptr = &cinfo->comp_info[ci];
1947 tjregion arrayRegion = {
1948 0, 0, compptr->width_in_blocks * DCTSIZE, DCTSIZE
1949 };
1950 tjregion planeRegion = {
1951 0, 0, compptr->width_in_blocks * DCTSIZE,
1952 compptr->height_in_blocks * DCTSIZE
1953 };
1954
1955 for (by = 0; by < compptr->height_in_blocks;
1956 by += compptr->v_samp_factor) {
1957 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
1958 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
1959 TRUE);
1960
1961 for (y = 0; y < compptr->v_samp_factor; y++) {
1962 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
1963 i, &t[i]) == -1)
DRCd8b95102019-04-12 07:49:35 -05001964 THROW("tjTransform(): Error in custom filter");
DRC19c791c2018-03-08 10:55:20 -06001965 arrayRegion.y += DCTSIZE;
1966 }
1967 }
1968 }
1969 }
1970 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
1971 }
1972
1973 jpeg_finish_decompress(dinfo);
1974
1975bailout:
1976 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1977 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1978 if (xinfo) free(xinfo);
1979 if (this->jerr.warning) retval = -1;
1980 this->jerr.stopOnWarning = FALSE;
1981 return retval;
DRC890f1e02011-02-26 22:02:37 +00001982}
DRCaa745902017-11-16 18:09:07 -06001983
1984
DRC19c791c2018-03-08 10:55:20 -06001985DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
1986 int align, int *height, int *pixelFormat,
1987 int flags)
DRCaa745902017-11-16 18:09:07 -06001988{
DRC3d9c64e2019-01-01 18:57:36 -06001989 int retval = 0, tempc;
1990 size_t pitch;
DRC19c791c2018-03-08 10:55:20 -06001991 tjhandle handle = NULL;
1992 tjinstance *this;
1993 j_compress_ptr cinfo = NULL;
1994 cjpeg_source_ptr src;
1995 unsigned char *dstBuf = NULL;
1996 FILE *file = NULL;
1997 boolean invert;
DRCaa745902017-11-16 18:09:07 -06001998
DRC19c791c2018-03-08 10:55:20 -06001999 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2000 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
DRCd8b95102019-04-12 07:49:35 -05002001 THROWG("tjLoadImage(): Invalid argument");
DRC19c791c2018-03-08 10:55:20 -06002002 if ((align & (align - 1)) != 0)
DRCd8b95102019-04-12 07:49:35 -05002003 THROWG("tjLoadImage(): Alignment must be a power of 2");
DRCaa745902017-11-16 18:09:07 -06002004
DRC19c791c2018-03-08 10:55:20 -06002005 if ((handle = tjInitCompress()) == NULL) return NULL;
2006 this = (tjinstance *)handle;
2007 cinfo = &this->cinfo;
DRCaa745902017-11-16 18:09:07 -06002008
DRC19c791c2018-03-08 10:55:20 -06002009 if ((file = fopen(filename, "rb")) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002010 THROW_UNIX("tjLoadImage(): Cannot open input file");
DRCaa745902017-11-16 18:09:07 -06002011
DRC19c791c2018-03-08 10:55:20 -06002012 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
DRCd8b95102019-04-12 07:49:35 -05002013 THROW_UNIX("tjLoadImage(): Could not read input file")
DRC19c791c2018-03-08 10:55:20 -06002014 else if (tempc == EOF)
DRCd8b95102019-04-12 07:49:35 -05002015 THROWG("tjLoadImage(): Input file contains no data");
DRCaa745902017-11-16 18:09:07 -06002016
DRC19c791c2018-03-08 10:55:20 -06002017 if (setjmp(this->jerr.setjmp_buffer)) {
2018 /* If we get here, the JPEG code has signaled an error. */
2019 retval = -1; goto bailout;
2020 }
DRCaa745902017-11-16 18:09:07 -06002021
DRC19c791c2018-03-08 10:55:20 -06002022 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2023 else cinfo->in_color_space = pf2cs[*pixelFormat];
2024 if (tempc == 'B') {
2025 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002026 THROWG("tjLoadImage(): Could not initialize bitmap loader");
DRC19c791c2018-03-08 10:55:20 -06002027 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2028 } else if (tempc == 'P') {
2029 if ((src = jinit_read_ppm(cinfo)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002030 THROWG("tjLoadImage(): Could not initialize bitmap loader");
DRC19c791c2018-03-08 10:55:20 -06002031 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2032 } else
DRCd8b95102019-04-12 07:49:35 -05002033 THROWG("tjLoadImage(): Unsupported file type");
DRCaa745902017-11-16 18:09:07 -06002034
DRC19c791c2018-03-08 10:55:20 -06002035 src->input_file = file;
2036 (*src->start_input) (cinfo, src);
2037 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
DRCaa745902017-11-16 18:09:07 -06002038
DRC19c791c2018-03-08 10:55:20 -06002039 *width = cinfo->image_width; *height = cinfo->image_height;
2040 *pixelFormat = cs2pf[cinfo->in_color_space];
DRCaa745902017-11-16 18:09:07 -06002041
DRC19c791c2018-03-08 10:55:20 -06002042 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
DRC3d9c64e2019-01-01 18:57:36 -06002043 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2044 (unsigned long long)((size_t)-1) ||
2045 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002046 THROWG("tjLoadImage(): Memory allocation failure");
DRCaa745902017-11-16 18:09:07 -06002047
DRC19c791c2018-03-08 10:55:20 -06002048 if (setjmp(this->jerr.setjmp_buffer)) {
2049 /* If we get here, the JPEG code has signaled an error. */
2050 retval = -1; goto bailout;
2051 }
DRCaa745902017-11-16 18:09:07 -06002052
DRC19c791c2018-03-08 10:55:20 -06002053 while (cinfo->next_scanline < cinfo->image_height) {
2054 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
DRCaa745902017-11-16 18:09:07 -06002055
DRC19c791c2018-03-08 10:55:20 -06002056 for (i = 0; i < nlines; i++) {
2057 unsigned char *dstptr;
2058 int row;
DRCaa745902017-11-16 18:09:07 -06002059
DRC19c791c2018-03-08 10:55:20 -06002060 row = cinfo->next_scanline + i;
2061 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2062 else dstptr = &dstBuf[row * pitch];
2063 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2064 }
2065 cinfo->next_scanline += nlines;
2066 }
2067
2068 (*src->finish_input) (cinfo, src);
2069
2070bailout:
2071 if (handle) tjDestroy(handle);
2072 if (file) fclose(file);
2073 if (retval < 0 && dstBuf) { free(dstBuf); dstBuf = NULL; }
2074 return dstBuf;
DRCaa745902017-11-16 18:09:07 -06002075}
2076
2077
DRC19c791c2018-03-08 10:55:20 -06002078DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2079 int width, int pitch, int height, int pixelFormat,
2080 int flags)
DRCaa745902017-11-16 18:09:07 -06002081{
DRC19c791c2018-03-08 10:55:20 -06002082 int retval = 0;
2083 tjhandle handle = NULL;
2084 tjinstance *this;
2085 j_decompress_ptr dinfo = NULL;
2086 djpeg_dest_ptr dst;
2087 FILE *file = NULL;
2088 char *ptr = NULL;
2089 boolean invert;
DRCaa745902017-11-16 18:09:07 -06002090
DRC19c791c2018-03-08 10:55:20 -06002091 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2092 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
DRCd8b95102019-04-12 07:49:35 -05002093 THROWG("tjSaveImage(): Invalid argument");
DRCaa745902017-11-16 18:09:07 -06002094
DRC19c791c2018-03-08 10:55:20 -06002095 if ((handle = tjInitDecompress()) == NULL)
2096 return -1;
2097 this = (tjinstance *)handle;
2098 dinfo = &this->dinfo;
DRCaa745902017-11-16 18:09:07 -06002099
DRC19c791c2018-03-08 10:55:20 -06002100 if ((file = fopen(filename, "wb")) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002101 THROW_UNIX("tjSaveImage(): Cannot open output file");
DRCaa745902017-11-16 18:09:07 -06002102
DRC19c791c2018-03-08 10:55:20 -06002103 if (setjmp(this->jerr.setjmp_buffer)) {
2104 /* If we get here, the JPEG code has signaled an error. */
2105 retval = -1; goto bailout;
2106 }
DRCaa745902017-11-16 18:09:07 -06002107
DRC19c791c2018-03-08 10:55:20 -06002108 this->dinfo.out_color_space = pf2cs[pixelFormat];
2109 dinfo->image_width = width; dinfo->image_height = height;
2110 dinfo->global_state = DSTATE_READY;
2111 dinfo->scale_num = dinfo->scale_denom = 1;
DRCaa745902017-11-16 18:09:07 -06002112
DRC19c791c2018-03-08 10:55:20 -06002113 ptr = strrchr(filename, '.');
2114 if (ptr && !strcasecmp(ptr, ".bmp")) {
2115 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002116 THROWG("tjSaveImage(): Could not initialize bitmap writer");
DRC19c791c2018-03-08 10:55:20 -06002117 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2118 } else {
2119 if ((dst = jinit_write_ppm(dinfo)) == NULL)
DRCd8b95102019-04-12 07:49:35 -05002120 THROWG("tjSaveImage(): Could not initialize PPM writer");
DRC19c791c2018-03-08 10:55:20 -06002121 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2122 }
DRCaa745902017-11-16 18:09:07 -06002123
DRC19c791c2018-03-08 10:55:20 -06002124 dst->output_file = file;
2125 (*dst->start_output) (dinfo, dst);
2126 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
DRCaa745902017-11-16 18:09:07 -06002127
DRC19c791c2018-03-08 10:55:20 -06002128 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
DRCaa745902017-11-16 18:09:07 -06002129
DRC19c791c2018-03-08 10:55:20 -06002130 while (dinfo->output_scanline < dinfo->output_height) {
2131 unsigned char *rowptr;
DRCaa745902017-11-16 18:09:07 -06002132
DRC19c791c2018-03-08 10:55:20 -06002133 if (invert)
2134 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2135 else
2136 rowptr = &buffer[dinfo->output_scanline * pitch];
2137 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2138 (*dst->put_pixel_rows) (dinfo, dst, 1);
2139 dinfo->output_scanline++;
2140 }
DRCaa745902017-11-16 18:09:07 -06002141
DRC19c791c2018-03-08 10:55:20 -06002142 (*dst->finish_output) (dinfo, dst);
2143
2144bailout:
2145 if (handle) tjDestroy(handle);
2146 if (file) fclose(file);
2147 return retval;
DRCaa745902017-11-16 18:09:07 -06002148}