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