Coverage Report

Created: 2025-07-01 06:26

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