Coverage Report

Created: 2026-05-11 06:55

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