Coverage Report

Created: 2025-11-24 06:36

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