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