Coverage Report

Created: 2026-10-28 06:06

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