Imported Upstream version 3.0.1
[platform/upstream/libjpeg-turbo.git] / turbojpeg.c
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 "./jpegapicomp.h"
45 #include "./cdjpeg.h"
46
47 extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, size_t *,
48                              boolean);
49 extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *, size_t);
50
51 #define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
52 #define IS_POW2(x)  (((x) & (x - 1)) == 0)
53
54
55 /* Error handling (based on example in example.c) */
56
57 static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
58
59 struct my_error_mgr {
60   struct jpeg_error_mgr pub;
61   jmp_buf setjmp_buffer;
62   void (*emit_message) (j_common_ptr, int);
63   boolean warning, stopOnWarning;
64 };
65 typedef struct my_error_mgr *my_error_ptr;
66
67 #define JMESSAGE(code, string)  string,
68 static const char *turbojpeg_message_table[] = {
69 #include "cderror.h"
70   NULL
71 };
72
73 static void my_error_exit(j_common_ptr cinfo)
74 {
75   my_error_ptr myerr = (my_error_ptr)cinfo->err;
76
77   (*cinfo->err->output_message) (cinfo);
78   longjmp(myerr->setjmp_buffer, 1);
79 }
80
81 /* Based on output_message() in jerror.c */
82
83 static void my_output_message(j_common_ptr cinfo)
84 {
85   (*cinfo->err->format_message) (cinfo, errStr);
86 }
87
88 static void my_emit_message(j_common_ptr cinfo, int msg_level)
89 {
90   my_error_ptr myerr = (my_error_ptr)cinfo->err;
91
92   myerr->emit_message(cinfo, msg_level);
93   if (msg_level < 0) {
94     myerr->warning = TRUE;
95     if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
96   }
97 }
98
99
100 /********************** Global structures, macros, etc. **********************/
101
102 enum { COMPRESS = 1, DECOMPRESS = 2 };
103
104 typedef struct _tjinstance {
105   struct jpeg_compress_struct cinfo;
106   struct jpeg_decompress_struct dinfo;
107   struct my_error_mgr jerr;
108   int init;
109   char errStr[JMSG_LENGTH_MAX];
110   boolean isInstanceError;
111   /* Parameters */
112 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
113   int maxPixels;
114 #endif
115   boolean bottomUp;
116   boolean noRealloc;
117   int quality;
118   int subsamp;
119   int jpegWidth;
120   int jpegHeight;
121   int precision;
122   int colorspace;
123   boolean fastUpsample;
124   boolean fastDCT;
125   boolean optimize;
126   boolean progressive;
127   int scanLimit;
128   boolean arithmetic;
129   boolean lossless;
130   int losslessPSV;
131   int losslessPt;
132   int restartIntervalBlocks;
133   int restartIntervalRows;
134   int xDensity;
135   int yDensity;
136   int densityUnits;
137   tjscalingfactor scalingFactor;
138   tjregion croppingRegion;
139 } tjinstance;
140
141 static tjhandle _tjInitCompress(tjinstance *this);
142 static tjhandle _tjInitDecompress(tjinstance *this);
143
144 struct my_progress_mgr {
145   struct jpeg_progress_mgr pub;
146   tjinstance *this;
147 };
148 typedef struct my_progress_mgr *my_progress_ptr;
149
150 static void my_progress_monitor(j_common_ptr dinfo)
151 {
152   my_error_ptr myerr = (my_error_ptr)dinfo->err;
153   my_progress_ptr myprog = (my_progress_ptr)dinfo->progress;
154
155   if (dinfo->is_decompressor) {
156     int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number;
157
158     if (scan_no > myprog->this->scanLimit) {
159       SNPRINTF(myprog->this->errStr, JMSG_LENGTH_MAX,
160                "Progressive JPEG image has more than %d scans",
161                myprog->this->scanLimit);
162       SNPRINTF(errStr, JMSG_LENGTH_MAX,
163                "Progressive JPEG image has more than %d scans",
164                myprog->this->scanLimit);
165       myprog->this->isInstanceError = TRUE;
166       myerr->warning = FALSE;
167       longjmp(myerr->setjmp_buffer, 1);
168     }
169   }
170 }
171
172 static const JXFORM_CODE xformtypes[TJ_NUMXOP] = {
173   JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
174   JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
175 };
176
177 #define NUMSF  16
178 static const tjscalingfactor sf[NUMSF] = {
179   { 2, 1 },
180   { 15, 8 },
181   { 7, 4 },
182   { 13, 8 },
183   { 3, 2 },
184   { 11, 8 },
185   { 5, 4 },
186   { 9, 8 },
187   { 1, 1 },
188   { 7, 8 },
189   { 3, 4 },
190   { 5, 8 },
191   { 1, 2 },
192   { 3, 8 },
193   { 1, 4 },
194   { 1, 8 }
195 };
196
197 static J_COLOR_SPACE pf2cs[TJ_NUMPF] = {
198   JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
199   JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
200   JCS_EXT_ARGB, JCS_CMYK
201 };
202
203 static int cs2pf[JPEG_NUMCS] = {
204   TJPF_UNKNOWN, TJPF_GRAY,
205 #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
206   TJPF_RGB,
207 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
208   TJPF_BGR,
209 #elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
210   TJPF_RGBX,
211 #elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
212   TJPF_BGRX,
213 #elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
214   TJPF_XBGR,
215 #elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
216   TJPF_XRGB,
217 #endif
218   TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
219   TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
220   TJPF_UNKNOWN
221 };
222
223 #define THROWG(m, rv) { \
224   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
225   retval = rv;  goto bailout; \
226 }
227 #ifdef _MSC_VER
228 #define THROW_UNIX(m) { \
229   char strerrorBuf[80] = { 0 }; \
230   strerror_s(strerrorBuf, 80, errno); \
231   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
232            strerrorBuf); \
233   this->isInstanceError = TRUE; \
234   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
235            strerrorBuf); \
236   retval = -1;  goto bailout; \
237 }
238 #else
239 #define THROW_UNIX(m) { \
240   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
241            strerror(errno)); \
242   this->isInstanceError = TRUE; \
243   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \
244            strerror(errno)); \
245   retval = -1;  goto bailout; \
246 }
247 #endif
248 #define THROW(m) { \
249   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
250   this->isInstanceError = TRUE;  THROWG(m, -1) \
251 }
252 #define THROWI(format, val1, val2) { \
253   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, \
254            val1, val2); \
255   this->isInstanceError = TRUE; \
256   SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val1, \
257            val2); \
258   retval = -1;  goto bailout; \
259 }
260
261 #define GET_INSTANCE(handle) \
262   tjinstance *this = (tjinstance *)handle; \
263   j_compress_ptr cinfo = NULL; \
264   j_decompress_ptr dinfo = NULL; \
265   \
266   if (!this) { \
267     SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
268     return -1; \
269   } \
270   cinfo = &this->cinfo;  dinfo = &this->dinfo; \
271   this->jerr.warning = FALSE; \
272   this->isInstanceError = FALSE;
273
274 #define GET_CINSTANCE(handle) \
275   tjinstance *this = (tjinstance *)handle; \
276   j_compress_ptr cinfo = NULL; \
277   \
278   if (!this) { \
279     SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
280     return -1; \
281   } \
282   cinfo = &this->cinfo; \
283   this->jerr.warning = FALSE; \
284   this->isInstanceError = FALSE;
285
286 #define GET_DINSTANCE(handle) \
287   tjinstance *this = (tjinstance *)handle; \
288   j_decompress_ptr dinfo = NULL; \
289   \
290   if (!this) { \
291     SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
292     return -1; \
293   } \
294   dinfo = &this->dinfo; \
295   this->jerr.warning = FALSE; \
296   this->isInstanceError = FALSE;
297
298 #define GET_TJINSTANCE(handle, errorReturn) \
299   tjinstance *this = (tjinstance *)handle; \
300   \
301   if (!this) { \
302     SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
303     return errorReturn; \
304   } \
305   this->jerr.warning = FALSE; \
306   this->isInstanceError = FALSE;
307
308 static int getPixelFormat(int pixelSize, int flags)
309 {
310   if (pixelSize == 1) return TJPF_GRAY;
311   if (pixelSize == 3) {
312     if (flags & TJ_BGR) return TJPF_BGR;
313     else return TJPF_RGB;
314   }
315   if (pixelSize == 4) {
316     if (flags & TJ_ALPHAFIRST) {
317       if (flags & TJ_BGR) return TJPF_XBGR;
318       else return TJPF_XRGB;
319     } else {
320       if (flags & TJ_BGR) return TJPF_BGRX;
321       else return TJPF_RGBX;
322     }
323   }
324   return -1;
325 }
326
327 static void setCompDefaults(tjinstance *this, int pixelFormat)
328 {
329   this->cinfo.in_color_space = pf2cs[pixelFormat];
330   this->cinfo.input_components = tjPixelSize[pixelFormat];
331   jpeg_set_defaults(&this->cinfo);
332
333   this->cinfo.restart_interval = this->restartIntervalBlocks;
334   this->cinfo.restart_in_rows = this->restartIntervalRows;
335   this->cinfo.X_density = (UINT16)this->xDensity;
336   this->cinfo.Y_density = (UINT16)this->yDensity;
337   this->cinfo.density_unit = (UINT8)this->densityUnits;
338
339   if (this->lossless) {
340 #ifdef C_LOSSLESS_SUPPORTED
341     jpeg_enable_lossless(&this->cinfo, this->losslessPSV, this->losslessPt);
342 #endif
343     if (pixelFormat == TJPF_GRAY)
344       this->subsamp = TJSAMP_GRAY;
345     else if (this->subsamp != TJSAMP_GRAY)
346       this->subsamp = TJSAMP_444;
347     return;
348   }
349
350   jpeg_set_quality(&this->cinfo, this->quality, TRUE);
351   this->cinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
352
353   switch (this->colorspace) {
354   case TJCS_RGB:
355     jpeg_set_colorspace(&this->cinfo, JCS_RGB);  break;
356   case TJCS_YCbCr:
357     jpeg_set_colorspace(&this->cinfo, JCS_YCbCr);  break;
358   case TJCS_GRAY:
359     jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE);  break;
360   case TJCS_CMYK:
361     jpeg_set_colorspace(&this->cinfo, JCS_CMYK);  break;
362   case TJCS_YCCK:
363     jpeg_set_colorspace(&this->cinfo, JCS_YCCK);  break;
364   default:
365     if (this->subsamp == TJSAMP_GRAY)
366       jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE);
367     else if (pixelFormat == TJPF_CMYK)
368       jpeg_set_colorspace(&this->cinfo, JCS_YCCK);
369     else
370       jpeg_set_colorspace(&this->cinfo, JCS_YCbCr);
371   }
372
373   this->cinfo.optimize_coding = this->optimize;
374 #ifdef C_PROGRESSIVE_SUPPORTED
375   if (this->progressive) jpeg_simple_progression(&this->cinfo);
376 #endif
377   this->cinfo.arith_code = this->arithmetic;
378
379   this->cinfo.comp_info[0].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
380   this->cinfo.comp_info[1].h_samp_factor = 1;
381   this->cinfo.comp_info[2].h_samp_factor = 1;
382   if (this->cinfo.num_components > 3)
383     this->cinfo.comp_info[3].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
384   this->cinfo.comp_info[0].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
385   this->cinfo.comp_info[1].v_samp_factor = 1;
386   this->cinfo.comp_info[2].v_samp_factor = 1;
387   if (this->cinfo.num_components > 3)
388     this->cinfo.comp_info[3].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
389 }
390
391
392 static int getSubsamp(j_decompress_ptr dinfo)
393 {
394   int retval = TJSAMP_UNKNOWN, i, k;
395
396   /* The sampling factors actually have no meaning with grayscale JPEG files,
397      and in fact it's possible to generate grayscale JPEGs with sampling
398      factors > 1 (even though those sampling factors are ignored by the
399      decompressor.)  Thus, we need to treat grayscale as a special case. */
400   if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE)
401     return TJSAMP_GRAY;
402
403   for (i = 0; i < TJ_NUMSAMP; i++) {
404     if (i == TJSAMP_GRAY) continue;
405
406     if (dinfo->num_components == 3 ||
407         ((dinfo->jpeg_color_space == JCS_YCCK ||
408           dinfo->jpeg_color_space == JCS_CMYK) &&
409          dinfo->num_components == 4)) {
410       if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 &&
411           dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) {
412         int match = 0;
413
414         for (k = 1; k < dinfo->num_components; k++) {
415           int href = 1, vref = 1;
416
417           if ((dinfo->jpeg_color_space == JCS_YCCK ||
418                dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
419             href = tjMCUWidth[i] / 8;  vref = tjMCUHeight[i] / 8;
420           }
421           if (dinfo->comp_info[k].h_samp_factor == href &&
422               dinfo->comp_info[k].v_samp_factor == vref)
423             match++;
424         }
425         if (match == dinfo->num_components - 1) {
426           retval = i;  break;
427         }
428       }
429       /* Handle 4:2:2 and 4:4:0 images whose sampling factors are specified
430          in non-standard ways. */
431       if (dinfo->comp_info[0].h_samp_factor == 2 &&
432           dinfo->comp_info[0].v_samp_factor == 2 &&
433           (i == TJSAMP_422 || i == TJSAMP_440)) {
434         int match = 0;
435
436         for (k = 1; k < dinfo->num_components; k++) {
437           int href = tjMCUHeight[i] / 8, vref = tjMCUWidth[i] / 8;
438
439           if ((dinfo->jpeg_color_space == JCS_YCCK ||
440                dinfo->jpeg_color_space == JCS_CMYK) && k == 3) {
441             href = vref = 2;
442           }
443           if (dinfo->comp_info[k].h_samp_factor == href &&
444               dinfo->comp_info[k].v_samp_factor == vref)
445             match++;
446         }
447         if (match == dinfo->num_components - 1) {
448           retval = i;  break;
449         }
450       }
451       /* Handle 4:4:4 images whose sampling factors are specified in
452          non-standard ways. */
453       if (dinfo->comp_info[0].h_samp_factor *
454           dinfo->comp_info[0].v_samp_factor <=
455           D_MAX_BLOCKS_IN_MCU / 3 && i == TJSAMP_444) {
456         int match = 0;
457         for (k = 1; k < dinfo->num_components; k++) {
458           if (dinfo->comp_info[k].h_samp_factor ==
459               dinfo->comp_info[0].h_samp_factor &&
460               dinfo->comp_info[k].v_samp_factor ==
461               dinfo->comp_info[0].v_samp_factor)
462             match++;
463           if (match == dinfo->num_components - 1) {
464             retval = i;  break;
465           }
466         }
467       }
468     }
469   }
470   return retval;
471 }
472
473
474 static void setDecompParameters(tjinstance *this)
475 {
476   this->subsamp = getSubsamp(&this->dinfo);
477   this->jpegWidth = this->dinfo.image_width;
478   this->jpegHeight = this->dinfo.image_height;
479   this->precision = this->dinfo.data_precision;
480   switch (this->dinfo.jpeg_color_space) {
481   case JCS_GRAYSCALE:  this->colorspace = TJCS_GRAY;  break;
482   case JCS_RGB:        this->colorspace = TJCS_RGB;  break;
483   case JCS_YCbCr:      this->colorspace = TJCS_YCbCr;  break;
484   case JCS_CMYK:       this->colorspace = TJCS_CMYK;  break;
485   case JCS_YCCK:       this->colorspace = TJCS_YCCK;  break;
486   default:             this->colorspace = -1;  break;
487   }
488   this->progressive = this->dinfo.progressive_mode;
489   this->arithmetic = this->dinfo.arith_code;
490   this->lossless = this->dinfo.master->lossless;
491   this->losslessPSV = this->dinfo.Ss;
492   this->losslessPt = this->dinfo.Al;
493   this->xDensity = this->dinfo.X_density;
494   this->yDensity = this->dinfo.Y_density;
495   this->densityUnits = this->dinfo.density_unit;
496 }
497
498
499 static void processFlags(tjhandle handle, int flags, int operation)
500 {
501   tjinstance *this = (tjinstance *)handle;
502
503   this->bottomUp = !!(flags & TJFLAG_BOTTOMUP);
504
505 #ifndef NO_PUTENV
506   if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1");
507   else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1");
508   else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1");
509 #endif
510
511   this->fastUpsample = !!(flags & TJFLAG_FASTUPSAMPLE);
512   this->noRealloc = !!(flags & TJFLAG_NOREALLOC);
513
514   if (operation == COMPRESS) {
515     if (this->quality >= 96 || flags & TJFLAG_ACCURATEDCT)
516       this->fastDCT = FALSE;
517     else
518       this->fastDCT = TRUE;
519   } else
520     this->fastDCT = !!(flags & TJFLAG_FASTDCT);
521
522   this->jerr.stopOnWarning = !!(flags & TJFLAG_STOPONWARNING);
523   this->progressive = !!(flags & TJFLAG_PROGRESSIVE);
524
525   if (flags & TJFLAG_LIMITSCANS) this->scanLimit = 500;
526 }
527
528
529 /*************************** General API functions ***************************/
530
531 /* TurboJPEG 3+ */
532 DLLEXPORT tjhandle tj3Init(int initType)
533 {
534   static const char FUNCTION_NAME[] = "tj3Init";
535   tjinstance *this = NULL;
536   tjhandle retval = NULL;
537
538   if (initType < 0 || initType >= TJ_NUMINIT)
539     THROWG("Invalid argument", NULL);
540
541   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL)
542     THROWG("Memory allocation failure", NULL);
543   memset(this, 0, sizeof(tjinstance));
544   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
545
546   this->quality = -1;
547   this->subsamp = TJSAMP_UNKNOWN;
548   this->jpegWidth = -1;
549   this->jpegHeight = -1;
550   this->precision = 8;
551   this->colorspace = -1;
552   this->losslessPSV = 1;
553   this->xDensity = 1;
554   this->yDensity = 1;
555   this->scalingFactor = TJUNSCALED;
556
557   switch (initType) {
558   case TJINIT_COMPRESS:  return _tjInitCompress(this);
559   case TJINIT_DECOMPRESS:  return _tjInitDecompress(this);
560   case TJINIT_TRANSFORM:
561     retval = _tjInitCompress(this);
562     if (!retval) return NULL;
563     retval = _tjInitDecompress(this);
564     return retval;
565   }
566
567 bailout:
568   return retval;
569 }
570
571
572 #define SET_PARAM(field, minValue, maxValue) { \
573   if (value < minValue || (maxValue > 0 && value > maxValue)) \
574     THROW("Parameter value out of range"); \
575   this->field = value; \
576 }
577
578 #define SET_BOOL_PARAM(field) { \
579   if (value < 0 || value > 1) \
580     THROW("Parameter value out of range"); \
581   this->field = (boolean)value; \
582 }
583
584 /* TurboJPEG 3+ */
585 DLLEXPORT int tj3Set(tjhandle handle, int param, int value)
586 {
587   static const char FUNCTION_NAME[] = "tj3Set";
588   int retval = 0;
589
590   GET_TJINSTANCE(handle, -1);
591
592   switch (param) {
593 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
594   case TJPARAM_MAXPIXELS:
595     SET_PARAM(maxPixels, 0, -1);
596     break;
597 #endif
598   case TJPARAM_STOPONWARNING:
599     SET_BOOL_PARAM(jerr.stopOnWarning);
600     break;
601   case TJPARAM_BOTTOMUP:
602     SET_BOOL_PARAM(bottomUp);
603     break;
604   case TJPARAM_NOREALLOC:
605     if (!(this->init & COMPRESS))
606       THROW("TJPARAM_NOREALLOC is not applicable to decompression instances.");
607     SET_BOOL_PARAM(noRealloc);
608     break;
609   case TJPARAM_QUALITY:
610     if (!(this->init & COMPRESS))
611       THROW("TJPARAM_QUALITY is not applicable to decompression instances.");
612     SET_PARAM(quality, 1, 100);
613     break;
614   case TJPARAM_SUBSAMP:
615     SET_PARAM(subsamp, 0, TJ_NUMSAMP - 1);
616     break;
617   case TJPARAM_JPEGWIDTH:
618     if (!(this->init & DECOMPRESS))
619       THROW("TJPARAM_JPEGWIDTH is not applicable to compression instances.");
620     THROW("TJPARAM_JPEGWIDTH is read-only in decompression instances.");
621     break;
622   case TJPARAM_JPEGHEIGHT:
623     if (!(this->init & DECOMPRESS))
624       THROW("TJPARAM_JPEGHEIGHT is not applicable to compression instances.");
625     THROW("TJPARAM_JPEGHEIGHT is read-only in decompression instances.");
626     break;
627   case TJPARAM_PRECISION:
628     if (!(this->init & DECOMPRESS))
629       THROW("TJPARAM_PRECISION is not applicable to compression instances.");
630     THROW("TJPARAM_PRECISION is read-only in decompression instances.");
631     break;
632   case TJPARAM_COLORSPACE:
633     if (!(this->init & COMPRESS))
634       THROW("TJPARAM_COLORSPACE is read-only in decompression instances.");
635     SET_PARAM(colorspace, 0, TJ_NUMCS - 1);
636     break;
637   case TJPARAM_FASTUPSAMPLE:
638     if (!(this->init & DECOMPRESS))
639       THROW("TJPARAM_FASTUPSAMPLE is not applicable to compression instances.");
640     SET_BOOL_PARAM(fastUpsample);
641     break;
642   case TJPARAM_FASTDCT:
643     SET_BOOL_PARAM(fastDCT);
644     break;
645   case TJPARAM_OPTIMIZE:
646     if (!(this->init & COMPRESS))
647       THROW("TJPARAM_OPTIMIZE is not applicable to decompression instances.");
648     SET_BOOL_PARAM(optimize);
649     break;
650   case TJPARAM_PROGRESSIVE:
651     if (!(this->init & COMPRESS))
652       THROW("TJPARAM_PROGRESSIVE is read-only in decompression instances.");
653     SET_BOOL_PARAM(progressive);
654     break;
655   case TJPARAM_SCANLIMIT:
656     if (!(this->init & DECOMPRESS))
657       THROW("TJPARAM_SCANLIMIT is not applicable to compression instances.");
658     SET_PARAM(scanLimit, 0, -1);
659     break;
660   case TJPARAM_ARITHMETIC:
661     if (!(this->init & COMPRESS))
662       THROW("TJPARAM_ARITHMETIC is read-only in decompression instances.");
663     SET_BOOL_PARAM(arithmetic);
664     break;
665   case TJPARAM_LOSSLESS:
666     if (!(this->init & COMPRESS))
667       THROW("TJPARAM_LOSSLESS is read-only in decompression instances.");
668     SET_BOOL_PARAM(lossless);
669     break;
670   case TJPARAM_LOSSLESSPSV:
671     if (!(this->init & COMPRESS))
672       THROW("TJPARAM_LOSSLESSPSV is read-only in decompression instances.");
673     SET_PARAM(losslessPSV, 1, 7);
674     break;
675   case TJPARAM_LOSSLESSPT:
676     if (!(this->init & COMPRESS))
677       THROW("TJPARAM_LOSSLESSPT is read-only in decompression instances.");
678     SET_PARAM(losslessPt, 0, this->precision - 1);
679     break;
680   case TJPARAM_RESTARTBLOCKS:
681     if (!(this->init & COMPRESS))
682       THROW("TJPARAM_RESTARTBLOCKS is not applicable to decompression instances.");
683     SET_PARAM(restartIntervalBlocks, 0, 65535);
684     if (value != 0) this->restartIntervalRows = 0;
685     break;
686   case TJPARAM_RESTARTROWS:
687     if (!(this->init & COMPRESS))
688       THROW("TJPARAM_RESTARTROWS is not applicable to decompression instances.");
689     SET_PARAM(restartIntervalRows, 0, 65535);
690     if (value != 0) this->restartIntervalBlocks = 0;
691     break;
692   case TJPARAM_XDENSITY:
693     if (!(this->init & COMPRESS))
694       THROW("TJPARAM_XDENSITY is read-only in decompression instances.");
695     SET_PARAM(xDensity, 1, 65535);
696     break;
697   case TJPARAM_YDENSITY:
698     if (!(this->init & COMPRESS))
699       THROW("TJPARAM_YDENSITY is read-only in decompression instances.");
700     SET_PARAM(yDensity, 1, 65535);
701     break;
702   case TJPARAM_DENSITYUNITS:
703     if (!(this->init & COMPRESS))
704       THROW("TJPARAM_DENSITYUNITS is read-only in decompression instances.");
705     SET_PARAM(densityUnits, 0, 2);
706     break;
707   default:
708     THROW("Invalid parameter");
709   }
710
711 bailout:
712   return retval;
713 }
714
715
716 /* TurboJPEG 3+ */
717 DLLEXPORT int tj3Get(tjhandle handle, int param)
718 {
719   tjinstance *this = (tjinstance *)handle;
720   if (!this) return -1;
721
722   switch (param) {
723   case TJPARAM_STOPONWARNING:
724     return this->jerr.stopOnWarning;
725   case TJPARAM_BOTTOMUP:
726     return this->bottomUp;
727   case TJPARAM_NOREALLOC:
728     return this->noRealloc;
729   case TJPARAM_QUALITY:
730     return this->quality;
731   case TJPARAM_SUBSAMP:
732     return this->subsamp;
733   case TJPARAM_JPEGWIDTH:
734     return this->jpegWidth;
735   case TJPARAM_JPEGHEIGHT:
736     return this->jpegHeight;
737   case TJPARAM_PRECISION:
738     return this->precision;
739   case TJPARAM_COLORSPACE:
740     return this->colorspace;
741   case TJPARAM_FASTUPSAMPLE:
742     return this->fastUpsample;
743   case TJPARAM_FASTDCT:
744     return this->fastDCT;
745   case TJPARAM_OPTIMIZE:
746     return this->optimize;
747   case TJPARAM_PROGRESSIVE:
748     return this->progressive;
749   case TJPARAM_SCANLIMIT:
750     return this->scanLimit;
751   case TJPARAM_ARITHMETIC:
752     return this->arithmetic;
753   case TJPARAM_LOSSLESS:
754     return this->lossless;
755   case TJPARAM_LOSSLESSPSV:
756     return this->losslessPSV;
757   case TJPARAM_LOSSLESSPT:
758     return this->losslessPt;
759   case TJPARAM_RESTARTBLOCKS:
760     return this->restartIntervalBlocks;
761   case TJPARAM_RESTARTROWS:
762     return this->restartIntervalRows;
763   case TJPARAM_XDENSITY:
764     return this->xDensity;
765   case TJPARAM_YDENSITY:
766     return this->yDensity;
767   case TJPARAM_DENSITYUNITS:
768     return this->densityUnits;
769   }
770
771   return -1;
772 }
773
774
775 /* TurboJPEG 3+ */
776 DLLEXPORT char *tj3GetErrorStr(tjhandle handle)
777 {
778   tjinstance *this = (tjinstance *)handle;
779
780   if (this && this->isInstanceError) {
781     this->isInstanceError = FALSE;
782     return this->errStr;
783   } else
784     return errStr;
785 }
786
787 /* TurboJPEG 2.0+ */
788 DLLEXPORT char *tjGetErrorStr2(tjhandle handle)
789 {
790   return tj3GetErrorStr(handle);
791 }
792
793 /* TurboJPEG 1.0+ */
794 DLLEXPORT char *tjGetErrorStr(void)
795 {
796   return errStr;
797 }
798
799
800 /* TurboJPEG 3+ */
801 DLLEXPORT int tj3GetErrorCode(tjhandle handle)
802 {
803   tjinstance *this = (tjinstance *)handle;
804
805   if (this && this->jerr.warning) return TJERR_WARNING;
806   else return TJERR_FATAL;
807 }
808
809 /* TurboJPEG 2.0+ */
810 DLLEXPORT int tjGetErrorCode(tjhandle handle)
811 {
812   return tj3GetErrorCode(handle);
813 }
814
815
816 /* TurboJPEG 3+ */
817 DLLEXPORT void tj3Destroy(tjhandle handle)
818 {
819   tjinstance *this = (tjinstance *)handle;
820   j_compress_ptr cinfo = NULL;
821   j_decompress_ptr dinfo = NULL;
822
823   if (!this) return;
824
825   cinfo = &this->cinfo;  dinfo = &this->dinfo;
826   this->jerr.warning = FALSE;
827   this->isInstanceError = FALSE;
828
829   if (setjmp(this->jerr.setjmp_buffer)) return;
830   if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
831   if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
832   free(this);
833 }
834
835 /* TurboJPEG 1.0+ */
836 DLLEXPORT int tjDestroy(tjhandle handle)
837 {
838   static const char FUNCTION_NAME[] = "tjDestroy";
839   int retval = 0;
840
841   if (!handle) THROWG("Invalid handle", -1);
842
843   SNPRINTF(errStr, JMSG_LENGTH_MAX, "No error");
844   tj3Destroy(handle);
845   if (strcmp(errStr, "No error")) retval = -1;
846
847 bailout:
848   return retval;
849 }
850
851
852 /* These are exposed mainly because Windows can't malloc() and free() across
853    DLL boundaries except when the CRT DLL is used, and we don't use the CRT DLL
854    with turbojpeg.dll for compatibility reasons.  However, these functions
855    can potentially be used for other purposes by different implementations. */
856
857 /* TurboJPEG 3+ */
858 DLLEXPORT void tj3Free(void *buf)
859 {
860   free(buf);
861 }
862
863 /* TurboJPEG 1.2+ */
864 DLLEXPORT void tjFree(unsigned char *buf)
865 {
866   tj3Free(buf);
867 }
868
869
870 /* TurboJPEG 3+ */
871 DLLEXPORT void *tj3Alloc(size_t bytes)
872 {
873   return malloc(bytes);
874 }
875
876 /* TurboJPEG 1.2+ */
877 DLLEXPORT unsigned char *tjAlloc(int bytes)
878 {
879   return (unsigned char *)tj3Alloc((size_t)bytes);
880 }
881
882
883 /******************************** Compressor *********************************/
884
885 static tjhandle _tjInitCompress(tjinstance *this)
886 {
887   static unsigned char buffer[1];
888   unsigned char *buf = buffer;
889   size_t size = 1;
890
891   /* This is also straight out of example.c */
892   this->cinfo.err = jpeg_std_error(&this->jerr.pub);
893   this->jerr.pub.error_exit = my_error_exit;
894   this->jerr.pub.output_message = my_output_message;
895   this->jerr.emit_message = this->jerr.pub.emit_message;
896   this->jerr.pub.emit_message = my_emit_message;
897   this->jerr.pub.addon_message_table = turbojpeg_message_table;
898   this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
899   this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
900
901   if (setjmp(this->jerr.setjmp_buffer)) {
902     /* If we get here, the JPEG code has signaled an error. */
903     free(this);
904     return NULL;
905   }
906
907   jpeg_create_compress(&this->cinfo);
908   /* Make an initial call so it will create the destination manager */
909   jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
910
911   this->init |= COMPRESS;
912   return (tjhandle)this;
913 }
914
915 /* TurboJPEG 1.0+ */
916 DLLEXPORT tjhandle tjInitCompress(void)
917 {
918   return tj3Init(TJINIT_COMPRESS);
919 }
920
921
922 /* TurboJPEG 3+ */
923 DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp)
924 {
925   static const char FUNCTION_NAME[] = "tj3JPEGBufSize";
926   unsigned long long retval = 0;
927   int mcuw, mcuh, chromasf;
928
929   if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN ||
930       jpegSubsamp >= TJ_NUMSAMP)
931     THROWG("Invalid argument", 0);
932
933   if (jpegSubsamp == TJSAMP_UNKNOWN)
934     jpegSubsamp = TJSAMP_444;
935
936   /* This allows for rare corner cases in which a JPEG image can actually be
937      larger than the uncompressed input (we wouldn't mention it if it hadn't
938      happened before.) */
939   mcuw = tjMCUWidth[jpegSubsamp];
940   mcuh = tjMCUHeight[jpegSubsamp];
941   chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
942   retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
943   if (retval > (unsigned long long)((unsigned long)-1))
944     THROWG("Image is too large", 0);
945
946 bailout:
947   return (size_t)retval;
948 }
949
950 /* TurboJPEG 1.2+ */
951 DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
952 {
953   static const char FUNCTION_NAME[] = "tjBufSize";
954   size_t retval;
955
956   if (jpegSubsamp < 0)
957     THROWG("Invalid argument", 0);
958
959   retval = tj3JPEGBufSize(width, height, jpegSubsamp);
960
961 bailout:
962   return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
963 }
964
965 /* TurboJPEG 1.0+ */
966 DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
967 {
968   static const char FUNCTION_NAME[] = "TJBUFSIZE";
969   unsigned long long retval = 0;
970
971   if (width < 1 || height < 1)
972     THROWG("Invalid argument", (unsigned long)-1);
973
974   /* This allows for rare corner cases in which a JPEG image can actually be
975      larger than the uncompressed input (we wouldn't mention it if it hadn't
976      happened before.) */
977   retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
978   if (retval > (unsigned long long)((unsigned long)-1))
979     THROWG("Image is too large", (unsigned long)-1);
980
981 bailout:
982   return (unsigned long)retval;
983 }
984
985
986 /* TurboJPEG 3+ */
987 DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp)
988 {
989   static const char FUNCTION_NAME[] = "tj3YUVBufSize";
990   unsigned long long retval = 0;
991   int nc, i;
992
993   if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP)
994     THROWG("Invalid argument", 0);
995
996   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
997   for (i = 0; i < nc; i++) {
998     int pw = tj3YUVPlaneWidth(i, width, subsamp);
999     int stride = PAD(pw, align);
1000     int ph = tj3YUVPlaneHeight(i, height, subsamp);
1001
1002     if (pw == 0 || ph == 0) return 0;
1003     else retval += (unsigned long long)stride * ph;
1004   }
1005   if (retval > (unsigned long long)((unsigned long)-1))
1006     THROWG("Image is too large", 0);
1007
1008 bailout:
1009   return (size_t)retval;
1010 }
1011
1012 /* TurboJPEG 1.4+ */
1013 DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height,
1014                                       int subsamp)
1015 {
1016   size_t retval = tj3YUVBufSize(width, align, height, subsamp);
1017   return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
1018 }
1019
1020 /* TurboJPEG 1.2+ */
1021 DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
1022 {
1023   return tjBufSizeYUV2(width, 4, height, subsamp);
1024 }
1025
1026 /* TurboJPEG 1.1+ */
1027 DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
1028 {
1029   return tjBufSizeYUV(width, height, subsamp);
1030 }
1031
1032
1033 /* TurboJPEG 3+ */
1034 DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp)
1035 {
1036   static const char FUNCTION_NAME[] = "tj3YUVPlaneWidth";
1037   unsigned long long pw, retval = 0;
1038   int nc;
1039
1040   if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1041     THROWG("Invalid argument", 0);
1042   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1043   if (componentID < 0 || componentID >= nc)
1044     THROWG("Invalid argument", 0);
1045
1046   pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8);
1047   if (componentID == 0)
1048     retval = pw;
1049   else
1050     retval = pw * 8 / tjMCUWidth[subsamp];
1051
1052   if (retval > (unsigned long long)INT_MAX)
1053     THROWG("Width is too large", 0);
1054
1055 bailout:
1056   return (int)retval;
1057 }
1058
1059 /* TurboJPEG 1.4+ */
1060 DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
1061 {
1062   int retval = tj3YUVPlaneWidth(componentID, width, subsamp);
1063   return (retval == 0) ? -1 : retval;
1064 }
1065
1066
1067 /* TurboJPEG 3+ */
1068 DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp)
1069 {
1070   static const char FUNCTION_NAME[] = "tj3YUVPlaneHeight";
1071   unsigned long long ph, retval = 0;
1072   int nc;
1073
1074   if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1075     THROWG("Invalid argument", 0);
1076   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1077   if (componentID < 0 || componentID >= nc)
1078     THROWG("Invalid argument", 0);
1079
1080   ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8);
1081   if (componentID == 0)
1082     retval = ph;
1083   else
1084     retval = ph * 8 / tjMCUHeight[subsamp];
1085
1086   if (retval > (unsigned long long)INT_MAX)
1087     THROWG("Height is too large", 0);
1088
1089 bailout:
1090   return (int)retval;
1091 }
1092
1093 /* TurboJPEG 1.4+ */
1094 DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
1095 {
1096   int retval = tj3YUVPlaneHeight(componentID, height, subsamp);
1097   return (retval == 0) ? -1 : retval;
1098 }
1099
1100
1101 /* TurboJPEG 3+ */
1102 DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride,
1103                                  int height, int subsamp)
1104 {
1105   static const char FUNCTION_NAME[] = "tj3YUVPlaneSize";
1106   unsigned long long retval = 0;
1107   int pw, ph;
1108
1109   if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1110     THROWG("Invalid argument", 0);
1111
1112   pw = tj3YUVPlaneWidth(componentID, width, subsamp);
1113   ph = tj3YUVPlaneHeight(componentID, height, subsamp);
1114   if (pw == 0 || ph == 0) return 0;
1115
1116   if (stride == 0) stride = pw;
1117   else stride = abs(stride);
1118
1119   retval = (unsigned long long)stride * (ph - 1) + pw;
1120   if (retval > (unsigned long long)((unsigned long)-1))
1121     THROWG("Image is too large", 0);
1122
1123 bailout:
1124   return (size_t)retval;
1125 }
1126
1127 /* TurboJPEG 1.4+ */
1128 DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
1129                                        int height, int subsamp)
1130 {
1131   size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
1132   return (retval == 0) ? -1 : (unsigned long)retval;
1133 }
1134
1135
1136 /* tj3Compress*() is implemented in turbojpeg-mp.c */
1137 #define BITS_IN_JSAMPLE  8
1138 #include "turbojpeg-mp.c"
1139 #undef BITS_IN_JSAMPLE
1140 #define BITS_IN_JSAMPLE  12
1141 #include "turbojpeg-mp.c"
1142 #undef BITS_IN_JSAMPLE
1143 #define BITS_IN_JSAMPLE  16
1144 #include "turbojpeg-mp.c"
1145 #undef BITS_IN_JSAMPLE
1146
1147 /* TurboJPEG 1.2+ */
1148 DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
1149                           int width, int pitch, int height, int pixelFormat,
1150                           unsigned char **jpegBuf, unsigned long *jpegSize,
1151                           int jpegSubsamp, int jpegQual, int flags)
1152 {
1153   static const char FUNCTION_NAME[] = "tjCompress2";
1154   int retval = 0;
1155   size_t size;
1156
1157   GET_TJINSTANCE(handle, -1);
1158
1159   if (jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP ||
1160       jpegQual < 0 || jpegQual > 100)
1161     THROW("Invalid argument");
1162
1163   this->quality = jpegQual;
1164   this->subsamp = jpegSubsamp;
1165   processFlags(handle, flags, COMPRESS);
1166
1167   size = (size_t)(*jpegSize);
1168   retval = tj3Compress8(handle, srcBuf, width, pitch, height, pixelFormat,
1169                         jpegBuf, &size);
1170   *jpegSize = (unsigned long)size;
1171
1172 bailout:
1173   return retval;
1174 }
1175
1176 /* TurboJPEG 1.0+ */
1177 DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
1178                          int pitch, int height, int pixelSize,
1179                          unsigned char *jpegBuf, unsigned long *jpegSize,
1180                          int jpegSubsamp, int jpegQual, int flags)
1181 {
1182   int retval = 0;
1183   unsigned long size = jpegSize ? *jpegSize : 0;
1184
1185   if (flags & TJ_YUV) {
1186     size = tjBufSizeYUV(width, height, jpegSubsamp);
1187     retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1188                           getPixelFormat(pixelSize, flags), jpegBuf,
1189                           jpegSubsamp, flags);
1190   } else {
1191     retval = tjCompress2(handle, srcBuf, width, pitch, height,
1192                          getPixelFormat(pixelSize, flags), &jpegBuf, &size,
1193                          jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
1194   }
1195   *jpegSize = size;
1196   return retval;
1197 }
1198
1199
1200 /* TurboJPEG 3+ */
1201 DLLEXPORT int tj3EncodeYUVPlanes8(tjhandle handle, const unsigned char *srcBuf,
1202                                   int width, int pitch, int height,
1203                                   int pixelFormat, unsigned char **dstPlanes,
1204                                   int *strides)
1205 {
1206   static const char FUNCTION_NAME[] = "tj3EncodeYUVPlanes8";
1207   JSAMPROW *row_pointer = NULL;
1208   JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
1209   JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
1210   JSAMPROW *outbuf[MAX_COMPONENTS];
1211   int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1212   JSAMPLE *ptr;
1213   jpeg_component_info *compptr;
1214
1215   GET_CINSTANCE(handle)
1216
1217   for (i = 0; i < MAX_COMPONENTS; i++) {
1218     tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;
1219     tmpbuf2[i] = NULL;  _tmpbuf2[i] = NULL;  outbuf[i] = NULL;
1220   }
1221
1222   if ((this->init & COMPRESS) == 0)
1223     THROW("Instance has not been initialized for compression");
1224
1225   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1226       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
1227       !dstPlanes[0])
1228     THROW("Invalid argument");
1229   if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1230     THROW("Invalid argument");
1231
1232   if (this->subsamp == TJSAMP_UNKNOWN)
1233     THROW("TJPARAM_SUBSAMP must be specified");
1234   if (pixelFormat == TJPF_CMYK)
1235     THROW("Cannot generate YUV images from packed-pixel CMYK images");
1236
1237   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1238
1239   if (setjmp(this->jerr.setjmp_buffer)) {
1240     /* If we get here, the JPEG code has signaled an error. */
1241     retval = -1;  goto bailout;
1242   }
1243
1244   cinfo->image_width = width;
1245   cinfo->image_height = height;
1246   cinfo->data_precision = 8;
1247
1248   setCompDefaults(this, pixelFormat);
1249
1250   /* Execute only the parts of jpeg_start_compress() that we need.  If we
1251      were to call the whole jpeg_start_compress() function, then it would try
1252      to write the file headers, which could overflow the output buffer if the
1253      YUV image were very small. */
1254   if (cinfo->global_state != CSTATE_START)
1255     THROW("libjpeg API is in the wrong state");
1256   (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
1257   jinit_c_master_control(cinfo, FALSE);
1258   jinit_color_converter(cinfo);
1259   jinit_downsampler(cinfo);
1260   (*cinfo->cconvert->start_pass) (cinfo);
1261
1262   pw0 = PAD(width, cinfo->max_h_samp_factor);
1263   ph0 = PAD(height, cinfo->max_v_samp_factor);
1264
1265   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1266     THROW("Memory allocation failure");
1267   for (i = 0; i < height; i++) {
1268     if (this->bottomUp)
1269       row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
1270     else
1271       row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
1272   }
1273   if (height < ph0)
1274     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
1275
1276   for (i = 0; i < cinfo->num_components; i++) {
1277     compptr = &cinfo->comp_info[i];
1278     _tmpbuf[i] = (JSAMPLE *)malloc(
1279       PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1280           compptr->h_samp_factor, 32) *
1281       cinfo->max_v_samp_factor + 32);
1282     if (!_tmpbuf[i])
1283       THROW("Memory allocation failure");
1284     tmpbuf[i] =
1285       (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
1286     if (!tmpbuf[i])
1287       THROW("Memory allocation failure");
1288     for (row = 0; row < cinfo->max_v_samp_factor; row++) {
1289       unsigned char *_tmpbuf_aligned =
1290         (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
1291
1292       tmpbuf[i][row] = &_tmpbuf_aligned[
1293         PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1294             compptr->h_samp_factor, 32) * row];
1295     }
1296     _tmpbuf2[i] =
1297       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1298                         compptr->v_samp_factor + 32);
1299     if (!_tmpbuf2[i])
1300       THROW("Memory allocation failure");
1301     tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1302     if (!tmpbuf2[i])
1303       THROW("Memory allocation failure");
1304     for (row = 0; row < compptr->v_samp_factor; row++) {
1305       unsigned char *_tmpbuf2_aligned =
1306         (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32);
1307
1308       tmpbuf2[i][row] =
1309         &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1310     }
1311     pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
1312     ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1313     outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1314     if (!outbuf[i])
1315       THROW("Memory allocation failure");
1316     ptr = dstPlanes[i];
1317     for (row = 0; row < ph[i]; row++) {
1318       outbuf[i][row] = ptr;
1319       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1320     }
1321   }
1322
1323   if (setjmp(this->jerr.setjmp_buffer)) {
1324     /* If we get here, the JPEG code has signaled an error. */
1325     retval = -1;  goto bailout;
1326   }
1327
1328   for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
1329     (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
1330                                        cinfo->max_v_samp_factor);
1331     (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
1332     for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
1333          i++, compptr++)
1334       jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
1335         row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
1336         compptr->v_samp_factor, pw[i]);
1337   }
1338   cinfo->next_scanline += height;
1339   jpeg_abort_compress(cinfo);
1340
1341 bailout:
1342   if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1343   free(row_pointer);
1344   for (i = 0; i < MAX_COMPONENTS; i++) {
1345     free(tmpbuf[i]);
1346     free(_tmpbuf[i]);
1347     free(tmpbuf2[i]);
1348     free(_tmpbuf2[i]);
1349     free(outbuf[i]);
1350   }
1351   if (this->jerr.warning) retval = -1;
1352   return retval;
1353 }
1354
1355 /* TurboJPEG 1.4+ */
1356 DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
1357                                 int width, int pitch, int height,
1358                                 int pixelFormat, unsigned char **dstPlanes,
1359                                 int *strides, int subsamp, int flags)
1360 {
1361   static const char FUNCTION_NAME[] = "tjEncodeYUVPlanes";
1362   int retval = 0;
1363
1364   GET_TJINSTANCE(handle, -1);
1365
1366   if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1367     THROW("Invalid argument");
1368
1369   this->subsamp = subsamp;
1370   processFlags(handle, flags, COMPRESS);
1371
1372   return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1373                              dstPlanes, strides);
1374
1375 bailout:
1376   return retval;
1377 }
1378
1379
1380 /* TurboJPEG 3+ */
1381 DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf,
1382                             int width, int pitch, int height, int pixelFormat,
1383                             unsigned char *dstBuf, int align)
1384 {
1385   static const char FUNCTION_NAME[] = "tj3EncodeYUV8";
1386   unsigned char *dstPlanes[3];
1387   int pw0, ph0, strides[3], retval = -1;
1388
1389   GET_TJINSTANCE(handle, -1);
1390
1391   if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 ||
1392       !IS_POW2(align))
1393     THROW("Invalid argument");
1394
1395   if (this->subsamp == TJSAMP_UNKNOWN)
1396     THROW("TJPARAM_SUBSAMP must be specified");
1397
1398   pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1399   ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1400   dstPlanes[0] = dstBuf;
1401   strides[0] = PAD(pw0, align);
1402   if (this->subsamp == TJSAMP_GRAY) {
1403     strides[1] = strides[2] = 0;
1404     dstPlanes[1] = dstPlanes[2] = NULL;
1405   } else {
1406     int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
1407     int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
1408
1409     strides[1] = strides[2] = PAD(pw1, align);
1410     dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1411     dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1412   }
1413
1414   return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1415                              dstPlanes, strides);
1416
1417 bailout:
1418   return retval;
1419 }
1420
1421 /* TurboJPEG 1.4+ */
1422 DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
1423                            int width, int pitch, int height, int pixelFormat,
1424                            unsigned char *dstBuf, int align, int subsamp,
1425                            int flags)
1426 {
1427   static const char FUNCTION_NAME[] = "tjEncodeYUV3";
1428   int retval = 0;
1429
1430   GET_TJINSTANCE(handle, -1);
1431
1432   if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1433     THROW("Invalid argument");
1434
1435   this->subsamp = subsamp;
1436   processFlags(handle, flags, COMPRESS);
1437
1438   return tj3EncodeYUV8(handle, srcBuf, width, pitch, height, pixelFormat,
1439                        dstBuf, align);
1440
1441 bailout:
1442   return retval;
1443 }
1444
1445 /* TurboJPEG 1.2+ */
1446 DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
1447                            int pitch, int height, int pixelFormat,
1448                            unsigned char *dstBuf, int subsamp, int flags)
1449 {
1450   return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
1451                       dstBuf, 4, subsamp, flags);
1452 }
1453
1454 /* TurboJPEG 1.1+ */
1455 DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
1456                           int pitch, int height, int pixelSize,
1457                           unsigned char *dstBuf, int subsamp, int flags)
1458 {
1459   return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1460                       getPixelFormat(pixelSize, flags), dstBuf, subsamp,
1461                       flags);
1462 }
1463
1464
1465 /* TurboJPEG 3+ */
1466 DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle,
1467                                         const unsigned char * const *srcPlanes,
1468                                         int width, const int *strides,
1469                                         int height, unsigned char **jpegBuf,
1470                                         size_t *jpegSize)
1471 {
1472   static const char FUNCTION_NAME[] = "tj3CompressFromYUVPlanes8";
1473   int i, row, retval = 0;
1474   boolean alloc = TRUE;
1475   int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1476     tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1477   JSAMPLE *_tmpbuf = NULL, *ptr;
1478   JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1479
1480   GET_CINSTANCE(handle)
1481
1482   for (i = 0; i < MAX_COMPONENTS; i++) {
1483     tmpbuf[i] = NULL;  inbuf[i] = NULL;
1484   }
1485
1486   if ((this->init & COMPRESS) == 0)
1487     THROW("Instance has not been initialized for compression");
1488
1489   if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
1490       jpegBuf == NULL || jpegSize == NULL)
1491     THROW("Invalid argument");
1492   if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1493     THROW("Invalid argument");
1494
1495   if (this->quality == -1)
1496     THROW("TJPARAM_QUALITY must be specified");
1497   if (this->subsamp == TJSAMP_UNKNOWN)
1498     THROW("TJPARAM_SUBSAMP must be specified");
1499
1500   if (setjmp(this->jerr.setjmp_buffer)) {
1501     /* If we get here, the JPEG code has signaled an error. */
1502     retval = -1;  goto bailout;
1503   }
1504
1505   cinfo->image_width = width;
1506   cinfo->image_height = height;
1507   cinfo->data_precision = 8;
1508
1509   if (this->noRealloc) {
1510     alloc = FALSE;  *jpegSize = tj3JPEGBufSize(width, height, this->subsamp);
1511   }
1512   jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
1513   setCompDefaults(this, TJPF_RGB);
1514   cinfo->raw_data_in = TRUE;
1515
1516   jpeg_start_compress(cinfo, TRUE);
1517   for (i = 0; i < cinfo->num_components; i++) {
1518     jpeg_component_info *compptr = &cinfo->comp_info[i];
1519     int ih;
1520
1521     iw[i] = compptr->width_in_blocks * DCTSIZE;
1522     ih = compptr->height_in_blocks * DCTSIZE;
1523     pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1524             compptr->h_samp_factor / cinfo->max_h_samp_factor;
1525     ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1526             compptr->v_samp_factor / cinfo->max_v_samp_factor;
1527     if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1528     th[i] = compptr->v_samp_factor * DCTSIZE;
1529     tmpbufsize += iw[i] * th[i];
1530     if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1531       THROW("Memory allocation failure");
1532     ptr = (JSAMPLE *)srcPlanes[i];
1533     for (row = 0; row < ph[i]; row++) {
1534       inbuf[i][row] = ptr;
1535       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1536     }
1537   }
1538   if (usetmpbuf) {
1539     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1540       THROW("Memory allocation failure");
1541     ptr = _tmpbuf;
1542     for (i = 0; i < cinfo->num_components; i++) {
1543       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1544         THROW("Memory allocation failure");
1545       for (row = 0; row < th[i]; row++) {
1546         tmpbuf[i][row] = ptr;
1547         ptr += iw[i];
1548       }
1549     }
1550   }
1551
1552   if (setjmp(this->jerr.setjmp_buffer)) {
1553     /* If we get here, the JPEG code has signaled an error. */
1554     retval = -1;  goto bailout;
1555   }
1556
1557   for (row = 0; row < (int)cinfo->image_height;
1558        row += cinfo->max_v_samp_factor * DCTSIZE) {
1559     JSAMPARRAY yuvptr[MAX_COMPONENTS];
1560     int crow[MAX_COMPONENTS];
1561
1562     for (i = 0; i < cinfo->num_components; i++) {
1563       jpeg_component_info *compptr = &cinfo->comp_info[i];
1564
1565       crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1566       if (usetmpbuf) {
1567         int j, k;
1568
1569         for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1570           memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1571           /* Duplicate last sample in row to fill out MCU */
1572           for (k = pw[i]; k < iw[i]; k++)
1573             tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1574         }
1575         /* Duplicate last row to fill out MCU */
1576         for (j = ph[i] - crow[i]; j < th[i]; j++)
1577           memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1578         yuvptr[i] = tmpbuf[i];
1579       } else
1580         yuvptr[i] = &inbuf[i][crow[i]];
1581     }
1582     jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1583   }
1584   jpeg_finish_compress(cinfo);
1585
1586 bailout:
1587   if (cinfo->global_state > CSTATE_START) {
1588     if (alloc) (*cinfo->dest->term_destination) (cinfo);
1589     jpeg_abort_compress(cinfo);
1590   }
1591   for (i = 0; i < MAX_COMPONENTS; i++) {
1592     free(tmpbuf[i]);
1593     free(inbuf[i]);
1594   }
1595   free(_tmpbuf);
1596   if (this->jerr.warning) retval = -1;
1597   return retval;
1598 }
1599
1600 /* TurboJPEG 1.4+ */
1601 DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
1602                                       const unsigned char **srcPlanes,
1603                                       int width, const int *strides,
1604                                       int height, int subsamp,
1605                                       unsigned char **jpegBuf,
1606                                       unsigned long *jpegSize, int jpegQual,
1607                                       int flags)
1608 {
1609   static const char FUNCTION_NAME[] = "tjCompressFromYUVPlanes";
1610   int retval = 0;
1611   size_t size;
1612
1613   GET_TJINSTANCE(handle, -1);
1614
1615   if (subsamp < 0 || subsamp >= TJ_NUMSAMP || jpegSize == NULL ||
1616       jpegQual < 0 || jpegQual > 100)
1617     THROW("Invalid argument");
1618
1619   this->quality = jpegQual;
1620   this->subsamp = subsamp;
1621   processFlags(handle, flags, COMPRESS);
1622
1623   size = (size_t)(*jpegSize);
1624   retval = tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1625                                      jpegBuf, &size);
1626   *jpegSize = (unsigned long)size;
1627
1628 bailout:
1629   return retval;
1630 }
1631
1632
1633 /* TurboJPEG 3+ */
1634 DLLEXPORT int tj3CompressFromYUV8(tjhandle handle,
1635                                   const unsigned char *srcBuf, int width,
1636                                   int align, int height,
1637                                   unsigned char **jpegBuf, size_t *jpegSize)
1638 {
1639   static const char FUNCTION_NAME[] = "tj3CompressFromYUV8";
1640   const unsigned char *srcPlanes[3];
1641   int pw0, ph0, strides[3], retval = -1;
1642
1643   GET_TJINSTANCE(handle, -1);
1644
1645   if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) ||
1646       height <= 0)
1647     THROW("Invalid argument");
1648
1649   if (this->subsamp == TJSAMP_UNKNOWN)
1650     THROW("TJPARAM_SUBSAMP must be specified");
1651
1652   pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1653   ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1654   srcPlanes[0] = srcBuf;
1655   strides[0] = PAD(pw0, align);
1656   if (this->subsamp == TJSAMP_GRAY) {
1657     strides[1] = strides[2] = 0;
1658     srcPlanes[1] = srcPlanes[2] = NULL;
1659   } else {
1660     int pw1 = tjPlaneWidth(1, width, this->subsamp);
1661     int ph1 = tjPlaneHeight(1, height, this->subsamp);
1662
1663     strides[1] = strides[2] = PAD(pw1, align);
1664     srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1665     srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1666   }
1667
1668   return tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1669                                    jpegBuf, jpegSize);
1670
1671 bailout:
1672   return retval;
1673 }
1674
1675 /* TurboJPEG 1.4+ */
1676 DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1677                                 int width, int align, int height, int subsamp,
1678                                 unsigned char **jpegBuf,
1679                                 unsigned long *jpegSize, int jpegQual,
1680                                 int flags)
1681 {
1682   static const char FUNCTION_NAME[] = "tjCompressFromYUV";
1683   int retval = -1;
1684   size_t size;
1685
1686   GET_TJINSTANCE(handle, -1);
1687
1688   if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
1689     THROW("Invalid argument");
1690
1691   this->quality = jpegQual;
1692   this->subsamp = subsamp;
1693   processFlags(handle, flags, COMPRESS);
1694
1695   size = (size_t)(*jpegSize);
1696   retval = tj3CompressFromYUV8(handle, srcBuf, width, align, height, jpegBuf,
1697                                &size);
1698   *jpegSize = (unsigned long)size;
1699
1700 bailout:
1701   return retval;
1702 }
1703
1704
1705 /******************************* Decompressor ********************************/
1706
1707 static tjhandle _tjInitDecompress(tjinstance *this)
1708 {
1709   static unsigned char buffer[1];
1710
1711   /* This is also straight out of example.c */
1712   this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1713   this->jerr.pub.error_exit = my_error_exit;
1714   this->jerr.pub.output_message = my_output_message;
1715   this->jerr.emit_message = this->jerr.pub.emit_message;
1716   this->jerr.pub.emit_message = my_emit_message;
1717   this->jerr.pub.addon_message_table = turbojpeg_message_table;
1718   this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1719   this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1720
1721   if (setjmp(this->jerr.setjmp_buffer)) {
1722     /* If we get here, the JPEG code has signaled an error. */
1723     free(this);
1724     return NULL;
1725   }
1726
1727   jpeg_create_decompress(&this->dinfo);
1728   /* Make an initial call so it will create the source manager */
1729   jpeg_mem_src_tj(&this->dinfo, buffer, 1);
1730
1731   this->init |= DECOMPRESS;
1732   return (tjhandle)this;
1733 }
1734
1735 /* TurboJPEG 1.0+ */
1736 DLLEXPORT tjhandle tjInitDecompress(void)
1737 {
1738   return tj3Init(TJINIT_DECOMPRESS);
1739 }
1740
1741
1742 /* TurboJPEG 3+ */
1743 DLLEXPORT int tj3DecompressHeader(tjhandle handle,
1744                                   const unsigned char *jpegBuf,
1745                                   size_t jpegSize)
1746 {
1747   static const char FUNCTION_NAME[] = "tj3DecompressHeader";
1748   int retval = 0;
1749
1750   GET_DINSTANCE(handle);
1751   if ((this->init & DECOMPRESS) == 0)
1752     THROW("Instance has not been initialized for decompression");
1753
1754   if (jpegBuf == NULL || jpegSize <= 0)
1755     THROW("Invalid argument");
1756
1757   if (setjmp(this->jerr.setjmp_buffer)) {
1758     /* If we get here, the JPEG code has signaled an error. */
1759     return -1;
1760   }
1761
1762   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1763
1764   /* jpeg_read_header() calls jpeg_abort() and returns JPEG_HEADER_TABLES_ONLY
1765      if the datastream is a tables-only datastream.  Since we aren't using a
1766      suspending data source, the only other value it can return is
1767      JPEG_HEADER_OK. */
1768   if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY)
1769     return 0;
1770
1771   setDecompParameters(this);
1772
1773   jpeg_abort_decompress(dinfo);
1774
1775   if (this->colorspace < 0)
1776     THROW("Could not determine colorspace of JPEG image");
1777   if (this->jpegWidth < 1 || this->jpegHeight < 1)
1778     THROW("Invalid data returned in header");
1779
1780 bailout:
1781   if (this->jerr.warning) retval = -1;
1782   return retval;
1783 }
1784
1785 /* TurboJPEG 1.4+ */
1786 DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1787                                   const unsigned char *jpegBuf,
1788                                   unsigned long jpegSize, int *width,
1789                                   int *height, int *jpegSubsamp,
1790                                   int *jpegColorspace)
1791 {
1792   static const char FUNCTION_NAME[] = "tjDecompressHeader3";
1793   int retval = 0;
1794
1795   GET_TJINSTANCE(handle, -1);
1796
1797   if (width == NULL || height == NULL || jpegSubsamp == NULL ||
1798       jpegColorspace == NULL)
1799     THROW("Invalid argument");
1800
1801   retval = tj3DecompressHeader(handle, jpegBuf, jpegSize);
1802
1803   *width = tj3Get(handle, TJPARAM_JPEGWIDTH);
1804   *height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
1805   *jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
1806   if (*jpegSubsamp == TJSAMP_UNKNOWN)
1807     THROW("Could not determine subsampling level of JPEG image");
1808   *jpegColorspace = tj3Get(handle, TJPARAM_COLORSPACE);
1809
1810 bailout:
1811   return retval;
1812 }
1813
1814 /* TurboJPEG 1.1+ */
1815 DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1816                                   unsigned long jpegSize, int *width,
1817                                   int *height, int *jpegSubsamp)
1818 {
1819   int jpegColorspace;
1820
1821   return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1822                              jpegSubsamp, &jpegColorspace);
1823 }
1824
1825 /* TurboJPEG 1.0+ */
1826 DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1827                                  unsigned long jpegSize, int *width,
1828                                  int *height)
1829 {
1830   int jpegSubsamp;
1831
1832   return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1833                              &jpegSubsamp);
1834 }
1835
1836
1837 /* TurboJPEG 3+ */
1838 DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors)
1839 {
1840   static const char FUNCTION_NAME[] = "tj3GetScalingFactors";
1841   tjscalingfactor *retval = (tjscalingfactor *)sf;
1842
1843   if (numScalingFactors == NULL)
1844     THROWG("Invalid argument", NULL);
1845
1846   *numScalingFactors = NUMSF;
1847
1848 bailout:
1849   return retval;
1850 }
1851
1852 /* TurboJPEG 1.2+ */
1853 DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors)
1854 {
1855   return tj3GetScalingFactors(numScalingFactors);
1856 }
1857
1858
1859 /* TurboJPEG 3+ */
1860 DLLEXPORT int tj3SetScalingFactor(tjhandle handle,
1861                                   tjscalingfactor scalingFactor)
1862 {
1863   static const char FUNCTION_NAME[] = "tj3SetScalingFactor";
1864   int i, retval = 0;
1865
1866   GET_TJINSTANCE(handle, -1);
1867   if ((this->init & DECOMPRESS) == 0)
1868     THROW("Instance has not been initialized for decompression");
1869
1870   for (i = 0; i < NUMSF; i++) {
1871     if (scalingFactor.num == sf[i].num && scalingFactor.denom == sf[i].denom)
1872       break;
1873   }
1874   if (i >= NUMSF)
1875     THROW("Unsupported scaling factor");
1876
1877   this->scalingFactor = scalingFactor;
1878
1879 bailout:
1880   return retval;
1881 }
1882
1883
1884 /* TurboJPEG 3+ */
1885 DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion)
1886 {
1887   static const char FUNCTION_NAME[] = "tj3SetCroppingRegion";
1888   int retval = 0, scaledWidth, scaledHeight;
1889
1890   GET_TJINSTANCE(handle, -1);
1891   if ((this->init & DECOMPRESS) == 0)
1892     THROW("Instance has not been initialized for decompression");
1893
1894   if (croppingRegion.x == 0 && croppingRegion.y == 0 &&
1895       croppingRegion.w == 0 && croppingRegion.h == 0) {
1896     this->croppingRegion = croppingRegion;
1897     return 0;
1898   }
1899
1900   if (croppingRegion.x < 0 || croppingRegion.y < 0 || croppingRegion.w < 0 ||
1901       croppingRegion.h < 0)
1902     THROW("Invalid cropping region");
1903   if (this->jpegWidth < 0 || this->jpegHeight < 0)
1904     THROW("JPEG header has not yet been read");
1905   if (this->precision == 16 || this->lossless)
1906     THROW("Cannot partially decompress lossless JPEG images");
1907   if (this->subsamp == TJSAMP_UNKNOWN)
1908     THROW("Could not determine subsampling level of JPEG image");
1909
1910   scaledWidth = TJSCALED(this->jpegWidth, this->scalingFactor);
1911   scaledHeight = TJSCALED(this->jpegHeight, this->scalingFactor);
1912
1913   if (croppingRegion.x %
1914       TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor) != 0)
1915     THROWI("The left boundary of the cropping region (%d) is not\n"
1916            "divisible by the scaled MCU width (%d)",
1917            croppingRegion.x,
1918            TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor));
1919   if (croppingRegion.w == 0)
1920     croppingRegion.w = scaledWidth - croppingRegion.x;
1921   if (croppingRegion.h == 0)
1922     croppingRegion.h = scaledHeight - croppingRegion.y;
1923   if (croppingRegion.w < 0 || croppingRegion.h < 0 ||
1924       croppingRegion.x + croppingRegion.w > scaledWidth ||
1925       croppingRegion.y + croppingRegion.h > scaledHeight)
1926     THROW("The cropping region exceeds the scaled image dimensions");
1927
1928   this->croppingRegion = croppingRegion;
1929
1930 bailout:
1931   return retval;
1932 }
1933
1934
1935 /* tj3Decompress*() is implemented in turbojpeg-mp.c */
1936
1937 /* TurboJPEG 1.2+ */
1938 DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
1939                             unsigned long jpegSize, unsigned char *dstBuf,
1940                             int width, int pitch, int height, int pixelFormat,
1941                             int flags)
1942 {
1943   static const char FUNCTION_NAME[] = "tjDecompress2";
1944   int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
1945
1946   GET_DINSTANCE(handle);
1947   if ((this->init & DECOMPRESS) == 0)
1948     THROW("Instance has not been initialized for decompression");
1949
1950   if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
1951     THROW("Invalid argument");
1952
1953   if (setjmp(this->jerr.setjmp_buffer)) {
1954     /* If we get here, the JPEG code has signaled an error. */
1955     retval = -1;  goto bailout;
1956   }
1957
1958   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1959   jpeg_read_header(dinfo, TRUE);
1960   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
1961   if (width == 0) width = jpegwidth;
1962   if (height == 0) height = jpegheight;
1963   for (i = 0; i < NUMSF; i++) {
1964     scaledw = TJSCALED(jpegwidth, sf[i]);
1965     scaledh = TJSCALED(jpegheight, sf[i]);
1966     if (scaledw <= width && scaledh <= height)
1967       break;
1968   }
1969   if (i >= NUMSF)
1970     THROW("Could not scale down to desired image dimensions");
1971
1972   processFlags(handle, flags, DECOMPRESS);
1973
1974   if (tj3SetScalingFactor(handle, sf[i]) == -1)
1975     return -1;
1976   if (tj3SetCroppingRegion(handle, TJUNCROPPED) == -1)
1977     return -1;
1978   return tj3Decompress8(handle, jpegBuf, jpegSize, dstBuf, pitch, pixelFormat);
1979
1980 bailout:
1981   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
1982   if (this->jerr.warning) retval = -1;
1983   return retval;
1984 }
1985
1986 /* TurboJPEG 1.0+ */
1987 DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
1988                            unsigned long jpegSize, unsigned char *dstBuf,
1989                            int width, int pitch, int height, int pixelSize,
1990                            int flags)
1991 {
1992   if (flags & TJ_YUV)
1993     return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
1994   else
1995     return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
1996                          height, getPixelFormat(pixelSize, flags), flags);
1997 }
1998
1999
2000 static void setDecodeDefaults(tjinstance *this, int pixelFormat)
2001 {
2002   int i;
2003
2004   this->dinfo.scale_num = this->dinfo.scale_denom = 1;
2005
2006   if (this->subsamp == TJSAMP_GRAY) {
2007     this->dinfo.num_components = this->dinfo.comps_in_scan = 1;
2008     this->dinfo.jpeg_color_space = JCS_GRAYSCALE;
2009   } else {
2010     this->dinfo.num_components = this->dinfo.comps_in_scan = 3;
2011     this->dinfo.jpeg_color_space = JCS_YCbCr;
2012   }
2013
2014   this->dinfo.comp_info = (jpeg_component_info *)
2015     (*this->dinfo.mem->alloc_small) ((j_common_ptr)&this->dinfo, JPOOL_IMAGE,
2016                                      this->dinfo.num_components *
2017                                      sizeof(jpeg_component_info));
2018
2019   for (i = 0; i < this->dinfo.num_components; i++) {
2020     jpeg_component_info *compptr = &this->dinfo.comp_info[i];
2021
2022     compptr->h_samp_factor = (i == 0) ? tjMCUWidth[this->subsamp] / 8 : 1;
2023     compptr->v_samp_factor = (i == 0) ? tjMCUHeight[this->subsamp] / 8 : 1;
2024     compptr->component_index = i;
2025     compptr->component_id = i + 1;
2026     compptr->quant_tbl_no = compptr->dc_tbl_no =
2027       compptr->ac_tbl_no = (i == 0) ? 0 : 1;
2028     this->dinfo.cur_comp_info[i] = compptr;
2029   }
2030   this->dinfo.data_precision = 8;
2031   for (i = 0; i < 2; i++) {
2032     if (this->dinfo.quant_tbl_ptrs[i] == NULL)
2033       this->dinfo.quant_tbl_ptrs[i] =
2034         jpeg_alloc_quant_table((j_common_ptr)&this->dinfo);
2035   }
2036 }
2037
2038
2039 static int my_read_markers(j_decompress_ptr dinfo)
2040 {
2041   return JPEG_REACHED_SOS;
2042 }
2043
2044 static void my_reset_marker_reader(j_decompress_ptr dinfo)
2045 {
2046 }
2047
2048 /* TurboJPEG 3+ */
2049 DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle,
2050                                   const unsigned char * const *srcPlanes,
2051                                   const int *strides, unsigned char *dstBuf,
2052                                   int width, int pitch, int height,
2053                                   int pixelFormat)
2054 {
2055   static const char FUNCTION_NAME[] = "tj3DecodeYUVPlanes8";
2056   JSAMPROW *row_pointer = NULL;
2057   JSAMPLE *_tmpbuf[MAX_COMPONENTS];
2058   JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
2059   int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
2060   JSAMPLE *ptr;
2061   jpeg_component_info *compptr;
2062   int (*old_read_markers) (j_decompress_ptr);
2063   void (*old_reset_marker_reader) (j_decompress_ptr);
2064
2065   GET_DINSTANCE(handle);
2066
2067   for (i = 0; i < MAX_COMPONENTS; i++) {
2068     tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;  inbuf[i] = NULL;
2069   }
2070
2071   if ((this->init & DECOMPRESS) == 0)
2072     THROW("Instance has not been initialized for decompression");
2073
2074   if (!srcPlanes || !srcPlanes[0] || dstBuf == NULL || width <= 0 ||
2075       pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2076     THROW("Invalid argument");
2077   if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
2078     THROW("Invalid argument");
2079
2080   if (setjmp(this->jerr.setjmp_buffer)) {
2081     /* If we get here, the JPEG code has signaled an error. */
2082     retval = -1;  goto bailout;
2083   }
2084
2085   if (this->subsamp == TJSAMP_UNKNOWN)
2086     THROW("TJPARAM_SUBSAMP must be specified");
2087   if (pixelFormat == TJPF_CMYK)
2088     THROW("Cannot decode YUV images into packed-pixel CMYK images.");
2089
2090   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2091   dinfo->image_width = width;
2092   dinfo->image_height = height;
2093
2094   dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
2095   dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
2096   dinfo->Se = DCTSIZE2 - 1;
2097   setDecodeDefaults(this, pixelFormat);
2098   old_read_markers = dinfo->marker->read_markers;
2099   dinfo->marker->read_markers = my_read_markers;
2100   old_reset_marker_reader = dinfo->marker->reset_marker_reader;
2101   dinfo->marker->reset_marker_reader = my_reset_marker_reader;
2102   jpeg_read_header(dinfo, TRUE);
2103   dinfo->marker->read_markers = old_read_markers;
2104   dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2105
2106   this->dinfo.out_color_space = pf2cs[pixelFormat];
2107   this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2108   dinfo->do_fancy_upsampling = FALSE;
2109   dinfo->Se = DCTSIZE2 - 1;
2110   jinit_master_decompress(dinfo);
2111   (*dinfo->upsample->start_pass) (dinfo);
2112
2113   pw0 = PAD(width, dinfo->max_h_samp_factor);
2114   ph0 = PAD(height, dinfo->max_v_samp_factor);
2115
2116   if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
2117
2118   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
2119     THROW("Memory allocation failure");
2120   for (i = 0; i < height; i++) {
2121     if (this->bottomUp)
2122       row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
2123     else
2124       row_pointer[i] = &dstBuf[i * (size_t)pitch];
2125   }
2126   if (height < ph0)
2127     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
2128
2129   for (i = 0; i < dinfo->num_components; i++) {
2130     compptr = &dinfo->comp_info[i];
2131     _tmpbuf[i] =
2132       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
2133                         compptr->v_samp_factor + 32);
2134     if (!_tmpbuf[i])
2135       THROW("Memory allocation failure");
2136     tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
2137     if (!tmpbuf[i])
2138       THROW("Memory allocation failure");
2139     for (row = 0; row < compptr->v_samp_factor; row++) {
2140       unsigned char *_tmpbuf_aligned =
2141         (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
2142
2143       tmpbuf[i][row] =
2144         &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
2145     }
2146     pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
2147     ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2148     inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
2149     if (!inbuf[i])
2150       THROW("Memory allocation failure");
2151     ptr = (JSAMPLE *)srcPlanes[i];
2152     for (row = 0; row < ph[i]; row++) {
2153       inbuf[i][row] = ptr;
2154       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2155     }
2156   }
2157
2158   if (setjmp(this->jerr.setjmp_buffer)) {
2159     /* If we get here, the JPEG code has signaled an error. */
2160     retval = -1;  goto bailout;
2161   }
2162
2163   for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
2164     JDIMENSION inrow = 0, outrow = 0;
2165
2166     for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
2167          i++, compptr++)
2168       jcopy_sample_rows(inbuf[i],
2169         row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
2170         compptr->v_samp_factor, pw[i]);
2171     (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
2172                                  dinfo->max_v_samp_factor, &row_pointer[row],
2173                                  &outrow, dinfo->max_v_samp_factor);
2174   }
2175   jpeg_abort_decompress(dinfo);
2176
2177 bailout:
2178   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2179   free(row_pointer);
2180   for (i = 0; i < MAX_COMPONENTS; i++) {
2181     free(tmpbuf[i]);
2182     free(_tmpbuf[i]);
2183     free(inbuf[i]);
2184   }
2185   if (this->jerr.warning) retval = -1;
2186   return retval;
2187 }
2188
2189 /* TurboJPEG 1.4+ */
2190 DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
2191                                 const unsigned char **srcPlanes,
2192                                 const int *strides, int subsamp,
2193                                 unsigned char *dstBuf, int width, int pitch,
2194                                 int height, int pixelFormat, int flags)
2195 {
2196   static const char FUNCTION_NAME[] = "tjDecodeYUVPlanes";
2197   int retval = 0;
2198
2199   GET_TJINSTANCE(handle, -1);
2200
2201   if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
2202     THROW("Invalid argument");
2203
2204   this->subsamp = subsamp;
2205   processFlags(handle, flags, DECOMPRESS);
2206
2207   return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2208                              height, pixelFormat);
2209
2210 bailout:
2211   return retval;
2212 }
2213
2214
2215 /* TurboJPEG 3+ */
2216 DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf,
2217                             int align, unsigned char *dstBuf, int width,
2218                             int pitch, int height, int pixelFormat)
2219 {
2220   static const char FUNCTION_NAME[] = "tj3DecodeYUV8";
2221   const unsigned char *srcPlanes[3];
2222   int pw0, ph0, strides[3], retval = -1;
2223
2224   GET_TJINSTANCE(handle, -1);
2225
2226   if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 ||
2227       height <= 0)
2228     THROW("Invalid argument");
2229
2230   if (this->subsamp == TJSAMP_UNKNOWN)
2231     THROW("TJPARAM_SUBSAMP must be specified");
2232
2233   pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2234   ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2235   srcPlanes[0] = srcBuf;
2236   strides[0] = PAD(pw0, align);
2237   if (this->subsamp == TJSAMP_GRAY) {
2238     strides[1] = strides[2] = 0;
2239     srcPlanes[1] = srcPlanes[2] = NULL;
2240   } else {
2241     int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2242     int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2243
2244     strides[1] = strides[2] = PAD(pw1, align);
2245     srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
2246     srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
2247   }
2248
2249   return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2250                              height, pixelFormat);
2251
2252 bailout:
2253   return retval;
2254 }
2255
2256 /* TurboJPEG 1.4+ */
2257 DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
2258                           int align, int subsamp, unsigned char *dstBuf,
2259                           int width, int pitch, int height, int pixelFormat,
2260                           int flags)
2261 {
2262   static const char FUNCTION_NAME[] = "tjDecodeYUV";
2263   int retval = -1;
2264
2265   GET_TJINSTANCE(handle, -1);
2266
2267   if (subsamp < 0 || subsamp >= TJ_NUMSAMP)
2268     THROW("Invalid argument");
2269
2270   this->subsamp = subsamp;
2271   processFlags(handle, flags, DECOMPRESS);
2272
2273   return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height,
2274                        pixelFormat);
2275
2276 bailout:
2277   return retval;
2278 }
2279
2280
2281 /* TurboJPEG 3+ */
2282 DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle,
2283                                         const unsigned char *jpegBuf,
2284                                         size_t jpegSize,
2285                                         unsigned char **dstPlanes,
2286                                         int *strides)
2287 {
2288   static const char FUNCTION_NAME[] = "tj3DecompressToYUVPlanes8";
2289   int i, row, retval = 0;
2290   int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
2291     tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
2292   JSAMPLE *_tmpbuf = NULL, *ptr;
2293   JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
2294   int dctsize;
2295   struct my_progress_mgr progress;
2296
2297   GET_DINSTANCE(handle);
2298
2299   for (i = 0; i < MAX_COMPONENTS; i++) {
2300     tmpbuf[i] = NULL;  outbuf[i] = NULL;
2301   }
2302
2303   if ((this->init & DECOMPRESS) == 0)
2304     THROW("Instance has not been initialized for decompression");
2305
2306   if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0])
2307     THROW("Invalid argument");
2308
2309   if (this->scanLimit) {
2310     memset(&progress, 0, sizeof(struct my_progress_mgr));
2311     progress.pub.progress_monitor = my_progress_monitor;
2312     progress.this = this;
2313     dinfo->progress = &progress.pub;
2314   } else
2315     dinfo->progress = NULL;
2316
2317   if (setjmp(this->jerr.setjmp_buffer)) {
2318     /* If we get here, the JPEG code has signaled an error. */
2319     retval = -1;  goto bailout;
2320   }
2321
2322   if (dinfo->global_state <= DSTATE_START) {
2323     jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2324     jpeg_read_header(dinfo, TRUE);
2325   }
2326   setDecompParameters(this);
2327   if (this->subsamp == TJSAMP_UNKNOWN)
2328     THROW("Could not determine subsampling level of JPEG image");
2329
2330   if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
2331     THROW("Invalid argument");
2332
2333   if (dinfo->num_components > 3)
2334     THROW("JPEG image must have 3 or fewer components");
2335
2336   dinfo->scale_num = this->scalingFactor.num;
2337   dinfo->scale_denom = this->scalingFactor.denom;
2338   jpeg_calc_output_dimensions(dinfo);
2339
2340   dctsize = DCTSIZE * this->scalingFactor.num / this->scalingFactor.denom;
2341
2342   for (i = 0; i < dinfo->num_components; i++) {
2343     jpeg_component_info *compptr = &dinfo->comp_info[i];
2344     int ih;
2345
2346     iw[i] = compptr->width_in_blocks * dctsize;
2347     ih = compptr->height_in_blocks * dctsize;
2348     pw[i] = tj3YUVPlaneWidth(i, dinfo->output_width, this->subsamp);
2349     ph[i] = tj3YUVPlaneHeight(i, dinfo->output_height, this->subsamp);
2350     if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
2351     th[i] = compptr->v_samp_factor * dctsize;
2352     tmpbufsize += iw[i] * th[i];
2353     if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
2354       THROW("Memory allocation failure");
2355     ptr = dstPlanes[i];
2356     for (row = 0; row < ph[i]; row++) {
2357       outbuf[i][row] = ptr;
2358       ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2359     }
2360   }
2361   if (usetmpbuf) {
2362     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
2363       THROW("Memory allocation failure");
2364     ptr = _tmpbuf;
2365     for (i = 0; i < dinfo->num_components; i++) {
2366       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
2367         THROW("Memory allocation failure");
2368       for (row = 0; row < th[i]; row++) {
2369         tmpbuf[i][row] = ptr;
2370         ptr += iw[i];
2371       }
2372     }
2373   }
2374
2375   if (setjmp(this->jerr.setjmp_buffer)) {
2376     /* If we get here, the JPEG code has signaled an error. */
2377     retval = -1;  goto bailout;
2378   }
2379
2380   dinfo->do_fancy_upsampling = !this->fastUpsample;
2381   dinfo->dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2382   dinfo->raw_data_out = TRUE;
2383
2384   jpeg_start_decompress(dinfo);
2385   for (row = 0; row < (int)dinfo->output_height;
2386        row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
2387     JSAMPARRAY yuvptr[MAX_COMPONENTS];
2388     int crow[MAX_COMPONENTS];
2389
2390     for (i = 0; i < dinfo->num_components; i++) {
2391       jpeg_component_info *compptr = &dinfo->comp_info[i];
2392
2393       if (this->subsamp == TJSAMP_420) {
2394         /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try
2395            to be clever and use the IDCT to perform upsampling on the U and V
2396            planes.  For instance, if the output image is to be scaled by 1/2
2397            relative to the JPEG image, then the scaling factor and upsampling
2398            effectively cancel each other, so a normal 8x8 IDCT can be used.
2399            However, this is not desirable when using the decompress-to-YUV
2400            functionality in TurboJPEG, since we want to output the U and V
2401            planes in their subsampled form.  Thus, we have to override some
2402            internal libjpeg parameters to force it to use the "scaled" IDCT
2403            functions on the U and V planes. */
2404         compptr->_DCT_scaled_size = dctsize;
2405         compptr->MCU_sample_width = tjMCUWidth[this->subsamp] *
2406           this->scalingFactor.num / this->scalingFactor.denom *
2407           compptr->v_samp_factor / dinfo->max_v_samp_factor;
2408         dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
2409       }
2410       crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2411       if (usetmpbuf) yuvptr[i] = tmpbuf[i];
2412       else yuvptr[i] = &outbuf[i][crow[i]];
2413     }
2414     jpeg_read_raw_data(dinfo, yuvptr,
2415                        dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
2416     if (usetmpbuf) {
2417       int j;
2418
2419       for (i = 0; i < dinfo->num_components; i++) {
2420         for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
2421           memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
2422         }
2423       }
2424     }
2425   }
2426   jpeg_finish_decompress(dinfo);
2427
2428 bailout:
2429   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2430   for (i = 0; i < MAX_COMPONENTS; i++) {
2431     free(tmpbuf[i]);
2432     free(outbuf[i]);
2433   }
2434   free(_tmpbuf);
2435   if (this->jerr.warning) retval = -1;
2436   return retval;
2437 }
2438
2439 /* TurboJPEG 1.4+ */
2440 DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
2441                                       const unsigned char *jpegBuf,
2442                                       unsigned long jpegSize,
2443                                       unsigned char **dstPlanes, int width,
2444                                       int *strides, int height, int flags)
2445 {
2446   static const char FUNCTION_NAME[] = "tjDecompressToYUVPlanes";
2447   int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2448
2449   GET_DINSTANCE(handle);
2450   if ((this->init & DECOMPRESS) == 0)
2451     THROW("Instance has not been initialized for decompression");
2452
2453   if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2454     THROW("Invalid argument");
2455
2456   if (setjmp(this->jerr.setjmp_buffer)) {
2457     /* If we get here, the JPEG code has signaled an error. */
2458     retval = -1;  goto bailout;
2459   }
2460
2461   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2462   jpeg_read_header(dinfo, TRUE);
2463   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2464   if (width == 0) width = jpegwidth;
2465   if (height == 0) height = jpegheight;
2466   for (i = 0; i < NUMSF; i++) {
2467     scaledw = TJSCALED(jpegwidth, sf[i]);
2468     scaledh = TJSCALED(jpegheight, sf[i]);
2469     if (scaledw <= width && scaledh <= height)
2470       break;
2471   }
2472   if (i >= NUMSF)
2473     THROW("Could not scale down to desired image dimensions");
2474
2475   processFlags(handle, flags, DECOMPRESS);
2476
2477   if (tj3SetScalingFactor(handle, sf[i]) == -1)
2478     return -1;
2479   return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2480                                    strides);
2481
2482 bailout:
2483   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2484   if (this->jerr.warning) retval = -1;
2485   return retval;
2486 }
2487
2488
2489 /* TurboJPEG 3+ */
2490 DLLEXPORT int tj3DecompressToYUV8(tjhandle handle,
2491                                   const unsigned char *jpegBuf,
2492                                   size_t jpegSize,
2493                                   unsigned char *dstBuf, int align)
2494 {
2495   static const char FUNCTION_NAME[] = "tj3DecompressToYUV8";
2496   unsigned char *dstPlanes[3];
2497   int pw0, ph0, strides[3], retval = -1;
2498   int width, height;
2499
2500   GET_DINSTANCE(handle);
2501
2502   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || align < 1 ||
2503       !IS_POW2(align))
2504     THROW("Invalid argument");
2505
2506   if (setjmp(this->jerr.setjmp_buffer)) {
2507     /* If we get here, the JPEG code has signaled an error. */
2508     retval = -1;  goto bailout;
2509   }
2510
2511   if (dinfo->global_state <= DSTATE_START) {
2512     jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2513     jpeg_read_header(dinfo, TRUE);
2514   }
2515   setDecompParameters(this);
2516   if (this->subsamp == TJSAMP_UNKNOWN)
2517     THROW("Could not determine subsampling level of JPEG image");
2518
2519   width = TJSCALED(dinfo->image_width, this->scalingFactor);
2520   height = TJSCALED(dinfo->image_height, this->scalingFactor);
2521
2522   pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2523   ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2524   dstPlanes[0] = dstBuf;
2525   strides[0] = PAD(pw0, align);
2526   if (this->subsamp == TJSAMP_GRAY) {
2527     strides[1] = strides[2] = 0;
2528     dstPlanes[1] = dstPlanes[2] = NULL;
2529   } else {
2530     int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2531     int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2532
2533     strides[1] = strides[2] = PAD(pw1, align);
2534     dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
2535     dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
2536   }
2537
2538   return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2539                                    strides);
2540
2541 bailout:
2542   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2543   if (this->jerr.warning) retval = -1;
2544   return retval;
2545 }
2546
2547 /* TurboJPEG 1.4+ */
2548 DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
2549                                  unsigned long jpegSize, unsigned char *dstBuf,
2550                                  int width, int align, int height, int flags)
2551 {
2552   static const char FUNCTION_NAME[] = "tjDecompressToYUV2";
2553   int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2554
2555   GET_DINSTANCE(handle);
2556   if ((this->init & DECOMPRESS) == 0)
2557     THROW("Instance has not been initialized for decompression");
2558
2559   if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2560     THROW("Invalid argument");
2561
2562   if (setjmp(this->jerr.setjmp_buffer)) {
2563     /* If we get here, the JPEG code has signaled an error. */
2564     retval = -1;  goto bailout;
2565   }
2566
2567   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2568   jpeg_read_header(dinfo, TRUE);
2569   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2570   if (width == 0) width = jpegwidth;
2571   if (height == 0) height = jpegheight;
2572   for (i = 0; i < NUMSF; i++) {
2573     scaledw = TJSCALED(jpegwidth, sf[i]);
2574     scaledh = TJSCALED(jpegheight, sf[i]);
2575     if (scaledw <= width && scaledh <= height)
2576       break;
2577   }
2578   if (i >= NUMSF)
2579     THROW("Could not scale down to desired image dimensions");
2580
2581   width = scaledw;  height = scaledh;
2582
2583   processFlags(handle, flags, DECOMPRESS);
2584
2585   if (tj3SetScalingFactor(handle, sf[i]) == -1)
2586     return -1;
2587   return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align);
2588
2589 bailout:
2590   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2591   if (this->jerr.warning) retval = -1;
2592   return retval;
2593 }
2594
2595 /* TurboJPEG 1.1+ */
2596 DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
2597                                 unsigned long jpegSize, unsigned char *dstBuf,
2598                                 int flags)
2599 {
2600   return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
2601 }
2602
2603
2604 /******************************** Transformer ********************************/
2605
2606 /* TurboJPEG 1.2+ */
2607 DLLEXPORT tjhandle tjInitTransform(void)
2608 {
2609   return tj3Init(TJINIT_TRANSFORM);
2610 }
2611
2612
2613 /* TurboJPEG 3+ */
2614 DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
2615                            size_t jpegSize, int n, unsigned char **dstBufs,
2616                            size_t *dstSizes, const tjtransform *t)
2617 {
2618   static const char FUNCTION_NAME[] = "tj3Transform";
2619   jpeg_transform_info *xinfo = NULL;
2620   jvirt_barray_ptr *srccoefs, *dstcoefs;
2621   int retval = 0, i, saveMarkers = 0;
2622   boolean alloc = TRUE;
2623   struct my_progress_mgr progress;
2624
2625   GET_INSTANCE(handle);
2626   if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2627     THROW("Instance has not been initialized for transformation");
2628
2629   if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
2630       dstSizes == NULL || t == NULL)
2631     THROW("Invalid argument");
2632
2633   if (this->scanLimit) {
2634     memset(&progress, 0, sizeof(struct my_progress_mgr));
2635     progress.pub.progress_monitor = my_progress_monitor;
2636     progress.this = this;
2637     dinfo->progress = &progress.pub;
2638   } else
2639     dinfo->progress = NULL;
2640
2641   if ((xinfo =
2642        (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
2643     THROW("Memory allocation failure");
2644   memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
2645
2646   if (setjmp(this->jerr.setjmp_buffer)) {
2647     /* If we get here, the JPEG code has signaled an error. */
2648     retval = -1;  goto bailout;
2649   }
2650
2651   if (dinfo->global_state <= DSTATE_START)
2652     jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2653
2654   for (i = 0; i < n; i++) {
2655     xinfo[i].transform = xformtypes[t[i].op];
2656     xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
2657     xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
2658     xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
2659     xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
2660     if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
2661     else xinfo[i].slow_hflip = 0;
2662
2663     if (xinfo[i].crop) {
2664       xinfo[i].crop_xoffset = t[i].r.x;  xinfo[i].crop_xoffset_set = JCROP_POS;
2665       xinfo[i].crop_yoffset = t[i].r.y;  xinfo[i].crop_yoffset_set = JCROP_POS;
2666       if (t[i].r.w != 0) {
2667         xinfo[i].crop_width = t[i].r.w;  xinfo[i].crop_width_set = JCROP_POS;
2668       } else
2669         xinfo[i].crop_width = JCROP_UNSET;
2670       if (t[i].r.h != 0) {
2671         xinfo[i].crop_height = t[i].r.h;  xinfo[i].crop_height_set = JCROP_POS;
2672       } else
2673         xinfo[i].crop_height = JCROP_UNSET;
2674     }
2675     if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
2676   }
2677
2678   jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
2679   if (dinfo->global_state <= DSTATE_START)
2680     jpeg_read_header(dinfo, TRUE);
2681   this->subsamp = getSubsamp(&this->dinfo);
2682
2683   for (i = 0; i < n; i++) {
2684     if (!jtransform_request_workspace(dinfo, &xinfo[i]))
2685       THROW("Transform is not perfect");
2686
2687     if (xinfo[i].crop) {
2688       if (this->subsamp == TJSAMP_UNKNOWN)
2689         THROW("Could not determine subsampling level of JPEG image");
2690       if ((t[i].r.x % tjMCUWidth[this->subsamp]) != 0 ||
2691           (t[i].r.y % tjMCUHeight[this->subsamp]) != 0)
2692         THROWI("To crop this JPEG image, x must be a multiple of %d\n"
2693                "and y must be a multiple of %d.", tjMCUWidth[this->subsamp],
2694                tjMCUHeight[this->subsamp]);
2695     }
2696   }
2697
2698   srccoefs = jpeg_read_coefficients(dinfo);
2699
2700   for (i = 0; i < n; i++) {
2701     int w, h;
2702
2703     if (!xinfo[i].crop) {
2704       w = dinfo->image_width;  h = dinfo->image_height;
2705       if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
2706           t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
2707         w = dinfo->image_height;  h = dinfo->image_width;
2708       }
2709     } else {
2710       w = xinfo[i].crop_width;  h = xinfo[i].crop_height;
2711     }
2712     if (this->noRealloc) {
2713       alloc = FALSE;  dstSizes[i] = tj3JPEGBufSize(w, h, this->subsamp);
2714     }
2715     if (!(t[i].options & TJXOPT_NOOUTPUT))
2716       jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
2717     jpeg_copy_critical_parameters(dinfo, cinfo);
2718     dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
2719     if (this->optimize || t[i].options & TJXOPT_OPTIMIZE)
2720       cinfo->optimize_coding = TRUE;
2721 #ifdef C_PROGRESSIVE_SUPPORTED
2722     if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE)
2723       jpeg_simple_progression(cinfo);
2724 #endif
2725     if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) {
2726       cinfo->arith_code = TRUE;
2727       cinfo->optimize_coding = FALSE;
2728     }
2729     if (!(t[i].options & TJXOPT_NOOUTPUT)) {
2730       jpeg_write_coefficients(cinfo, dstcoefs);
2731       jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
2732                                           JCOPYOPT_NONE : JCOPYOPT_ALL);
2733     } else
2734       jinit_c_master_control(cinfo, TRUE);
2735     jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
2736     if (t[i].customFilter) {
2737       int ci, y;
2738       JDIMENSION by;
2739
2740       for (ci = 0; ci < cinfo->num_components; ci++) {
2741         jpeg_component_info *compptr = &cinfo->comp_info[ci];
2742         tjregion arrayRegion = { 0, 0, 0, 0 };
2743         tjregion planeRegion = { 0, 0, 0, 0 };
2744
2745         arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
2746         arrayRegion.h = DCTSIZE;
2747         planeRegion.w = compptr->width_in_blocks * DCTSIZE;
2748         planeRegion.h = compptr->height_in_blocks * DCTSIZE;
2749
2750         for (by = 0; by < compptr->height_in_blocks;
2751              by += compptr->v_samp_factor) {
2752           JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
2753             ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
2754              TRUE);
2755
2756           for (y = 0; y < compptr->v_samp_factor; y++) {
2757             if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
2758                                   i, (tjtransform *)&t[i]) == -1)
2759               THROW("Error in custom filter");
2760             arrayRegion.y += DCTSIZE;
2761           }
2762         }
2763       }
2764     }
2765     if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
2766   }
2767
2768   jpeg_finish_decompress(dinfo);
2769
2770 bailout:
2771   if (cinfo->global_state > CSTATE_START) {
2772     if (alloc) (*cinfo->dest->term_destination) (cinfo);
2773     jpeg_abort_compress(cinfo);
2774   }
2775   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2776   free(xinfo);
2777   if (this->jerr.warning) retval = -1;
2778   return retval;
2779 }
2780
2781 /* TurboJPEG 1.2+ */
2782 DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
2783                           unsigned long jpegSize, int n,
2784                           unsigned char **dstBufs, unsigned long *dstSizes,
2785                           tjtransform *t, int flags)
2786 {
2787   static const char FUNCTION_NAME[] = "tjTransform";
2788   int i, retval = 0;
2789   size_t *sizes = NULL;
2790
2791   GET_DINSTANCE(handle);
2792   if ((this->init & DECOMPRESS) == 0)
2793     THROW("Instance has not been initialized for decompression");
2794
2795   if (n < 1 || dstSizes == NULL)
2796     THROW("Invalid argument");
2797
2798   if (setjmp(this->jerr.setjmp_buffer)) {
2799     /* If we get here, the JPEG code has signaled an error. */
2800     retval = -1;  goto bailout;
2801   }
2802
2803   jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2804   jpeg_read_header(dinfo, TRUE);
2805   if (getSubsamp(dinfo) == TJSAMP_UNKNOWN)
2806     THROW("Could not determine subsampling level of JPEG image");
2807   processFlags(handle, flags, COMPRESS);
2808
2809   if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL)
2810     THROW("Memory allocation failure");
2811   for (i = 0; i < n; i++)
2812     sizes[i] = (size_t)dstSizes[i];
2813   retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes,
2814                         t);
2815   for (i = 0; i < n; i++)
2816     dstSizes[i] = (unsigned long)sizes[i];
2817
2818 bailout:
2819   free(sizes);
2820   return retval;
2821 }
2822
2823
2824 /*************************** Packed-Pixel Image I/O **************************/
2825
2826 /* tj3LoadImage*() is implemented in turbojpeg-mp.c */
2827
2828 /* TurboJPEG 2.0+ */
2829 DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
2830                                      int align, int *height,
2831                                      int *pixelFormat, int flags)
2832 {
2833   tjhandle handle = NULL;
2834   unsigned char *dstBuf = NULL;
2835
2836   if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL;
2837
2838   processFlags(handle, flags, COMPRESS);
2839
2840   dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat);
2841
2842   tj3Destroy(handle);
2843   return dstBuf;
2844 }
2845
2846
2847 /* tj3SaveImage*() is implemented in turbojpeg-mp.c */
2848
2849 /* TurboJPEG 2.0+ */
2850 DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
2851                           int width, int pitch, int height, int pixelFormat,
2852                           int flags)
2853 {
2854   tjhandle handle = NULL;
2855   int retval = -1;
2856
2857   if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1;
2858
2859   processFlags(handle, flags, DECOMPRESS);
2860
2861   retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height,
2862                          pixelFormat);
2863
2864   tj3Destroy(handle);
2865   return retval;
2866 }