2 * Copyright (C)2009-2022 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
35 #define JPEG_INTERNALS
40 #include "./turbojpeg.h"
43 #include "./jpegcomp.h"
45 #include "jconfigint.h"
47 extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
49 extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
52 #define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
53 #define IS_POW2(x) (((x) & (x - 1)) == 0)
56 /* Error handling (based on example in example.txt) */
58 static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
61 struct jpeg_error_mgr pub;
62 jmp_buf setjmp_buffer;
63 void (*emit_message) (j_common_ptr, int);
64 boolean warning, stopOnWarning;
66 typedef struct my_error_mgr *my_error_ptr;
68 #define JMESSAGE(code, string) string,
69 static const char *turbojpeg_message_table[] = {
74 static void my_error_exit(j_common_ptr cinfo)
76 my_error_ptr myerr = (my_error_ptr)cinfo->err;
78 (*cinfo->err->output_message) (cinfo);
79 longjmp(myerr->setjmp_buffer, 1);
82 /* Based on output_message() in jerror.c */
84 static void my_output_message(j_common_ptr cinfo)
86 (*cinfo->err->format_message) (cinfo, errStr);
89 static void my_emit_message(j_common_ptr cinfo, int msg_level)
91 my_error_ptr myerr = (my_error_ptr)cinfo->err;
93 myerr->emit_message(cinfo, msg_level);
95 myerr->warning = TRUE;
96 if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
101 /* Global structures, macros, etc. */
103 enum { COMPRESS = 1, DECOMPRESS = 2 };
105 typedef 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;
114 struct my_progress_mgr {
115 struct jpeg_progress_mgr pub;
118 typedef struct my_progress_mgr *my_progress_ptr;
120 static void my_progress_monitor(j_common_ptr dinfo)
122 my_error_ptr myerr = (my_error_ptr)dinfo->err;
123 my_progress_ptr myprog = (my_progress_ptr)dinfo->progress;
125 if (dinfo->is_decompressor) {
126 int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number;
129 SNPRINTF(myprog->this->errStr, JMSG_LENGTH_MAX,
130 "Progressive JPEG image has more than 500 scans");
131 SNPRINTF(errStr, JMSG_LENGTH_MAX,
132 "Progressive JPEG image has more than 500 scans");
133 myprog->this->isInstanceError = TRUE;
134 myerr->warning = FALSE;
135 longjmp(myerr->setjmp_buffer, 1);
140 static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
142 static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
143 JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
144 JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
148 static const tjscalingfactor sf[NUMSF] = {
167 static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
168 JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
169 JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
170 JCS_EXT_ARGB, JCS_CMYK
173 static int cs2pf[JPEG_NUMCS] = {
174 TJPF_UNKNOWN, TJPF_GRAY,
175 #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
177 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
179 #elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
181 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
183 #elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
185 #elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
188 TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
189 TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
193 #define THROWG(m) { \
194 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s", m); \
195 retval = -1; goto bailout; \
198 #define THROW_UNIX(m) { \
199 char strerrorBuf[80] = { 0 }; \
200 strerror_s(strerrorBuf, 80, errno); \
201 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerrorBuf); \
202 retval = -1; goto bailout; \
205 #define THROW_UNIX(m) { \
206 SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
207 retval = -1; goto bailout; \
211 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
212 this->isInstanceError = TRUE; THROWG(m) \
215 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
216 /* Private flag that triggers different TurboJPEG API behavior when fuzzing */
217 #define TJFLAG_FUZZING (1 << 30)
220 #define GET_INSTANCE(handle) \
221 tjinstance *this = (tjinstance *)handle; \
222 j_compress_ptr cinfo = NULL; \
223 j_decompress_ptr dinfo = NULL; \
226 SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
229 cinfo = &this->cinfo; dinfo = &this->dinfo; \
230 this->jerr.warning = FALSE; \
231 this->isInstanceError = FALSE;
233 #define GET_CINSTANCE(handle) \
234 tjinstance *this = (tjinstance *)handle; \
235 j_compress_ptr cinfo = NULL; \
238 SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
241 cinfo = &this->cinfo; \
242 this->jerr.warning = FALSE; \
243 this->isInstanceError = FALSE;
245 #define GET_DINSTANCE(handle) \
246 tjinstance *this = (tjinstance *)handle; \
247 j_decompress_ptr dinfo = NULL; \
250 SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
253 dinfo = &this->dinfo; \
254 this->jerr.warning = FALSE; \
255 this->isInstanceError = FALSE;
257 static int getPixelFormat(int pixelSize, int flags)
259 if (pixelSize == 1) return TJPF_GRAY;
260 if (pixelSize == 3) {
261 if (flags & TJ_BGR) return TJPF_BGR;
262 else return TJPF_RGB;
264 if (pixelSize == 4) {
265 if (flags & TJ_ALPHAFIRST) {
266 if (flags & TJ_BGR) return TJPF_XBGR;
267 else return TJPF_XRGB;
269 if (flags & TJ_BGR) return TJPF_BGRX;
270 else return TJPF_RGBX;
276 static void setCompDefaults(struct jpeg_compress_struct *cinfo,
277 int pixelFormat, int subsamp, int jpegQual,
284 cinfo->in_color_space = pf2cs[pixelFormat];
285 cinfo->input_components = tjPixelSize[pixelFormat];
286 jpeg_set_defaults(cinfo);
289 if (!GETENV_S(env, 7, "TJ_OPTIMIZE") && !strcmp(env, "1"))
290 cinfo->optimize_coding = TRUE;
291 if (!GETENV_S(env, 7, "TJ_ARITHMETIC") && !strcmp(env, "1"))
292 cinfo->arith_code = TRUE;
293 if (!GETENV_S(env, 7, "TJ_RESTART") && strlen(env) > 0) {
298 if (sscanf_s(env, "%d%c", &temp, &tempc, 1) >= 1 && temp >= 0 &&
301 if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
304 if (toupper(tempc) == 'B') {
305 cinfo->restart_interval = temp;
306 cinfo->restart_in_rows = 0;
308 cinfo->restart_in_rows = temp;
314 jpeg_set_quality(cinfo, jpegQual, TRUE);
315 if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
316 cinfo->dct_method = JDCT_ISLOW;
318 cinfo->dct_method = JDCT_FASTEST;
320 if (subsamp == TJSAMP_GRAY)
321 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
322 else if (pixelFormat == TJPF_CMYK)
323 jpeg_set_colorspace(cinfo, JCS_YCCK);
325 jpeg_set_colorspace(cinfo, JCS_YCbCr);
327 if (flags & TJFLAG_PROGRESSIVE)
328 jpeg_simple_progression(cinfo);
330 else if (!GETENV_S(env, 7, "TJ_PROGRESSIVE") && !strcmp(env, "1"))
331 jpeg_simple_progression(cinfo);
334 cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
335 cinfo->comp_info[1].h_samp_factor = 1;
336 cinfo->comp_info[2].h_samp_factor = 1;
337 if (cinfo->num_components > 3)
338 cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
339 cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
340 cinfo->comp_info[1].v_samp_factor = 1;
341 cinfo->comp_info[2].v_samp_factor = 1;
342 if (cinfo->num_components > 3)
343 cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
347 static int getSubsamp(j_decompress_ptr dinfo)
349 int retval = -1, i, k;
351 /* The sampling factors actually have no meaning with grayscale JPEG files,
352 and in fact it's possible to generate grayscale JPEGs with sampling
353 factors > 1 (even though those sampling factors are ignored by the
354 decompressor.) Thus, we need to treat grayscale as a special case. */
355 if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
358 for (i = 0; i < NUMSUBOPT; i++) {
359 if (dinfo->num_components == pixelsize[i] ||
360 ((dinfo->jpeg_color_space == JCS_YCCK ||
361 dinfo->jpeg_color_space == JCS_CMYK) &&
362 pixelsize[i] == 3 && dinfo->num_components == 4)) {
363 if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
364 dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
367 for (k = 1; k < dinfo->num_components; k++) {
368 int href = 1, vref = 1;
370 if ((dinfo->jpeg_color_space == JCS_YCCK ||
371 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
372 href = tjMCUWidth[i] / 8; vref = tjMCUHeight[i] / 8;
374 if (dinfo->comp_info[k].h_samp_factor == href &&
375 dinfo->comp_info[k].v_samp_factor == vref)
378 if (match == dinfo->num_components - 1) {
382 /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
383 in non-standard ways. */
384 if (dinfo->comp_info[0].h_samp_factor == 2 &&
385 dinfo->comp_info[0].v_samp_factor == 2 &&
386 (i == TJSAMP_422 || i == TJSAMP_440)) {
389 for (k = 1; k < dinfo->num_components; k++) {
390 int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
392 if ((dinfo->jpeg_color_space == JCS_YCCK ||
393 dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
396 if (dinfo->comp_info[k].h_samp_factor == href &&
397 dinfo->comp_info[k].v_samp_factor == vref)
400 if (match == dinfo->num_components - 1) {
404 /* Handle 4:4:4 images whose sampling factors are specified in
405 non-standard ways. */
406 if (dinfo->comp_info[0].h_samp_factor *
407 dinfo->comp_info[0].v_samp_factor <=
408 D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
410 for (k = 1; k < dinfo->num_components; k++) {
411 if (dinfo->comp_info[k].h_samp_factor ==
412 dinfo->comp_info[0].h_samp_factor &&
413 dinfo->comp_info[k].v_samp_factor ==
414 dinfo->comp_info[0].v_samp_factor)
416 if (match == dinfo->num_components - 1) {
427 /* General API functions */
429 DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
431 tjinstance *this = (tjinstance *)handle;
433 if (this && this->isInstanceError) {
434 this->isInstanceError = FALSE;
441 DLLEXPORT char *tjGetErrorStr(void)
447 DLLEXPORT int tjGetErrorCode(tjhandle handle)
449 tjinstance *this = (tjinstance *)handle;
451 if (this && this->jerr.warning) return TJERR_WARNING;
452 else return TJERR_FATAL;
456 DLLEXPORT int tjDestroy(tjhandle handle)
458 GET_INSTANCE(handle);
460 if (setjmp(this->jerr.setjmp_buffer)) return -1;
461 if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
462 if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
468 /* These are exposed mainly because Windows can't malloc() and free() across
469 DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
470 with turbojpeg.dll for compatibility reasons. However, these functions
471 can potentially be used for other purposes by different implementations. */
473 DLLEXPORT void tjFree(unsigned char *buf)
479 DLLEXPORT unsigned char *tjAlloc(int bytes)
481 return (unsigned char *)malloc(bytes);
487 static tjhandle _tjInitCompress(tjinstance *this)
489 static unsigned char buffer[1];
490 unsigned char *buf = buffer;
491 unsigned long size = 1;
493 /* This is also straight out of example.txt */
494 this->cinfo.err = jpeg_std_error(&this->jerr.pub);
495 this->jerr.pub.error_exit = my_error_exit;
496 this->jerr.pub.output_message = my_output_message;
497 this->jerr.emit_message = this->jerr.pub.emit_message;
498 this->jerr.pub.emit_message = my_emit_message;
499 this->jerr.pub.addon_message_table = turbojpeg_message_table;
500 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
501 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
503 if (setjmp(this->jerr.setjmp_buffer)) {
504 /* If we get here, the JPEG code has signaled an error. */
509 jpeg_create_compress(&this->cinfo);
510 /* Make an initial call so it will create the destination manager */
511 jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
513 this->init |= COMPRESS;
514 return (tjhandle)this;
517 DLLEXPORT tjhandle tjInitCompress(void)
519 tjinstance *this = NULL;
521 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
522 SNPRINTF(errStr, JMSG_LENGTH_MAX,
523 "tjInitCompress(): Memory allocation failure");
526 memset(this, 0, sizeof(tjinstance));
527 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
528 return _tjInitCompress(this);
532 DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
534 unsigned long long retval = 0;
535 int mcuw, mcuh, chromasf;
537 if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
538 THROWG("tjBufSize(): Invalid argument");
540 /* This allows for rare corner cases in which a JPEG image can actually be
541 larger than the uncompressed input (we wouldn't mention it if it hadn't
543 mcuw = tjMCUWidth[jpegSubsamp];
544 mcuh = tjMCUHeight[jpegSubsamp];
545 chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
546 retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
547 if (retval > (unsigned long long)((unsigned long)-1))
548 THROWG("tjBufSize(): Image is too large");
551 return (unsigned long)retval;
554 DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
556 unsigned long long retval = 0;
558 if (width < 1 || height < 1)
559 THROWG("TJBUFSIZE(): Invalid argument");
561 /* This allows for rare corner cases in which a JPEG image can actually be
562 larger than the uncompressed input (we wouldn't mention it if it hadn't
564 retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
565 if (retval > (unsigned long long)((unsigned long)-1))
566 THROWG("TJBUFSIZE(): Image is too large");
569 return (unsigned long)retval;
573 DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
576 unsigned long long retval = 0;
579 if (subsamp < 0 || subsamp >= NUMSUBOPT)
580 THROWG("tjBufSizeYUV2(): Invalid argument");
582 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
583 for (i = 0; i < nc; i++) {
584 int pw = tjPlaneWidth(i, width, subsamp);
585 int stride = PAD(pw, pad);
586 int ph = tjPlaneHeight(i, height, subsamp);
588 if (pw < 0 || ph < 0) return -1;
589 else retval += (unsigned long long)stride * ph;
591 if (retval > (unsigned long long)((unsigned long)-1))
592 THROWG("tjBufSizeYUV2(): Image is too large");
595 return (unsigned long)retval;
598 DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
600 return tjBufSizeYUV2(width, 4, height, subsamp);
603 DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
605 return tjBufSizeYUV(width, height, subsamp);
609 DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
611 int pw, nc, retval = 0;
613 if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
614 THROWG("tjPlaneWidth(): Invalid argument");
615 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
616 if (componentID < 0 || componentID >= nc)
617 THROWG("tjPlaneWidth(): Invalid argument");
619 pw = PAD(width, tjMCUWidth[subsamp] / 8);
620 if (componentID == 0)
623 retval = pw * 8 / tjMCUWidth[subsamp];
630 DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
632 int ph, nc, retval = 0;
634 if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
635 THROWG("tjPlaneHeight(): Invalid argument");
636 nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
637 if (componentID < 0 || componentID >= nc)
638 THROWG("tjPlaneHeight(): Invalid argument");
640 ph = PAD(height, tjMCUHeight[subsamp] / 8);
641 if (componentID == 0)
644 retval = ph * 8 / tjMCUHeight[subsamp];
651 DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
652 int height, int subsamp)
654 unsigned long long retval = 0;
657 if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
658 THROWG("tjPlaneSizeYUV(): Invalid argument");
660 pw = tjPlaneWidth(componentID, width, subsamp);
661 ph = tjPlaneHeight(componentID, height, subsamp);
662 if (pw < 0 || ph < 0) return -1;
664 if (stride == 0) stride = pw;
665 else stride = abs(stride);
667 retval = (unsigned long long)stride * (ph - 1) + pw;
668 if (retval > (unsigned long long)((unsigned long)-1))
669 THROWG("tjPlaneSizeYUV(): Image is too large");
672 return (unsigned long)retval;
676 DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
677 int width, int pitch, int height, int pixelFormat,
678 unsigned char **jpegBuf, unsigned long *jpegSize,
679 int jpegSubsamp, int jpegQual, int flags)
682 boolean alloc = TRUE;
683 JSAMPROW *row_pointer = NULL;
685 GET_CINSTANCE(handle)
686 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
687 if ((this->init & COMPRESS) == 0)
688 THROW("tjCompress2(): Instance has not been initialized for compression");
690 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
691 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
692 jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
693 jpegQual < 0 || jpegQual > 100)
694 THROW("tjCompress2(): Invalid argument");
696 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
698 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
699 THROW("tjCompress2(): Memory allocation failure");
701 if (setjmp(this->jerr.setjmp_buffer)) {
702 /* If we get here, the JPEG code has signaled an error. */
703 retval = -1; goto bailout;
706 cinfo->image_width = width;
707 cinfo->image_height = height;
710 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
711 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
712 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
715 if (flags & TJFLAG_NOREALLOC) {
716 alloc = FALSE; *jpegSize = tjBufSize(width, height, jpegSubsamp);
718 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
719 setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags);
721 jpeg_start_compress(cinfo, TRUE);
722 for (i = 0; i < height; i++) {
723 if (flags & TJFLAG_BOTTOMUP)
724 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
726 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
728 while (cinfo->next_scanline < cinfo->image_height)
729 jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
730 cinfo->image_height - cinfo->next_scanline);
731 jpeg_finish_compress(cinfo);
734 if (cinfo->global_state > CSTATE_START) {
735 if (alloc) (*cinfo->dest->term_destination) (cinfo);
736 jpeg_abort_compress(cinfo);
739 if (this->jerr.warning) retval = -1;
740 this->jerr.stopOnWarning = FALSE;
744 DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
745 int pitch, int height, int pixelSize,
746 unsigned char *jpegBuf, unsigned long *jpegSize,
747 int jpegSubsamp, int jpegQual, int flags)
752 if (flags & TJ_YUV) {
753 size = tjBufSizeYUV(width, height, jpegSubsamp);
754 retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
755 getPixelFormat(pixelSize, flags), jpegBuf,
758 retval = tjCompress2(handle, srcBuf, width, pitch, height,
759 getPixelFormat(pixelSize, flags), &jpegBuf, &size,
760 jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
767 DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
768 int width, int pitch, int height,
769 int pixelFormat, unsigned char **dstPlanes,
770 int *strides, int subsamp, int flags)
772 JSAMPROW *row_pointer = NULL;
773 JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
774 JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
775 JSAMPROW *outbuf[MAX_COMPONENTS];
776 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
778 jpeg_component_info *compptr;
780 GET_CINSTANCE(handle);
781 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
783 for (i = 0; i < MAX_COMPONENTS; i++) {
784 tmpbuf[i] = NULL; _tmpbuf[i] = NULL;
785 tmpbuf2[i] = NULL; _tmpbuf2[i] = NULL; outbuf[i] = NULL;
788 if ((this->init & COMPRESS) == 0)
789 THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
791 if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
792 pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
793 !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
794 THROW("tjEncodeYUVPlanes(): Invalid argument");
795 if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
796 THROW("tjEncodeYUVPlanes(): Invalid argument");
798 if (pixelFormat == TJPF_CMYK)
799 THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
801 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
803 if (setjmp(this->jerr.setjmp_buffer)) {
804 /* If we get here, the JPEG code has signaled an error. */
805 retval = -1; goto bailout;
808 cinfo->image_width = width;
809 cinfo->image_height = height;
812 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
813 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
814 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
817 setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags);
819 /* Execute only the parts of jpeg_start_compress() that we need. If we
820 were to call the whole jpeg_start_compress() function, then it would try
821 to write the file headers, which could overflow the output buffer if the
822 YUV image were very small. */
823 if (cinfo->global_state != CSTATE_START)
824 THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
825 (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
826 jinit_c_master_control(cinfo, FALSE);
827 jinit_color_converter(cinfo);
828 jinit_downsampler(cinfo);
829 (*cinfo->cconvert->start_pass) (cinfo);
831 pw0 = PAD(width, cinfo->max_h_samp_factor);
832 ph0 = PAD(height, cinfo->max_v_samp_factor);
834 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
835 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
836 for (i = 0; i < height; i++) {
837 if (flags & TJFLAG_BOTTOMUP)
838 row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
840 row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
843 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
845 for (i = 0; i < cinfo->num_components; i++) {
846 compptr = &cinfo->comp_info[i];
847 _tmpbuf[i] = (JSAMPLE *)malloc(
848 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
849 compptr->h_samp_factor, 32) *
850 cinfo->max_v_samp_factor + 32);
852 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
854 (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
856 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
857 for (row = 0; row < cinfo->max_v_samp_factor; row++) {
858 unsigned char *_tmpbuf_aligned =
859 (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
861 tmpbuf[i][row] = &_tmpbuf_aligned[
862 PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
863 compptr->h_samp_factor, 32) * row];
866 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
867 compptr->v_samp_factor + 32);
869 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
870 tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
872 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
873 for (row = 0; row < compptr->v_samp_factor; row++) {
874 unsigned char *_tmpbuf2_aligned =
875 (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32);
878 &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
880 pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
881 ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
882 outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
884 THROW("tjEncodeYUVPlanes(): Memory allocation failure");
886 for (row = 0; row < ph[i]; row++) {
887 outbuf[i][row] = ptr;
888 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
892 if (setjmp(this->jerr.setjmp_buffer)) {
893 /* If we get here, the JPEG code has signaled an error. */
894 retval = -1; goto bailout;
897 for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
898 (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
899 cinfo->max_v_samp_factor);
900 (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
901 for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
903 jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
904 row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
905 compptr->v_samp_factor, pw[i]);
907 cinfo->next_scanline += height;
908 jpeg_abort_compress(cinfo);
911 if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
913 for (i = 0; i < MAX_COMPONENTS; i++) {
920 if (this->jerr.warning) retval = -1;
921 this->jerr.stopOnWarning = FALSE;
925 DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
926 int width, int pitch, int height, int pixelFormat,
927 unsigned char *dstBuf, int pad, int subsamp,
930 unsigned char *dstPlanes[3];
931 int pw0, ph0, strides[3], retval = -1;
932 tjinstance *this = (tjinstance *)handle;
934 if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
935 this->isInstanceError = FALSE;
937 if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
938 !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
939 THROW("tjEncodeYUV3(): Invalid argument");
941 pw0 = tjPlaneWidth(0, width, subsamp);
942 ph0 = tjPlaneHeight(0, height, subsamp);
943 dstPlanes[0] = dstBuf;
944 strides[0] = PAD(pw0, pad);
945 if (subsamp == TJSAMP_GRAY) {
946 strides[1] = strides[2] = 0;
947 dstPlanes[1] = dstPlanes[2] = NULL;
949 int pw1 = tjPlaneWidth(1, width, subsamp);
950 int ph1 = tjPlaneHeight(1, height, subsamp);
952 strides[1] = strides[2] = PAD(pw1, pad);
953 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
954 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
957 return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
958 dstPlanes, strides, subsamp, flags);
964 DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
965 int pitch, int height, int pixelFormat,
966 unsigned char *dstBuf, int subsamp, int flags)
968 return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
969 dstBuf, 4, subsamp, flags);
972 DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
973 int pitch, int height, int pixelSize,
974 unsigned char *dstBuf, int subsamp, int flags)
976 return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
977 getPixelFormat(pixelSize, flags), dstBuf, subsamp,
982 DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
983 const unsigned char **srcPlanes,
984 int width, const int *strides,
985 int height, int subsamp,
986 unsigned char **jpegBuf,
987 unsigned long *jpegSize, int jpegQual,
990 int i, row, retval = 0;
991 boolean alloc = TRUE;
992 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
993 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
994 JSAMPLE *_tmpbuf = NULL, *ptr;
995 JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
997 GET_CINSTANCE(handle)
998 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1000 for (i = 0; i < MAX_COMPONENTS; i++) {
1001 tmpbuf[i] = NULL; inbuf[i] = NULL;
1004 if ((this->init & COMPRESS) == 0)
1005 THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
1007 if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
1008 subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
1009 jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
1010 THROW("tjCompressFromYUVPlanes(): Invalid argument");
1011 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1012 THROW("tjCompressFromYUVPlanes(): Invalid argument");
1014 if (setjmp(this->jerr.setjmp_buffer)) {
1015 /* If we get here, the JPEG code has signaled an error. */
1016 retval = -1; goto bailout;
1019 cinfo->image_width = width;
1020 cinfo->image_height = height;
1023 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1024 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1025 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1028 if (flags & TJFLAG_NOREALLOC) {
1029 alloc = FALSE; *jpegSize = tjBufSize(width, height, subsamp);
1031 jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
1032 setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags);
1033 cinfo->raw_data_in = TRUE;
1035 jpeg_start_compress(cinfo, TRUE);
1036 for (i = 0; i < cinfo->num_components; i++) {
1037 jpeg_component_info *compptr = &cinfo->comp_info[i];
1040 iw[i] = compptr->width_in_blocks * DCTSIZE;
1041 ih = compptr->height_in_blocks * DCTSIZE;
1042 pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1043 compptr->h_samp_factor / cinfo->max_h_samp_factor;
1044 ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1045 compptr->v_samp_factor / cinfo->max_v_samp_factor;
1046 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1047 th[i] = compptr->v_samp_factor * DCTSIZE;
1048 tmpbufsize += iw[i] * th[i];
1049 if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1050 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1051 ptr = (JSAMPLE *)srcPlanes[i];
1052 for (row = 0; row < ph[i]; row++) {
1053 inbuf[i][row] = ptr;
1054 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1058 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1059 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1061 for (i = 0; i < cinfo->num_components; i++) {
1062 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1063 THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1064 for (row = 0; row < th[i]; row++) {
1065 tmpbuf[i][row] = ptr;
1071 if (setjmp(this->jerr.setjmp_buffer)) {
1072 /* If we get here, the JPEG code has signaled an error. */
1073 retval = -1; goto bailout;
1076 for (row = 0; row < (int)cinfo->image_height;
1077 row += cinfo->max_v_samp_factor * DCTSIZE) {
1078 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1079 int crow[MAX_COMPONENTS];
1081 for (i = 0; i < cinfo->num_components; i++) {
1082 jpeg_component_info *compptr = &cinfo->comp_info[i];
1084 crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1088 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1089 memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1090 /* Duplicate last sample in row to fill out MCU */
1091 for (k = pw[i]; k < iw[i]; k++)
1092 tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1094 /* Duplicate last row to fill out MCU */
1095 for (j = ph[i] - crow[i]; j < th[i]; j++)
1096 memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1097 yuvptr[i] = tmpbuf[i];
1099 yuvptr[i] = &inbuf[i][crow[i]];
1101 jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1103 jpeg_finish_compress(cinfo);
1106 if (cinfo->global_state > CSTATE_START) {
1107 if (alloc) (*cinfo->dest->term_destination) (cinfo);
1108 jpeg_abort_compress(cinfo);
1110 for (i = 0; i < MAX_COMPONENTS; i++) {
1115 if (this->jerr.warning) retval = -1;
1116 this->jerr.stopOnWarning = FALSE;
1120 DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1121 int width, int pad, int height, int subsamp,
1122 unsigned char **jpegBuf,
1123 unsigned long *jpegSize, int jpegQual,
1126 const unsigned char *srcPlanes[3];
1127 int pw0, ph0, strides[3], retval = -1;
1128 tjinstance *this = (tjinstance *)handle;
1130 if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
1131 this->isInstanceError = FALSE;
1133 if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
1134 subsamp >= NUMSUBOPT)
1135 THROW("tjCompressFromYUV(): Invalid argument");
1137 pw0 = tjPlaneWidth(0, width, subsamp);
1138 ph0 = tjPlaneHeight(0, height, subsamp);
1139 srcPlanes[0] = srcBuf;
1140 strides[0] = PAD(pw0, pad);
1141 if (subsamp == TJSAMP_GRAY) {
1142 strides[1] = strides[2] = 0;
1143 srcPlanes[1] = srcPlanes[2] = NULL;
1145 int pw1 = tjPlaneWidth(1, width, subsamp);
1146 int ph1 = tjPlaneHeight(1, height, subsamp);
1148 strides[1] = strides[2] = PAD(pw1, pad);
1149 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1150 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1153 return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1154 subsamp, jpegBuf, jpegSize, jpegQual, flags);
1163 static tjhandle _tjInitDecompress(tjinstance *this)
1165 static unsigned char buffer[1];
1167 /* This is also straight out of example.txt */
1168 this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1169 this->jerr.pub.error_exit = my_error_exit;
1170 this->jerr.pub.output_message = my_output_message;
1171 this->jerr.emit_message = this->jerr.pub.emit_message;
1172 this->jerr.pub.emit_message = my_emit_message;
1173 this->jerr.pub.addon_message_table = turbojpeg_message_table;
1174 this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1175 this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1177 if (setjmp(this->jerr.setjmp_buffer)) {
1178 /* If we get here, the JPEG code has signaled an error. */
1183 jpeg_create_decompress(&this->dinfo);
1184 /* Make an initial call so it will create the source manager */
1185 jpeg_mem_src_tj(&this->dinfo, buffer, 1);
1187 this->init |= DECOMPRESS;
1188 return (tjhandle)this;
1191 DLLEXPORT tjhandle tjInitDecompress(void)
1195 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1196 SNPRINTF(errStr, JMSG_LENGTH_MAX,
1197 "tjInitDecompress(): Memory allocation failure");
1200 memset(this, 0, sizeof(tjinstance));
1201 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
1202 return _tjInitDecompress(this);
1206 DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1207 const unsigned char *jpegBuf,
1208 unsigned long jpegSize, int *width,
1209 int *height, int *jpegSubsamp,
1210 int *jpegColorspace)
1214 GET_DINSTANCE(handle);
1215 if ((this->init & DECOMPRESS) == 0)
1216 THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
1218 if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1219 jpegSubsamp == NULL || jpegColorspace == NULL)
1220 THROW("tjDecompressHeader3(): Invalid argument");
1222 if (setjmp(this->jerr.setjmp_buffer)) {
1223 /* If we get here, the JPEG code has signaled an error. */
1227 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1229 /* jpeg_read_header() calls jpeg_abort() and returns JPEG_HEADER_TABLES_ONLY
1230 if the datastream is a tables-only datastream. Since we aren't using a
1231 suspending data source, the only other value it can return is
1233 if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY)
1236 *width = dinfo->image_width;
1237 *height = dinfo->image_height;
1238 *jpegSubsamp = getSubsamp(dinfo);
1239 switch (dinfo->jpeg_color_space) {
1240 case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break;
1241 case JCS_RGB: *jpegColorspace = TJCS_RGB; break;
1242 case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break;
1243 case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break;
1244 case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break;
1245 default: *jpegColorspace = -1; break;
1248 jpeg_abort_decompress(dinfo);
1250 if (*jpegSubsamp < 0)
1251 THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
1252 if (*jpegColorspace < 0)
1253 THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
1254 if (*width < 1 || *height < 1)
1255 THROW("tjDecompressHeader3(): Invalid data returned in header");
1258 if (this->jerr.warning) retval = -1;
1262 DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1263 unsigned long jpegSize, int *width,
1264 int *height, int *jpegSubsamp)
1268 return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1269 jpegSubsamp, &jpegColorspace);
1272 DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1273 unsigned long jpegSize, int *width,
1278 return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1283 DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
1285 if (numscalingfactors == NULL) {
1286 SNPRINTF(errStr, JMSG_LENGTH_MAX,
1287 "tjGetScalingFactors(): Invalid argument");
1291 *numscalingfactors = NUMSF;
1292 return (tjscalingfactor *)sf;
1296 DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1297 unsigned long jpegSize, unsigned char *dstBuf,
1298 int width, int pitch, int height, int pixelFormat,
1301 JSAMPROW *row_pointer = NULL;
1302 int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
1303 struct my_progress_mgr progress;
1305 GET_DINSTANCE(handle);
1306 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1307 if ((this->init & DECOMPRESS) == 0)
1308 THROW("tjDecompress2(): Instance has not been initialized for decompression");
1310 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1311 pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1312 THROW("tjDecompress2(): Invalid argument");
1315 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1316 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1317 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1320 if (flags & TJFLAG_LIMITSCANS) {
1321 memset(&progress, 0, sizeof(struct my_progress_mgr));
1322 progress.pub.progress_monitor = my_progress_monitor;
1323 progress.this = this;
1324 dinfo->progress = &progress.pub;
1326 dinfo->progress = NULL;
1328 if (setjmp(this->jerr.setjmp_buffer)) {
1329 /* If we get here, the JPEG code has signaled an error. */
1330 retval = -1; goto bailout;
1333 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1334 jpeg_read_header(dinfo, TRUE);
1335 this->dinfo.out_color_space = pf2cs[pixelFormat];
1336 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1337 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1339 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1340 if (width == 0) width = jpegwidth;
1341 if (height == 0) height = jpegheight;
1342 for (i = 0; i < NUMSF; i++) {
1343 scaledw = TJSCALED(jpegwidth, sf[i]);
1344 scaledh = TJSCALED(jpegheight, sf[i]);
1345 if (scaledw <= width && scaledh <= height)
1349 THROW("tjDecompress2(): Could not scale down to desired image dimensions");
1350 width = scaledw; height = scaledh;
1351 dinfo->scale_num = sf[i].num;
1352 dinfo->scale_denom = sf[i].denom;
1354 jpeg_start_decompress(dinfo);
1355 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
1358 (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
1359 THROW("tjDecompress2(): Memory allocation failure");
1360 if (setjmp(this->jerr.setjmp_buffer)) {
1361 /* If we get here, the JPEG code has signaled an error. */
1362 retval = -1; goto bailout;
1364 for (i = 0; i < (int)dinfo->output_height; i++) {
1365 if (flags & TJFLAG_BOTTOMUP)
1366 row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
1368 row_pointer[i] = &dstBuf[i * (size_t)pitch];
1370 while (dinfo->output_scanline < dinfo->output_height)
1371 jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1372 dinfo->output_height - dinfo->output_scanline);
1373 jpeg_finish_decompress(dinfo);
1376 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1378 if (this->jerr.warning) retval = -1;
1379 this->jerr.stopOnWarning = FALSE;
1383 DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1384 unsigned long jpegSize, unsigned char *dstBuf,
1385 int width, int pitch, int height, int pixelSize,
1389 return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1391 return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1392 height, getPixelFormat(pixelSize, flags), flags);
1396 static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
1397 int pixelFormat, int subsamp, int flags)
1401 dinfo->scale_num = dinfo->scale_denom = 1;
1403 if (subsamp == TJSAMP_GRAY) {
1404 dinfo->num_components = dinfo->comps_in_scan = 1;
1405 dinfo->jpeg_color_space = JCS_GRAYSCALE;
1407 dinfo->num_components = dinfo->comps_in_scan = 3;
1408 dinfo->jpeg_color_space = JCS_YCbCr;
1411 dinfo->comp_info = (jpeg_component_info *)
1412 (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1413 dinfo->num_components *
1414 sizeof(jpeg_component_info));
1416 for (i = 0; i < dinfo->num_components; i++) {
1417 jpeg_component_info *compptr = &dinfo->comp_info[i];
1419 compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1420 compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1421 compptr->component_index = i;
1422 compptr->component_id = i + 1;
1423 compptr->quant_tbl_no = compptr->dc_tbl_no =
1424 compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1425 dinfo->cur_comp_info[i] = compptr;
1427 dinfo->data_precision = 8;
1428 for (i = 0; i < 2; i++) {
1429 if (dinfo->quant_tbl_ptrs[i] == NULL)
1430 dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1437 static int my_read_markers(j_decompress_ptr dinfo)
1439 return JPEG_REACHED_SOS;
1442 static void my_reset_marker_reader(j_decompress_ptr dinfo)
1446 DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1447 const unsigned char **srcPlanes,
1448 const int *strides, int subsamp,
1449 unsigned char *dstBuf, int width, int pitch,
1450 int height, int pixelFormat, int flags)
1452 JSAMPROW *row_pointer = NULL;
1453 JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1454 JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1455 int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1457 jpeg_component_info *compptr;
1458 int (*old_read_markers) (j_decompress_ptr);
1459 void (*old_reset_marker_reader) (j_decompress_ptr);
1461 GET_DINSTANCE(handle);
1462 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1464 for (i = 0; i < MAX_COMPONENTS; i++) {
1465 tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL;
1468 if ((this->init & DECOMPRESS) == 0)
1469 THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
1471 if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
1472 dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1473 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1474 THROW("tjDecodeYUVPlanes(): Invalid argument");
1475 if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1476 THROW("tjDecodeYUVPlanes(): Invalid argument");
1478 if (setjmp(this->jerr.setjmp_buffer)) {
1479 /* If we get here, the JPEG code has signaled an error. */
1480 retval = -1; goto bailout;
1483 if (pixelFormat == TJPF_CMYK)
1484 THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
1486 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1487 dinfo->image_width = width;
1488 dinfo->image_height = height;
1491 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1492 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1493 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1496 dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1497 dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1498 dinfo->Se = DCTSIZE2 - 1;
1499 if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
1500 retval = -1; goto bailout;
1502 old_read_markers = dinfo->marker->read_markers;
1503 dinfo->marker->read_markers = my_read_markers;
1504 old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1505 dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1506 jpeg_read_header(dinfo, TRUE);
1507 dinfo->marker->read_markers = old_read_markers;
1508 dinfo->marker->reset_marker_reader = old_reset_marker_reader;
1510 this->dinfo.out_color_space = pf2cs[pixelFormat];
1511 if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1512 dinfo->do_fancy_upsampling = FALSE;
1513 dinfo->Se = DCTSIZE2 - 1;
1514 jinit_master_decompress(dinfo);
1515 (*dinfo->upsample->start_pass) (dinfo);
1517 pw0 = PAD(width, dinfo->max_h_samp_factor);
1518 ph0 = PAD(height, dinfo->max_v_samp_factor);
1520 if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
1522 if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1523 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1524 for (i = 0; i < height; i++) {
1525 if (flags & TJFLAG_BOTTOMUP)
1526 row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
1528 row_pointer[i] = &dstBuf[i * (size_t)pitch];
1531 for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
1533 for (i = 0; i < dinfo->num_components; i++) {
1534 compptr = &dinfo->comp_info[i];
1536 (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1537 compptr->v_samp_factor + 32);
1539 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1540 tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1542 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1543 for (row = 0; row < compptr->v_samp_factor; row++) {
1544 unsigned char *_tmpbuf_aligned =
1545 (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
1548 &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1550 pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1551 ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1552 inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1554 THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1555 ptr = (JSAMPLE *)srcPlanes[i];
1556 for (row = 0; row < ph[i]; row++) {
1557 inbuf[i][row] = ptr;
1558 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1562 if (setjmp(this->jerr.setjmp_buffer)) {
1563 /* If we get here, the JPEG code has signaled an error. */
1564 retval = -1; goto bailout;
1567 for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1568 JDIMENSION inrow = 0, outrow = 0;
1570 for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1572 jcopy_sample_rows(inbuf[i],
1573 row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1574 compptr->v_samp_factor, pw[i]);
1575 (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1576 dinfo->max_v_samp_factor, &row_pointer[row],
1577 &outrow, dinfo->max_v_samp_factor);
1579 jpeg_abort_decompress(dinfo);
1582 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1584 for (i = 0; i < MAX_COMPONENTS; i++) {
1589 if (this->jerr.warning) retval = -1;
1590 this->jerr.stopOnWarning = FALSE;
1594 DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1595 int pad, int subsamp, unsigned char *dstBuf,
1596 int width, int pitch, int height, int pixelFormat,
1599 const unsigned char *srcPlanes[3];
1600 int pw0, ph0, strides[3], retval = -1;
1601 tjinstance *this = (tjinstance *)handle;
1603 if (!this) THROWG("tjDecodeYUV(): Invalid handle");
1604 this->isInstanceError = FALSE;
1606 if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
1607 subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
1608 THROW("tjDecodeYUV(): Invalid argument");
1610 pw0 = tjPlaneWidth(0, width, subsamp);
1611 ph0 = tjPlaneHeight(0, height, subsamp);
1612 srcPlanes[0] = srcBuf;
1613 strides[0] = PAD(pw0, pad);
1614 if (subsamp == TJSAMP_GRAY) {
1615 strides[1] = strides[2] = 0;
1616 srcPlanes[1] = srcPlanes[2] = NULL;
1618 int pw1 = tjPlaneWidth(1, width, subsamp);
1619 int ph1 = tjPlaneHeight(1, height, subsamp);
1621 strides[1] = strides[2] = PAD(pw1, pad);
1622 srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1623 srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1626 return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1627 pitch, height, pixelFormat, flags);
1633 DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1634 const unsigned char *jpegBuf,
1635 unsigned long jpegSize,
1636 unsigned char **dstPlanes, int width,
1637 int *strides, int height, int flags)
1639 int i, sfi, row, retval = 0;
1640 int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1641 int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1642 tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1643 JSAMPLE *_tmpbuf = NULL, *ptr;
1644 JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1646 struct my_progress_mgr progress;
1648 GET_DINSTANCE(handle);
1649 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1651 for (i = 0; i < MAX_COMPONENTS; i++) {
1652 tmpbuf[i] = NULL; outbuf[i] = NULL;
1655 if ((this->init & DECOMPRESS) == 0)
1656 THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
1658 if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1659 width < 0 || height < 0)
1660 THROW("tjDecompressToYUVPlanes(): Invalid argument");
1663 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1664 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1665 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1668 if (flags & TJFLAG_LIMITSCANS) {
1669 memset(&progress, 0, sizeof(struct my_progress_mgr));
1670 progress.pub.progress_monitor = my_progress_monitor;
1671 progress.this = this;
1672 dinfo->progress = &progress.pub;
1674 dinfo->progress = NULL;
1676 if (setjmp(this->jerr.setjmp_buffer)) {
1677 /* If we get here, the JPEG code has signaled an error. */
1678 retval = -1; goto bailout;
1681 if (!this->headerRead) {
1682 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1683 jpeg_read_header(dinfo, TRUE);
1685 this->headerRead = 0;
1686 jpegSubsamp = getSubsamp(dinfo);
1687 if (jpegSubsamp < 0)
1688 THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
1690 if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1691 THROW("tjDecompressToYUVPlanes(): Invalid argument");
1693 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1694 if (width == 0) width = jpegwidth;
1695 if (height == 0) height = jpegheight;
1696 for (i = 0; i < NUMSF; i++) {
1697 scaledw = TJSCALED(jpegwidth, sf[i]);
1698 scaledh = TJSCALED(jpegheight, sf[i]);
1699 if (scaledw <= width && scaledh <= height)
1703 THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
1704 if (dinfo->num_components > 3)
1705 THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
1707 width = scaledw; height = scaledh;
1708 dinfo->scale_num = sf[i].num;
1709 dinfo->scale_denom = sf[i].denom;
1711 jpeg_calc_output_dimensions(dinfo);
1713 dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
1715 for (i = 0; i < dinfo->num_components; i++) {
1716 jpeg_component_info *compptr = &dinfo->comp_info[i];
1719 iw[i] = compptr->width_in_blocks * dctsize;
1720 ih = compptr->height_in_blocks * dctsize;
1721 pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp);
1722 ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp);
1723 if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1724 th[i] = compptr->v_samp_factor * dctsize;
1725 tmpbufsize += iw[i] * th[i];
1726 if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1727 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1729 for (row = 0; row < ph[i]; row++) {
1730 outbuf[i][row] = ptr;
1731 ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1735 if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1736 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1738 for (i = 0; i < dinfo->num_components; i++) {
1739 if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1740 THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1741 for (row = 0; row < th[i]; row++) {
1742 tmpbuf[i][row] = ptr;
1748 if (setjmp(this->jerr.setjmp_buffer)) {
1749 /* If we get here, the JPEG code has signaled an error. */
1750 retval = -1; goto bailout;
1753 if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1754 if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1755 dinfo->raw_data_out = TRUE;
1757 jpeg_start_decompress(dinfo);
1758 for (row = 0; row < (int)dinfo->output_height;
1759 row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1760 JSAMPARRAY yuvptr[MAX_COMPONENTS];
1761 int crow[MAX_COMPONENTS];
1763 for (i = 0; i < dinfo->num_components; i++) {
1764 jpeg_component_info *compptr = &dinfo->comp_info[i];
1766 if (jpegSubsamp == TJ_420) {
1767 /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1768 to be clever and use the IDCT to perform upsampling on the U and V
1769 planes. For instance, if the output image is to be scaled by 1/2
1770 relative to the JPEG image, then the scaling factor and upsampling
1771 effectively cancel each other, so a normal 8x8 IDCT can be used.
1772 However, this is not desirable when using the decompress-to-YUV
1773 functionality in TurboJPEG, since we want to output the U and V
1774 planes in their subsampled form. Thus, we have to override some
1775 internal libjpeg parameters to force it to use the "scaled" IDCT
1776 functions on the U and V planes. */
1777 compptr->_DCT_scaled_size = dctsize;
1778 compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1779 sf[sfi].num / sf[sfi].denom *
1780 compptr->v_samp_factor / dinfo->max_v_samp_factor;
1781 dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1783 crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1784 if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1785 else yuvptr[i] = &outbuf[i][crow[i]];
1787 jpeg_read_raw_data(dinfo, yuvptr,
1788 dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1792 for (i = 0; i < dinfo->num_components; i++) {
1793 for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1794 memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1799 jpeg_finish_decompress(dinfo);
1802 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1803 for (i = 0; i < MAX_COMPONENTS; i++) {
1808 if (this->jerr.warning) retval = -1;
1809 this->jerr.stopOnWarning = FALSE;
1813 DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1814 unsigned long jpegSize, unsigned char *dstBuf,
1815 int width, int pad, int height, int flags)
1817 unsigned char *dstPlanes[3];
1818 int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1819 int i, jpegwidth, jpegheight, scaledw, scaledh;
1821 GET_DINSTANCE(handle);
1822 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1824 if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1825 pad < 1 || !IS_POW2(pad) || height < 0)
1826 THROW("tjDecompressToYUV2(): Invalid argument");
1828 if (setjmp(this->jerr.setjmp_buffer)) {
1829 /* If we get here, the JPEG code has signaled an error. */
1833 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1834 jpeg_read_header(dinfo, TRUE);
1835 jpegSubsamp = getSubsamp(dinfo);
1836 if (jpegSubsamp < 0)
1837 THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
1839 jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height;
1840 if (width == 0) width = jpegwidth;
1841 if (height == 0) height = jpegheight;
1843 for (i = 0; i < NUMSF; i++) {
1844 scaledw = TJSCALED(jpegwidth, sf[i]);
1845 scaledh = TJSCALED(jpegheight, sf[i]);
1846 if (scaledw <= width && scaledh <= height)
1850 THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
1852 pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1853 ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1854 dstPlanes[0] = dstBuf;
1855 strides[0] = PAD(pw0, pad);
1856 if (jpegSubsamp == TJSAMP_GRAY) {
1857 strides[1] = strides[2] = 0;
1858 dstPlanes[1] = dstPlanes[2] = NULL;
1860 int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1861 int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
1863 strides[1] = strides[2] = PAD(pw1, pad);
1864 dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1865 dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1868 this->headerRead = 1;
1869 return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1870 strides, height, flags);
1873 this->jerr.stopOnWarning = FALSE;
1877 DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1878 unsigned long jpegSize, unsigned char *dstBuf,
1881 return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
1887 DLLEXPORT tjhandle tjInitTransform(void)
1889 tjinstance *this = NULL;
1890 tjhandle handle = NULL;
1892 if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1893 SNPRINTF(errStr, JMSG_LENGTH_MAX,
1894 "tjInitTransform(): Memory allocation failure");
1897 memset(this, 0, sizeof(tjinstance));
1898 SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
1899 handle = _tjInitCompress(this);
1900 if (!handle) return NULL;
1901 handle = _tjInitDecompress(this);
1906 DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1907 unsigned long jpegSize, int n,
1908 unsigned char **dstBufs, unsigned long *dstSizes,
1909 tjtransform *t, int flags)
1911 jpeg_transform_info *xinfo = NULL;
1912 jvirt_barray_ptr *srccoefs, *dstcoefs;
1913 int retval = 0, i, jpegSubsamp, saveMarkers = 0;
1914 boolean alloc = TRUE;
1915 struct my_progress_mgr progress;
1917 GET_INSTANCE(handle);
1918 this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1919 if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
1920 THROW("tjTransform(): Instance has not been initialized for transformation");
1922 if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1923 dstSizes == NULL || t == NULL || flags < 0)
1924 THROW("tjTransform(): Invalid argument");
1927 if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1928 else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1929 else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1932 if (flags & TJFLAG_LIMITSCANS) {
1933 memset(&progress, 0, sizeof(struct my_progress_mgr));
1934 progress.pub.progress_monitor = my_progress_monitor;
1935 progress.this = this;
1936 dinfo->progress = &progress.pub;
1938 dinfo->progress = NULL;
1941 (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
1942 THROW("tjTransform(): Memory allocation failure");
1943 memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
1945 if (setjmp(this->jerr.setjmp_buffer)) {
1946 /* If we get here, the JPEG code has signaled an error. */
1947 retval = -1; goto bailout;
1950 jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1952 for (i = 0; i < n; i++) {
1953 xinfo[i].transform = xformtypes[t[i].op];
1954 xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1955 xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
1956 xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
1957 xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
1958 if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
1959 else xinfo[i].slow_hflip = 0;
1961 if (xinfo[i].crop) {
1962 xinfo[i].crop_xoffset = t[i].r.x; xinfo[i].crop_xoffset_set = JCROP_POS;
1963 xinfo[i].crop_yoffset = t[i].r.y; xinfo[i].crop_yoffset_set = JCROP_POS;
1964 if (t[i].r.w != 0) {
1965 xinfo[i].crop_width = t[i].r.w; xinfo[i].crop_width_set = JCROP_POS;
1967 xinfo[i].crop_width = JCROP_UNSET;
1968 if (t[i].r.h != 0) {
1969 xinfo[i].crop_height = t[i].r.h; xinfo[i].crop_height_set = JCROP_POS;
1971 xinfo[i].crop_height = JCROP_UNSET;
1973 if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
1976 jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
1977 jpeg_read_header(dinfo, TRUE);
1978 jpegSubsamp = getSubsamp(dinfo);
1979 if (jpegSubsamp < 0)
1980 THROW("tjTransform(): Could not determine subsampling type for JPEG image");
1982 for (i = 0; i < n; i++) {
1983 if (!jtransform_request_workspace(dinfo, &xinfo[i]))
1984 THROW("tjTransform(): Transform is not perfect");
1986 if (xinfo[i].crop) {
1987 if ((t[i].r.x % tjMCUWidth[jpegSubsamp]) != 0 ||
1988 (t[i].r.y % tjMCUHeight[jpegSubsamp]) != 0) {
1989 SNPRINTF(this->errStr, JMSG_LENGTH_MAX,
1990 "To crop this JPEG image, x must be a multiple of %d\n"
1991 "and y must be a multiple of %d.\n",
1992 tjMCUWidth[jpegSubsamp], tjMCUHeight[jpegSubsamp]);
1993 this->isInstanceError = TRUE;
1994 retval = -1; goto bailout;
1999 srccoefs = jpeg_read_coefficients(dinfo);
2001 for (i = 0; i < n; i++) {
2004 if (!xinfo[i].crop) {
2005 w = dinfo->image_width; h = dinfo->image_height;
2007 w = xinfo[i].crop_width; h = xinfo[i].crop_height;
2009 if (flags & TJFLAG_NOREALLOC) {
2010 alloc = FALSE; dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
2012 if (!(t[i].options & TJXOPT_NOOUTPUT))
2013 jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
2014 jpeg_copy_critical_parameters(dinfo, cinfo);
2015 dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
2016 if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
2017 jpeg_simple_progression(cinfo);
2018 if (!(t[i].options & TJXOPT_NOOUTPUT)) {
2019 jpeg_write_coefficients(cinfo, dstcoefs);
2020 jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
2021 JCOPYOPT_NONE : JCOPYOPT_ALL);
2023 jinit_c_master_control(cinfo, TRUE);
2024 jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
2025 if (t[i].customFilter) {
2029 for (ci = 0; ci < cinfo->num_components; ci++) {
2030 jpeg_component_info *compptr = &cinfo->comp_info[ci];
2031 tjregion arrayRegion = { 0, 0, 0, 0 };
2032 tjregion planeRegion = { 0, 0, 0, 0 };
2034 arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
2035 arrayRegion.h = DCTSIZE;
2036 planeRegion.w = compptr->width_in_blocks * DCTSIZE;
2037 planeRegion.h = compptr->height_in_blocks * DCTSIZE;
2039 for (by = 0; by < compptr->height_in_blocks;
2040 by += compptr->v_samp_factor) {
2041 JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
2042 ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
2045 for (y = 0; y < compptr->v_samp_factor; y++) {
2046 if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
2048 THROW("tjTransform(): Error in custom filter");
2049 arrayRegion.y += DCTSIZE;
2054 if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
2057 jpeg_finish_decompress(dinfo);
2060 if (cinfo->global_state > CSTATE_START) {
2061 if (alloc) (*cinfo->dest->term_destination) (cinfo);
2062 jpeg_abort_compress(cinfo);
2064 if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2066 if (this->jerr.warning) retval = -1;
2067 this->jerr.stopOnWarning = FALSE;
2072 DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
2073 int align, int *height, int *pixelFormat,
2076 int retval = 0, tempc;
2078 tjhandle handle = NULL;
2080 j_compress_ptr cinfo = NULL;
2081 cjpeg_source_ptr src;
2082 unsigned char *dstBuf = NULL;
2086 if (!filename || !width || align < 1 || !height || !pixelFormat ||
2087 *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
2088 THROWG("tjLoadImage(): Invalid argument");
2089 if ((align & (align - 1)) != 0)
2090 THROWG("tjLoadImage(): Alignment must be a power of 2");
2092 if ((handle = tjInitCompress()) == NULL) return NULL;
2093 this = (tjinstance *)handle;
2094 cinfo = &this->cinfo;
2097 if (fopen_s(&file, filename, "rb") || file == NULL)
2099 if ((file = fopen(filename, "rb")) == NULL)
2101 THROW_UNIX("tjLoadImage(): Cannot open input file");
2103 if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
2104 THROW_UNIX("tjLoadImage(): Could not read input file")
2105 else if (tempc == EOF)
2106 THROWG("tjLoadImage(): Input file contains no data");
2108 if (setjmp(this->jerr.setjmp_buffer)) {
2109 /* If we get here, the JPEG code has signaled an error. */
2110 retval = -1; goto bailout;
2113 if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2114 else cinfo->in_color_space = pf2cs[*pixelFormat];
2116 if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
2117 THROWG("tjLoadImage(): Could not initialize bitmap loader");
2118 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2119 } else if (tempc == 'P') {
2120 if ((src = jinit_read_ppm(cinfo)) == NULL)
2121 THROWG("tjLoadImage(): Could not initialize bitmap loader");
2122 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2124 THROWG("tjLoadImage(): Unsupported file type");
2126 src->input_file = file;
2127 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2128 /* Refuse to load images larger than 1 Megapixel when fuzzing. */
2129 if (flags & TJFLAG_FUZZING)
2130 src->max_pixels = 1048576;
2132 (*src->start_input) (cinfo, src);
2133 (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
2135 *width = cinfo->image_width; *height = cinfo->image_height;
2136 *pixelFormat = cs2pf[cinfo->in_color_space];
2138 pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
2139 if ((unsigned long long)pitch * (unsigned long long)(*height) >
2140 (unsigned long long)((size_t)-1) ||
2141 (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2142 THROWG("tjLoadImage(): Memory allocation failure");
2144 if (setjmp(this->jerr.setjmp_buffer)) {
2145 /* If we get here, the JPEG code has signaled an error. */
2146 retval = -1; goto bailout;
2149 while (cinfo->next_scanline < cinfo->image_height) {
2150 int i, nlines = (*src->get_pixel_rows) (cinfo, src);
2152 for (i = 0; i < nlines; i++) {
2153 unsigned char *dstptr;
2156 row = cinfo->next_scanline + i;
2157 if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2158 else dstptr = &dstBuf[row * pitch];
2159 memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2161 cinfo->next_scanline += nlines;
2164 (*src->finish_input) (cinfo, src);
2167 if (handle) tjDestroy(handle);
2168 if (file) fclose(file);
2169 if (retval < 0) { free(dstBuf); dstBuf = NULL; }
2174 DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2175 int width, int pitch, int height, int pixelFormat,
2179 tjhandle handle = NULL;
2181 j_decompress_ptr dinfo = NULL;
2187 if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2188 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2189 THROWG("tjSaveImage(): Invalid argument");
2191 if ((handle = tjInitDecompress()) == NULL)
2193 this = (tjinstance *)handle;
2194 dinfo = &this->dinfo;
2197 if (fopen_s(&file, filename, "wb") || file == NULL)
2199 if ((file = fopen(filename, "wb")) == NULL)
2201 THROW_UNIX("tjSaveImage(): Cannot open output file");
2203 if (setjmp(this->jerr.setjmp_buffer)) {
2204 /* If we get here, the JPEG code has signaled an error. */
2205 retval = -1; goto bailout;
2208 this->dinfo.out_color_space = pf2cs[pixelFormat];
2209 dinfo->image_width = width; dinfo->image_height = height;
2210 dinfo->global_state = DSTATE_READY;
2211 dinfo->scale_num = dinfo->scale_denom = 1;
2213 ptr = strrchr(filename, '.');
2214 if (ptr && !strcasecmp(ptr, ".bmp")) {
2215 if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
2216 THROWG("tjSaveImage(): Could not initialize bitmap writer");
2217 invert = (flags & TJFLAG_BOTTOMUP) == 0;
2219 if ((dst = jinit_write_ppm(dinfo)) == NULL)
2220 THROWG("tjSaveImage(): Could not initialize PPM writer");
2221 invert = (flags & TJFLAG_BOTTOMUP) != 0;
2224 dst->output_file = file;
2225 (*dst->start_output) (dinfo, dst);
2226 (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
2228 if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2230 while (dinfo->output_scanline < dinfo->output_height) {
2231 unsigned char *rowptr;
2234 rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2236 rowptr = &buffer[dinfo->output_scanline * pitch];
2237 memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2238 (*dst->put_pixel_rows) (dinfo, dst, 1);
2239 dinfo->output_scanline++;
2242 (*dst->finish_output) (dinfo, dst);
2245 if (handle) tjDestroy(handle);
2246 if (file) fclose(file);