Coverage Report

Created: 2026-10-08 08:01

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