Coverage Report

Created: 2026-07-30 06:42

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