2 * Copyright (C)2009-2023 D. R. Commander. All Rights Reserved.
3 * Copyright (C)2021 Alex Richardson. All Rights Reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * - Neither the name of the libjpeg-turbo Project nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
30 /* TurboJPEG/LJT: this implements the TurboJPEG API using libjpeg or
36 #define JPEG_INTERNALS
41 #include "./turbojpeg.h"
44 #include "./jpegapicomp.h"
47 extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, size_t *,
49 extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *, size_t);
51 #define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
52 #define IS_POW2(x) (((x) & (x - 1)) == 0)
55 /* Error handling (based on example in example.c) */
57 static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
60 struct jpeg_error_mgr pub;
61 jmp_buf setjmp_buffer;
62 void (*emit_message) (j_common_ptr, int);
63 boolean warning, stopOnWarning;
65 typedef struct my_error_mgr *my_error_ptr;
67 #define JMESSAGE(code, string) string,
68 static const char *turbojpeg_message_table[] = {
73 static void my_error_exit(j_common_ptr cinfo)
75 my_error_ptr myerr = (my_error_ptr)cinfo->err;
77 (*cinfo->err->output_message) (cinfo);
78 longjmp(myerr->setjmp_buffer, 1);
81 /* Based on output_message() in jerror.c */
83 static void my_output_message(j_common_ptr cinfo)
85 (*cinfo->err->format_message) (cinfo, errStr);
88 static void my_emit_message(j_common_ptr cinfo, int msg_level)
90 my_error_ptr myerr = (my_error_ptr)cinfo->err;
92 myerr->emit_message(cinfo, msg_level);
94 myerr->warning = TRUE;
95 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
100 /********************** Global structures, macros, etc. **********************/
102 enum { COMPRESS = 1, DECOMPRESS = 2 };
104 typedef struct _tjinstance {
105 struct jpeg_compress_struct cinfo;
106 struct jpeg_decompress_struct dinfo;
107 struct my_error_mgr jerr;
109 char errStr[JMSG_LENGTH_MAX];
110 boolean isInstanceError;
112 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
123 boolean fastUpsample;
132 int restartIntervalBlocks;
133 int restartIntervalRows;
137 tjscalingfactor scalingFactor;
138 tjregion croppingRegion;
141 static tjhandle _tjInitCompress(tjinstance *this);
142 static tjhandle _tjInitDecompress(tjinstance *this);
144 struct my_progress_mgr {
145 struct jpeg_progress_mgr pub;
148 typedef struct my_progress_mgr *my_progress_ptr;
150 static void my_progress_monitor(j_common_ptr dinfo)
152 my_error_ptr myerr = (my_error_ptr)dinfo->err;
153 my_progress_ptr myprog = (my_progress_ptr)dinfo->progress;
155 if (dinfo->is_decompressor) {
156 int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number;
158 if (scan_no > myprog->this->scanLimit) {
159 SNPRINTF(myprog->this->errStr, JMSG_LENGTH_MAX,
160 "Progressive JPEG image has more than %d scans",
161 myprog->this->scanLimit);
162 SNPRINTF(errStr, JMSG_LENGTH_MAX,
163 "Progressive JPEG image has more than %d scans",
164 myprog->this->scanLimit);
165 myprog->this->isInstanceError = TRUE;
166 myerr->warning = FALSE;
167 longjmp(myerr->setjmp_buffer, 1);
172 static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
173 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
174 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
178 static const tjscalingfactor sf[NUMSF] = {
197 static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
198 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
199 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
200 JCS_EXT_ARGB, JCS_CMYK
203 static int cs2pf[JPEG_NUMCS] = {
204 TJPF_UNKNOWN, TJPF_GRAY,
205 #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
207 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
209 #elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
211 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
213 #elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
215 #elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
218 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
219 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
223 #define THROWG(m, rv) { \
224 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
225 retval = rv; goto bailout; \
228 #define THROW_UNIX(m) { \
229 char strerrorBuf[80] = { 0 }; \
230 strerror_s(strerrorBuf, 80, errno); \
231 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
233 this->isInstanceError = TRUE; \
234 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
236 retval = -1; goto bailout; \
239 #define THROW_UNIX(m) { \
240 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
242 this->isInstanceError = TRUE; \
243 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
245 retval = -1; goto bailout; \
249 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
250 this->isInstanceError = TRUE; THROWG(m, -1) \
252 #define THROWI(format, val1, val2) { \
253 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, \
255 this->isInstanceError = TRUE; \
256 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val1, \
258 retval = -1; goto bailout; \
261 #define GET_INSTANCE(handle) \
262 tjinstance *this = (tjinstance *)handle; \
263 j_compress_ptr cinfo = NULL; \
264 j_decompress_ptr dinfo = NULL; \
267 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
270 cinfo = &this->cinfo; dinfo = &this->dinfo; \
271 this->jerr.warning = FALSE; \
272 this->isInstanceError = FALSE;
274 #define GET_CINSTANCE(handle) \
275 tjinstance *this = (tjinstance *)handle; \
276 j_compress_ptr cinfo = NULL; \
279 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
282 cinfo = &this->cinfo; \
283 this->jerr.warning = FALSE; \
284 this->isInstanceError = FALSE;
286 #define GET_DINSTANCE(handle) \
287 tjinstance *this = (tjinstance *)handle; \
288 j_decompress_ptr dinfo = NULL; \
291 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
294 dinfo = &this->dinfo; \
295 this->jerr.warning = FALSE; \
296 this->isInstanceError = FALSE;
298 #define GET_TJINSTANCE(handle, errorReturn) \
299 tjinstance *this = (tjinstance *)handle; \
302 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
303 return errorReturn; \
305 this->jerr.warning = FALSE; \
306 this->isInstanceError = FALSE;
308 static int getPixelFormat(int pixelSize, int flags)
310 if (pixelSize == 1) return TJPF_GRAY;
311 if (pixelSize == 3) {
312 if (flags & TJ_BGR) return TJPF_BGR;
313 else return TJPF_RGB;
315 if (pixelSize == 4) {
316 if (flags & TJ_ALPHAFIRST) {
317 if (flags & TJ_BGR) return TJPF_XBGR;
318 else return TJPF_XRGB;
320 if (flags & TJ_BGR) return TJPF_BGRX;
321 else return TJPF_RGBX;
327 static void setCompDefaults(tjinstance *this, int pixelFormat)
329 this->cinfo.in_color_space = pf2cs[pixelFormat];
330 this->cinfo.input_components = tjPixelSize[pixelFormat];
331 jpeg_set_defaults(&this->cinfo);
333 this->cinfo.restart_interval = this->restartIntervalBlocks;
334 this->cinfo.restart_in_rows = this->restartIntervalRows;
335 this->cinfo.X_density = (UINT16)this->xDensity;
336 this->cinfo.Y_density = (UINT16)this->yDensity;
337 this->cinfo.density_unit = (UINT8)this->densityUnits;
339 if (this->lossless) {
340 #ifdef C_LOSSLESS_SUPPORTED
341 jpeg_enable_lossless(&this->cinfo, this->losslessPSV, this->losslessPt);
343 if (pixelFormat == TJPF_GRAY)
344 this->subsamp = TJSAMP_GRAY;
345 else if (this->subsamp != TJSAMP_GRAY)
346 this->subsamp = TJSAMP_444;
350 jpeg_set_quality(&this->cinfo, this->quality, TRUE);
351 this->cinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
353 switch (this->colorspace) {
355 jpeg_set_colorspace(&this->cinfo, JCS_RGB); break;
357 jpeg_set_colorspace(&this->cinfo, JCS_YCbCr); break;
359 jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE); break;
361 jpeg_set_colorspace(&this->cinfo, JCS_CMYK); break;
363 jpeg_set_colorspace(&this->cinfo, JCS_YCCK); break;
365 if (this->subsamp == TJSAMP_GRAY)
366 jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE);
367 else if (pixelFormat == TJPF_CMYK)
368 jpeg_set_colorspace(&this->cinfo, JCS_YCCK);
370 jpeg_set_colorspace(&this->cinfo, JCS_YCbCr);
373 this->cinfo.optimize_coding = this->optimize;
374 #ifdef C_PROGRESSIVE_SUPPORTED
375 if (this->progressive) jpeg_simple_progression(&this->cinfo);
377 this->cinfo.arith_code = this->arithmetic;
379 this->cinfo.comp_info[0].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
380 this->cinfo.comp_info[1].h_samp_factor = 1;
381 this->cinfo.comp_info[2].h_samp_factor = 1;
382 if (this->cinfo.num_components > 3)
383 this->cinfo.comp_info[3].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
384 this->cinfo.comp_info[0].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
385 this->cinfo.comp_info[1].v_samp_factor = 1;
386 this->cinfo.comp_info[2].v_samp_factor = 1;
387 if (this->cinfo.num_components > 3)
388 this->cinfo.comp_info[3].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
392 static int getSubsamp(j_decompress_ptr dinfo)
394 int retval = TJSAMP_UNKNOWN, i, k;
396 /* The sampling factors actually have no meaning with grayscale JPEG files,
397 and in fact it's possible to generate grayscale JPEGs with sampling
398 factors > 1 (even though those sampling factors are ignored by the
399 decompressor.) Thus, we need to treat grayscale as a special case. */
400 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
403 for (i = 0; i < TJ_NUMSAMP; i++) {
404 if (i == TJSAMP_GRAY) continue;
406 if (dinfo->num_components == 3 ||
407 ((dinfo->jpeg_color_space == JCS_YCCK ||
408 dinfo->jpeg_color_space == JCS_CMYK) &&
409 dinfo->num_components == 4)) {
410 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
411 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
414 for (k = 1; k < dinfo->num_components; k++) {
415 int href = 1, vref = 1;
417 if ((dinfo->jpeg_color_space == JCS_YCCK ||
418 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
419 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
421 if (dinfo->comp_info[k].h_samp_factor == href &&
422 dinfo->comp_info[k].v_samp_factor == vref)
425 if (match == dinfo->num_components - 1) {
429 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
430 in non-standard ways. */
431 if (dinfo->comp_info[0].h_samp_factor == 2 &&
432 dinfo->comp_info[0].v_samp_factor == 2 &&
433 (i == TJSAMP_422 || i == TJSAMP_440)) {
436 for (k = 1; k < dinfo->num_components; k++) {
437 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
439 if ((dinfo->jpeg_color_space == JCS_YCCK ||
440 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
443 if (dinfo->comp_info[k].h_samp_factor == href &&
444 dinfo->comp_info[k].v_samp_factor == vref)
447 if (match == dinfo->num_components - 1) {
451 /* Handle 4:4:4 images whose sampling factors are specified in
452 non-standard ways. */
453 if (dinfo->comp_info[0].h_samp_factor *
454 dinfo->comp_info[0].v_samp_factor <=
455 D_MAX_BLOCKS_IN_MCU / 3 && i == TJSAMP_444) {
457 for (k = 1; k < dinfo->num_components; k++) {
458 if (dinfo->comp_info[k].h_samp_factor ==
459 dinfo->comp_info[0].h_samp_factor &&
460 dinfo->comp_info[k].v_samp_factor ==
461 dinfo->comp_info[0].v_samp_factor)
463 if (match == dinfo->num_components - 1) {
474 static void setDecompParameters(tjinstance *this)
476 this->subsamp = getSubsamp(&this->dinfo);
477 this->jpegWidth = this->dinfo.image_width;
478 this->jpegHeight = this->dinfo.image_height;
479 this->precision = this->dinfo.data_precision;
480 switch (this->dinfo.jpeg_color_space) {
481 case JCS_GRAYSCALE: this->colorspace = TJCS_GRAY; break;
482 case JCS_RGB: this->colorspace = TJCS_RGB; break;
483 case JCS_YCbCr: this->colorspace = TJCS_YCbCr; break;
484 case JCS_CMYK: this->colorspace = TJCS_CMYK; break;
485 case JCS_YCCK: this->colorspace = TJCS_YCCK; break;
486 default: this->colorspace = -1; break;
488 this->progressive = this->dinfo.progressive_mode;
489 this->arithmetic = this->dinfo.arith_code;
490 this->lossless = this->dinfo.master->lossless;
491 this->losslessPSV = this->dinfo.Ss;
492 this->losslessPt = this->dinfo.Al;
493 this->xDensity = this->dinfo.X_density;
494 this->yDensity = this->dinfo.Y_density;
495 this->densityUnits = this->dinfo.density_unit;
499 static void processFlags(tjhandle handle, int flags, int operation)
501 tjinstance *this = (tjinstance *)handle;
503 this->bottomUp = !!(flags & TJFLAG_BOTTOMUP);
506 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
507 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
508 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
511 this->fastUpsample = !!(flags & TJFLAG_FASTUPSAMPLE);
512 this->noRealloc = !!(flags & TJFLAG_NOREALLOC);
514 if (operation == COMPRESS) {
515 if (this->quality >= 96 || flags & TJFLAG_ACCURATEDCT)
516 this->fastDCT = FALSE;
518 this->fastDCT = TRUE;
520 this->fastDCT = !!(flags & TJFLAG_FASTDCT);
522 this->jerr.stopOnWarning = !!(flags & TJFLAG_STOPONWARNING);
523 this->progressive = !!(flags & TJFLAG_PROGRESSIVE);
525 if (flags & TJFLAG_LIMITSCANS) this->scanLimit = 500;
529 /*************************** General API functions ***************************/
532 DLLEXPORT tjhandle tj3Init(int initType)
534 static const char FUNCTION_NAME[] = "tj3Init";
535 tjinstance *this = NULL;
536 tjhandle retval = NULL;
538 if (initType < 0 || initType >= TJ_NUMINIT)
539 THROWG("Invalid argument", NULL);
541 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL)
542 THROWG("Memory allocation failure", NULL);
543 memset(this, 0, sizeof(tjinstance));
544 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
547 this->subsamp = TJSAMP_UNKNOWN;
548 this->jpegWidth = -1;
549 this->jpegHeight = -1;
551 this->colorspace = -1;
552 this->losslessPSV = 1;
555 this->scalingFactor = TJUNSCALED;
558 case TJINIT_COMPRESS: return _tjInitCompress(this);
559 case TJINIT_DECOMPRESS: return _tjInitDecompress(this);
560 case TJINIT_TRANSFORM:
561 retval = _tjInitCompress(this);
562 if (!retval) return NULL;
563 retval = _tjInitDecompress(this);
572 #define SET_PARAM(field, minValue, maxValue) { \
573 if (value < minValue || (maxValue > 0 && value > maxValue)) \
574 THROW("Parameter value out of range"); \
575 this->field = value; \
578 #define SET_BOOL_PARAM(field) { \
579 if (value < 0 || value > 1) \
580 THROW("Parameter value out of range"); \
581 this->field = (boolean)value; \
585 DLLEXPORT int tj3Set(tjhandle handle, int param, int value)
587 static const char FUNCTION_NAME[] = "tj3Set";
590 GET_TJINSTANCE(handle, -1);
593 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
594 case TJPARAM_MAXPIXELS:
595 SET_PARAM(maxPixels, 0, -1);
598 case TJPARAM_STOPONWARNING:
599 SET_BOOL_PARAM(jerr.stopOnWarning);
601 case TJPARAM_BOTTOMUP:
602 SET_BOOL_PARAM(bottomUp);
604 case TJPARAM_NOREALLOC:
605 if (!(this->init & COMPRESS))
606 THROW("TJPARAM_NOREALLOC is not applicable to decompression instances.");
607 SET_BOOL_PARAM(noRealloc);
609 case TJPARAM_QUALITY:
610 if (!(this->init & COMPRESS))
611 THROW("TJPARAM_QUALITY is not applicable to decompression instances.");
612 SET_PARAM(quality, 1, 100);
614 case TJPARAM_SUBSAMP:
615 SET_PARAM(subsamp, 0, TJ_NUMSAMP - 1);
617 case TJPARAM_JPEGWIDTH:
618 if (!(this->init & DECOMPRESS))
619 THROW("TJPARAM_JPEGWIDTH is not applicable to compression instances.");
620 THROW("TJPARAM_JPEGWIDTH is read-only in decompression instances.");
622 case TJPARAM_JPEGHEIGHT:
623 if (!(this->init & DECOMPRESS))
624 THROW("TJPARAM_JPEGHEIGHT is not applicable to compression instances.");
625 THROW("TJPARAM_JPEGHEIGHT is read-only in decompression instances.");
627 case TJPARAM_PRECISION:
628 if (!(this->init & DECOMPRESS))
629 THROW("TJPARAM_PRECISION is not applicable to compression instances.");
630 THROW("TJPARAM_PRECISION is read-only in decompression instances.");
632 case TJPARAM_COLORSPACE:
633 if (!(this->init & COMPRESS))
634 THROW("TJPARAM_COLORSPACE is read-only in decompression instances.");
635 SET_PARAM(colorspace, 0, TJ_NUMCS - 1);
637 case TJPARAM_FASTUPSAMPLE:
638 if (!(this->init & DECOMPRESS))
639 THROW("TJPARAM_FASTUPSAMPLE is not applicable to compression instances.");
640 SET_BOOL_PARAM(fastUpsample);
642 case TJPARAM_FASTDCT:
643 SET_BOOL_PARAM(fastDCT);
645 case TJPARAM_OPTIMIZE:
646 if (!(this->init & COMPRESS))
647 THROW("TJPARAM_OPTIMIZE is not applicable to decompression instances.");
648 SET_BOOL_PARAM(optimize);
650 case TJPARAM_PROGRESSIVE:
651 if (!(this->init & COMPRESS))
652 THROW("TJPARAM_PROGRESSIVE is read-only in decompression instances.");
653 SET_BOOL_PARAM(progressive);
655 case TJPARAM_SCANLIMIT:
656 if (!(this->init & DECOMPRESS))
657 THROW("TJPARAM_SCANLIMIT is not applicable to compression instances.");
658 SET_PARAM(scanLimit, 0, -1);
660 case TJPARAM_ARITHMETIC:
661 if (!(this->init & COMPRESS))
662 THROW("TJPARAM_ARITHMETIC is read-only in decompression instances.");
663 SET_BOOL_PARAM(arithmetic);
665 case TJPARAM_LOSSLESS:
666 if (!(this->init & COMPRESS))
667 THROW("TJPARAM_LOSSLESS is read-only in decompression instances.");
668 SET_BOOL_PARAM(lossless);
670 case TJPARAM_LOSSLESSPSV:
671 if (!(this->init & COMPRESS))
672 THROW("TJPARAM_LOSSLESSPSV is read-only in decompression instances.");
673 SET_PARAM(losslessPSV, 1, 7);
675 case TJPARAM_LOSSLESSPT:
676 if (!(this->init & COMPRESS))
677 THROW("TJPARAM_LOSSLESSPT is read-only in decompression instances.");
678 SET_PARAM(losslessPt, 0, this->precision - 1);
680 case TJPARAM_RESTARTBLOCKS:
681 if (!(this->init & COMPRESS))
682 THROW("TJPARAM_RESTARTBLOCKS is not applicable to decompression instances.");
683 SET_PARAM(restartIntervalBlocks, 0, 65535);
684 if (value != 0) this->restartIntervalRows = 0;
686 case TJPARAM_RESTARTROWS:
687 if (!(this->init & COMPRESS))
688 THROW("TJPARAM_RESTARTROWS is not applicable to decompression instances.");
689 SET_PARAM(restartIntervalRows, 0, 65535);
690 if (value != 0) this->restartIntervalBlocks = 0;
692 case TJPARAM_XDENSITY:
693 if (!(this->init & COMPRESS))
694 THROW("TJPARAM_XDENSITY is read-only in decompression instances.");
695 SET_PARAM(xDensity, 1, 65535);
697 case TJPARAM_YDENSITY:
698 if (!(this->init & COMPRESS))
699 THROW("TJPARAM_YDENSITY is read-only in decompression instances.");
700 SET_PARAM(yDensity, 1, 65535);
702 case TJPARAM_DENSITYUNITS:
703 if (!(this->init & COMPRESS))
704 THROW("TJPARAM_DENSITYUNITS is read-only in decompression instances.");
705 SET_PARAM(densityUnits, 0, 2);
708 THROW("Invalid parameter");
717 DLLEXPORT int tj3Get(tjhandle handle, int param)
719 tjinstance *this = (tjinstance *)handle;
720 if (!this) return -1;
723 case TJPARAM_STOPONWARNING:
724 return this->jerr.stopOnWarning;
725 case TJPARAM_BOTTOMUP:
726 return this->bottomUp;
727 case TJPARAM_NOREALLOC:
728 return this->noRealloc;
729 case TJPARAM_QUALITY:
730 return this->quality;
731 case TJPARAM_SUBSAMP:
732 return this->subsamp;
733 case TJPARAM_JPEGWIDTH:
734 return this->jpegWidth;
735 case TJPARAM_JPEGHEIGHT:
736 return this->jpegHeight;
737 case TJPARAM_PRECISION:
738 return this->precision;
739 case TJPARAM_COLORSPACE:
740 return this->colorspace;
741 case TJPARAM_FASTUPSAMPLE:
742 return this->fastUpsample;
743 case TJPARAM_FASTDCT:
744 return this->fastDCT;
745 case TJPARAM_OPTIMIZE:
746 return this->optimize;
747 case TJPARAM_PROGRESSIVE:
748 return this->progressive;
749 case TJPARAM_SCANLIMIT:
750 return this->scanLimit;
751 case TJPARAM_ARITHMETIC:
752 return this->arithmetic;
753 case TJPARAM_LOSSLESS:
754 return this->lossless;
755 case TJPARAM_LOSSLESSPSV:
756 return this->losslessPSV;
757 case TJPARAM_LOSSLESSPT:
758 return this->losslessPt;
759 case TJPARAM_RESTARTBLOCKS:
760 return this->restartIntervalBlocks;
761 case TJPARAM_RESTARTROWS:
762 return this->restartIntervalRows;
763 case TJPARAM_XDENSITY:
764 return this->xDensity;
765 case TJPARAM_YDENSITY:
766 return this->yDensity;
767 case TJPARAM_DENSITYUNITS:
768 return this->densityUnits;
776 DLLEXPORT char *tj3GetErrorStr(tjhandle handle)
778 tjinstance *this = (tjinstance *)handle;
780 if (this && this->isInstanceError) {
781 this->isInstanceError = FALSE;
788 DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
790 return tj3GetErrorStr(handle);
794 DLLEXPORT char *tjGetErrorStr(void)
801 DLLEXPORT int tj3GetErrorCode(tjhandle handle)
803 tjinstance *this = (tjinstance *)handle;
805 if (this && this->jerr.warning) return TJERR_WARNING;
806 else return TJERR_FATAL;
810 DLLEXPORT int tjGetErrorCode(tjhandle handle)
812 return tj3GetErrorCode(handle);
817 DLLEXPORT void tj3Destroy(tjhandle handle)
819 tjinstance *this = (tjinstance *)handle;
820 j_compress_ptr cinfo = NULL;
821 j_decompress_ptr dinfo = NULL;
825 cinfo = &this->cinfo; dinfo = &this->dinfo;
826 this->jerr.warning = FALSE;
827 this->isInstanceError = FALSE;
829 if (setjmp(this->jerr.setjmp_buffer)) return;
830 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
831 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
836 DLLEXPORT int tjDestroy(tjhandle handle)
838 static const char FUNCTION_NAME[] = "tjDestroy";
841 if (!handle) THROWG("Invalid handle", -1);
843 SNPRINTF(errStr, JMSG_LENGTH_MAX, "No error");
845 if (strcmp(errStr, "No error")) retval = -1;
852 /* These are exposed mainly because Windows can't malloc() and free() across
853 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
854 with turbojpeg.dll for compatibility reasons. However, these functions
855 can potentially be used for other purposes by different implementations. */
858 DLLEXPORT void tj3Free(void *buf)
864 DLLEXPORT void tjFree(unsigned char *buf)
871 DLLEXPORT void *tj3Alloc(size_t bytes)
873 return malloc(bytes);
877 DLLEXPORT unsigned char *tjAlloc(int bytes)
879 return (unsigned char *)tj3Alloc((size_t)bytes);
883 /******************************** Compressor *********************************/
885 static tjhandle _tjInitCompress(tjinstance *this)
887 static unsigned char buffer[1];
888 unsigned char *buf = buffer;
891 /* This is also straight out of example.c */
892 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
893 this->jerr.pub.error_exit = my_error_exit;
894 this->jerr.pub.output_message = my_output_message;
895 this->jerr.emit_message = this->jerr.pub.emit_message;
896 this->jerr.pub.emit_message = my_emit_message;
897 this->jerr.pub.addon_message_table = turbojpeg_message_table;
898 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
899 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
901 if (setjmp(this->jerr.setjmp_buffer)) {
902 /* If we get here, the JPEG code has signaled an error. */
907 jpeg_create_compress(&this->cinfo);
908 /* Make an initial call so it will create the destination manager */
909 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
911 this->init |= COMPRESS;
912 return (tjhandle)this;
916 DLLEXPORT tjhandle tjInitCompress(void)
918 return tj3Init(TJINIT_COMPRESS);
923 DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp)
925 static const char FUNCTION_NAME[] = "tj3JPEGBufSize";
926 unsigned long long retval = 0;
927 int mcuw, mcuh, chromasf;
929 if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN ||
930 jpegSubsamp >= TJ_NUMSAMP)
931 THROWG("Invalid argument", 0);
933 if (jpegSubsamp == TJSAMP_UNKNOWN)
934 jpegSubsamp = TJSAMP_444;
936 /* This allows for rare corner cases in which a JPEG image can actually be
937 larger than the uncompressed input (we wouldn't mention it if it hadn't
939 mcuw = tjMCUWidth[jpegSubsamp];
940 mcuh = tjMCUHeight[jpegSubsamp];
941 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
942 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
943 if (retval > (unsigned long long)((unsigned long)-1))
944 THROWG("Image is too large", 0);
947 return (size_t)retval;
951 DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
953 static const char FUNCTION_NAME[] = "tjBufSize";
957 THROWG("Invalid argument", 0);
959 retval = tj3JPEGBufSize(width, height, jpegSubsamp);
962 return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
966 DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
968 static const char FUNCTION_NAME[] = "TJBUFSIZE";
969 unsigned long long retval = 0;
971 if (width < 1 || height < 1)
972 THROWG("Invalid argument", (unsigned long)-1);
974 /* This allows for rare corner cases in which a JPEG image can actually be
975 larger than the uncompressed input (we wouldn't mention it if it hadn't
977 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
978 if (retval > (unsigned long long)((unsigned long)-1))
979 THROWG("Image is too large", (unsigned long)-1);
982 return (unsigned long)retval;
987 DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp)
989 static const char FUNCTION_NAME[] = "tj3YUVBufSize";
990 unsigned long long retval = 0;
993 if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP)
994 THROWG("Invalid argument", 0);
996 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
997 for (i = 0; i < nc; i++) {
998 int pw = tj3YUVPlaneWidth(i, width, subsamp);
999 int stride = PAD(pw, align);
1000 int ph = tj3YUVPlaneHeight(i, height, subsamp);
1002 if (pw == 0 || ph == 0) return 0;
1003 else retval += (unsigned long long)stride * ph;
1005 if (retval > (unsigned long long)((unsigned long)-1))
1006 THROWG("Image is too large", 0);
1009 return (size_t)retval;
1012 /* TurboJPEG 1.4+ */
1013 DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height,
1016 size_t retval = tj3YUVBufSize(width, align, height, subsamp);
1017 return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
1020 /* TurboJPEG 1.2+ */
1021 DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
1023 return tjBufSizeYUV2(width, 4, height, subsamp);
1026 /* TurboJPEG 1.1+ */
1027 DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
1029 return tjBufSizeYUV(width, height, subsamp);
1034 DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp)
1036 static const char FUNCTION_NAME[] = "tj3YUVPlaneWidth";
1037 unsigned long long pw, retval = 0;
1040 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1041 THROWG("Invalid argument", 0);
1042 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1043 if (componentID < 0 || componentID >= nc)
1044 THROWG("Invalid argument", 0);
1046 pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8);
1047 if (componentID == 0)
1050 retval = pw * 8 / tjMCUWidth[subsamp];
1052 if (retval > (unsigned long long)INT_MAX)
1053 THROWG("Width is too large", 0);
1059 /* TurboJPEG 1.4+ */
1060 DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
1062 int retval = tj3YUVPlaneWidth(componentID, width, subsamp);
1063 return (retval == 0) ? -1 : retval;
1068 DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp)
1070 static const char FUNCTION_NAME[] = "tj3YUVPlaneHeight";
1071 unsigned long long ph, retval = 0;
1074 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1075 THROWG("Invalid argument", 0);
1076 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1077 if (componentID < 0 || componentID >= nc)
1078 THROWG("Invalid argument", 0);
1080 ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8);
1081 if (componentID == 0)
1084 retval = ph * 8 / tjMCUHeight[subsamp];
1086 if (retval > (unsigned long long)INT_MAX)
1087 THROWG("Height is too large", 0);
1093 /* TurboJPEG 1.4+ */
1094 DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
1096 int retval = tj3YUVPlaneHeight(componentID, height, subsamp);
1097 return (retval == 0) ? -1 : retval;
1102 DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride,
1103 int height, int subsamp)
1105 static const char FUNCTION_NAME[] = "tj3YUVPlaneSize";
1106 unsigned long long retval = 0;
1109 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1110 THROWG("Invalid argument", 0);
1112 pw = tj3YUVPlaneWidth(componentID, width, subsamp);
1113 ph = tj3YUVPlaneHeight(componentID, height, subsamp);
1114 if (pw == 0 || ph == 0) return 0;
1116 if (stride == 0) stride = pw;
1117 else stride = abs(stride);
1119 retval = (unsigned long long)stride * (ph - 1) + pw;
1120 if (retval > (unsigned long long)((unsigned long)-1))
1121 THROWG("Image is too large", 0);
1124 return (size_t)retval;
1127 /* TurboJPEG 1.4+ */
1128 DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
1129 int height, int subsamp)
1131 size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
1132 return (retval == 0) ? -1 : (unsigned long)retval;
1136 /* tj3Compress*() is implemented in turbojpeg-mp.c */
1137 #define BITS_IN_JSAMPLE 8
1138 #include "turbojpeg-mp.c"
1139 #undef BITS_IN_JSAMPLE
1140 #define BITS_IN_JSAMPLE 12
1141 #include "turbojpeg-mp.c"
1142 #undef BITS_IN_JSAMPLE
1143 #define BITS_IN_JSAMPLE 16
1144 #include "turbojpeg-mp.c"
1145 #undef BITS_IN_JSAMPLE
1147 /* TurboJPEG 1.2+ */
1148 DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
1149 int width, int pitch, int height, int pixelFormat,
1150 unsigned char **jpegBuf, unsigned long *jpegSize,
1151 int jpegSubsamp, int jpegQual, int flags)
1153 static const char FUNCTION_NAME[] = "tjCompress2";
1157 GET_TJINSTANCE(handle, -1);
1159 if (jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP ||
1160 jpegQual < 0 || jpegQual > 100)
1161 THROW("Invalid argument");
1163 this->quality = jpegQual;
1164 this->subsamp = jpegSubsamp;
1165 processFlags(handle, flags, COMPRESS);
1167 size = (size_t)(*jpegSize);
1168 retval = tj3Compress8(handle, srcBuf, width, pitch, height, pixelFormat,
1170 *jpegSize = (unsigned long)size;
1176 /* TurboJPEG 1.0+ */
1177 DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
1178 int pitch, int height, int pixelSize,
1179 unsigned char *jpegBuf, unsigned long *jpegSize,
1180 int jpegSubsamp, int jpegQual, int flags)
1183 unsigned long size = jpegSize ? *jpegSize : 0;
1185 if (flags & TJ_YUV) {
1186 size = tjBufSizeYUV(width, height, jpegSubsamp);
1187 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1188 getPixelFormat(pixelSize, flags), jpegBuf,
1189 jpegSubsamp, flags);
1191 retval = tjCompress2(handle, srcBuf, width, pitch, height,
1192 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
1193 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
1201 DLLEXPORT int tj3EncodeYUVPlanes8(tjhandle handle, const unsigned char *srcBuf,
1202 int width, int pitch, int height,
1203 int pixelFormat, unsigned char **dstPlanes,
1206 static const char FUNCTION_NAME[] = "tj3EncodeYUVPlanes8";
1207 JSAMPROW *row_pointer = NULL;
1208 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
1209 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
1210 JSAMPROW *outbuf[MAX_COMPONENTS];
1211 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1213 jpeg_component_info *compptr;
1215 GET_CINSTANCE(handle)
1217 for (i = 0; i < MAX_COMPONENTS; i++) {
1218 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
1219 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
1222 if ((this->init & COMPRESS) == 0)
1223 THROW("Instance has not been initialized for compression");
1225 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1226 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
1228 THROW("Invalid argument");
1229 if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1230 THROW("Invalid argument");
1232 if (this->subsamp == TJSAMP_UNKNOWN)
1233 THROW("TJPARAM_SUBSAMP must be specified");
1234 if (pixelFormat == TJPF_CMYK)
1235 THROW("Cannot generate YUV images from packed-pixel CMYK images");
1237 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1239 if (setjmp(this->jerr.setjmp_buffer)) {
1240 /* If we get here, the JPEG code has signaled an error. */
1241 retval = -1; goto bailout;
1244 cinfo->image_width = width;
1245 cinfo->image_height = height;
1246 cinfo->data_precision = 8;
1248 setCompDefaults(this, pixelFormat);
1250 /* Execute only the parts of jpeg_start_compress() that we need. If we
1251 were to call the whole jpeg_start_compress() function, then it would try
1252 to write the file headers, which could overflow the output buffer if the
1253 YUV image were very small. */
1254 if (cinfo->global_state != CSTATE_START)
1255 THROW("libjpeg API is in the wrong state");
1256 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
1257 jinit_c_master_control(cinfo, FALSE);
1258 jinit_color_converter(cinfo);
1259 jinit_downsampler(cinfo);
1260 (*cinfo->cconvert->start_pass) (cinfo);
1262 pw0 = PAD(width, cinfo->max_h_samp_factor);
1263 ph0 = PAD(height, cinfo->max_v_samp_factor);
1265 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1266 THROW("Memory allocation failure");
1267 for (i = 0; i < height; i++) {
1269 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
1271 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
1274 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
1276 for (i = 0; i < cinfo->num_components; i++) {
1277 compptr = &cinfo->comp_info[i];
1278 _tmpbuf[i] = (JSAMPLE *)malloc(
1279 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1280 compptr->h_samp_factor, 32) *
1281 cinfo->max_v_samp_factor + 32);
1283 THROW("Memory allocation failure");
1285 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
1287 THROW("Memory allocation failure");
1288 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
1289 unsigned char *_tmpbuf_aligned =
1290 (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
1292 tmpbuf[i][row] = &_tmpbuf_aligned[
1293 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1294 compptr->h_samp_factor, 32) * row];
1297 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1298 compptr->v_samp_factor + 32);
1300 THROW("Memory allocation failure");
1301 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1303 THROW("Memory allocation failure");
1304 for (row = 0; row < compptr->v_samp_factor; row++) {
1305 unsigned char *_tmpbuf2_aligned =
1306 (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32);
1309 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1311 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
1312 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1313 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1315 THROW("Memory allocation failure");
1317 for (row = 0; row < ph[i]; row++) {
1318 outbuf[i][row] = ptr;
1319 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1323 if (setjmp(this->jerr.setjmp_buffer)) {
1324 /* If we get here, the JPEG code has signaled an error. */
1325 retval = -1; goto bailout;
1328 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
1329 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
1330 cinfo->max_v_samp_factor);
1331 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
1332 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
1334 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
1335 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
1336 compptr->v_samp_factor, pw[i]);
1338 cinfo->next_scanline += height;
1339 jpeg_abort_compress(cinfo);
1342 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1344 for (i = 0; i < MAX_COMPONENTS; i++) {
1351 if (this->jerr.warning) retval = -1;
1355 /* TurboJPEG 1.4+ */
1356 DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
1357 int width, int pitch, int height,
1358 int pixelFormat, unsigned char **dstPlanes,
1359 int *strides, int subsamp, int flags)
1361 static const char FUNCTION_NAME[] = "tjEncodeYUVPlanes";
1364 GET_TJINSTANCE(handle, -1);
1366 if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1367 THROW("Invalid argument");
1369 this->subsamp = subsamp;
1370 processFlags(handle, flags, COMPRESS);
1372 return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1373 dstPlanes, strides);
1381 DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf,
1382 int width, int pitch, int height, int pixelFormat,
1383 unsigned char *dstBuf, int align)
1385 static const char FUNCTION_NAME[] = "tj3EncodeYUV8";
1386 unsigned char *dstPlanes[3];
1387 int pw0, ph0, strides[3], retval = -1;
1389 GET_TJINSTANCE(handle, -1);
1391 if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 ||
1393 THROW("Invalid argument");
1395 if (this->subsamp == TJSAMP_UNKNOWN)
1396 THROW("TJPARAM_SUBSAMP must be specified");
1398 pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1399 ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1400 dstPlanes[0] = dstBuf;
1401 strides[0] = PAD(pw0, align);
1402 if (this->subsamp == TJSAMP_GRAY) {
1403 strides[1] = strides[2] = 0;
1404 dstPlanes[1] = dstPlanes[2] = NULL;
1406 int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
1407 int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
1409 strides[1] = strides[2] = PAD(pw1, align);
1410 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1411 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1414 return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1415 dstPlanes, strides);
1421 /* TurboJPEG 1.4+ */
1422 DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
1423 int width, int pitch, int height, int pixelFormat,
1424 unsigned char *dstBuf, int align, int subsamp,
1427 static const char FUNCTION_NAME[] = "tjEncodeYUV3";
1430 GET_TJINSTANCE(handle, -1);
1432 if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1433 THROW("Invalid argument");
1435 this->subsamp = subsamp;
1436 processFlags(handle, flags, COMPRESS);
1438 return tj3EncodeYUV8(handle, srcBuf, width, pitch, height, pixelFormat,
1445 /* TurboJPEG 1.2+ */
1446 DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
1447 int pitch, int height, int pixelFormat,
1448 unsigned char *dstBuf, int subsamp, int flags)
1450 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
1451 dstBuf, 4, subsamp, flags);
1454 /* TurboJPEG 1.1+ */
1455 DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
1456 int pitch, int height, int pixelSize,
1457 unsigned char *dstBuf, int subsamp, int flags)
1459 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1460 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
1466 DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle,
1467 const unsigned char * const *srcPlanes,
1468 int width, const int *strides,
1469 int height, unsigned char **jpegBuf,
1472 static const char FUNCTION_NAME[] = "tj3CompressFromYUVPlanes8";
1473 int i, row, retval = 0;
1474 boolean alloc = TRUE;
1475 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1476 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1477 JSAMPLE *_tmpbuf = NULL, *ptr;
1478 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1480 GET_CINSTANCE(handle)
1482 for (i = 0; i < MAX_COMPONENTS; i++) {
1483 tmpbuf[i] = NULL; inbuf[i] = NULL;
1486 if ((this->init & COMPRESS) == 0)
1487 THROW("Instance has not been initialized for compression");
1489 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
1490 jpegBuf == NULL || jpegSize == NULL)
1491 THROW("Invalid argument");
1492 if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1493 THROW("Invalid argument");
1495 if (this->quality == -1)
1496 THROW("TJPARAM_QUALITY must be specified");
1497 if (this->subsamp == TJSAMP_UNKNOWN)
1498 THROW("TJPARAM_SUBSAMP must be specified");
1500 if (setjmp(this->jerr.setjmp_buffer)) {
1501 /* If we get here, the JPEG code has signaled an error. */
1502 retval = -1; goto bailout;
1505 cinfo->image_width = width;
1506 cinfo->image_height = height;
1507 cinfo->data_precision = 8;
1509 if (this->noRealloc) {
1510 alloc = FALSE; *jpegSize = tj3JPEGBufSize(width, height, this->subsamp);
1512 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
1513 setCompDefaults(this, TJPF_RGB);
1514 cinfo->raw_data_in = TRUE;
1516 jpeg_start_compress(cinfo, TRUE);
1517 for (i = 0; i < cinfo->num_components; i++) {
1518 jpeg_component_info *compptr = &cinfo->comp_info[i];
1521 iw[i] = compptr->width_in_blocks * DCTSIZE;
1522 ih = compptr->height_in_blocks * DCTSIZE;
1523 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1524 compptr->h_samp_factor / cinfo->max_h_samp_factor;
1525 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1526 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1527 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1528 th[i] = compptr->v_samp_factor * DCTSIZE;
1529 tmpbufsize += iw[i] * th[i];
1530 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1531 THROW("Memory allocation failure");
1532 ptr = (JSAMPLE *)srcPlanes[i];
1533 for (row = 0; row < ph[i]; row++) {
1534 inbuf[i][row] = ptr;
1535 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1539 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1540 THROW("Memory allocation failure");
1542 for (i = 0; i < cinfo->num_components; i++) {
1543 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1544 THROW("Memory allocation failure");
1545 for (row = 0; row < th[i]; row++) {
1546 tmpbuf[i][row] = ptr;
1552 if (setjmp(this->jerr.setjmp_buffer)) {
1553 /* If we get here, the JPEG code has signaled an error. */
1554 retval = -1; goto bailout;
1557 for (row = 0; row < (int)cinfo->image_height;
1558 row += cinfo->max_v_samp_factor * DCTSIZE) {
1559 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1560 int crow[MAX_COMPONENTS];
1562 for (i = 0; i < cinfo->num_components; i++) {
1563 jpeg_component_info *compptr = &cinfo->comp_info[i];
1565 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1569 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1570 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1571 /* Duplicate last sample in row to fill out MCU */
1572 for (k = pw[i]; k < iw[i]; k++)
1573 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1575 /* Duplicate last row to fill out MCU */
1576 for (j = ph[i] - crow[i]; j < th[i]; j++)
1577 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1578 yuvptr[i] = tmpbuf[i];
1580 yuvptr[i] = &inbuf[i][crow[i]];
1582 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1584 jpeg_finish_compress(cinfo);
1587 if (cinfo->global_state > CSTATE_START) {
1588 if (alloc) (*cinfo->dest->term_destination) (cinfo);
1589 jpeg_abort_compress(cinfo);
1591 for (i = 0; i < MAX_COMPONENTS; i++) {
1596 if (this->jerr.warning) retval = -1;
1600 /* TurboJPEG 1.4+ */
1601 DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
1602 const unsigned char **srcPlanes,
1603 int width, const int *strides,
1604 int height, int subsamp,
1605 unsigned char **jpegBuf,
1606 unsigned long *jpegSize, int jpegQual,
1609 static const char FUNCTION_NAME[] = "tjCompressFromYUVPlanes";
1613 GET_TJINSTANCE(handle, -1);
1615 if (subsamp < 0 || subsamp >= TJ_NUMSAMP || jpegSize == NULL ||
1616 jpegQual < 0 || jpegQual > 100)
1617 THROW("Invalid argument");
1619 this->quality = jpegQual;
1620 this->subsamp = subsamp;
1621 processFlags(handle, flags, COMPRESS);
1623 size = (size_t)(*jpegSize);
1624 retval = tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1626 *jpegSize = (unsigned long)size;
1634 DLLEXPORT int tj3CompressFromYUV8(tjhandle handle,
1635 const unsigned char *srcBuf, int width,
1636 int align, int height,
1637 unsigned char **jpegBuf, size_t *jpegSize)
1639 static const char FUNCTION_NAME[] = "tj3CompressFromYUV8";
1640 const unsigned char *srcPlanes[3];
1641 int pw0, ph0, strides[3], retval = -1;
1643 GET_TJINSTANCE(handle, -1);
1645 if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) ||
1647 THROW("Invalid argument");
1649 if (this->subsamp == TJSAMP_UNKNOWN)
1650 THROW("TJPARAM_SUBSAMP must be specified");
1652 pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1653 ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1654 srcPlanes[0] = srcBuf;
1655 strides[0] = PAD(pw0, align);
1656 if (this->subsamp == TJSAMP_GRAY) {
1657 strides[1] = strides[2] = 0;
1658 srcPlanes[1] = srcPlanes[2] = NULL;
1660 int pw1 = tjPlaneWidth(1, width, this->subsamp);
1661 int ph1 = tjPlaneHeight(1, height, this->subsamp);
1663 strides[1] = strides[2] = PAD(pw1, align);
1664 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1665 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1668 return tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1675 /* TurboJPEG 1.4+ */
1676 DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1677 int width, int align, int height, int subsamp,
1678 unsigned char **jpegBuf,
1679 unsigned long *jpegSize, int jpegQual,
1682 static const char FUNCTION_NAME[] = "tjCompressFromYUV";
1686 GET_TJINSTANCE(handle, -1);
1688 if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1689 THROW("Invalid argument");
1691 this->quality = jpegQual;
1692 this->subsamp = subsamp;
1693 processFlags(handle, flags, COMPRESS);
1695 size = (size_t)(*jpegSize);
1696 retval = tj3CompressFromYUV8(handle, srcBuf, width, align, height, jpegBuf,
1698 *jpegSize = (unsigned long)size;
1705 /******************************* Decompressor ********************************/
1707 static tjhandle _tjInitDecompress(tjinstance *this)
1709 static unsigned char buffer[1];
1711 /* This is also straight out of example.c */
1712 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1713 this->jerr.pub.error_exit = my_error_exit;
1714 this->jerr.pub.output_message = my_output_message;
1715 this->jerr.emit_message = this->jerr.pub.emit_message;
1716 this->jerr.pub.emit_message = my_emit_message;
1717 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1718 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1719 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1721 if (setjmp(this->jerr.setjmp_buffer)) {
1722 /* If we get here, the JPEG code has signaled an error. */
1727 jpeg_create_decompress(&this->dinfo);
1728 /* Make an initial call so it will create the source manager */
1729 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
1731 this->init |= DECOMPRESS;
1732 return (tjhandle)this;
1735 /* TurboJPEG 1.0+ */
1736 DLLEXPORT tjhandle tjInitDecompress(void)
1738 return tj3Init(TJINIT_DECOMPRESS);
1743 DLLEXPORT int tj3DecompressHeader(tjhandle handle,
1744 const unsigned char *jpegBuf,
1747 static const char FUNCTION_NAME[] = "tj3DecompressHeader";
1750 GET_DINSTANCE(handle);
1751 if ((this->init & DECOMPRESS) == 0)
1752 THROW("Instance has not been initialized for decompression");
1754 if (jpegBuf == NULL || jpegSize <= 0)
1755 THROW("Invalid argument");
1757 if (setjmp(this->jerr.setjmp_buffer)) {
1758 /* If we get here, the JPEG code has signaled an error. */
1762 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1764 /* jpeg_read_header() calls jpeg_abort() and returns JPEG_HEADER_TABLES_ONLY
1765 if the datastream is a tables-only datastream. Since we aren't using a
1766 suspending data source, the only other value it can return is
1768 if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY)
1771 setDecompParameters(this);
1773 jpeg_abort_decompress(dinfo);
1775 if (this->colorspace < 0)
1776 THROW("Could not determine colorspace of JPEG image");
1777 if (this->jpegWidth < 1 || this->jpegHeight < 1)
1778 THROW("Invalid data returned in header");
1781 if (this->jerr.warning) retval = -1;
1785 /* TurboJPEG 1.4+ */
1786 DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1787 const unsigned char *jpegBuf,
1788 unsigned long jpegSize, int *width,
1789 int *height, int *jpegSubsamp,
1790 int *jpegColorspace)
1792 static const char FUNCTION_NAME[] = "tjDecompressHeader3";
1795 GET_TJINSTANCE(handle, -1);
1797 if (width == NULL || height == NULL || jpegSubsamp == NULL ||
1798 jpegColorspace == NULL)
1799 THROW("Invalid argument");
1801 retval = tj3DecompressHeader(handle, jpegBuf, jpegSize);
1803 *width = tj3Get(handle, TJPARAM_JPEGWIDTH);
1804 *height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
1805 *jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
1806 if (*jpegSubsamp == TJSAMP_UNKNOWN)
1807 THROW("Could not determine subsampling level of JPEG image");
1808 *jpegColorspace = tj3Get(handle, TJPARAM_COLORSPACE);
1814 /* TurboJPEG 1.1+ */
1815 DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1816 unsigned long jpegSize, int *width,
1817 int *height, int *jpegSubsamp)
1821 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1822 jpegSubsamp, &jpegColorspace);
1825 /* TurboJPEG 1.0+ */
1826 DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1827 unsigned long jpegSize, int *width,
1832 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1838 DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors)
1840 static const char FUNCTION_NAME[] = "tj3GetScalingFactors";
1841 tjscalingfactor *retval = (tjscalingfactor *)sf;
1843 if (numScalingFactors == NULL)
1844 THROWG("Invalid argument", NULL);
1846 *numScalingFactors = NUMSF;
1852 /* TurboJPEG 1.2+ */
1853 DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors)
1855 return tj3GetScalingFactors(numScalingFactors);
1860 DLLEXPORT int tj3SetScalingFactor(tjhandle handle,
1861 tjscalingfactor scalingFactor)
1863 static const char FUNCTION_NAME[] = "tj3SetScalingFactor";
1866 GET_TJINSTANCE(handle, -1);
1867 if ((this->init & DECOMPRESS) == 0)
1868 THROW("Instance has not been initialized for decompression");
1870 for (i = 0; i < NUMSF; i++) {
1871 if (scalingFactor.num == sf[i].num && scalingFactor.denom == sf[i].denom)
1875 THROW("Unsupported scaling factor");
1877 this->scalingFactor = scalingFactor;
1885 DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion)
1887 static const char FUNCTION_NAME[] = "tj3SetCroppingRegion";
1888 int retval = 0, scaledWidth, scaledHeight;
1890 GET_TJINSTANCE(handle, -1);
1891 if ((this->init & DECOMPRESS) == 0)
1892 THROW("Instance has not been initialized for decompression");
1894 if (croppingRegion.x == 0 && croppingRegion.y == 0 &&
1895 croppingRegion.w == 0 && croppingRegion.h == 0) {
1896 this->croppingRegion = croppingRegion;
1900 if (croppingRegion.x < 0 || croppingRegion.y < 0 || croppingRegion.w < 0 ||
1901 croppingRegion.h < 0)
1902 THROW("Invalid cropping region");
1903 if (this->jpegWidth < 0 || this->jpegHeight < 0)
1904 THROW("JPEG header has not yet been read");
1905 if (this->precision == 16 || this->lossless)
1906 THROW("Cannot partially decompress lossless JPEG images");
1907 if (this->subsamp == TJSAMP_UNKNOWN)
1908 THROW("Could not determine subsampling level of JPEG image");
1910 scaledWidth = TJSCALED(this->jpegWidth, this->scalingFactor);
1911 scaledHeight = TJSCALED(this->jpegHeight, this->scalingFactor);
1913 if (croppingRegion.x %
1914 TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor) != 0)
1915 THROWI("The left boundary of the cropping region (%d) is not\n"
1916 "divisible by the scaled MCU width (%d)",
1918 TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor));
1919 if (croppingRegion.w == 0)
1920 croppingRegion.w = scaledWidth - croppingRegion.x;
1921 if (croppingRegion.h == 0)
1922 croppingRegion.h = scaledHeight - croppingRegion.y;
1923 if (croppingRegion.w < 0 || croppingRegion.h < 0 ||
1924 croppingRegion.x + croppingRegion.w > scaledWidth ||
1925 croppingRegion.y + croppingRegion.h > scaledHeight)
1926 THROW("The cropping region exceeds the scaled image dimensions");
1928 this->croppingRegion = croppingRegion;
1935 /* tj3Decompress*() is implemented in turbojpeg-mp.c */
1937 /* TurboJPEG 1.2+ */
1938 DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1939 unsigned long jpegSize, unsigned char *dstBuf,
1940 int width, int pitch, int height, int pixelFormat,
1943 static const char FUNCTION_NAME[] = "tjDecompress2";
1944 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
1946 GET_DINSTANCE(handle);
1947 if ((this->init & DECOMPRESS) == 0)
1948 THROW("Instance has not been initialized for decompression");
1950 if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
1951 THROW("Invalid argument");
1953 if (setjmp(this->jerr.setjmp_buffer)) {
1954 /* If we get here, the JPEG code has signaled an error. */
1955 retval = -1; goto bailout;
1958 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1959 jpeg_read_header(dinfo, TRUE);
1960 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1961 if (width == 0) width = jpegwidth;
1962 if (height == 0) height = jpegheight;
1963 for (i = 0; i < NUMSF; i++) {
1964 scaledw = TJSCALED(jpegwidth, sf[i]);
1965 scaledh = TJSCALED(jpegheight, sf[i]);
1966 if (scaledw <= width && scaledh <= height)
1970 THROW("Could not scale down to desired image dimensions");
1972 processFlags(handle, flags, DECOMPRESS);
1974 if (tj3SetScalingFactor(handle, sf[i]) == -1)
1976 if (tj3SetCroppingRegion(handle, TJUNCROPPED) == -1)
1978 return tj3Decompress8(handle, jpegBuf, jpegSize, dstBuf, pitch, pixelFormat);
1981 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1982 if (this->jerr.warning) retval = -1;
1986 /* TurboJPEG 1.0+ */
1987 DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1988 unsigned long jpegSize, unsigned char *dstBuf,
1989 int width, int pitch, int height, int pixelSize,
1993 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1995 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1996 height, getPixelFormat(pixelSize, flags), flags);
2000 static void setDecodeDefaults(tjinstance *this, int pixelFormat)
2004 this->dinfo.scale_num = this->dinfo.scale_denom = 1;
2006 if (this->subsamp == TJSAMP_GRAY) {
2007 this->dinfo.num_components = this->dinfo.comps_in_scan = 1;
2008 this->dinfo.jpeg_color_space = JCS_GRAYSCALE;
2010 this->dinfo.num_components = this->dinfo.comps_in_scan = 3;
2011 this->dinfo.jpeg_color_space = JCS_YCbCr;
2014 this->dinfo.comp_info = (jpeg_component_info *)
2015 (*this->dinfo.mem->alloc_small) ((j_common_ptr)&this->dinfo, JPOOL_IMAGE,
2016 this->dinfo.num_components *
2017 sizeof(jpeg_component_info));
2019 for (i = 0; i < this->dinfo.num_components; i++) {
2020 jpeg_component_info *compptr = &this->dinfo.comp_info[i];
2022 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[this->subsamp] / 8 : 1;
2023 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[this->subsamp] / 8 : 1;
2024 compptr->component_index = i;
2025 compptr->component_id = i + 1;
2026 compptr->quant_tbl_no = compptr->dc_tbl_no =
2027 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
2028 this->dinfo.cur_comp_info[i] = compptr;
2030 this->dinfo.data_precision = 8;
2031 for (i = 0; i < 2; i++) {
2032 if (this->dinfo.quant_tbl_ptrs[i] == NULL)
2033 this->dinfo.quant_tbl_ptrs[i] =
2034 jpeg_alloc_quant_table((j_common_ptr)&this->dinfo);
2039 static int my_read_markers(j_decompress_ptr dinfo)
2041 return JPEG_REACHED_SOS;
2044 static void my_reset_marker_reader(j_decompress_ptr dinfo)
2049 DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle,
2050 const unsigned char * const *srcPlanes,
2051 const int *strides, unsigned char *dstBuf,
2052 int width, int pitch, int height,
2055 static const char FUNCTION_NAME[] = "tj3DecodeYUVPlanes8";
2056 JSAMPROW *row_pointer = NULL;
2057 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
2058 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
2059 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
2061 jpeg_component_info *compptr;
2062 int (*old_read_markers) (j_decompress_ptr);
2063 void (*old_reset_marker_reader) (j_decompress_ptr);
2065 GET_DINSTANCE(handle);
2067 for (i = 0; i < MAX_COMPONENTS; i++) {
2068 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
2071 if ((this->init & DECOMPRESS) == 0)
2072 THROW("Instance has not been initialized for decompression");
2074 if (!srcPlanes || !srcPlanes[0] || dstBuf == NULL || width <= 0 ||
2075 pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2076 THROW("Invalid argument");
2077 if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
2078 THROW("Invalid argument");
2080 if (setjmp(this->jerr.setjmp_buffer)) {
2081 /* If we get here, the JPEG code has signaled an error. */
2082 retval = -1; goto bailout;
2085 if (this->subsamp == TJSAMP_UNKNOWN)
2086 THROW("TJPARAM_SUBSAMP must be specified");
2087 if (pixelFormat == TJPF_CMYK)
2088 THROW("Cannot decode YUV images into packed-pixel CMYK images.");
2090 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2091 dinfo->image_width = width;
2092 dinfo->image_height = height;
2094 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
2095 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
2096 dinfo->Se = DCTSIZE2 - 1;
2097 setDecodeDefaults(this, pixelFormat);
2098 old_read_markers = dinfo->marker->read_markers;
2099 dinfo->marker->read_markers = my_read_markers;
2100 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
2101 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
2102 jpeg_read_header(dinfo, TRUE);
2103 dinfo->marker->read_markers = old_read_markers;
2104 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2106 this->dinfo.out_color_space = pf2cs[pixelFormat];
2107 this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2108 dinfo->do_fancy_upsampling = FALSE;
2109 dinfo->Se = DCTSIZE2 - 1;
2110 jinit_master_decompress(dinfo);
2111 (*dinfo->upsample->start_pass) (dinfo);
2113 pw0 = PAD(width, dinfo->max_h_samp_factor);
2114 ph0 = PAD(height, dinfo->max_v_samp_factor);
2116 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
2118 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
2119 THROW("Memory allocation failure");
2120 for (i = 0; i < height; i++) {
2122 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
2124 row_pointer[i] = &dstBuf[i * (size_t)pitch];
2127 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
2129 for (i = 0; i < dinfo->num_components; i++) {
2130 compptr = &dinfo->comp_info[i];
2132 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
2133 compptr->v_samp_factor + 32);
2135 THROW("Memory allocation failure");
2136 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
2138 THROW("Memory allocation failure");
2139 for (row = 0; row < compptr->v_samp_factor; row++) {
2140 unsigned char *_tmpbuf_aligned =
2141 (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
2144 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
2146 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
2147 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2148 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
2150 THROW("Memory allocation failure");
2151 ptr = (JSAMPLE *)srcPlanes[i];
2152 for (row = 0; row < ph[i]; row++) {
2153 inbuf[i][row] = ptr;
2154 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2158 if (setjmp(this->jerr.setjmp_buffer)) {
2159 /* If we get here, the JPEG code has signaled an error. */
2160 retval = -1; goto bailout;
2163 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
2164 JDIMENSION inrow = 0, outrow = 0;
2166 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
2168 jcopy_sample_rows(inbuf[i],
2169 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
2170 compptr->v_samp_factor, pw[i]);
2171 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
2172 dinfo->max_v_samp_factor, &row_pointer[row],
2173 &outrow, dinfo->max_v_samp_factor);
2175 jpeg_abort_decompress(dinfo);
2178 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2180 for (i = 0; i < MAX_COMPONENTS; i++) {
2185 if (this->jerr.warning) retval = -1;
2189 /* TurboJPEG 1.4+ */
2190 DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
2191 const unsigned char **srcPlanes,
2192 const int *strides, int subsamp,
2193 unsigned char *dstBuf, int width, int pitch,
2194 int height, int pixelFormat, int flags)
2196 static const char FUNCTION_NAME[] = "tjDecodeYUVPlanes";
2199 GET_TJINSTANCE(handle, -1);
2201 if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
2202 THROW("Invalid argument");
2204 this->subsamp = subsamp;
2205 processFlags(handle, flags, DECOMPRESS);
2207 return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2208 height, pixelFormat);
2216 DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf,
2217 int align, unsigned char *dstBuf, int width,
2218 int pitch, int height, int pixelFormat)
2220 static const char FUNCTION_NAME[] = "tj3DecodeYUV8";
2221 const unsigned char *srcPlanes[3];
2222 int pw0, ph0, strides[3], retval = -1;
2224 GET_TJINSTANCE(handle, -1);
2226 if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 ||
2228 THROW("Invalid argument");
2230 if (this->subsamp == TJSAMP_UNKNOWN)
2231 THROW("TJPARAM_SUBSAMP must be specified");
2233 pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2234 ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2235 srcPlanes[0] = srcBuf;
2236 strides[0] = PAD(pw0, align);
2237 if (this->subsamp == TJSAMP_GRAY) {
2238 strides[1] = strides[2] = 0;
2239 srcPlanes[1] = srcPlanes[2] = NULL;
2241 int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2242 int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2244 strides[1] = strides[2] = PAD(pw1, align);
2245 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
2246 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
2249 return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2250 height, pixelFormat);
2256 /* TurboJPEG 1.4+ */
2257 DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
2258 int align, int subsamp, unsigned char *dstBuf,
2259 int width, int pitch, int height, int pixelFormat,
2262 static const char FUNCTION_NAME[] = "tjDecodeYUV";
2265 GET_TJINSTANCE(handle, -1);
2267 if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
2268 THROW("Invalid argument");
2270 this->subsamp = subsamp;
2271 processFlags(handle, flags, DECOMPRESS);
2273 return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height,
2282 DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle,
2283 const unsigned char *jpegBuf,
2285 unsigned char **dstPlanes,
2288 static const char FUNCTION_NAME[] = "tj3DecompressToYUVPlanes8";
2289 int i, row, retval = 0;
2290 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
2291 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
2292 JSAMPLE *_tmpbuf = NULL, *ptr;
2293 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
2295 struct my_progress_mgr progress;
2297 GET_DINSTANCE(handle);
2299 for (i = 0; i < MAX_COMPONENTS; i++) {
2300 tmpbuf[i] = NULL; outbuf[i] = NULL;
2303 if ((this->init & DECOMPRESS) == 0)
2304 THROW("Instance has not been initialized for decompression");
2306 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0])
2307 THROW("Invalid argument");
2309 if (this->scanLimit) {
2310 memset(&progress, 0, sizeof(struct my_progress_mgr));
2311 progress.pub.progress_monitor = my_progress_monitor;
2312 progress.this = this;
2313 dinfo->progress = &progress.pub;
2315 dinfo->progress = NULL;
2317 if (setjmp(this->jerr.setjmp_buffer)) {
2318 /* If we get here, the JPEG code has signaled an error. */
2319 retval = -1; goto bailout;
2322 if (dinfo->global_state <= DSTATE_START) {
2323 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2324 jpeg_read_header(dinfo, TRUE);
2326 setDecompParameters(this);
2327 if (this->subsamp == TJSAMP_UNKNOWN)
2328 THROW("Could not determine subsampling level of JPEG image");
2330 if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
2331 THROW("Invalid argument");
2333 if (dinfo->num_components > 3)
2334 THROW("JPEG image must have 3 or fewer components");
2336 dinfo->scale_num = this->scalingFactor.num;
2337 dinfo->scale_denom = this->scalingFactor.denom;
2338 jpeg_calc_output_dimensions(dinfo);
2340 dctsize = DCTSIZE * this->scalingFactor.num / this->scalingFactor.denom;
2342 for (i = 0; i < dinfo->num_components; i++) {
2343 jpeg_component_info *compptr = &dinfo->comp_info[i];
2346 iw[i] = compptr->width_in_blocks * dctsize;
2347 ih = compptr->height_in_blocks * dctsize;
2348 pw[i] = tj3YUVPlaneWidth(i, dinfo->output_width, this->subsamp);
2349 ph[i] = tj3YUVPlaneHeight(i, dinfo->output_height, this->subsamp);
2350 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
2351 th[i] = compptr->v_samp_factor * dctsize;
2352 tmpbufsize += iw[i] * th[i];
2353 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
2354 THROW("Memory allocation failure");
2356 for (row = 0; row < ph[i]; row++) {
2357 outbuf[i][row] = ptr;
2358 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2362 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
2363 THROW("Memory allocation failure");
2365 for (i = 0; i < dinfo->num_components; i++) {
2366 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
2367 THROW("Memory allocation failure");
2368 for (row = 0; row < th[i]; row++) {
2369 tmpbuf[i][row] = ptr;
2375 if (setjmp(this->jerr.setjmp_buffer)) {
2376 /* If we get here, the JPEG code has signaled an error. */
2377 retval = -1; goto bailout;
2380 dinfo->do_fancy_upsampling = !this->fastUpsample;
2381 dinfo->dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2382 dinfo->raw_data_out = TRUE;
2384 jpeg_start_decompress(dinfo);
2385 for (row = 0; row < (int)dinfo->output_height;
2386 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
2387 JSAMPARRAY yuvptr[MAX_COMPONENTS];
2388 int crow[MAX_COMPONENTS];
2390 for (i = 0; i < dinfo->num_components; i++) {
2391 jpeg_component_info *compptr = &dinfo->comp_info[i];
2393 if (this->subsamp == TJSAMP_420) {
2394 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
2395 to be clever and use the IDCT to perform upsampling on the U and V
2396 planes. For instance, if the output image is to be scaled by 1/2
2397 relative to the JPEG image, then the scaling factor and upsampling
2398 effectively cancel each other, so a normal 8x8 IDCT can be used.
2399 However, this is not desirable when using the decompress-to-YUV
2400 functionality in TurboJPEG, since we want to output the U and V
2401 planes in their subsampled form. Thus, we have to override some
2402 internal libjpeg parameters to force it to use the "scaled" IDCT
2403 functions on the U and V planes. */
2404 compptr->_DCT_scaled_size = dctsize;
2405 compptr->MCU_sample_width = tjMCUWidth[this->subsamp] *
2406 this->scalingFactor.num / this->scalingFactor.denom *
2407 compptr->v_samp_factor / dinfo->max_v_samp_factor;
2408 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
2410 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2411 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
2412 else yuvptr[i] = &outbuf[i][crow[i]];
2414 jpeg_read_raw_data(dinfo, yuvptr,
2415 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
2419 for (i = 0; i < dinfo->num_components; i++) {
2420 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
2421 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
2426 jpeg_finish_decompress(dinfo);
2429 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2430 for (i = 0; i < MAX_COMPONENTS; i++) {
2435 if (this->jerr.warning) retval = -1;
2439 /* TurboJPEG 1.4+ */
2440 DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
2441 const unsigned char *jpegBuf,
2442 unsigned long jpegSize,
2443 unsigned char **dstPlanes, int width,
2444 int *strides, int height, int flags)
2446 static const char FUNCTION_NAME[] = "tjDecompressToYUVPlanes";
2447 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2449 GET_DINSTANCE(handle);
2450 if ((this->init & DECOMPRESS) == 0)
2451 THROW("Instance has not been initialized for decompression");
2453 if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2454 THROW("Invalid argument");
2456 if (setjmp(this->jerr.setjmp_buffer)) {
2457 /* If we get here, the JPEG code has signaled an error. */
2458 retval = -1; goto bailout;
2461 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2462 jpeg_read_header(dinfo, TRUE);
2463 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
2464 if (width == 0) width = jpegwidth;
2465 if (height == 0) height = jpegheight;
2466 for (i = 0; i < NUMSF; i++) {
2467 scaledw = TJSCALED(jpegwidth, sf[i]);
2468 scaledh = TJSCALED(jpegheight, sf[i]);
2469 if (scaledw <= width && scaledh <= height)
2473 THROW("Could not scale down to desired image dimensions");
2475 processFlags(handle, flags, DECOMPRESS);
2477 if (tj3SetScalingFactor(handle, sf[i]) == -1)
2479 return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2483 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2484 if (this->jerr.warning) retval = -1;
2490 DLLEXPORT int tj3DecompressToYUV8(tjhandle handle,
2491 const unsigned char *jpegBuf,
2493 unsigned char *dstBuf, int align)
2495 static const char FUNCTION_NAME[] = "tj3DecompressToYUV8";
2496 unsigned char *dstPlanes[3];
2497 int pw0, ph0, strides[3], retval = -1;
2500 GET_DINSTANCE(handle);
2502 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || align < 1 ||
2504 THROW("Invalid argument");
2506 if (setjmp(this->jerr.setjmp_buffer)) {
2507 /* If we get here, the JPEG code has signaled an error. */
2508 retval = -1; goto bailout;
2511 if (dinfo->global_state <= DSTATE_START) {
2512 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2513 jpeg_read_header(dinfo, TRUE);
2515 setDecompParameters(this);
2516 if (this->subsamp == TJSAMP_UNKNOWN)
2517 THROW("Could not determine subsampling level of JPEG image");
2519 width = TJSCALED(dinfo->image_width, this->scalingFactor);
2520 height = TJSCALED(dinfo->image_height, this->scalingFactor);
2522 pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2523 ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2524 dstPlanes[0] = dstBuf;
2525 strides[0] = PAD(pw0, align);
2526 if (this->subsamp == TJSAMP_GRAY) {
2527 strides[1] = strides[2] = 0;
2528 dstPlanes[1] = dstPlanes[2] = NULL;
2530 int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2531 int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2533 strides[1] = strides[2] = PAD(pw1, align);
2534 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
2535 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
2538 return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2542 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2543 if (this->jerr.warning) retval = -1;
2547 /* TurboJPEG 1.4+ */
2548 DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
2549 unsigned long jpegSize, unsigned char *dstBuf,
2550 int width, int align, int height, int flags)
2552 static const char FUNCTION_NAME[] = "tjDecompressToYUV2";
2553 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2555 GET_DINSTANCE(handle);
2556 if ((this->init & DECOMPRESS) == 0)
2557 THROW("Instance has not been initialized for decompression");
2559 if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2560 THROW("Invalid argument");
2562 if (setjmp(this->jerr.setjmp_buffer)) {
2563 /* If we get here, the JPEG code has signaled an error. */
2564 retval = -1; goto bailout;
2567 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2568 jpeg_read_header(dinfo, TRUE);
2569 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
2570 if (width == 0) width = jpegwidth;
2571 if (height == 0) height = jpegheight;
2572 for (i = 0; i < NUMSF; i++) {
2573 scaledw = TJSCALED(jpegwidth, sf[i]);
2574 scaledh = TJSCALED(jpegheight, sf[i]);
2575 if (scaledw <= width && scaledh <= height)
2579 THROW("Could not scale down to desired image dimensions");
2581 width = scaledw; height = scaledh;
2583 processFlags(handle, flags, DECOMPRESS);
2585 if (tj3SetScalingFactor(handle, sf[i]) == -1)
2587 return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align);
2590 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2591 if (this->jerr.warning) retval = -1;
2595 /* TurboJPEG 1.1+ */
2596 DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
2597 unsigned long jpegSize, unsigned char *dstBuf,
2600 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
2604 /******************************** Transformer ********************************/
2606 /* TurboJPEG 1.2+ */
2607 DLLEXPORT tjhandle tjInitTransform(void)
2609 return tj3Init(TJINIT_TRANSFORM);
2614 DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
2615 size_t jpegSize, int n, unsigned char **dstBufs,
2616 size_t *dstSizes, const tjtransform *t)
2618 static const char FUNCTION_NAME[] = "tj3Transform";
2619 jpeg_transform_info *xinfo = NULL;
2620 jvirt_barray_ptr *srccoefs, *dstcoefs;
2621 int retval = 0, i, saveMarkers = 0;
2622 boolean alloc = TRUE;
2623 struct my_progress_mgr progress;
2625 GET_INSTANCE(handle);
2626 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2627 THROW("Instance has not been initialized for transformation");
2629 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
2630 dstSizes == NULL || t == NULL)
2631 THROW("Invalid argument");
2633 if (this->scanLimit) {
2634 memset(&progress, 0, sizeof(struct my_progress_mgr));
2635 progress.pub.progress_monitor = my_progress_monitor;
2636 progress.this = this;
2637 dinfo->progress = &progress.pub;
2639 dinfo->progress = NULL;
2642 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
2643 THROW("Memory allocation failure");
2644 memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
2646 if (setjmp(this->jerr.setjmp_buffer)) {
2647 /* If we get here, the JPEG code has signaled an error. */
2648 retval = -1; goto bailout;
2651 if (dinfo->global_state <= DSTATE_START)
2652 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2654 for (i = 0; i < n; i++) {
2655 xinfo[i].transform = xformtypes[t[i].op];
2656 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
2657 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
2658 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
2659 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
2660 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
2661 else xinfo[i].slow_hflip = 0;
2663 if (xinfo[i].crop) {
2664 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
2665 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
2666 if (t[i].r.w != 0) {
2667 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
2669 xinfo[i].crop_width = JCROP_UNSET;
2670 if (t[i].r.h != 0) {
2671 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
2673 xinfo[i].crop_height = JCROP_UNSET;
2675 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
2678 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
2679 if (dinfo->global_state <= DSTATE_START)
2680 jpeg_read_header(dinfo, TRUE);
2681 this->subsamp = getSubsamp(&this->dinfo);
2683 for (i = 0; i < n; i++) {
2684 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
2685 THROW("Transform is not perfect");
2687 if (xinfo[i].crop) {
2688 if (this->subsamp == TJSAMP_UNKNOWN)
2689 THROW("Could not determine subsampling level of JPEG image");
2690 if ((t[i].r.x % tjMCUWidth[this->subsamp]) != 0 ||
2691 (t[i].r.y % tjMCUHeight[this->subsamp]) != 0)
2692 THROWI("To crop this JPEG image, x must be a multiple of %d\n"
2693 "and y must be a multiple of %d.", tjMCUWidth[this->subsamp],
2694 tjMCUHeight[this->subsamp]);
2698 srccoefs = jpeg_read_coefficients(dinfo);
2700 for (i = 0; i < n; i++) {
2703 if (!xinfo[i].crop) {
2704 w = dinfo->image_width; h = dinfo->image_height;
2705 if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
2706 t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
2707 w = dinfo->image_height; h = dinfo->image_width;
2710 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
2712 if (this->noRealloc) {
2713 alloc = FALSE; dstSizes[i] = tj3JPEGBufSize(w, h, this->subsamp);
2715 if (!(t[i].options & TJXOPT_NOOUTPUT))
2716 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
2717 jpeg_copy_critical_parameters(dinfo, cinfo);
2718 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
2719 if (this->optimize || t[i].options & TJXOPT_OPTIMIZE)
2720 cinfo->optimize_coding = TRUE;
2721 #ifdef C_PROGRESSIVE_SUPPORTED
2722 if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE)
2723 jpeg_simple_progression(cinfo);
2725 if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) {
2726 cinfo->arith_code = TRUE;
2727 cinfo->optimize_coding = FALSE;
2729 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
2730 jpeg_write_coefficients(cinfo, dstcoefs);
2731 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
2732 JCOPYOPT_NONE : JCOPYOPT_ALL);
2734 jinit_c_master_control(cinfo, TRUE);
2735 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
2736 if (t[i].customFilter) {
2740 for (ci = 0; ci < cinfo->num_components; ci++) {
2741 jpeg_component_info *compptr = &cinfo->comp_info[ci];
2742 tjregion arrayRegion = { 0, 0, 0, 0 };
2743 tjregion planeRegion = { 0, 0, 0, 0 };
2745 arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
2746 arrayRegion.h = DCTSIZE;
2747 planeRegion.w = compptr->width_in_blocks * DCTSIZE;
2748 planeRegion.h = compptr->height_in_blocks * DCTSIZE;
2750 for (by = 0; by < compptr->height_in_blocks;
2751 by += compptr->v_samp_factor) {
2752 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
2753 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
2756 for (y = 0; y < compptr->v_samp_factor; y++) {
2757 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
2758 i, (tjtransform *)&t[i]) == -1)
2759 THROW("Error in custom filter");
2760 arrayRegion.y += DCTSIZE;
2765 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
2768 jpeg_finish_decompress(dinfo);
2771 if (cinfo->global_state > CSTATE_START) {
2772 if (alloc) (*cinfo->dest->term_destination) (cinfo);
2773 jpeg_abort_compress(cinfo);
2775 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2777 if (this->jerr.warning) retval = -1;
2781 /* TurboJPEG 1.2+ */
2782 DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
2783 unsigned long jpegSize, int n,
2784 unsigned char **dstBufs, unsigned long *dstSizes,
2785 tjtransform *t, int flags)
2787 static const char FUNCTION_NAME[] = "tjTransform";
2789 size_t *sizes = NULL;
2791 GET_DINSTANCE(handle);
2792 if ((this->init & DECOMPRESS) == 0)
2793 THROW("Instance has not been initialized for decompression");
2795 if (n < 1 || dstSizes == NULL)
2796 THROW("Invalid argument");
2798 if (setjmp(this->jerr.setjmp_buffer)) {
2799 /* If we get here, the JPEG code has signaled an error. */
2800 retval = -1; goto bailout;
2803 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2804 jpeg_read_header(dinfo, TRUE);
2805 if (getSubsamp(dinfo) == TJSAMP_UNKNOWN)
2806 THROW("Could not determine subsampling level of JPEG image");
2807 processFlags(handle, flags, COMPRESS);
2809 if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL)
2810 THROW("Memory allocation failure");
2811 for (i = 0; i < n; i++)
2812 sizes[i] = (size_t)dstSizes[i];
2813 retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes,
2815 for (i = 0; i < n; i++)
2816 dstSizes[i] = (unsigned long)sizes[i];
2824 /*************************** Packed-Pixel Image I/O **************************/
2826 /* tj3LoadImage*() is implemented in turbojpeg-mp.c */
2828 /* TurboJPEG 2.0+ */
2829 DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
2830 int align, int *height,
2831 int *pixelFormat, int flags)
2833 tjhandle handle = NULL;
2834 unsigned char *dstBuf = NULL;
2836 if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL;
2838 processFlags(handle, flags, COMPRESS);
2840 dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat);
2847 /* tj3SaveImage*() is implemented in turbojpeg-mp.c */
2849 /* TurboJPEG 2.0+ */
2850 DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2851 int width, int pitch, int height, int pixelFormat,
2854 tjhandle handle = NULL;
2857 if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1;
2859 processFlags(handle, flags, DECOMPRESS);
2861 retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height,