xref: /aosp_15_r20/external/libjpeg-turbo/turbojpeg.c (revision dfc6aa5c1cfd4bc4e2018dc74aa96e29ee49c6da)
1 /*
2  * Copyright (C)2009-2023 D. R. Commander.  All Rights Reserved.
3  * Copyright (C)2021 Alex Richardson.  All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
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.
16  *
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.
28  */
29 
30 /* TurboJPEG/LJT:  this implements the TurboJPEG API using libjpeg or
31    libjpeg-turbo */
32 
33 #include <ctype.h>
34 #include <limits.h>
35 #include <jinclude.h>
36 #define JPEG_INTERNALS
37 #include <jpeglib.h>
38 #include <jerror.h>
39 #include <setjmp.h>
40 #include <errno.h>
41 #include "./turbojpeg.h"
42 #include "./tjutil.h"
43 #include "transupp.h"
44 #include "./jpegcomp.h"
45 #include "./cdjpeg.h"
46 
47 extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
48                              boolean);
49 extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *,
50                             unsigned long);
51 
52 #define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
53 #define IS_POW2(x)  (((x) & (x - 1)) == 0)
54 
55 
56 /* Error handling (based on example in example.txt) */
57 
58 static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
59 
60 struct my_error_mgr {
61   struct jpeg_error_mgr pub;
62   jmp_buf setjmp_buffer;
63   void (*emit_message) (j_common_ptr, int);
64   boolean warning, stopOnWarning;
65 };
66 typedef struct my_error_mgr *my_error_ptr;
67 
68 #define JMESSAGE(code, string)  string,
69 static const char *turbojpeg_message_table[] = {
70 #include "cderror.h"
71   NULL
72 };
73 
my_error_exit(j_common_ptr cinfo)74 static void my_error_exit(j_common_ptr cinfo)
75 {
76   my_error_ptr myerr = (my_error_ptr)cinfo->err;
77 
78   (*cinfo->err->output_message) (cinfo);
79   longjmp(myerr->setjmp_buffer, 1);
80 }
81 
82 /* Based on output_message() in jerror.c */
83 
my_output_message(j_common_ptr cinfo)84 static void my_output_message(j_common_ptr cinfo)
85 {
86   (*cinfo->err->format_message) (cinfo, errStr);
87 }
88 
my_emit_message(j_common_ptr cinfo,int msg_level)89 static void my_emit_message(j_common_ptr cinfo, int msg_level)
90 {
91   my_error_ptr myerr = (my_error_ptr)cinfo->err;
92 
93   myerr->emit_message(cinfo, msg_level);
94   if (msg_level < 0) {
95     myerr->warning = TRUE;
96     if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
97   }
98 }
99 
100 
101 /********************** Global structures, macros, etc. **********************/
102 
103 enum { COMPRESS = 1, DECOMPRESS = 2 };
104 
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;
112 } tjinstance;
113 
114 struct my_progress_mgr {
115   struct jpeg_progress_mgr pub;
116   tjinstance *this;
117 };
118 typedef struct my_progress_mgr *my_progress_ptr;
119 
my_progress_monitor(j_common_ptr dinfo)120 static void my_progress_monitor(j_common_ptr dinfo)
121 {
122   my_error_ptr myerr = (my_error_ptr)dinfo->err;
123   my_progress_ptr myprog = (my_progress_ptr)dinfo->progress;
124 
125   if (dinfo->is_decompressor) {
126     int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number;
127 
128     if (scan_no > 500) {
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);
136     }
137   }
138 }
139 
140 static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 };
141 
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
145 };
146 
147 #define NUMSF  16
148 static const tjscalingfactor sf[NUMSF] = {
149   { 2, 1 },
150   { 15, 8 },
151   { 7, 4 },
152   { 13, 8 },
153   { 3, 2 },
154   { 11, 8 },
155   { 5, 4 },
156   { 9, 8 },
157   { 1, 1 },
158   { 7, 8 },
159   { 3, 4 },
160   { 5, 8 },
161   { 1, 2 },
162   { 3, 8 },
163   { 1, 4 },
164   { 1, 8 }
165 };
166 
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
171 };
172 
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
176   TJPF_RGB,
177 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
178   TJPF_BGR,
179 #elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
180   TJPF_RGBX,
181 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
182   TJPF_BGRX,
183 #elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
184   TJPF_XBGR,
185 #elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
186   TJPF_XRGB,
187 #endif
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,
190   TJPF_UNKNOWN
191 };
192 
193 #define THROWG(m) { \
194   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s", m); \
195   retval = -1;  goto bailout; \
196 }
197 #ifdef _MSC_VER
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; \
203 }
204 #else
205 #define THROW_UNIX(m) { \
206   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
207   retval = -1;  goto bailout; \
208 }
209 #endif
210 #define THROW(m) { \
211   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
212   this->isInstanceError = TRUE;  THROWG(m) \
213 }
214 
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)
218 #endif
219 
220 #define GET_INSTANCE(handle) \
221   tjinstance *this = (tjinstance *)handle; \
222   j_compress_ptr cinfo = NULL; \
223   j_decompress_ptr dinfo = NULL; \
224   \
225   if (!this) { \
226     SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
227     return -1; \
228   } \
229   cinfo = &this->cinfo;  dinfo = &this->dinfo; \
230   this->jerr.warning = FALSE; \
231   this->isInstanceError = FALSE;
232 
233 #define GET_CINSTANCE(handle) \
234   tjinstance *this = (tjinstance *)handle; \
235   j_compress_ptr cinfo = NULL; \
236   \
237   if (!this) { \
238     SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
239     return -1; \
240   } \
241   cinfo = &this->cinfo; \
242   this->jerr.warning = FALSE; \
243   this->isInstanceError = FALSE;
244 
245 #define GET_DINSTANCE(handle) \
246   tjinstance *this = (tjinstance *)handle; \
247   j_decompress_ptr dinfo = NULL; \
248   \
249   if (!this) { \
250     SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
251     return -1; \
252   } \
253   dinfo = &this->dinfo; \
254   this->jerr.warning = FALSE; \
255   this->isInstanceError = FALSE;
256 
getPixelFormat(int pixelSize,int flags)257 static int getPixelFormat(int pixelSize, int flags)
258 {
259   if (pixelSize == 1) return TJPF_GRAY;
260   if (pixelSize == 3) {
261     if (flags & TJ_BGR) return TJPF_BGR;
262     else return TJPF_RGB;
263   }
264   if (pixelSize == 4) {
265     if (flags & TJ_ALPHAFIRST) {
266       if (flags & TJ_BGR) return TJPF_XBGR;
267       else return TJPF_XRGB;
268     } else {
269       if (flags & TJ_BGR) return TJPF_BGRX;
270       else return TJPF_RGBX;
271     }
272   }
273   return -1;
274 }
275 
setCompDefaults(struct jpeg_compress_struct * cinfo,int pixelFormat,int subsamp,int jpegQual,int flags)276 static void setCompDefaults(struct jpeg_compress_struct *cinfo,
277                             int pixelFormat, int subsamp, int jpegQual,
278                             int flags)
279 {
280 #ifndef NO_GETENV
281   char env[7] = { 0 };
282 #endif
283 
284   cinfo->in_color_space = pf2cs[pixelFormat];
285   cinfo->input_components = tjPixelSize[pixelFormat];
286   jpeg_set_defaults(cinfo);
287 
288 #ifndef NO_GETENV
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) {
294     int temp = -1;
295     char tempc = 0;
296 
297 #ifdef _MSC_VER
298     if (sscanf_s(env, "%d%c", &temp, &tempc, 1) >= 1 && temp >= 0 &&
299         temp <= 65535) {
300 #else
301     if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
302         temp <= 65535) {
303 #endif
304       if (toupper(tempc) == 'B') {
305         cinfo->restart_interval = temp;
306         cinfo->restart_in_rows = 0;
307       } else
308         cinfo->restart_in_rows = temp;
309     }
310   }
311 #endif
312 
313   if (jpegQual >= 0) {
314     jpeg_set_quality(cinfo, jpegQual, TRUE);
315     if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
316       cinfo->dct_method = JDCT_ISLOW;
317     else
318       cinfo->dct_method = JDCT_FASTEST;
319   }
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);
324   else
325     jpeg_set_colorspace(cinfo, JCS_YCbCr);
326 
327 #ifdef C_PROGRESSIVE_SUPPORTED
328   if (flags & TJFLAG_PROGRESSIVE)
329     jpeg_simple_progression(cinfo);
330 #ifndef NO_GETENV
331   else if (!GETENV_S(env, 7, "TJ_PROGRESSIVE") && !strcmp(env, "1"))
332     jpeg_simple_progression(cinfo);
333 #endif
334 #endif
335 
336   cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
337   cinfo->comp_info[1].h_samp_factor = 1;
338   cinfo->comp_info[2].h_samp_factor = 1;
339   if (cinfo->num_components > 3)
340     cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
341   cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
342   cinfo->comp_info[1].v_samp_factor = 1;
343   cinfo->comp_info[2].v_samp_factor = 1;
344   if (cinfo->num_components > 3)
345     cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;
346 }
347 
348 
349 static int getSubsamp(j_decompress_ptr dinfo)
350 {
351   int retval = -1, i, k;
352 
353   /* The sampling factors actually have no meaning with grayscale JPEG files,
354      and in fact it's possible to generate grayscale JPEGs with sampling
355      factors > 1 (even though those sampling factors are ignored by the
356      decompressor.)  Thus, we need to treat grayscale as a special case. */
357   if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
358     return TJSAMP_GRAY;
359 
360   for (i = 0; i < TJ_NUMSAMP; i++) {
361     if (dinfo->num_components == pixelsize[i] ||
362         ((dinfo->jpeg_color_space == JCS_YCCK ||
363           dinfo->jpeg_color_space == JCS_CMYK) &&
364          pixelsize[i] == 3 && dinfo->num_components == 4)) {
365       if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
366           dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
367         int match = 0;
368 
369         for (k = 1; k < dinfo->num_components; k++) {
370           int href = 1, vref = 1;
371 
372           if ((dinfo->jpeg_color_space == JCS_YCCK ||
373                dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
374             href = tjMCUWidth[i] / 8;  vref = tjMCUHeight[i] / 8;
375           }
376           if (dinfo->comp_info[k].h_samp_factor == href &&
377               dinfo->comp_info[k].v_samp_factor == vref)
378             match++;
379         }
380         if (match == dinfo->num_components - 1) {
381           retval = i;  break;
382         }
383       }
384       /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
385          in non-standard ways. */
386       if (dinfo->comp_info[0].h_samp_factor == 2 &&
387           dinfo->comp_info[0].v_samp_factor == 2 &&
388           (i == TJSAMP_422 || i == TJSAMP_440)) {
389         int match = 0;
390 
391         for (k = 1; k < dinfo->num_components; k++) {
392           int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
393 
394           if ((dinfo->jpeg_color_space == JCS_YCCK ||
395                dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
396             href = vref = 2;
397           }
398           if (dinfo->comp_info[k].h_samp_factor == href &&
399               dinfo->comp_info[k].v_samp_factor == vref)
400             match++;
401         }
402         if (match == dinfo->num_components - 1) {
403           retval = i;  break;
404         }
405       }
406       /* Handle 4:4:4 images whose sampling factors are specified in
407          non-standard ways. */
408       if (dinfo->comp_info[0].h_samp_factor *
409           dinfo->comp_info[0].v_samp_factor <=
410           D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) {
411         int match = 0;
412         for (k = 1; k < dinfo->num_components; k++) {
413           if (dinfo->comp_info[k].h_samp_factor ==
414               dinfo->comp_info[0].h_samp_factor &&
415               dinfo->comp_info[k].v_samp_factor ==
416               dinfo->comp_info[0].v_samp_factor)
417             match++;
418           if (match == dinfo->num_components - 1) {
419             retval = i;  break;
420           }
421         }
422       }
423     }
424   }
425   return retval;
426 }
427 
428 
429 /*************************** General API functions ***************************/
430 
431 /* TurboJPEG 2.0+ */
432 DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
433 {
434   tjinstance *this = (tjinstance *)handle;
435 
436   if (this && this->isInstanceError) {
437     this->isInstanceError = FALSE;
438     return this->errStr;
439   } else
440     return errStr;
441 }
442 
443 
444 /* TurboJPEG 1.0+ */
445 DLLEXPORT char *tjGetErrorStr(void)
446 {
447   return errStr;
448 }
449 
450 
451 /* TurboJPEG 2.0+ */
452 DLLEXPORT int tjGetErrorCode(tjhandle handle)
453 {
454   tjinstance *this = (tjinstance *)handle;
455 
456   if (this && this->jerr.warning) return TJERR_WARNING;
457   else return TJERR_FATAL;
458 }
459 
460 
461 /* TurboJPEG 1.0+ */
462 DLLEXPORT int tjDestroy(tjhandle handle)
463 {
464   GET_INSTANCE(handle);
465 
466   if (setjmp(this->jerr.setjmp_buffer)) return -1;
467   if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
468   if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
469   free(this);
470   return 0;
471 }
472 
473 
474 /* These are exposed mainly because Windows can't malloc() and free() across
475    DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
476    with turbojpeg.dll for compatibility reasons.  However, these functions
477    can potentially be used for other purposes by different implementations. */
478 
479 /* TurboJPEG 1.2+ */
480 DLLEXPORT void tjFree(unsigned char *buf)
481 {
482   free(buf);
483 }
484 
485 
486 /* TurboJPEG 1.2+ */
487 DLLEXPORT unsigned char *tjAlloc(int bytes)
488 {
489   return (unsigned char *)malloc(bytes);
490 }
491 
492 
493 /******************************** Compressor *********************************/
494 
495 static tjhandle _tjInitCompress(tjinstance *this)
496 {
497   static unsigned char buffer[1];
498   unsigned char *buf = buffer;
499   unsigned long size = 1;
500 
501   /* This is also straight out of example.txt */
502   this->cinfo.err = jpeg_std_error(&this->jerr.pub);
503   this->jerr.pub.error_exit = my_error_exit;
504   this->jerr.pub.output_message = my_output_message;
505   this->jerr.emit_message = this->jerr.pub.emit_message;
506   this->jerr.pub.emit_message = my_emit_message;
507   this->jerr.pub.addon_message_table = turbojpeg_message_table;
508   this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
509   this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
510 
511   if (setjmp(this->jerr.setjmp_buffer)) {
512     /* If we get here, the JPEG code has signaled an error. */
513     free(this);
514     return NULL;
515   }
516 
517   jpeg_create_compress(&this->cinfo);
518   /* Make an initial call so it will create the destination manager */
519   jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
520 
521   this->init |= COMPRESS;
522   return (tjhandle)this;
523 }
524 
525 /* TurboJPEG 1.0+ */
526 DLLEXPORT tjhandle tjInitCompress(void)
527 {
528   tjinstance *this = NULL;
529 
530   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
531     SNPRINTF(errStr, JMSG_LENGTH_MAX,
532              "tjInitCompress(): Memory allocation failure");
533     return NULL;
534   }
535   memset(this, 0, sizeof(tjinstance));
536   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
537   return _tjInitCompress(this);
538 }
539 
540 
541 /* TurboJPEG 1.2+ */
542 DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
543 {
544   unsigned long long retval = 0;
545   int mcuw, mcuh, chromasf;
546 
547   if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP)
548     THROWG("tjBufSize(): Invalid argument");
549 
550   /* This allows for rare corner cases in which a JPEG image can actually be
551      larger than the uncompressed input (we wouldn't mention it if it hadn't
552      happened before.) */
553   mcuw = tjMCUWidth[jpegSubsamp];
554   mcuh = tjMCUHeight[jpegSubsamp];
555   chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
556   retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
557   if (retval > (unsigned long long)((unsigned long)-1))
558     THROWG("tjBufSize(): Image is too large");
559 
560 bailout:
561   return (unsigned long)retval;
562 }
563 
564 /* TurboJPEG 1.0+ */
565 DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
566 {
567   unsigned long long retval = 0;
568 
569   if (width < 1 || height < 1)
570     THROWG("TJBUFSIZE(): Invalid argument");
571 
572   /* This allows for rare corner cases in which a JPEG image can actually be
573      larger than the uncompressed input (we wouldn't mention it if it hadn't
574      happened before.) */
575   retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
576   if (retval > (unsigned long long)((unsigned long)-1))
577     THROWG("TJBUFSIZE(): Image is too large");
578 
579 bailout:
580   return (unsigned long)retval;
581 }
582 
583 
584 /* TurboJPEG 1.4+ */
585 DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height,
586                                       int subsamp)
587 {
588   unsigned long long retval = 0;
589   int nc, i;
590 
591   if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP)
592     THROWG("tjBufSizeYUV2(): Invalid argument");
593 
594   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
595   for (i = 0; i < nc; i++) {
596     int pw = tjPlaneWidth(i, width, subsamp);
597     int stride = PAD(pw, align);
598     int ph = tjPlaneHeight(i, height, subsamp);
599 
600     if (pw < 0 || ph < 0) return -1;
601     else retval += (unsigned long long)stride * ph;
602   }
603   if (retval > (unsigned long long)((unsigned long)-1))
604     THROWG("tjBufSizeYUV2(): Image is too large");
605 
606 bailout:
607   return (unsigned long)retval;
608 }
609 
610 /* TurboJPEG 1.2+ */
611 DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
612 {
613   return tjBufSizeYUV2(width, 4, height, subsamp);
614 }
615 
616 /* TurboJPEG 1.1+ */
617 DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
618 {
619   return tjBufSizeYUV(width, height, subsamp);
620 }
621 
622 
623 /* TurboJPEG 1.4+ */
624 DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
625 {
626   unsigned long long pw, retval = 0;
627   int nc;
628 
629   if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
630     THROWG("tjPlaneWidth(): Invalid argument");
631   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
632   if (componentID < 0 || componentID >= nc)
633     THROWG("tjPlaneWidth(): Invalid argument");
634 
635   pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8);
636   if (componentID == 0)
637     retval = pw;
638   else
639     retval = pw * 8 / tjMCUWidth[subsamp];
640 
641   if (retval > (unsigned long long)INT_MAX)
642     THROWG("tjPlaneWidth(): Width is too large");
643 
644 bailout:
645   return (int)retval;
646 }
647 
648 
649 /* TurboJPEG 1.4+ */
650 DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
651 {
652   unsigned long long ph, retval = 0;
653   int nc;
654 
655   if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
656     THROWG("tjPlaneHeight(): Invalid argument");
657   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
658   if (componentID < 0 || componentID >= nc)
659     THROWG("tjPlaneHeight(): Invalid argument");
660 
661   ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8);
662   if (componentID == 0)
663     retval = ph;
664   else
665     retval = ph * 8 / tjMCUHeight[subsamp];
666 
667   if (retval > (unsigned long long)INT_MAX)
668     THROWG("tjPlaneHeight(): Height is too large");
669 
670 bailout:
671   return (int)retval;
672 }
673 
674 
675 /* TurboJPEG 1.4+ */
676 DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
677                                        int height, int subsamp)
678 {
679   unsigned long long retval = 0;
680   int pw, ph;
681 
682   if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
683     THROWG("tjPlaneSizeYUV(): Invalid argument");
684 
685   pw = tjPlaneWidth(componentID, width, subsamp);
686   ph = tjPlaneHeight(componentID, height, subsamp);
687   if (pw < 0 || ph < 0) return -1;
688 
689   if (stride == 0) stride = pw;
690   else stride = abs(stride);
691 
692   retval = (unsigned long long)stride * (ph - 1) + pw;
693   if (retval > (unsigned long long)((unsigned long)-1))
694     THROWG("tjPlaneSizeYUV(): Image is too large");
695 
696 bailout:
697   return (unsigned long)retval;
698 }
699 
700 
701 /* TurboJPEG 1.2+ */
702 DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
703                           int width, int pitch, int height, int pixelFormat,
704                           unsigned char **jpegBuf, unsigned long *jpegSize,
705                           int jpegSubsamp, int jpegQual, int flags)
706 {
707   int i, retval = 0;
708   boolean alloc = TRUE;
709   JSAMPROW *row_pointer = NULL;
710 
711   GET_CINSTANCE(handle)
712   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
713   if ((this->init & COMPRESS) == 0)
714     THROW("tjCompress2(): Instance has not been initialized for compression");
715 
716   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
717       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
718       jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP ||
719       jpegQual < 0 || jpegQual > 100)
720     THROW("tjCompress2(): Invalid argument");
721 
722   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
723 
724   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
725     THROW("tjCompress2(): Memory allocation failure");
726 
727   if (setjmp(this->jerr.setjmp_buffer)) {
728     /* If we get here, the JPEG code has signaled an error. */
729     retval = -1;  goto bailout;
730   }
731 
732   cinfo->image_width = width;
733   cinfo->image_height = height;
734 
735 #ifndef NO_PUTENV
736   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
737   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
738   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
739 #endif
740 
741   if (flags & TJFLAG_NOREALLOC) {
742     alloc = FALSE;  *jpegSize = tjBufSize(width, height, jpegSubsamp);
743   }
744   jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
745   setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags);
746 
747   jpeg_start_compress(cinfo, TRUE);
748   for (i = 0; i < height; i++) {
749     if (flags & TJFLAG_BOTTOMUP)
750       row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
751     else
752       row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
753   }
754   while (cinfo->next_scanline < cinfo->image_height)
755     jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
756                          cinfo->image_height - cinfo->next_scanline);
757   jpeg_finish_compress(cinfo);
758 
759 bailout:
760   if (cinfo->global_state > CSTATE_START) {
761     if (alloc) (*cinfo->dest->term_destination) (cinfo);
762     jpeg_abort_compress(cinfo);
763   }
764   free(row_pointer);
765   if (this->jerr.warning) retval = -1;
766   this->jerr.stopOnWarning = FALSE;
767   return retval;
768 }
769 
770 /* TurboJPEG 1.0+ */
771 DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
772                          int pitch, int height, int pixelSize,
773                          unsigned char *jpegBuf, unsigned long *jpegSize,
774                          int jpegSubsamp, int jpegQual, int flags)
775 {
776   int retval = 0;
777   unsigned long size;
778 
779   if (flags & TJ_YUV) {
780     size = tjBufSizeYUV(width, height, jpegSubsamp);
781     retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
782                           getPixelFormat(pixelSize, flags), jpegBuf,
783                           jpegSubsamp, flags);
784   } else {
785     retval = tjCompress2(handle, srcBuf, width, pitch, height,
786                          getPixelFormat(pixelSize, flags), &jpegBuf, &size,
787                          jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
788   }
789   *jpegSize = size;
790   return retval;
791 }
792 
793 
794 /* TurboJPEG 1.4+ */
795 DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
796                                 int width, int pitch, int height,
797                                 int pixelFormat, unsigned char **dstPlanes,
798                                 int *strides, int subsamp, int flags)
799 {
800   JSAMPROW *row_pointer = NULL;
801   JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
802   JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
803   JSAMPROW *outbuf[MAX_COMPONENTS];
804   int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
805   JSAMPLE *ptr;
806   jpeg_component_info *compptr;
807 
808   GET_CINSTANCE(handle);
809   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
810 
811   for (i = 0; i < MAX_COMPONENTS; i++) {
812     tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;
813     tmpbuf2[i] = NULL;  _tmpbuf2[i] = NULL;  outbuf[i] = NULL;
814   }
815 
816   if ((this->init & COMPRESS) == 0)
817     THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
818 
819   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
820       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
821       !dstPlanes[0] || subsamp < 0 || subsamp >= TJ_NUMSAMP)
822     THROW("tjEncodeYUVPlanes(): Invalid argument");
823   if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
824     THROW("tjEncodeYUVPlanes(): Invalid argument");
825 
826   if (pixelFormat == TJPF_CMYK)
827     THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from packed-pixel CMYK images");
828 
829   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
830 
831   if (setjmp(this->jerr.setjmp_buffer)) {
832     /* If we get here, the JPEG code has signaled an error. */
833     retval = -1;  goto bailout;
834   }
835 
836   cinfo->image_width = width;
837   cinfo->image_height = height;
838 
839 #ifndef NO_PUTENV
840   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
841   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
842   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
843 #endif
844 
845   setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags);
846 
847   /* Execute only the parts of jpeg_start_compress() that we need.  If we
848      were to call the whole jpeg_start_compress() function, then it would try
849      to write the file headers, which could overflow the output buffer if the
850      YUV image were very small. */
851   if (cinfo->global_state != CSTATE_START)
852     THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
853   (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
854   jinit_c_master_control(cinfo, FALSE);
855   jinit_color_converter(cinfo);
856   jinit_downsampler(cinfo);
857   (*cinfo->cconvert->start_pass) (cinfo);
858 
859   pw0 = PAD(width, cinfo->max_h_samp_factor);
860   ph0 = PAD(height, cinfo->max_v_samp_factor);
861 
862   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
863     THROW("tjEncodeYUVPlanes(): Memory allocation failure");
864   for (i = 0; i < height; i++) {
865     if (flags & TJFLAG_BOTTOMUP)
866       row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
867     else
868       row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
869   }
870   if (height < ph0)
871     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
872 
873   for (i = 0; i < cinfo->num_components; i++) {
874     compptr = &cinfo->comp_info[i];
875     _tmpbuf[i] = (JSAMPLE *)malloc(
876       PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
877           compptr->h_samp_factor, 32) *
878       cinfo->max_v_samp_factor + 32);
879     if (!_tmpbuf[i])
880       THROW("tjEncodeYUVPlanes(): Memory allocation failure");
881     tmpbuf[i] =
882       (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
883     if (!tmpbuf[i])
884       THROW("tjEncodeYUVPlanes(): Memory allocation failure");
885     for (row = 0; row < cinfo->max_v_samp_factor; row++) {
886       unsigned char *_tmpbuf_aligned =
887         (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
888 
889       tmpbuf[i][row] = &_tmpbuf_aligned[
890         PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
891             compptr->h_samp_factor, 32) * row];
892     }
893     _tmpbuf2[i] =
894       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
895                         compptr->v_samp_factor + 32);
896     if (!_tmpbuf2[i])
897       THROW("tjEncodeYUVPlanes(): Memory allocation failure");
898     tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
899     if (!tmpbuf2[i])
900       THROW("tjEncodeYUVPlanes(): Memory allocation failure");
901     for (row = 0; row < compptr->v_samp_factor; row++) {
902       unsigned char *_tmpbuf2_aligned =
903         (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32);
904 
905       tmpbuf2[i][row] =
906         &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
907     }
908     pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
909     ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
910     outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
911     if (!outbuf[i])
912       THROW("tjEncodeYUVPlanes(): Memory allocation failure");
913     ptr = dstPlanes[i];
914     for (row = 0; row < ph[i]; row++) {
915       outbuf[i][row] = ptr;
916       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
917     }
918   }
919 
920   if (setjmp(this->jerr.setjmp_buffer)) {
921     /* If we get here, the JPEG code has signaled an error. */
922     retval = -1;  goto bailout;
923   }
924 
925   for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
926     (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
927                                        cinfo->max_v_samp_factor);
928     (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
929     for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
930          i++, compptr++)
931       jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
932         row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
933         compptr->v_samp_factor, pw[i]);
934   }
935   cinfo->next_scanline += height;
936   jpeg_abort_compress(cinfo);
937 
938 bailout:
939   if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
940   free(row_pointer);
941   for (i = 0; i < MAX_COMPONENTS; i++) {
942     free(tmpbuf[i]);
943     free(_tmpbuf[i]);
944     free(tmpbuf2[i]);
945     free(_tmpbuf2[i]);
946     free(outbuf[i]);
947   }
948   if (this->jerr.warning) retval = -1;
949   this->jerr.stopOnWarning = FALSE;
950   return retval;
951 }
952 
953 /* TurboJPEG 1.4+ */
954 DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
955                            int width, int pitch, int height, int pixelFormat,
956                            unsigned char *dstBuf, int align, int subsamp,
957                            int flags)
958 {
959   unsigned char *dstPlanes[3];
960   int pw0, ph0, strides[3], retval = -1;
961   tjinstance *this = (tjinstance *)handle;
962 
963   if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
964   this->isInstanceError = FALSE;
965 
966   if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 ||
967       !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP)
968     THROW("tjEncodeYUV3(): Invalid argument");
969 
970   pw0 = tjPlaneWidth(0, width, subsamp);
971   ph0 = tjPlaneHeight(0, height, subsamp);
972   dstPlanes[0] = dstBuf;
973   strides[0] = PAD(pw0, align);
974   if (subsamp == TJSAMP_GRAY) {
975     strides[1] = strides[2] = 0;
976     dstPlanes[1] = dstPlanes[2] = NULL;
977   } else {
978     int pw1 = tjPlaneWidth(1, width, subsamp);
979     int ph1 = tjPlaneHeight(1, height, subsamp);
980 
981     strides[1] = strides[2] = PAD(pw1, align);
982     dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
983     dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
984   }
985 
986   return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat,
987                            dstPlanes, strides, subsamp, flags);
988 
989 bailout:
990   return retval;
991 }
992 
993 /* TurboJPEG 1.2+ */
994 DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
995                            int pitch, int height, int pixelFormat,
996                            unsigned char *dstBuf, int subsamp, int flags)
997 {
998   return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
999                       dstBuf, 4, subsamp, flags);
1000 }
1001 
1002 /* TurboJPEG 1.1+ */
1003 DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
1004                           int pitch, int height, int pixelSize,
1005                           unsigned char *dstBuf, int subsamp, int flags)
1006 {
1007   return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1008                       getPixelFormat(pixelSize, flags), dstBuf, subsamp,
1009                       flags);
1010 }
1011 
1012 
1013 /* TurboJPEG 1.4+ */
1014 DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
1015                                       const unsigned char **srcPlanes,
1016                                       int width, const int *strides,
1017                                       int height, int subsamp,
1018                                       unsigned char **jpegBuf,
1019                                       unsigned long *jpegSize, int jpegQual,
1020                                       int flags)
1021 {
1022   int i, row, retval = 0;
1023   boolean alloc = TRUE;
1024   int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1025     tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1026   JSAMPLE *_tmpbuf = NULL, *ptr;
1027   JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1028 
1029   GET_CINSTANCE(handle)
1030   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1031 
1032   for (i = 0; i < MAX_COMPONENTS; i++) {
1033     tmpbuf[i] = NULL;  inbuf[i] = NULL;
1034   }
1035 
1036   if ((this->init & COMPRESS) == 0)
1037     THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
1038 
1039   if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
1040       subsamp < 0 || subsamp >= TJ_NUMSAMP || jpegBuf == NULL ||
1041       jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
1042     THROW("tjCompressFromYUVPlanes(): Invalid argument");
1043   if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1044     THROW("tjCompressFromYUVPlanes(): Invalid argument");
1045 
1046   if (setjmp(this->jerr.setjmp_buffer)) {
1047     /* If we get here, the JPEG code has signaled an error. */
1048     retval = -1;  goto bailout;
1049   }
1050 
1051   cinfo->image_width = width;
1052   cinfo->image_height = height;
1053 
1054 #ifndef NO_PUTENV
1055   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1056   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1057   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1058 #endif
1059 
1060   if (flags & TJFLAG_NOREALLOC) {
1061     alloc = FALSE;  *jpegSize = tjBufSize(width, height, subsamp);
1062   }
1063   jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
1064   setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags);
1065   cinfo->raw_data_in = TRUE;
1066 
1067   jpeg_start_compress(cinfo, TRUE);
1068   for (i = 0; i < cinfo->num_components; i++) {
1069     jpeg_component_info *compptr = &cinfo->comp_info[i];
1070     int ih;
1071 
1072     iw[i] = compptr->width_in_blocks * DCTSIZE;
1073     ih = compptr->height_in_blocks * DCTSIZE;
1074     pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1075             compptr->h_samp_factor / cinfo->max_h_samp_factor;
1076     ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1077             compptr->v_samp_factor / cinfo->max_v_samp_factor;
1078     if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1079     th[i] = compptr->v_samp_factor * DCTSIZE;
1080     tmpbufsize += iw[i] * th[i];
1081     if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1082       THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1083     ptr = (JSAMPLE *)srcPlanes[i];
1084     for (row = 0; row < ph[i]; row++) {
1085       inbuf[i][row] = ptr;
1086       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1087     }
1088   }
1089   if (usetmpbuf) {
1090     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1091       THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1092     ptr = _tmpbuf;
1093     for (i = 0; i < cinfo->num_components; i++) {
1094       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1095         THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
1096       for (row = 0; row < th[i]; row++) {
1097         tmpbuf[i][row] = ptr;
1098         ptr += iw[i];
1099       }
1100     }
1101   }
1102 
1103   if (setjmp(this->jerr.setjmp_buffer)) {
1104     /* If we get here, the JPEG code has signaled an error. */
1105     retval = -1;  goto bailout;
1106   }
1107 
1108   for (row = 0; row < (int)cinfo->image_height;
1109        row += cinfo->max_v_samp_factor * DCTSIZE) {
1110     JSAMPARRAY yuvptr[MAX_COMPONENTS];
1111     int crow[MAX_COMPONENTS];
1112 
1113     for (i = 0; i < cinfo->num_components; i++) {
1114       jpeg_component_info *compptr = &cinfo->comp_info[i];
1115 
1116       crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1117       if (usetmpbuf) {
1118         int j, k;
1119 
1120         for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1121           memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1122           /* Duplicate last sample in row to fill out MCU */
1123           for (k = pw[i]; k < iw[i]; k++)
1124             tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1125         }
1126         /* Duplicate last row to fill out MCU */
1127         for (j = ph[i] - crow[i]; j < th[i]; j++)
1128           memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1129         yuvptr[i] = tmpbuf[i];
1130       } else
1131         yuvptr[i] = &inbuf[i][crow[i]];
1132     }
1133     jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1134   }
1135   jpeg_finish_compress(cinfo);
1136 
1137 bailout:
1138   if (cinfo->global_state > CSTATE_START) {
1139     if (alloc) (*cinfo->dest->term_destination) (cinfo);
1140     jpeg_abort_compress(cinfo);
1141   }
1142   for (i = 0; i < MAX_COMPONENTS; i++) {
1143     free(tmpbuf[i]);
1144     free(inbuf[i]);
1145   }
1146   free(_tmpbuf);
1147   if (this->jerr.warning) retval = -1;
1148   this->jerr.stopOnWarning = FALSE;
1149   return retval;
1150 }
1151 
1152 /* TurboJPEG 1.4+ */
1153 DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1154                                 int width, int align, int height, int subsamp,
1155                                 unsigned char **jpegBuf,
1156                                 unsigned long *jpegSize, int jpegQual,
1157                                 int flags)
1158 {
1159   const unsigned char *srcPlanes[3];
1160   int pw0, ph0, strides[3], retval = -1;
1161   tjinstance *this = (tjinstance *)handle;
1162 
1163   if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
1164   this->isInstanceError = FALSE;
1165 
1166   if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) ||
1167       height <= 0 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1168     THROW("tjCompressFromYUV(): Invalid argument");
1169 
1170   pw0 = tjPlaneWidth(0, width, subsamp);
1171   ph0 = tjPlaneHeight(0, height, subsamp);
1172   srcPlanes[0] = srcBuf;
1173   strides[0] = PAD(pw0, align);
1174   if (subsamp == TJSAMP_GRAY) {
1175     strides[1] = strides[2] = 0;
1176     srcPlanes[1] = srcPlanes[2] = NULL;
1177   } else {
1178     int pw1 = tjPlaneWidth(1, width, subsamp);
1179     int ph1 = tjPlaneHeight(1, height, subsamp);
1180 
1181     strides[1] = strides[2] = PAD(pw1, align);
1182     srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1183     srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1184   }
1185 
1186   return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height,
1187                                  subsamp, jpegBuf, jpegSize, jpegQual, flags);
1188 
1189 bailout:
1190   return retval;
1191 }
1192 
1193 
1194 /******************************* Decompressor ********************************/
1195 
1196 static tjhandle _tjInitDecompress(tjinstance *this)
1197 {
1198   static unsigned char buffer[1];
1199 
1200   /* This is also straight out of example.txt */
1201   this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1202   this->jerr.pub.error_exit = my_error_exit;
1203   this->jerr.pub.output_message = my_output_message;
1204   this->jerr.emit_message = this->jerr.pub.emit_message;
1205   this->jerr.pub.emit_message = my_emit_message;
1206   this->jerr.pub.addon_message_table = turbojpeg_message_table;
1207   this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1208   this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1209 
1210   if (setjmp(this->jerr.setjmp_buffer)) {
1211     /* If we get here, the JPEG code has signaled an error. */
1212     free(this);
1213     return NULL;
1214   }
1215 
1216   jpeg_create_decompress(&this->dinfo);
1217   /* Make an initial call so it will create the source manager */
1218   jpeg_mem_src_tj(&this->dinfo, buffer, 1);
1219 
1220   this->init |= DECOMPRESS;
1221   return (tjhandle)this;
1222 }
1223 
1224 /* TurboJPEG 1.0+ */
1225 DLLEXPORT tjhandle tjInitDecompress(void)
1226 {
1227   tjinstance *this;
1228 
1229   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1230     SNPRINTF(errStr, JMSG_LENGTH_MAX,
1231              "tjInitDecompress(): Memory allocation failure");
1232     return NULL;
1233   }
1234   memset(this, 0, sizeof(tjinstance));
1235   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
1236   return _tjInitDecompress(this);
1237 }
1238 
1239 
1240 /* TurboJPEG 1.4+ */
1241 DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1242                                   const unsigned char *jpegBuf,
1243                                   unsigned long jpegSize, int *width,
1244                                   int *height, int *jpegSubsamp,
1245                                   int *jpegColorspace)
1246 {
1247   int retval = 0;
1248 
1249   GET_DINSTANCE(handle);
1250   if ((this->init & DECOMPRESS) == 0)
1251     THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
1252 
1253   if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
1254       jpegSubsamp == NULL || jpegColorspace == NULL)
1255     THROW("tjDecompressHeader3(): Invalid argument");
1256 
1257   if (setjmp(this->jerr.setjmp_buffer)) {
1258     /* If we get here, the JPEG code has signaled an error. */
1259     return -1;
1260   }
1261 
1262   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1263 
1264   /* jpeg_read_header() calls jpeg_abort() and returns JPEG_HEADER_TABLES_ONLY
1265      if the datastream is a tables-only datastream.  Since we aren't using a
1266      suspending data source, the only other value it can return is
1267      JPEG_HEADER_OK. */
1268   if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY)
1269     return 0;
1270 
1271   *width = dinfo->image_width;
1272   *height = dinfo->image_height;
1273   *jpegSubsamp = getSubsamp(dinfo);
1274   switch (dinfo->jpeg_color_space) {
1275   case JCS_GRAYSCALE:  *jpegColorspace = TJCS_GRAY;  break;
1276   case JCS_RGB:        *jpegColorspace = TJCS_RGB;  break;
1277   case JCS_YCbCr:      *jpegColorspace = TJCS_YCbCr;  break;
1278   case JCS_CMYK:       *jpegColorspace = TJCS_CMYK;  break;
1279   case JCS_YCCK:       *jpegColorspace = TJCS_YCCK;  break;
1280   default:             *jpegColorspace = -1;  break;
1281   }
1282 
1283   jpeg_abort_decompress(dinfo);
1284 
1285   if (*jpegSubsamp < 0)
1286     THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
1287   if (*jpegColorspace < 0)
1288     THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
1289   if (*width < 1 || *height < 1)
1290     THROW("tjDecompressHeader3(): Invalid data returned in header");
1291 
1292 bailout:
1293   if (this->jerr.warning) retval = -1;
1294   return retval;
1295 }
1296 
1297 /* TurboJPEG 1.1+ */
1298 DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1299                                   unsigned long jpegSize, int *width,
1300                                   int *height, int *jpegSubsamp)
1301 {
1302   int jpegColorspace;
1303 
1304   return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1305                              jpegSubsamp, &jpegColorspace);
1306 }
1307 
1308 /* TurboJPEG 1.0+ */
1309 DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1310                                  unsigned long jpegSize, int *width,
1311                                  int *height)
1312 {
1313   int jpegSubsamp;
1314 
1315   return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1316                              &jpegSubsamp);
1317 }
1318 
1319 
1320 /* TurboJPEG 1.2+ */
1321 DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors)
1322 {
1323   if (numScalingFactors == NULL) {
1324     SNPRINTF(errStr, JMSG_LENGTH_MAX,
1325              "tjGetScalingFactors(): Invalid argument");
1326     return NULL;
1327   }
1328 
1329   *numScalingFactors = NUMSF;
1330   return (tjscalingfactor *)sf;
1331 }
1332 
1333 
1334 /* TurboJPEG 1.2+ */
1335 DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1336                             unsigned long jpegSize, unsigned char *dstBuf,
1337                             int width, int pitch, int height, int pixelFormat,
1338                             int flags)
1339 {
1340   JSAMPROW *row_pointer = NULL;
1341   int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
1342   struct my_progress_mgr progress;
1343 
1344   GET_DINSTANCE(handle);
1345   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1346   if ((this->init & DECOMPRESS) == 0)
1347     THROW("tjDecompress2(): Instance has not been initialized for decompression");
1348 
1349   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1350       pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1351     THROW("tjDecompress2(): Invalid argument");
1352 
1353 #ifndef NO_PUTENV
1354   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1355   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1356   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1357 #endif
1358 
1359   if (flags & TJFLAG_LIMITSCANS) {
1360     memset(&progress, 0, sizeof(struct my_progress_mgr));
1361     progress.pub.progress_monitor = my_progress_monitor;
1362     progress.this = this;
1363     dinfo->progress = &progress.pub;
1364   } else
1365     dinfo->progress = NULL;
1366 
1367   if (setjmp(this->jerr.setjmp_buffer)) {
1368     /* If we get here, the JPEG code has signaled an error. */
1369     retval = -1;  goto bailout;
1370   }
1371 
1372   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1373   jpeg_read_header(dinfo, TRUE);
1374   this->dinfo.out_color_space = pf2cs[pixelFormat];
1375   if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1376   if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1377 
1378   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
1379   if (width == 0) width = jpegwidth;
1380   if (height == 0) height = jpegheight;
1381   for (i = 0; i < NUMSF; i++) {
1382     scaledw = TJSCALED(jpegwidth, sf[i]);
1383     scaledh = TJSCALED(jpegheight, sf[i]);
1384     if (scaledw <= width && scaledh <= height)
1385       break;
1386   }
1387   if (i >= NUMSF)
1388     THROW("tjDecompress2(): Could not scale down to desired image dimensions");
1389   width = scaledw;  height = scaledh;
1390   dinfo->scale_num = sf[i].num;
1391   dinfo->scale_denom = sf[i].denom;
1392 
1393   jpeg_start_decompress(dinfo);
1394   if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
1395 
1396   if ((row_pointer =
1397        (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
1398     THROW("tjDecompress2(): Memory allocation failure");
1399   if (setjmp(this->jerr.setjmp_buffer)) {
1400     /* If we get here, the JPEG code has signaled an error. */
1401     retval = -1;  goto bailout;
1402   }
1403   for (i = 0; i < (int)dinfo->output_height; i++) {
1404     if (flags & TJFLAG_BOTTOMUP)
1405       row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
1406     else
1407       row_pointer[i] = &dstBuf[i * (size_t)pitch];
1408   }
1409   while (dinfo->output_scanline < dinfo->output_height)
1410     jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
1411                         dinfo->output_height - dinfo->output_scanline);
1412   jpeg_finish_decompress(dinfo);
1413 
1414 bailout:
1415   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1416   free(row_pointer);
1417   if (this->jerr.warning) retval = -1;
1418   this->jerr.stopOnWarning = FALSE;
1419   return retval;
1420 }
1421 
1422 /* TurboJPEG 1.0+ */
1423 DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1424                            unsigned long jpegSize, unsigned char *dstBuf,
1425                            int width, int pitch, int height, int pixelSize,
1426                            int flags)
1427 {
1428   if (flags & TJ_YUV)
1429     return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1430   else
1431     return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1432                          height, getPixelFormat(pixelSize, flags), flags);
1433 }
1434 
1435 
1436 static void setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
1437                               int pixelFormat, int subsamp, int flags)
1438 {
1439   int i;
1440 
1441   dinfo->scale_num = dinfo->scale_denom = 1;
1442 
1443   if (subsamp == TJSAMP_GRAY) {
1444     dinfo->num_components = dinfo->comps_in_scan = 1;
1445     dinfo->jpeg_color_space = JCS_GRAYSCALE;
1446   } else {
1447     dinfo->num_components = dinfo->comps_in_scan = 3;
1448     dinfo->jpeg_color_space = JCS_YCbCr;
1449   }
1450 
1451   dinfo->comp_info = (jpeg_component_info *)
1452     (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE,
1453                                 dinfo->num_components *
1454                                 sizeof(jpeg_component_info));
1455 
1456   for (i = 0; i < dinfo->num_components; i++) {
1457     jpeg_component_info *compptr = &dinfo->comp_info[i];
1458 
1459     compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1;
1460     compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1;
1461     compptr->component_index = i;
1462     compptr->component_id = i + 1;
1463     compptr->quant_tbl_no = compptr->dc_tbl_no =
1464       compptr->ac_tbl_no = (i == 0) ? 0 : 1;
1465     dinfo->cur_comp_info[i] = compptr;
1466   }
1467   dinfo->data_precision = 8;
1468   for (i = 0; i < 2; i++) {
1469     if (dinfo->quant_tbl_ptrs[i] == NULL)
1470       dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo);
1471   }
1472 }
1473 
1474 
1475 static int my_read_markers(j_decompress_ptr dinfo)
1476 {
1477   return JPEG_REACHED_SOS;
1478 }
1479 
1480 static void my_reset_marker_reader(j_decompress_ptr dinfo)
1481 {
1482 }
1483 
1484 /* TurboJPEG 1.4+ */
1485 DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
1486                                 const unsigned char **srcPlanes,
1487                                 const int *strides, int subsamp,
1488                                 unsigned char *dstBuf, int width, int pitch,
1489                                 int height, int pixelFormat, int flags)
1490 {
1491   JSAMPROW *row_pointer = NULL;
1492   JSAMPLE *_tmpbuf[MAX_COMPONENTS];
1493   JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
1494   int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1495   JSAMPLE *ptr;
1496   jpeg_component_info *compptr;
1497   int (*old_read_markers) (j_decompress_ptr);
1498   void (*old_reset_marker_reader) (j_decompress_ptr);
1499 
1500   GET_DINSTANCE(handle);
1501   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1502 
1503   for (i = 0; i < MAX_COMPONENTS; i++) {
1504     tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;  inbuf[i] = NULL;
1505   }
1506 
1507   if ((this->init & DECOMPRESS) == 0)
1508     THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
1509 
1510   if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= TJ_NUMSAMP ||
1511       dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1512       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1513     THROW("tjDecodeYUVPlanes(): Invalid argument");
1514   if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1515     THROW("tjDecodeYUVPlanes(): Invalid argument");
1516 
1517   if (setjmp(this->jerr.setjmp_buffer)) {
1518     /* If we get here, the JPEG code has signaled an error. */
1519     retval = -1;  goto bailout;
1520   }
1521 
1522   if (pixelFormat == TJPF_CMYK)
1523     THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into packed-pixel CMYK images.");
1524 
1525   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1526   dinfo->image_width = width;
1527   dinfo->image_height = height;
1528 
1529 #ifndef NO_PUTENV
1530   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1531   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1532   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1533 #endif
1534 
1535   dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
1536   dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
1537   dinfo->Se = DCTSIZE2 - 1;
1538   setDecodeDefaults(dinfo, pixelFormat, subsamp, flags);
1539   old_read_markers = dinfo->marker->read_markers;
1540   dinfo->marker->read_markers = my_read_markers;
1541   old_reset_marker_reader = dinfo->marker->reset_marker_reader;
1542   dinfo->marker->reset_marker_reader = my_reset_marker_reader;
1543   jpeg_read_header(dinfo, TRUE);
1544   dinfo->marker->read_markers = old_read_markers;
1545   dinfo->marker->reset_marker_reader = old_reset_marker_reader;
1546 
1547   this->dinfo.out_color_space = pf2cs[pixelFormat];
1548   if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
1549   dinfo->do_fancy_upsampling = FALSE;
1550   dinfo->Se = DCTSIZE2 - 1;
1551   jinit_master_decompress(dinfo);
1552   (*dinfo->upsample->start_pass) (dinfo);
1553 
1554   pw0 = PAD(width, dinfo->max_h_samp_factor);
1555   ph0 = PAD(height, dinfo->max_v_samp_factor);
1556 
1557   if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
1558 
1559   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1560     THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1561   for (i = 0; i < height; i++) {
1562     if (flags & TJFLAG_BOTTOMUP)
1563       row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
1564     else
1565       row_pointer[i] = &dstBuf[i * (size_t)pitch];
1566   }
1567   if (height < ph0)
1568     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
1569 
1570   for (i = 0; i < dinfo->num_components; i++) {
1571     compptr = &dinfo->comp_info[i];
1572     _tmpbuf[i] =
1573       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1574                         compptr->v_samp_factor + 32);
1575     if (!_tmpbuf[i])
1576       THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1577     tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1578     if (!tmpbuf[i])
1579       THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1580     for (row = 0; row < compptr->v_samp_factor; row++) {
1581       unsigned char *_tmpbuf_aligned =
1582         (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
1583 
1584       tmpbuf[i][row] =
1585         &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1586     }
1587     pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
1588     ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1589     inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1590     if (!inbuf[i])
1591       THROW("tjDecodeYUVPlanes(): Memory allocation failure");
1592     ptr = (JSAMPLE *)srcPlanes[i];
1593     for (row = 0; row < ph[i]; row++) {
1594       inbuf[i][row] = ptr;
1595       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1596     }
1597   }
1598 
1599   if (setjmp(this->jerr.setjmp_buffer)) {
1600     /* If we get here, the JPEG code has signaled an error. */
1601     retval = -1;  goto bailout;
1602   }
1603 
1604   for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
1605     JDIMENSION inrow = 0, outrow = 0;
1606 
1607     for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
1608          i++, compptr++)
1609       jcopy_sample_rows(inbuf[i],
1610         row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
1611         compptr->v_samp_factor, pw[i]);
1612     (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
1613                                  dinfo->max_v_samp_factor, &row_pointer[row],
1614                                  &outrow, dinfo->max_v_samp_factor);
1615   }
1616   jpeg_abort_decompress(dinfo);
1617 
1618 bailout:
1619   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1620   free(row_pointer);
1621   for (i = 0; i < MAX_COMPONENTS; i++) {
1622     free(tmpbuf[i]);
1623     free(_tmpbuf[i]);
1624     free(inbuf[i]);
1625   }
1626   if (this->jerr.warning) retval = -1;
1627   this->jerr.stopOnWarning = FALSE;
1628   return retval;
1629 }
1630 
1631 /* TurboJPEG 1.4+ */
1632 DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
1633                           int align, int subsamp, unsigned char *dstBuf,
1634                           int width, int pitch, int height, int pixelFormat,
1635                           int flags)
1636 {
1637   const unsigned char *srcPlanes[3];
1638   int pw0, ph0, strides[3], retval = -1;
1639   tjinstance *this = (tjinstance *)handle;
1640 
1641   if (!this) THROWG("tjDecodeYUV(): Invalid handle");
1642   this->isInstanceError = FALSE;
1643 
1644   if (srcBuf == NULL || align < 1 || !IS_POW2(align) || subsamp < 0 ||
1645       subsamp >= TJ_NUMSAMP || width <= 0 || height <= 0)
1646     THROW("tjDecodeYUV(): Invalid argument");
1647 
1648   pw0 = tjPlaneWidth(0, width, subsamp);
1649   ph0 = tjPlaneHeight(0, height, subsamp);
1650   srcPlanes[0] = srcBuf;
1651   strides[0] = PAD(pw0, align);
1652   if (subsamp == TJSAMP_GRAY) {
1653     strides[1] = strides[2] = 0;
1654     srcPlanes[1] = srcPlanes[2] = NULL;
1655   } else {
1656     int pw1 = tjPlaneWidth(1, width, subsamp);
1657     int ph1 = tjPlaneHeight(1, height, subsamp);
1658 
1659     strides[1] = strides[2] = PAD(pw1, align);
1660     srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1661     srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1662   }
1663 
1664   return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width,
1665                            pitch, height, pixelFormat, flags);
1666 
1667 bailout:
1668   return retval;
1669 }
1670 
1671 /* TurboJPEG 1.4+ */
1672 DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
1673                                       const unsigned char *jpegBuf,
1674                                       unsigned long jpegSize,
1675                                       unsigned char **dstPlanes, int width,
1676                                       int *strides, int height, int flags)
1677 {
1678   int i, sfi, row, retval = 0;
1679   int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh;
1680   int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1681     tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1682   JSAMPLE *_tmpbuf = NULL, *ptr;
1683   JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1684   int dctsize;
1685   struct my_progress_mgr progress;
1686 
1687   GET_DINSTANCE(handle);
1688   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1689 
1690   for (i = 0; i < MAX_COMPONENTS; i++) {
1691     tmpbuf[i] = NULL;  outbuf[i] = NULL;
1692   }
1693 
1694   if ((this->init & DECOMPRESS) == 0)
1695     THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
1696 
1697   if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
1698       width < 0 || height < 0)
1699     THROW("tjDecompressToYUVPlanes(): Invalid argument");
1700 
1701 #ifndef NO_PUTENV
1702   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1703   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1704   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1705 #endif
1706 
1707   if (flags & TJFLAG_LIMITSCANS) {
1708     memset(&progress, 0, sizeof(struct my_progress_mgr));
1709     progress.pub.progress_monitor = my_progress_monitor;
1710     progress.this = this;
1711     dinfo->progress = &progress.pub;
1712   } else
1713     dinfo->progress = NULL;
1714 
1715   if (setjmp(this->jerr.setjmp_buffer)) {
1716     /* If we get here, the JPEG code has signaled an error. */
1717     retval = -1;  goto bailout;
1718   }
1719 
1720   if (!this->headerRead) {
1721     jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1722     jpeg_read_header(dinfo, TRUE);
1723   }
1724   this->headerRead = 0;
1725   jpegSubsamp = getSubsamp(dinfo);
1726   if (jpegSubsamp < 0)
1727     THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
1728 
1729   if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1730     THROW("tjDecompressToYUVPlanes(): Invalid argument");
1731 
1732   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
1733   if (width == 0) width = jpegwidth;
1734   if (height == 0) height = jpegheight;
1735   for (i = 0; i < NUMSF; i++) {
1736     scaledw = TJSCALED(jpegwidth, sf[i]);
1737     scaledh = TJSCALED(jpegheight, sf[i]);
1738     if (scaledw <= width && scaledh <= height)
1739       break;
1740   }
1741   if (i >= NUMSF)
1742     THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
1743   if (dinfo->num_components > 3)
1744     THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
1745 
1746   width = scaledw;  height = scaledh;
1747   dinfo->scale_num = sf[i].num;
1748   dinfo->scale_denom = sf[i].denom;
1749   sfi = i;
1750   jpeg_calc_output_dimensions(dinfo);
1751 
1752   dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom;
1753 
1754   for (i = 0; i < dinfo->num_components; i++) {
1755     jpeg_component_info *compptr = &dinfo->comp_info[i];
1756     int ih;
1757 
1758     iw[i] = compptr->width_in_blocks * dctsize;
1759     ih = compptr->height_in_blocks * dctsize;
1760     pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp);
1761     ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp);
1762     if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1763     th[i] = compptr->v_samp_factor * dctsize;
1764     tmpbufsize += iw[i] * th[i];
1765     if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1766       THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1767     ptr = dstPlanes[i];
1768     for (row = 0; row < ph[i]; row++) {
1769       outbuf[i][row] = ptr;
1770       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1771     }
1772   }
1773   if (usetmpbuf) {
1774     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1775       THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1776     ptr = _tmpbuf;
1777     for (i = 0; i < dinfo->num_components; i++) {
1778       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1779         THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
1780       for (row = 0; row < th[i]; row++) {
1781         tmpbuf[i][row] = ptr;
1782         ptr += iw[i];
1783       }
1784     }
1785   }
1786 
1787   if (setjmp(this->jerr.setjmp_buffer)) {
1788     /* If we get here, the JPEG code has signaled an error. */
1789     retval = -1;  goto bailout;
1790   }
1791 
1792   if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE;
1793   if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST;
1794   dinfo->raw_data_out = TRUE;
1795 
1796   jpeg_start_decompress(dinfo);
1797   for (row = 0; row < (int)dinfo->output_height;
1798        row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
1799     JSAMPARRAY yuvptr[MAX_COMPONENTS];
1800     int crow[MAX_COMPONENTS];
1801 
1802     for (i = 0; i < dinfo->num_components; i++) {
1803       jpeg_component_info *compptr = &dinfo->comp_info[i];
1804 
1805       if (jpegSubsamp == TJSAMP_420) {
1806         /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
1807            to be clever and use the IDCT to perform upsampling on the U and V
1808            planes.  For instance, if the output image is to be scaled by 1/2
1809            relative to the JPEG image, then the scaling factor and upsampling
1810            effectively cancel each other, so a normal 8x8 IDCT can be used.
1811            However, this is not desirable when using the decompress-to-YUV
1812            functionality in TurboJPEG, since we want to output the U and V
1813            planes in their subsampled form.  Thus, we have to override some
1814            internal libjpeg parameters to force it to use the "scaled" IDCT
1815            functions on the U and V planes. */
1816         compptr->_DCT_scaled_size = dctsize;
1817         compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] *
1818           sf[sfi].num / sf[sfi].denom *
1819           compptr->v_samp_factor / dinfo->max_v_samp_factor;
1820         dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
1821       }
1822       crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
1823       if (usetmpbuf) yuvptr[i] = tmpbuf[i];
1824       else yuvptr[i] = &outbuf[i][crow[i]];
1825     }
1826     jpeg_read_raw_data(dinfo, yuvptr,
1827                        dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
1828     if (usetmpbuf) {
1829       int j;
1830 
1831       for (i = 0; i < dinfo->num_components; i++) {
1832         for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1833           memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
1834         }
1835       }
1836     }
1837   }
1838   jpeg_finish_decompress(dinfo);
1839 
1840 bailout:
1841   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1842   for (i = 0; i < MAX_COMPONENTS; i++) {
1843     free(tmpbuf[i]);
1844     free(outbuf[i]);
1845   }
1846   free(_tmpbuf);
1847   if (this->jerr.warning) retval = -1;
1848   this->jerr.stopOnWarning = FALSE;
1849   return retval;
1850 }
1851 
1852 /* TurboJPEG 1.4+ */
1853 DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
1854                                  unsigned long jpegSize, unsigned char *dstBuf,
1855                                  int width, int align, int height, int flags)
1856 {
1857   unsigned char *dstPlanes[3];
1858   int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
1859   int i, jpegwidth, jpegheight, scaledw, scaledh;
1860 
1861   GET_DINSTANCE(handle);
1862   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1863 
1864   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
1865       align < 1 || !IS_POW2(align) || height < 0)
1866     THROW("tjDecompressToYUV2(): Invalid argument");
1867 
1868   if (setjmp(this->jerr.setjmp_buffer)) {
1869     /* If we get here, the JPEG code has signaled an error. */
1870     return -1;
1871   }
1872 
1873   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1874   jpeg_read_header(dinfo, TRUE);
1875   jpegSubsamp = getSubsamp(dinfo);
1876   if (jpegSubsamp < 0)
1877     THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
1878 
1879   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
1880   if (width == 0) width = jpegwidth;
1881   if (height == 0) height = jpegheight;
1882   for (i = 0; i < NUMSF; i++) {
1883     scaledw = TJSCALED(jpegwidth, sf[i]);
1884     scaledh = TJSCALED(jpegheight, sf[i]);
1885     if (scaledw <= width && scaledh <= height)
1886       break;
1887   }
1888   if (i >= NUMSF)
1889     THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
1890 
1891   width = scaledw;  height = scaledh;
1892 
1893   pw0 = tjPlaneWidth(0, width, jpegSubsamp);
1894   ph0 = tjPlaneHeight(0, height, jpegSubsamp);
1895   dstPlanes[0] = dstBuf;
1896   strides[0] = PAD(pw0, align);
1897   if (jpegSubsamp == TJSAMP_GRAY) {
1898     strides[1] = strides[2] = 0;
1899     dstPlanes[1] = dstPlanes[2] = NULL;
1900   } else {
1901     int pw1 = tjPlaneWidth(1, width, jpegSubsamp);
1902     int ph1 = tjPlaneHeight(1, height, jpegSubsamp);
1903 
1904     strides[1] = strides[2] = PAD(pw1, align);
1905     dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1906     dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1907   }
1908 
1909   this->headerRead = 1;
1910   return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width,
1911                                  strides, height, flags);
1912 
1913 bailout:
1914   this->jerr.stopOnWarning = FALSE;
1915   return retval;
1916 }
1917 
1918 /* TurboJPEG 1.1+ */
1919 DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
1920                                 unsigned long jpegSize, unsigned char *dstBuf,
1921                                 int flags)
1922 {
1923   return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
1924 }
1925 
1926 
1927 /******************************** Transformer ********************************/
1928 
1929 /* TurboJPEG 1.2+ */
1930 DLLEXPORT tjhandle tjInitTransform(void)
1931 {
1932   tjinstance *this = NULL;
1933   tjhandle handle = NULL;
1934 
1935   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
1936     SNPRINTF(errStr, JMSG_LENGTH_MAX,
1937              "tjInitTransform(): Memory allocation failure");
1938     return NULL;
1939   }
1940   memset(this, 0, sizeof(tjinstance));
1941   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
1942   handle = _tjInitCompress(this);
1943   if (!handle) return NULL;
1944   handle = _tjInitDecompress(this);
1945   return handle;
1946 }
1947 
1948 
1949 /* TurboJPEG 1.2+ */
1950 DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
1951                           unsigned long jpegSize, int n,
1952                           unsigned char **dstBufs, unsigned long *dstSizes,
1953                           tjtransform *t, int flags)
1954 {
1955   jpeg_transform_info *xinfo = NULL;
1956   jvirt_barray_ptr *srccoefs, *dstcoefs;
1957   int retval = 0, i, jpegSubsamp, saveMarkers = 0;
1958   boolean alloc = TRUE;
1959   struct my_progress_mgr progress;
1960 
1961   GET_INSTANCE(handle);
1962   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
1963   if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
1964     THROW("tjTransform(): Instance has not been initialized for transformation");
1965 
1966   if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
1967       dstSizes == NULL || t == NULL || flags < 0)
1968     THROW("tjTransform(): Invalid argument");
1969 
1970 #ifndef NO_PUTENV
1971   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
1972   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
1973   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
1974 #endif
1975 
1976   if (flags & TJFLAG_LIMITSCANS) {
1977     memset(&progress, 0, sizeof(struct my_progress_mgr));
1978     progress.pub.progress_monitor = my_progress_monitor;
1979     progress.this = this;
1980     dinfo->progress = &progress.pub;
1981   } else
1982     dinfo->progress = NULL;
1983 
1984   if ((xinfo =
1985        (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
1986     THROW("tjTransform(): Memory allocation failure");
1987   memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
1988 
1989   if (setjmp(this->jerr.setjmp_buffer)) {
1990     /* If we get here, the JPEG code has signaled an error. */
1991     retval = -1;  goto bailout;
1992   }
1993 
1994   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1995 
1996   for (i = 0; i < n; i++) {
1997     xinfo[i].transform = xformtypes[t[i].op];
1998     xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
1999     xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
2000     xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
2001     xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
2002     if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
2003     else xinfo[i].slow_hflip = 0;
2004 
2005     if (xinfo[i].crop) {
2006       xinfo[i].crop_xoffset = t[i].r.x;  xinfo[i].crop_xoffset_set = JCROP_POS;
2007       xinfo[i].crop_yoffset = t[i].r.y;  xinfo[i].crop_yoffset_set = JCROP_POS;
2008       if (t[i].r.w != 0) {
2009         xinfo[i].crop_width = t[i].r.w;  xinfo[i].crop_width_set = JCROP_POS;
2010       } else
2011         xinfo[i].crop_width = JCROP_UNSET;
2012       if (t[i].r.h != 0) {
2013         xinfo[i].crop_height = t[i].r.h;  xinfo[i].crop_height_set = JCROP_POS;
2014       } else
2015         xinfo[i].crop_height = JCROP_UNSET;
2016     }
2017     if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
2018   }
2019 
2020   jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
2021   jpeg_read_header(dinfo, TRUE);
2022   jpegSubsamp = getSubsamp(dinfo);
2023   if (jpegSubsamp < 0)
2024     THROW("tjTransform(): Could not determine subsampling type for JPEG image");
2025 
2026   for (i = 0; i < n; i++) {
2027     if (!jtransform_request_workspace(dinfo, &xinfo[i]))
2028       THROW("tjTransform(): Transform is not perfect");
2029 
2030     if (xinfo[i].crop) {
2031       if ((t[i].r.x % tjMCUWidth[jpegSubsamp]) != 0 ||
2032           (t[i].r.y % tjMCUHeight[jpegSubsamp]) != 0) {
2033         SNPRINTF(this->errStr, JMSG_LENGTH_MAX,
2034                  "To crop this JPEG image, x must be a multiple of %d\n"
2035                  "and y must be a multiple of %d.\n",
2036                  tjMCUWidth[jpegSubsamp], tjMCUHeight[jpegSubsamp]);
2037         this->isInstanceError = TRUE;
2038         retval = -1;  goto bailout;
2039       }
2040     }
2041   }
2042 
2043   srccoefs = jpeg_read_coefficients(dinfo);
2044 
2045   for (i = 0; i < n; i++) {
2046     int w, h;
2047 
2048     if (!xinfo[i].crop) {
2049       w = dinfo->image_width;  h = dinfo->image_height;
2050     } else {
2051       w = xinfo[i].crop_width;  h = xinfo[i].crop_height;
2052     }
2053     if (flags & TJFLAG_NOREALLOC) {
2054       alloc = FALSE;  dstSizes[i] = tjBufSize(w, h, jpegSubsamp);
2055     }
2056     if (!(t[i].options & TJXOPT_NOOUTPUT))
2057       jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
2058     jpeg_copy_critical_parameters(dinfo, cinfo);
2059     dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
2060 #ifdef C_PROGRESSIVE_SUPPORTED
2061     if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE)
2062       jpeg_simple_progression(cinfo);
2063 #endif
2064     if (!(t[i].options & TJXOPT_NOOUTPUT)) {
2065       jpeg_write_coefficients(cinfo, dstcoefs);
2066       jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
2067                                           JCOPYOPT_NONE : JCOPYOPT_ALL);
2068     } else
2069       jinit_c_master_control(cinfo, TRUE);
2070     jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
2071     if (t[i].customFilter) {
2072       int ci, y;
2073       JDIMENSION by;
2074 
2075       for (ci = 0; ci < cinfo->num_components; ci++) {
2076         jpeg_component_info *compptr = &cinfo->comp_info[ci];
2077         tjregion arrayRegion = { 0, 0, 0, 0 };
2078         tjregion planeRegion = { 0, 0, 0, 0 };
2079 
2080         arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
2081         arrayRegion.h = DCTSIZE;
2082         planeRegion.w = compptr->width_in_blocks * DCTSIZE;
2083         planeRegion.h = compptr->height_in_blocks * DCTSIZE;
2084 
2085         for (by = 0; by < compptr->height_in_blocks;
2086              by += compptr->v_samp_factor) {
2087           JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
2088             ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
2089              TRUE);
2090 
2091           for (y = 0; y < compptr->v_samp_factor; y++) {
2092             if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
2093                                   i, &t[i]) == -1)
2094               THROW("tjTransform(): Error in custom filter");
2095             arrayRegion.y += DCTSIZE;
2096           }
2097         }
2098       }
2099     }
2100     if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
2101   }
2102 
2103   jpeg_finish_decompress(dinfo);
2104 
2105 bailout:
2106   if (cinfo->global_state > CSTATE_START) {
2107     if (alloc) (*cinfo->dest->term_destination) (cinfo);
2108     jpeg_abort_compress(cinfo);
2109   }
2110   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2111   free(xinfo);
2112   if (this->jerr.warning) retval = -1;
2113   this->jerr.stopOnWarning = FALSE;
2114   return retval;
2115 }
2116 
2117 
2118 /*************************** Packed-Pixel Image I/O **************************/
2119 
2120 /* TurboJPEG 2.0+ */
2121 DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
2122                                      int align, int *height, int *pixelFormat,
2123                                      int flags)
2124 {
2125   int retval = 0, tempc;
2126   size_t pitch;
2127   tjhandle handle = NULL;
2128   tjinstance *this;
2129   j_compress_ptr cinfo = NULL;
2130   cjpeg_source_ptr src;
2131   unsigned char *dstBuf = NULL;
2132   FILE *file = NULL;
2133   boolean invert;
2134 
2135   if (!filename || !width || align < 1 || !height || !pixelFormat ||
2136       *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
2137     THROWG("tjLoadImage(): Invalid argument");
2138   if ((align & (align - 1)) != 0)
2139     THROWG("tjLoadImage(): Alignment must be a power of 2");
2140 
2141   if ((handle = tjInitCompress()) == NULL) return NULL;
2142   this = (tjinstance *)handle;
2143   cinfo = &this->cinfo;
2144 
2145 #ifdef _MSC_VER
2146   if (fopen_s(&file, filename, "rb") || file == NULL)
2147 #else
2148   if ((file = fopen(filename, "rb")) == NULL)
2149 #endif
2150     THROW_UNIX("tjLoadImage(): Cannot open input file");
2151 
2152   if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
2153     THROW_UNIX("tjLoadImage(): Could not read input file")
2154   else if (tempc == EOF)
2155     THROWG("tjLoadImage(): Input file contains no data");
2156 
2157   if (setjmp(this->jerr.setjmp_buffer)) {
2158     /* If we get here, the JPEG code has signaled an error. */
2159     retval = -1;  goto bailout;
2160   }
2161 
2162   if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
2163   else cinfo->in_color_space = pf2cs[*pixelFormat];
2164   if (tempc == 'B') {
2165     if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
2166       THROWG("tjLoadImage(): Could not initialize bitmap loader");
2167     invert = (flags & TJFLAG_BOTTOMUP) == 0;
2168   } else if (tempc == 'P') {
2169     if ((src = jinit_read_ppm(cinfo)) == NULL)
2170       THROWG("tjLoadImage(): Could not initialize PPM loader");
2171     invert = (flags & TJFLAG_BOTTOMUP) != 0;
2172   } else
2173     THROWG("tjLoadImage(): Unsupported file type");
2174 
2175   src->input_file = file;
2176 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2177   /* Refuse to load images larger than 1 Megapixel when fuzzing. */
2178   if (flags & TJFLAG_FUZZING)
2179     src->max_pixels = 1048576;
2180 #endif
2181   (*src->start_input) (cinfo, src);
2182   (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
2183 
2184   *width = cinfo->image_width;  *height = cinfo->image_height;
2185   *pixelFormat = cs2pf[cinfo->in_color_space];
2186 
2187   pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
2188   if ((unsigned long long)pitch * (unsigned long long)(*height) >
2189       (unsigned long long)((size_t)-1) ||
2190       (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
2191     THROWG("tjLoadImage(): Memory allocation failure");
2192 
2193   if (setjmp(this->jerr.setjmp_buffer)) {
2194     /* If we get here, the JPEG code has signaled an error. */
2195     retval = -1;  goto bailout;
2196   }
2197 
2198   while (cinfo->next_scanline < cinfo->image_height) {
2199     int i, nlines = (*src->get_pixel_rows) (cinfo, src);
2200 
2201     for (i = 0; i < nlines; i++) {
2202       unsigned char *dstptr;
2203       int row;
2204 
2205       row = cinfo->next_scanline + i;
2206       if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
2207       else dstptr = &dstBuf[row * pitch];
2208       memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]);
2209     }
2210     cinfo->next_scanline += nlines;
2211   }
2212 
2213   (*src->finish_input) (cinfo, src);
2214 
2215 bailout:
2216   if (handle) tjDestroy(handle);
2217   if (file) fclose(file);
2218   if (retval < 0) { free(dstBuf);  dstBuf = NULL; }
2219   return dstBuf;
2220 }
2221 
2222 
2223 /* TurboJPEG 2.0+ */
2224 DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2225                           int width, int pitch, int height, int pixelFormat,
2226                           int flags)
2227 {
2228   int retval = 0;
2229   tjhandle handle = NULL;
2230   tjinstance *this;
2231   j_decompress_ptr dinfo = NULL;
2232   djpeg_dest_ptr dst;
2233   FILE *file = NULL;
2234   char *ptr = NULL;
2235   boolean invert;
2236 
2237   if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
2238       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2239     THROWG("tjSaveImage(): Invalid argument");
2240 
2241   if ((handle = tjInitDecompress()) == NULL)
2242     return -1;
2243   this = (tjinstance *)handle;
2244   dinfo = &this->dinfo;
2245 
2246 #ifdef _MSC_VER
2247   if (fopen_s(&file, filename, "wb") || file == NULL)
2248 #else
2249   if ((file = fopen(filename, "wb")) == NULL)
2250 #endif
2251     THROW_UNIX("tjSaveImage(): Cannot open output file");
2252 
2253   if (setjmp(this->jerr.setjmp_buffer)) {
2254     /* If we get here, the JPEG code has signaled an error. */
2255     retval = -1;  goto bailout;
2256   }
2257 
2258   this->dinfo.out_color_space = pf2cs[pixelFormat];
2259   dinfo->image_width = width;  dinfo->image_height = height;
2260   dinfo->global_state = DSTATE_READY;
2261   dinfo->scale_num = dinfo->scale_denom = 1;
2262 
2263   ptr = strrchr(filename, '.');
2264   if (ptr && !strcasecmp(ptr, ".bmp")) {
2265     if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
2266       THROWG("tjSaveImage(): Could not initialize bitmap writer");
2267     invert = (flags & TJFLAG_BOTTOMUP) == 0;
2268   } else {
2269     if ((dst = jinit_write_ppm(dinfo)) == NULL)
2270       THROWG("tjSaveImage(): Could not initialize PPM writer");
2271     invert = (flags & TJFLAG_BOTTOMUP) != 0;
2272   }
2273 
2274   dst->output_file = file;
2275   (*dst->start_output) (dinfo, dst);
2276   (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
2277 
2278   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2279 
2280   while (dinfo->output_scanline < dinfo->output_height) {
2281     unsigned char *rowptr;
2282 
2283     if (invert)
2284       rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch];
2285     else
2286       rowptr = &buffer[dinfo->output_scanline * pitch];
2287     memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]);
2288     (*dst->put_pixel_rows) (dinfo, dst, 1);
2289     dinfo->output_scanline++;
2290   }
2291 
2292   (*dst->finish_output) (dinfo, dst);
2293 
2294 bailout:
2295   if (handle) tjDestroy(handle);
2296   if (file) fclose(file);
2297   return retval;
2298 }
2299