Coverage Report

Created: 2026-08-31 07:18

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
64.5k
#define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
58
0
#define IS_POW2(x)  (((x) & (x - 1)) == 0)
59
60
61
/* Error handling (based on example in example.c) */
62
63
static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
64
65
struct my_error_mgr {
66
  struct jpeg_error_mgr pub;
67
  jmp_buf setjmp_buffer;
68
  void (*emit_message) (j_common_ptr, int);
69
  boolean warning, stopOnWarning;
70
  tjhandle tjHandle;
71
};
72
typedef struct my_error_mgr *my_error_ptr;
73
74
#define JMESSAGE(code, string)  string,
75
static const char *turbojpeg_message_table[] = {
76
#include "cderror.h"
77
  NULL
78
};
79
80
static void my_error_exit(j_common_ptr cinfo)
81
36.2k
{
82
36.2k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
83
84
36.2k
  (*cinfo->err->output_message) (cinfo);
85
36.2k
  longjmp(myerr->setjmp_buffer, 1);
86
36.2k
}
87
88
static void my_emit_message(j_common_ptr cinfo, int msg_level)
89
34.5k
{
90
34.5k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
91
92
34.5k
  myerr->emit_message(cinfo, msg_level);
93
34.5k
  if (msg_level < 0) {
94
0
    myerr->warning = TRUE;
95
0
    if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
96
0
  }
97
34.5k
}
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
36.2k
{
150
36.2k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
151
36.2k
  tjinstance *this = (tjinstance *)myerr->tjHandle;
152
153
36.2k
  if (this) {
154
36.2k
    this->isInstanceError = TRUE;
155
36.2k
    (*cinfo->err->format_message) (cinfo, this->errStr);
156
36.2k
  } else
157
0
    (*cinfo->err->format_message) (cinfo, errStr);
158
36.2k
}
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
693
  SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
256
0
  retval = rv;  goto bailout; \
257
693
}
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
693
  SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
281
693
  this->isInstanceError = TRUE;  THROWG(m, rv) \
282
693
}
283
693
#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
21.7k
#define CATCH_LIBJPEG(this) { \
294
21.7k
  if (setjmp(this->jerr.setjmp_buffer)) { \
295
3
    /* If we get here, the JPEG code has signaled an error. */ \
296
3
    retval = -1;  goto bailout; \
297
3
  } \
298
21.7k
}
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
117k
#define CATCH_LIBJPEG2() { \
304
117k
  if (setjmp(this2->jerr.setjmp_buffer)) { \
305
36.2k
    memcpy(this->errStr, this2->errStr, JMSG_LENGTH_MAX); \
306
36.2k
    this->jerr.warning = this2->jerr.warning; \
307
36.2k
    this->isInstanceError = this2->isInstanceError; \
308
36.2k
    retval = -1;  goto bailout; \
309
36.2k
  } \
310
117k
}
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
21.7k
  tjinstance *this = (tjinstance *)handle; \
327
21.7k
  j_compress_ptr cinfo = NULL; \
328
21.7k
  \
329
21.7k
  if (!this) { \
330
0
    SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
331
0
    return -1; \
332
0
  } \
333
21.7k
  cinfo = &this->cinfo; \
334
21.7k
  this->jerr.warning = FALSE; \
335
21.7k
  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
500k
  tjinstance *this = (tjinstance *)handle; \
351
500k
  \
352
500k
  if (!this) { \
353
0
    SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
354
0
    return errorReturn; \
355
0
  } \
356
500k
  this->jerr.warning = FALSE; \
357
500k
  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
21.7k
{
380
21.7k
  int colorspace = yuv ? TJCS_DEFAULT : this->colorspace;
381
382
21.7k
  this->cinfo.in_color_space = pf2cs[pixelFormat];
383
21.7k
  this->cinfo.input_components = tjPixelSize[pixelFormat];
384
21.7k
  jpeg_set_defaults(&this->cinfo);
385
386
21.7k
  this->cinfo.restart_interval = this->restartIntervalBlocks;
387
21.7k
  this->cinfo.restart_in_rows = this->restartIntervalRows;
388
21.7k
  this->cinfo.X_density = (UINT16)this->xDensity;
389
21.7k
  this->cinfo.Y_density = (UINT16)this->yDensity;
390
21.7k
  this->cinfo.density_unit = (UINT8)this->densityUnits;
391
21.7k
  this->cinfo.mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
392
393
21.7k
  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
21.7k
  jpeg_set_quality(&this->cinfo, this->quality, TRUE);
401
21.7k
  this->cinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
402
403
21.7k
  switch (colorspace) {
404
2.99k
  case TJCS_RGB:
405
2.99k
    jpeg_set_colorspace(&this->cinfo, JCS_RGB);  break;
406
3.08k
  case TJCS_YCbCr:
407
3.08k
    jpeg_set_colorspace(&this->cinfo, JCS_YCbCr);  break;
408
2.99k
  case TJCS_GRAY:
409
2.99k
    jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE);  break;
410
0
  case TJCS_CMYK:
411
0
    jpeg_set_colorspace(&this->cinfo, JCS_CMYK);  break;
412
2.99k
  case TJCS_YCCK:
413
2.99k
    jpeg_set_colorspace(&this->cinfo, JCS_YCCK);  break;
414
9.71k
  default:
415
9.71k
    if (this->subsamp == TJSAMP_GRAY)
416
3.63k
      jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE);
417
6.07k
    else if (pixelFormat == TJPF_CMYK)
418
0
      jpeg_set_colorspace(&this->cinfo, JCS_YCCK);
419
6.07k
    else
420
6.07k
      jpeg_set_colorspace(&this->cinfo, JCS_YCbCr);
421
21.7k
  }
422
423
21.7k
  if (this->cinfo.data_precision == 8)
424
0
    this->cinfo.optimize_coding = this->optimize;
425
21.7k
#ifdef C_PROGRESSIVE_SUPPORTED
426
21.7k
  if (this->progressive) jpeg_simple_progression(&this->cinfo);
427
21.7k
#endif
428
21.7k
  this->cinfo.arith_code = this->arithmetic;
429
430
21.7k
  this->cinfo.comp_info[0].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
431
21.7k
  this->cinfo.comp_info[1].h_samp_factor = 1;
432
21.7k
  this->cinfo.comp_info[2].h_samp_factor = 1;
433
21.7k
  if (this->cinfo.num_components > 3)
434
2.99k
    this->cinfo.comp_info[3].h_samp_factor = tjMCUWidth[this->subsamp] / 8;
435
21.7k
  this->cinfo.comp_info[0].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
436
21.7k
  this->cinfo.comp_info[1].v_samp_factor = 1;
437
21.7k
  this->cinfo.comp_info[2].v_samp_factor = 1;
438
21.7k
  if (this->cinfo.num_components > 3)
439
2.99k
    this->cinfo.comp_info[3].v_samp_factor = tjMCUHeight[this->subsamp] / 8;
440
21.7k
}
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
55.6k
{
597
55.6k
  static const char FUNCTION_NAME[] = "tj3Init";
598
55.6k
  tjinstance *this = NULL;
599
55.6k
  tjhandle retval = NULL;
600
601
55.6k
  if (initType < 0 || initType >= TJ_NUMINIT || apiVersion < 1000000 ||
602
55.6k
      apiVersion > 999999999)
603
55.6k
    THROWG("Invalid argument", NULL);
604
605
55.6k
  if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL)
606
55.6k
    THROWG("Memory allocation failure", NULL);
607
55.6k
  memset(this, 0, sizeof(tjinstance));
608
55.6k
  SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
609
610
55.6k
  this->quality = -1;
611
55.6k
  this->subsamp = TJSAMP_UNKNOWN;
612
55.6k
  this->jpegWidth = -1;
613
55.6k
  this->jpegHeight = -1;
614
55.6k
  this->precision = 8;
615
55.6k
  this->colorspace = TJCS_DEFAULT;
616
55.6k
  this->losslessPSV = 1;
617
55.6k
  this->xDensity = 1;
618
55.6k
  this->yDensity = 1;
619
55.6k
  this->scalingFactor = TJUNSCALED;
620
55.6k
  this->saveMarkers = 2;
621
622
55.6k
  this->apiVersion = apiVersion;
623
55.6k
  this->numSamp = apiVersion >= 3002000 ? TJ_NUMSAMP : 7;
624
625
55.6k
  switch (initType) {
626
55.6k
  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
55.6k
  }
634
635
0
bailout:
636
0
  return retval;
637
55.6k
}
638
639
/* TurboJPEG 3.0+ */
640
DLLEXPORT tjhandle tj3Init(int initType)
641
48.6k
{
642
48.6k
  return tj3InitVersion(initType, 3001000);
643
48.6k
}
644
645
646
/* TurboJPEG 3.0+ */
647
DLLEXPORT void tj3Destroy(tjhandle handle)
648
55.6k
{
649
55.6k
  tjinstance *this = (tjinstance *)handle;
650
55.6k
  j_compress_ptr cinfo = NULL;
651
55.6k
  j_decompress_ptr dinfo = NULL;
652
653
55.6k
  if (!this) return;
654
655
55.6k
  cinfo = &this->cinfo;  dinfo = &this->dinfo;
656
55.6k
  this->jerr.warning = FALSE;
657
55.6k
  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
55.6k
  if (setjmp(this->jerr.setjmp_buffer)) goto destroy_decompress;
662
55.6k
  if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
663
55.6k
destroy_decompress:
664
55.6k
  if (setjmp(this->jerr.setjmp_buffer)) goto bailout;
665
55.6k
  if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
666
55.6k
bailout:
667
55.6k
  free(this->iccBuf);
668
55.6k
  free(this->decompICCBuf);
669
55.6k
  free(this);
670
55.6k
}
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
189k
#define SET_PARAM(field, minValue, maxValue) { \
731
189k
  if (value < minValue || (maxValue > 0 && value > maxValue)) \
732
189k
    THROW("Parameter value out of range"); \
733
189k
  this->field = value; \
734
189k
}
735
736
243k
#define SET_BOOL_PARAM(field) { \
737
243k
  if (value < 0 || value > 1) \
738
243k
    THROW("Parameter value out of range"); \
739
243k
  this->field = (boolean)value; \
740
243k
}
741
742
/* TurboJPEG 3.0+ */
743
DLLEXPORT int tj3Set(tjhandle handle, int param, int value)
744
433k
{
745
433k
  static const char FUNCTION_NAME[] = "tj3Set";
746
433k
  int retval = 0;
747
748
433k
  GET_TJINSTANCE(handle, -1);
749
750
433k
  switch (param) {
751
0
  case TJPARAM_STOPONWARNING:
752
0
    SET_BOOL_PARAM(jerr.stopOnWarning);
753
0
    break;
754
48.6k
  case TJPARAM_BOTTOMUP:
755
48.6k
    SET_BOOL_PARAM(bottomUp);
756
48.6k
    break;
757
48.6k
  case TJPARAM_NOREALLOC:
758
48.6k
    if (!(this->init & COMPRESS))
759
48.6k
      THROW("TJPARAM_NOREALLOC is not applicable to decompression instances.");
760
48.6k
    SET_BOOL_PARAM(noRealloc);
761
48.6k
    break;
762
21.7k
  case TJPARAM_QUALITY:
763
21.7k
    if (!(this->init & COMPRESS))
764
21.7k
      THROW("TJPARAM_QUALITY is not applicable to decompression instances.");
765
21.7k
    SET_PARAM(quality, 1, 100);
766
21.7k
    break;
767
21.7k
  case TJPARAM_SUBSAMP:
768
21.7k
    SET_PARAM(subsamp, 0, this->numSamp - 1);
769
21.7k
    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
48.6k
  case TJPARAM_COLORSPACE:
784
48.6k
    if (!(this->init & COMPRESS))
785
48.6k
      THROW("TJPARAM_COLORSPACE is read-only in decompression instances.");
786
48.6k
    SET_PARAM(colorspace, TJCS_DEFAULT, TJ_NUMCS - 1);
787
48.6k
    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
48.6k
  case TJPARAM_FASTDCT:
794
48.6k
    SET_BOOL_PARAM(fastDCT);
795
48.6k
    break;
796
0
  case TJPARAM_OPTIMIZE:
797
0
    if (!(this->init & COMPRESS))
798
0
      THROW("TJPARAM_OPTIMIZE is not applicable to decompression instances.");
799
0
    SET_BOOL_PARAM(optimize);
800
0
    break;
801
48.6k
  case TJPARAM_PROGRESSIVE:
802
48.6k
    if (!(this->init & COMPRESS))
803
48.6k
      THROW("TJPARAM_PROGRESSIVE is read-only in decompression instances.");
804
48.6k
    SET_BOOL_PARAM(progressive);
805
48.6k
    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
48.6k
  case TJPARAM_ARITHMETIC:
812
48.6k
    if (!(this->init & COMPRESS))
813
48.6k
      THROW("TJPARAM_ARITHMETIC is read-only in decompression instances.");
814
48.6k
    SET_BOOL_PARAM(arithmetic);
815
48.6k
    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
48.6k
  case TJPARAM_RESTARTROWS:
838
48.6k
    if (!(this->init & COMPRESS))
839
48.6k
      THROW("TJPARAM_RESTARTROWS is not applicable to decompression instances.");
840
48.6k
    SET_PARAM(restartIntervalRows, 0, 65535);
841
48.6k
    if (value != 0) this->restartIntervalBlocks = 0;
842
48.6k
    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
48.6k
  case TJPARAM_MAXPIXELS:
862
48.6k
    SET_PARAM(maxPixels, 0, -1);
863
48.6k
    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
433k
  }
870
871
433k
bailout:
872
433k
  return retval;
873
433k
}
874
875
876
/* TurboJPEG 3.0+ */
877
DLLEXPORT int tj3Get(tjhandle handle, int param)
878
21.7k
{
879
21.7k
  tjinstance *this = (tjinstance *)handle;
880
21.7k
  if (!this) return -1;
881
882
21.7k
  switch (param) {
883
0
  case TJPARAM_STOPONWARNING:
884
0
    return this->jerr.stopOnWarning;
885
0
  case TJPARAM_BOTTOMUP:
886
0
    return this->bottomUp;
887
21.7k
  case TJPARAM_NOREALLOC:
888
21.7k
    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
21.7k
  }
936
937
0
  return -1;
938
21.7k
}
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
3.08k
{
949
3.08k
  return MALLOC(bytes);
950
3.08k
}
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
57.4k
{
962
57.4k
  free(buf);
963
57.4k
}
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
21.7k
{
975
21.7k
  static const char FUNCTION_NAME[] = "tj3JPEGBufSize";
976
21.7k
  unsigned long long retval = 0;
977
21.7k
  int mcuw, mcuh, chromasf;
978
979
21.7k
  if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN ||
980
21.7k
      jpegSubsamp >= TJ_NUMSAMP)
981
21.7k
    THROWG("Invalid argument", 0);
982
983
21.7k
  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
21.7k
  mcuw = tjMCUWidth[jpegSubsamp];
990
21.7k
  mcuh = tjMCUHeight[jpegSubsamp];
991
21.7k
  chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
992
21.7k
  retval = (unsigned long long)PAD(width, mcuw) * PAD(height, mcuh) *
993
21.7k
           (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
21.7k
bailout:
1000
21.7k
  return (size_t)retval;
1001
21.7k
}
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
55.6k
{
1201
55.6k
  static unsigned char buffer[1];
1202
55.6k
  unsigned char *buf = buffer;
1203
55.6k
  size_t size = 1;
1204
1205
  /* This is also straight out of example.c */
1206
55.6k
  this->cinfo.err = jpeg_std_error(&this->jerr.pub);
1207
55.6k
  this->jerr.pub.error_exit = my_error_exit;
1208
55.6k
  this->jerr.pub.output_message = my_output_message;
1209
55.6k
  this->jerr.emit_message = this->jerr.pub.emit_message;
1210
55.6k
  this->jerr.pub.emit_message = my_emit_message;
1211
55.6k
  this->jerr.pub.addon_message_table = turbojpeg_message_table;
1212
55.6k
  this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1213
55.6k
  this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1214
55.6k
  this->jerr.tjHandle = (tjhandle)this;
1215
1216
55.6k
  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
55.6k
  jpeg_create_compress(&this->cinfo);
1223
  /* Make an initial call so it will create the destination manager */
1224
55.6k
  jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
1225
1226
55.6k
  this->init |= COMPRESS;
1227
55.6k
  return (tjhandle)this;
1228
55.6k
}
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
18.3k
{
1241
18.3k
  static const char FUNCTION_NAME[] = "tj3SetICCProfile";
1242
18.3k
  int retval = 0;
1243
1244
18.3k
  GET_TJINSTANCE(handle, -1)
1245
18.3k
  if ((this->init & COMPRESS) == 0)
1246
18.3k
    THROW("Instance has not been initialized for compression");
1247
1248
18.3k
  if (iccBuf == this->iccBuf && iccSize == this->iccSize)
1249
0
    return 0;
1250
1251
18.3k
  free(this->iccBuf);
1252
18.3k
  this->iccBuf = NULL;
1253
18.3k
  this->iccSize = 0;
1254
18.3k
  if (iccBuf && iccSize) {
1255
18.3k
    if ((this->iccBuf = (unsigned char *)malloc(iccSize)) == NULL)
1256
18.3k
      THROW("Memory allocation failure");
1257
18.3k
    memcpy(this->iccBuf, iccBuf, iccSize);
1258
18.3k
    this->iccSize = iccSize;
1259
18.3k
  }
1260
1261
18.3k
bailout:
1262
18.3k
  return retval;
1263
18.3k
}
1264
1265
1266
/* tj3Compress*() is implemented in turbojpeg-mp.c */
1267
0
#define BITS_IN_JSAMPLE  8
1268
#include "turbojpeg-mp.c"
1269
#undef BITS_IN_JSAMPLE
1270
118k
#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
  /* Referring to the comment below, this function will never use the "fast"
2298
     IDCT algorithm with decompression scaling, so if the scaling factor is not
2299
     1/1, we need to explicitly set dinfo->dct_method = JDCT_ISLOW so that the
2300
     "slow" IDCT tables will be assigned to all components.  Otherwise, when
2301
     libjpeg tries to be clever, it will assign the "fast" IDCT tables to the
2302
     chrominance components, and those tables don't work properly with the
2303
     scaled IDCT algorithms. */
2304
0
  dinfo->dct_method =
2305
0
    (dctsize == DCTSIZE && this->fastDCT) ? JDCT_FASTEST : JDCT_ISLOW;
2306
0
  dinfo->raw_data_out = TRUE;
2307
2308
0
  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2309
2310
0
  jpeg_start_decompress(dinfo);
2311
0
  for (row = 0; row < (int)dinfo->output_height;
2312
0
       row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
2313
0
    JSAMPARRAY yuvptr[MAX_COMPONENTS];
2314
0
    int crow[MAX_COMPONENTS];
2315
2316
0
    for (i = 0; i < dinfo->num_components; i++) {
2317
0
      jpeg_component_info *compptr = &dinfo->comp_info[i];
2318
2319
0
      if (this->subsamp == TJSAMP_420 || this->subsamp == TJSAMP_410 ||
2320
0
          this->subsamp == TJSAMP_24) {
2321
        /* When 4:2:0, 4:1:0, or 2:4 subsampling is used with IDCT scaling,
2322
           libjpeg will try to be clever and use the IDCT to perform upsampling
2323
           on the U and V planes.  For instance, if the output image is to be
2324
           scaled by 1/2 relative to the JPEG image, then the scaling factor
2325
           and upsampling effectively cancel each other, so a normal 8x8 IDCT
2326
           can be used. However, this is not desirable when using the
2327
           decompress-to-YUV functionality in TurboJPEG, since we want to
2328
           output the U and V planes in their subsampled form.  Thus, we have
2329
           to override some internal libjpeg parameters to force it to use the
2330
           "scaled" IDCT functions on the U and V planes. */
2331
0
        compptr->_DCT_scaled_size = dctsize;
2332
0
        compptr->MCU_sample_width = tjMCUWidth[this->subsamp] *
2333
0
          this->scalingFactor.num / this->scalingFactor.denom *
2334
0
          compptr->h_samp_factor / dinfo->max_h_samp_factor;
2335
0
        dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
2336
0
      }
2337
0
      crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2338
0
      if (usetmpbuf) yuvptr[i] = tmpbuf[i];
2339
0
      else yuvptr[i] = &outbuf[i][crow[i]];
2340
0
    }
2341
0
    jpeg_read_raw_data(dinfo, yuvptr,
2342
0
                       dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
2343
0
    if (usetmpbuf) {
2344
0
      int j;
2345
2346
0
      for (i = 0; i < dinfo->num_components; i++) {
2347
0
        for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
2348
0
          memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
2349
0
        }
2350
0
      }
2351
0
    }
2352
0
  }
2353
0
  jpeg_finish_decompress(dinfo);
2354
2355
0
bailout:
2356
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2357
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2358
0
    free(tmpbuf[i]);
2359
0
    free(outbuf[i]);
2360
0
  }
2361
0
  free(_tmpbuf);
2362
0
  if (this->jerr.warning) retval = -1;
2363
0
  return retval;
2364
0
}
2365
2366
/* TurboJPEG 1.4+ */
2367
DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
2368
                                      const unsigned char *jpegBuf,
2369
                                      unsigned long jpegSize,
2370
                                      unsigned char **dstPlanes, int width,
2371
                                      int *strides, int height, int flags)
2372
0
{
2373
0
  static const char FUNCTION_NAME[] = "tjDecompressToYUVPlanes";
2374
0
  int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2375
2376
0
  GET_DINSTANCE(handle);
2377
0
  if ((this->init & DECOMPRESS) == 0)
2378
0
    THROW("Instance has not been initialized for decompression");
2379
2380
0
  if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2381
0
    THROW("Invalid argument");
2382
2383
0
  CATCH_LIBJPEG(this);
2384
2385
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2386
0
  jpeg_read_header(dinfo, TRUE);
2387
0
  jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2388
0
  if (width == 0) width = jpegwidth;
2389
0
  if (height == 0) height = jpegheight;
2390
0
  for (i = 0; i < NUMSF; i++) {
2391
0
    scaledw = TJSCALED(jpegwidth, sf[i]);
2392
0
    scaledh = TJSCALED(jpegheight, sf[i]);
2393
0
    if (scaledw <= width && scaledh <= height)
2394
0
      break;
2395
0
  }
2396
0
  if (i >= NUMSF)
2397
0
    THROW("Could not scale down to desired image dimensions");
2398
2399
0
  processFlags(handle, flags, DECOMPRESS);
2400
2401
0
  if (tj3SetScalingFactor(handle, sf[i]) == -1)
2402
0
    return -1;
2403
0
  return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2404
0
                                   strides);
2405
2406
0
bailout:
2407
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2408
0
  if (this->jerr.warning) retval = -1;
2409
0
  return retval;
2410
0
}
2411
2412
2413
/* TurboJPEG 3.0+ */
2414
DLLEXPORT int tj3DecompressToYUV8(tjhandle handle,
2415
                                  const unsigned char *jpegBuf,
2416
                                  size_t jpegSize,
2417
                                  unsigned char *dstBuf, int align)
2418
0
{
2419
0
  static const char FUNCTION_NAME[] = "tj3DecompressToYUV8";
2420
0
  unsigned char *dstPlanes[3];
2421
0
  int pw0, ph0, strides[3], retval = -1;
2422
0
  int width, height;
2423
2424
0
  GET_DINSTANCE(handle);
2425
2426
0
  if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || align < 1 ||
2427
0
      !IS_POW2(align))
2428
0
    THROW("Invalid argument");
2429
2430
0
  CATCH_LIBJPEG(this);
2431
2432
0
  if (dinfo->global_state <= DSTATE_INHEADER) {
2433
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2434
0
    jpeg_read_header(dinfo, TRUE);
2435
0
  }
2436
0
  setDecompParameters(this);
2437
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2438
0
    THROW("Could not determine subsampling level of JPEG image");
2439
2440
0
  width = TJSCALED(dinfo->image_width, this->scalingFactor);
2441
0
  height = TJSCALED(dinfo->image_height, this->scalingFactor);
2442
2443
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2444
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2445
0
  dstPlanes[0] = dstBuf;
2446
0
  strides[0] = PAD(pw0, align);
2447
0
  if (this->subsamp == TJSAMP_GRAY) {
2448
0
    strides[1] = strides[2] = 0;
2449
0
    dstPlanes[1] = dstPlanes[2] = NULL;
2450
0
  } else {
2451
0
    int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2452
0
    int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2453
2454
0
    strides[1] = strides[2] = PAD(pw1, align);
2455
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
2456
0
        (unsigned long long)INT_MAX ||
2457
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
2458
0
        (unsigned long long)INT_MAX)
2459
0
      THROW("Image or row alignment is too large");
2460
0
    dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
2461
0
    dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
2462
0
  }
2463
2464
0
  return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2465
0
                                   strides);
2466
2467
0
bailout:
2468
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2469
0
  if (this->jerr.warning) retval = -1;
2470
0
  return retval;
2471
0
}
2472
2473
/* TurboJPEG 1.4+ */
2474
DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
2475
                                 unsigned long jpegSize, unsigned char *dstBuf,
2476
                                 int width, int align, int height, int flags)
2477
0
{
2478
0
  static const char FUNCTION_NAME[] = "tjDecompressToYUV2";
2479
0
  int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2480
2481
0
  GET_DINSTANCE(handle);
2482
0
  if ((this->init & DECOMPRESS) == 0)
2483
0
    THROW("Instance has not been initialized for decompression");
2484
2485
0
  if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2486
0
    THROW("Invalid argument");
2487
2488
0
  CATCH_LIBJPEG(this);
2489
2490
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2491
0
  jpeg_read_header(dinfo, TRUE);
2492
0
  jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2493
0
  if (width == 0) width = jpegwidth;
2494
0
  if (height == 0) height = jpegheight;
2495
0
  for (i = 0; i < NUMSF; i++) {
2496
0
    scaledw = TJSCALED(jpegwidth, sf[i]);
2497
0
    scaledh = TJSCALED(jpegheight, sf[i]);
2498
0
    if (scaledw <= width && scaledh <= height)
2499
0
      break;
2500
0
  }
2501
0
  if (i >= NUMSF)
2502
0
    THROW("Could not scale down to desired image dimensions");
2503
2504
0
  width = scaledw;  height = scaledh;
2505
2506
0
  processFlags(handle, flags, DECOMPRESS);
2507
2508
0
  if (tj3SetScalingFactor(handle, sf[i]) == -1)
2509
0
    return -1;
2510
0
  return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align);
2511
2512
0
bailout:
2513
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2514
0
  if (this->jerr.warning) retval = -1;
2515
0
  return retval;
2516
0
}
2517
2518
/* TurboJPEG 1.1+ */
2519
DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
2520
                                unsigned long jpegSize, unsigned char *dstBuf,
2521
                                int flags)
2522
0
{
2523
0
  return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
2524
0
}
2525
2526
2527
static void setDecodeDefaults(tjinstance *this, int pixelFormat)
2528
0
{
2529
0
  int i;
2530
2531
0
  this->dinfo.scale_num = this->dinfo.scale_denom = 1;
2532
2533
0
  if (this->subsamp == TJSAMP_GRAY) {
2534
0
    this->dinfo.num_components = this->dinfo.comps_in_scan = 1;
2535
0
    this->dinfo.jpeg_color_space = JCS_GRAYSCALE;
2536
0
  } else {
2537
0
    this->dinfo.num_components = this->dinfo.comps_in_scan = 3;
2538
0
    this->dinfo.jpeg_color_space = JCS_YCbCr;
2539
0
  }
2540
2541
0
  this->dinfo.comp_info = (jpeg_component_info *)
2542
0
    (*this->dinfo.mem->alloc_small) ((j_common_ptr)&this->dinfo, JPOOL_IMAGE,
2543
0
                                     this->dinfo.num_components *
2544
0
                                     sizeof(jpeg_component_info));
2545
2546
0
  for (i = 0; i < this->dinfo.num_components; i++) {
2547
0
    jpeg_component_info *compptr = &this->dinfo.comp_info[i];
2548
2549
0
    compptr->h_samp_factor = (i == 0) ? tjMCUWidth[this->subsamp] / 8 : 1;
2550
0
    compptr->v_samp_factor = (i == 0) ? tjMCUHeight[this->subsamp] / 8 : 1;
2551
0
    compptr->component_index = i;
2552
0
    compptr->component_id = i + 1;
2553
0
    compptr->quant_tbl_no = compptr->dc_tbl_no =
2554
0
      compptr->ac_tbl_no = (i == 0) ? 0 : 1;
2555
0
    this->dinfo.cur_comp_info[i] = compptr;
2556
0
  }
2557
0
  this->dinfo.data_precision = 8;
2558
0
  for (i = 0; i < 2; i++) {
2559
0
    if (this->dinfo.quant_tbl_ptrs[i] == NULL)
2560
0
      this->dinfo.quant_tbl_ptrs[i] =
2561
0
        jpeg_alloc_quant_table((j_common_ptr)&this->dinfo);
2562
0
  }
2563
2564
0
  this->dinfo.mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2565
0
}
2566
2567
2568
static int my_read_markers(j_decompress_ptr dinfo)
2569
0
{
2570
0
  return JPEG_REACHED_SOS;
2571
0
}
2572
2573
static void my_reset_marker_reader(j_decompress_ptr dinfo)
2574
0
{
2575
0
}
2576
2577
/* TurboJPEG 3.0+ */
2578
DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle,
2579
                                  const unsigned char * const *srcPlanes,
2580
                                  const int *strides, unsigned char *dstBuf,
2581
                                  int width, int pitch, int height,
2582
                                  int pixelFormat)
2583
0
{
2584
0
  static const char FUNCTION_NAME[] = "tj3DecodeYUVPlanes8";
2585
0
  JSAMPROW *row_pointer = NULL;
2586
0
  JSAMPLE *_tmpbuf[MAX_COMPONENTS];
2587
0
  JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
2588
0
  int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
2589
0
  JSAMPLE *ptr;
2590
0
  jpeg_component_info *compptr;
2591
0
  int (*old_read_markers) (j_decompress_ptr) = NULL;
2592
0
  void (*old_reset_marker_reader) (j_decompress_ptr) = NULL;
2593
2594
0
  GET_DINSTANCE(handle);
2595
2596
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2597
0
    tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;  inbuf[i] = NULL;
2598
0
  }
2599
2600
0
  if ((this->init & DECOMPRESS) == 0)
2601
0
    THROW("Instance has not been initialized for decompression");
2602
2603
0
  if (!srcPlanes || !srcPlanes[0] || dstBuf == NULL || width <= 0 ||
2604
0
      pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2605
0
    THROW("Invalid argument");
2606
0
  if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
2607
0
    THROW("Invalid argument");
2608
2609
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2610
0
    THROW("TJPARAM_SUBSAMP must be specified");
2611
0
  if (pixelFormat == TJPF_CMYK)
2612
0
    THROW("Cannot decode YUV images into packed-pixel CMYK images.");
2613
2614
0
  if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2615
0
  else if (pitch < width * tjPixelSize[pixelFormat])
2616
0
    THROW("Invalid argument");
2617
0
  dinfo->image_width = width;
2618
0
  dinfo->image_height = height;
2619
2620
0
  dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
2621
0
  dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
2622
0
  dinfo->Se = DCTSIZE2 - 1;
2623
0
  setDecodeDefaults(this, pixelFormat);
2624
0
  old_read_markers = dinfo->marker->read_markers;
2625
0
  dinfo->marker->read_markers = my_read_markers;
2626
0
  old_reset_marker_reader = dinfo->marker->reset_marker_reader;
2627
0
  dinfo->marker->reset_marker_reader = my_reset_marker_reader;
2628
0
  CATCH_LIBJPEG(this);
2629
0
  jpeg_read_header(dinfo, TRUE);
2630
0
  dinfo->marker->read_markers = old_read_markers;
2631
0
  dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2632
2633
0
  this->dinfo.out_color_space = pf2cs[pixelFormat];
2634
0
  this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2635
0
  dinfo->do_fancy_upsampling = FALSE;
2636
0
  dinfo->Se = DCTSIZE2 - 1;
2637
0
  jinit_master_decompress(dinfo);
2638
0
  (*dinfo->upsample->start_pass) (dinfo);
2639
2640
0
  pw0 = PAD(width, dinfo->max_h_samp_factor);
2641
0
  ph0 = PAD(height, dinfo->max_v_samp_factor);
2642
2643
0
  if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
2644
2645
0
  if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
2646
0
    THROW("Memory allocation failure");
2647
0
  for (i = 0; i < height; i++) {
2648
0
    if (this->bottomUp)
2649
0
      row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
2650
0
    else
2651
0
      row_pointer[i] = &dstBuf[i * (size_t)pitch];
2652
0
  }
2653
0
  if (height < ph0)
2654
0
    for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
2655
2656
0
  for (i = 0; i < dinfo->num_components; i++) {
2657
0
    compptr = &dinfo->comp_info[i];
2658
0
    _tmpbuf[i] =
2659
0
      (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
2660
0
                        compptr->v_samp_factor + 32);
2661
0
    if (!_tmpbuf[i])
2662
0
      THROW("Memory allocation failure");
2663
0
    tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
2664
0
    if (!tmpbuf[i])
2665
0
      THROW("Memory allocation failure");
2666
0
    for (row = 0; row < compptr->v_samp_factor; row++) {
2667
0
      unsigned char *_tmpbuf_aligned =
2668
0
        (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
2669
2670
0
      tmpbuf[i][row] =
2671
0
        &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
2672
0
    }
2673
0
    pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
2674
0
    if (strides && strides[i] != 0 && strides[i] < pw[i])
2675
0
      THROW("Invalid argument");
2676
0
    ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2677
0
    inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
2678
0
    if (!inbuf[i])
2679
0
      THROW("Memory allocation failure");
2680
0
    ptr = (JSAMPLE *)srcPlanes[i];
2681
0
    for (row = 0; row < ph[i]; row++) {
2682
0
      inbuf[i][row] = ptr;
2683
0
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2684
0
    }
2685
0
  }
2686
2687
0
  CATCH_LIBJPEG(this);
2688
2689
0
  for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
2690
0
    JDIMENSION inrow = 0, outrow = 0;
2691
2692
0
    for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
2693
0
         i++, compptr++)
2694
0
      jcopy_sample_rows(inbuf[i],
2695
0
        row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
2696
0
        compptr->v_samp_factor, pw[i]);
2697
0
    (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
2698
0
                                 dinfo->max_v_samp_factor, &row_pointer[row],
2699
0
                                 &outrow, dinfo->max_v_samp_factor);
2700
0
  }
2701
0
  jpeg_abort_decompress(dinfo);
2702
2703
0
bailout:
2704
0
  if (old_read_markers)
2705
0
    dinfo->marker->read_markers = old_read_markers;
2706
0
  if (old_reset_marker_reader)
2707
0
    dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2708
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2709
0
  free(row_pointer);
2710
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2711
0
    free(tmpbuf[i]);
2712
0
    free(_tmpbuf[i]);
2713
0
    free(inbuf[i]);
2714
0
  }
2715
0
  if (this->jerr.warning) retval = -1;
2716
0
  return retval;
2717
0
}
2718
2719
/* TurboJPEG 1.4+ */
2720
DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
2721
                                const unsigned char **srcPlanes,
2722
                                const int *strides, int subsamp,
2723
                                unsigned char *dstBuf, int width, int pitch,
2724
                                int height, int pixelFormat, int flags)
2725
0
{
2726
0
  static const char FUNCTION_NAME[] = "tjDecodeYUVPlanes";
2727
0
  int retval = 0;
2728
2729
0
  GET_TJINSTANCE(handle, -1);
2730
2731
0
  if (subsamp < 0 || subsamp >= this->numSamp)
2732
0
    THROW("Invalid argument");
2733
2734
0
  this->subsamp = subsamp;
2735
0
  processFlags(handle, flags, DECOMPRESS);
2736
2737
0
  return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2738
0
                             height, pixelFormat);
2739
2740
0
bailout:
2741
0
  return retval;
2742
0
}
2743
2744
2745
/* TurboJPEG 3.0+ */
2746
DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf,
2747
                            int align, unsigned char *dstBuf, int width,
2748
                            int pitch, int height, int pixelFormat)
2749
0
{
2750
0
  static const char FUNCTION_NAME[] = "tj3DecodeYUV8";
2751
0
  const unsigned char *srcPlanes[3];
2752
0
  int pw0, ph0, strides[3], retval = -1;
2753
2754
0
  GET_TJINSTANCE(handle, -1);
2755
2756
0
  if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 ||
2757
0
      height <= 0)
2758
0
    THROW("Invalid argument");
2759
2760
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2761
0
    THROW("TJPARAM_SUBSAMP must be specified");
2762
2763
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2764
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2765
0
  srcPlanes[0] = srcBuf;
2766
0
  strides[0] = (int)PAD((unsigned long long)pw0, align);
2767
0
  if (this->subsamp == TJSAMP_GRAY) {
2768
0
    strides[1] = strides[2] = 0;
2769
0
    srcPlanes[1] = srcPlanes[2] = NULL;
2770
0
  } else {
2771
0
    int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2772
0
    int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2773
2774
0
    strides[1] = strides[2] = (int)PAD((unsigned long long)pw1, align);
2775
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
2776
0
        (unsigned long long)INT_MAX ||
2777
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
2778
0
        (unsigned long long)INT_MAX)
2779
0
      THROW("Image or row alignment is too large");
2780
0
    srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
2781
0
    srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
2782
0
  }
2783
2784
0
  return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2785
0
                             height, pixelFormat);
2786
2787
0
bailout:
2788
0
  return retval;
2789
0
}
2790
2791
/* TurboJPEG 1.4+ */
2792
DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
2793
                          int align, int subsamp, unsigned char *dstBuf,
2794
                          int width, int pitch, int height, int pixelFormat,
2795
                          int flags)
2796
0
{
2797
0
  static const char FUNCTION_NAME[] = "tjDecodeYUV";
2798
0
  int retval = -1;
2799
2800
0
  GET_TJINSTANCE(handle, -1);
2801
2802
0
  if (subsamp < 0 || subsamp >= this->numSamp)
2803
0
    THROW("Invalid argument");
2804
2805
0
  this->subsamp = subsamp;
2806
0
  processFlags(handle, flags, DECOMPRESS);
2807
2808
0
  return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height,
2809
0
                       pixelFormat);
2810
2811
0
bailout:
2812
0
  return retval;
2813
0
}
2814
2815
2816
/******************************** Transformer ********************************/
2817
2818
/* TurboJPEG 1.2+ */
2819
DLLEXPORT tjhandle tjInitTransform(void)
2820
0
{
2821
0
  return tj3Init(TJINIT_TRANSFORM);
2822
0
}
2823
2824
2825
static int getDstSubsamp(int srcSubsamp, const tjtransform *transform)
2826
0
{
2827
0
  int dstSubsamp;
2828
2829
0
  if (!transform)
2830
0
    return srcSubsamp;
2831
2832
0
  dstSubsamp = (transform->options & TJXOPT_GRAY) ? TJSAMP_GRAY : srcSubsamp;
2833
2834
0
  if (transform->op == TJXOP_TRANSPOSE || transform->op == TJXOP_TRANSVERSE ||
2835
0
      transform->op == TJXOP_ROT90 || transform->op == TJXOP_ROT270) {
2836
0
    if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
2837
0
    else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
2838
0
    else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
2839
0
    else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
2840
0
    else if (dstSubsamp == TJSAMP_410) dstSubsamp = TJSAMP_24;
2841
0
    else if (dstSubsamp == TJSAMP_24) dstSubsamp = TJSAMP_410;
2842
0
  }
2843
2844
0
  return dstSubsamp;
2845
0
}
2846
2847
static int getTransformedSpecs(tjhandle handle, int *width, int *height,
2848
                               int *subsamp, const tjtransform *transform,
2849
                               const char *FUNCTION_NAME)
2850
0
{
2851
0
  int retval = 0, dstWidth, dstHeight, dstSubsamp;
2852
2853
0
  GET_TJINSTANCE(handle, -1);
2854
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2855
0
    THROW("Instance has not been initialized for transformation");
2856
2857
0
  if (!width || !height || !subsamp || !transform || *width < 1 ||
2858
0
      *height < 1 || *subsamp < TJSAMP_UNKNOWN || *subsamp >= this->numSamp)
2859
0
    THROW("Invalid argument");
2860
2861
0
  dstWidth = *width;  dstHeight = *height;
2862
0
  if (transform->op == TJXOP_TRANSPOSE || transform->op == TJXOP_TRANSVERSE ||
2863
0
      transform->op == TJXOP_ROT90 || transform->op == TJXOP_ROT270) {
2864
0
    dstWidth = *height;  dstHeight = *width;
2865
0
  }
2866
0
  dstSubsamp = getDstSubsamp(*subsamp, transform);
2867
2868
0
  if (transform->options & TJXOPT_CROP) {
2869
0
    int croppedWidth, croppedHeight;
2870
2871
0
    if (transform->r.x < 0 || transform->r.y < 0 || transform->r.w < 0 ||
2872
0
        transform->r.h < 0)
2873
0
      THROW("Invalid cropping region");
2874
0
    if (dstSubsamp == TJSAMP_UNKNOWN)
2875
0
      THROW("Could not determine subsampling level of JPEG image");
2876
0
    if ((transform->r.x % tjMCUWidth[dstSubsamp]) != 0 ||
2877
0
        (transform->r.y % tjMCUHeight[dstSubsamp]) != 0)
2878
0
      THROWI("To crop this JPEG image, x must be a multiple of %d\n"
2879
0
             "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
2880
0
             tjMCUHeight[dstSubsamp]);
2881
0
    if (transform->r.x >= dstWidth || transform->r.y >= dstHeight)
2882
0
      THROW("The cropping region exceeds the destination image dimensions");
2883
0
    croppedWidth = transform->r.w == 0 ? dstWidth - transform->r.x :
2884
0
                                         transform->r.w;
2885
0
    croppedHeight = transform->r.h == 0 ? dstHeight - transform->r.y :
2886
0
                                          transform->r.h;
2887
0
    if (transform->r.x + croppedWidth > dstWidth ||
2888
0
        transform->r.y + croppedHeight > dstHeight)
2889
0
      THROW("The cropping region exceeds the destination image dimensions");
2890
0
    dstWidth = croppedWidth;  dstHeight = croppedHeight;
2891
0
  }
2892
2893
0
  *width = dstWidth;  *height = dstHeight;  *subsamp = dstSubsamp;
2894
2895
0
bailout:
2896
0
  return retval;
2897
0
}
2898
2899
2900
/* TurboJPEG 3.1+ */
2901
DLLEXPORT size_t tj3TransformBufSize(tjhandle handle,
2902
                                     const tjtransform *transform)
2903
0
{
2904
0
  static const char FUNCTION_NAME[] = "tj3TransformBufSize";
2905
0
  size_t retval = 0;
2906
0
  int dstWidth, dstHeight, dstSubsamp;
2907
2908
0
  GET_TJINSTANCE(handle, 0);
2909
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2910
0
    THROWRV("Instance has not been initialized for transformation", 0);
2911
2912
0
  if (transform == NULL)
2913
0
    THROWRV("Invalid argument", 0)
2914
2915
0
  if (this->jpegWidth < 0 || this->jpegHeight < 0)
2916
0
    THROWRV("JPEG header has not yet been read", 0);
2917
2918
0
  dstWidth = this->jpegWidth;
2919
0
  dstHeight = this->jpegHeight;
2920
0
  dstSubsamp = this->subsamp;
2921
0
  if (getTransformedSpecs(handle, &dstWidth, &dstHeight, &dstSubsamp,
2922
0
                          transform, FUNCTION_NAME) == -1) {
2923
0
    retval = 0;
2924
0
    goto bailout;
2925
0
  }
2926
2927
0
  retval = tj3JPEGBufSize(dstWidth, dstHeight, dstSubsamp);
2928
0
  if ((this->saveMarkers == 2 || this->saveMarkers == 4) &&
2929
0
      !(transform->options & TJXOPT_COPYNONE))
2930
0
    retval += this->decompICCSize;
2931
0
  else
2932
0
    retval += this->iccSize;
2933
2934
0
bailout:
2935
0
  return retval;
2936
0
}
2937
2938
2939
/* TurboJPEG 3.0+ */
2940
DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
2941
                           size_t jpegSize, int n, unsigned char **dstBufs,
2942
                           size_t *dstSizes, const tjtransform *t)
2943
0
{
2944
0
  static const char FUNCTION_NAME[] = "tj3Transform";
2945
0
  int retval = 0;
2946
2947
0
#if TRANSFORMS_SUPPORTED
2948
2949
0
  jpeg_transform_info *xinfo = NULL;
2950
0
  jvirt_barray_ptr *srccoefs, *dstcoefs;
2951
0
  int i, saveMarkers = 0, srcSubsamp;
2952
0
  boolean alloc = TRUE;
2953
0
  struct my_progress_mgr progress;
2954
2955
0
  GET_INSTANCE(handle);
2956
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2957
0
    THROW("Instance has not been initialized for transformation");
2958
2959
0
  if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
2960
0
      dstSizes == NULL || t == NULL)
2961
0
    THROW("Invalid argument");
2962
2963
0
  if (this->scanLimit) {
2964
0
    memset(&progress, 0, sizeof(struct my_progress_mgr));
2965
0
    progress.pub.progress_monitor = my_progress_monitor;
2966
0
    progress.this = this;
2967
0
    dinfo->progress = &progress.pub;
2968
0
  } else
2969
0
    dinfo->progress = NULL;
2970
2971
0
  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2972
2973
0
  if ((xinfo =
2974
0
       (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
2975
0
    THROW("Memory allocation failure");
2976
0
  memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
2977
2978
0
  CATCH_LIBJPEG(this);
2979
2980
0
  if (dinfo->global_state <= DSTATE_INHEADER)
2981
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2982
2983
0
  for (i = 0; i < n; i++) {
2984
0
    if (t[i].op < 0 || t[i].op >= TJ_NUMXOP)
2985
0
      THROW("Invalid transform operation");
2986
0
    xinfo[i].transform = xformtypes[t[i].op];
2987
0
    xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
2988
0
    xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
2989
0
    xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
2990
0
    xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
2991
0
    if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
2992
0
    else xinfo[i].slow_hflip = 0;
2993
2994
0
    if (xinfo[i].crop) {
2995
0
      if (t[i].r.x < 0 || t[i].r.y < 0 || t[i].r.w < 0 || t[i].r.h < 0)
2996
0
        THROW("Invalid cropping region");
2997
0
      xinfo[i].crop_xoffset = t[i].r.x;  xinfo[i].crop_xoffset_set = JCROP_POS;
2998
0
      xinfo[i].crop_yoffset = t[i].r.y;  xinfo[i].crop_yoffset_set = JCROP_POS;
2999
0
      if (t[i].r.w != 0) {
3000
0
        xinfo[i].crop_width = t[i].r.w;  xinfo[i].crop_width_set = JCROP_POS;
3001
0
      } else
3002
0
        xinfo[i].crop_width = JCROP_UNSET;
3003
0
      if (t[i].r.h != 0) {
3004
0
        xinfo[i].crop_height = t[i].r.h;  xinfo[i].crop_height_set = JCROP_POS;
3005
0
      } else
3006
0
        xinfo[i].crop_height = JCROP_UNSET;
3007
0
    }
3008
0
    if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
3009
0
  }
3010
3011
0
  jcopy_markers_setup(dinfo, saveMarkers ?
3012
0
                             (JCOPY_OPTION)this->saveMarkers : JCOPYOPT_NONE);
3013
0
  if (dinfo->global_state <= DSTATE_INHEADER)
3014
0
    jpeg_read_header(dinfo, TRUE);
3015
0
  if (this->maxPixels &&
3016
0
      (unsigned long long)dinfo->image_width * dinfo->image_height >
3017
0
      (unsigned long long)this->maxPixels)
3018
0
    THROW("Image is too large");
3019
0
  srcSubsamp = getSubsamp(this);
3020
3021
0
  for (i = 0; i < n; i++) {
3022
0
    if (!jtransform_request_workspace(dinfo, &xinfo[i]))
3023
0
      THROW("Transform is not perfect");
3024
3025
0
    if (xinfo[i].crop) {
3026
0
      int dstSubsamp = getDstSubsamp(srcSubsamp, &t[i]);
3027
3028
0
      if (dstSubsamp == TJSAMP_UNKNOWN)
3029
0
        THROW("Could not determine subsampling level of destination image");
3030
0
      if ((t[i].r.x % tjMCUWidth[dstSubsamp]) != 0 ||
3031
0
          (t[i].r.y % tjMCUHeight[dstSubsamp]) != 0)
3032
0
        THROWI("To crop this JPEG image, x must be a multiple of %d\n"
3033
0
               "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
3034
0
               tjMCUHeight[dstSubsamp]);
3035
0
    }
3036
0
  }
3037
3038
0
  srccoefs = jpeg_read_coefficients(dinfo);
3039
3040
0
  for (i = 0; i < n; i++) {
3041
0
    if (this->noRealloc) alloc = FALSE;
3042
0
    if (!(t[i].options & TJXOPT_NOOUTPUT))
3043
0
      jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
3044
0
    jpeg_copy_critical_parameters(dinfo, cinfo);
3045
0
    dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
3046
0
    if (this->optimize || t[i].options & TJXOPT_OPTIMIZE)
3047
0
      cinfo->optimize_coding = TRUE;
3048
0
#ifdef C_PROGRESSIVE_SUPPORTED
3049
0
    if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE)
3050
0
      jpeg_simple_progression(cinfo);
3051
0
#endif
3052
0
    if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) {
3053
0
      cinfo->arith_code = TRUE;
3054
0
      cinfo->optimize_coding = FALSE;
3055
0
    }
3056
0
    cinfo->restart_interval = this->restartIntervalBlocks;
3057
0
    cinfo->restart_in_rows = this->restartIntervalRows;
3058
0
    if (!(t[i].options & TJXOPT_NOOUTPUT)) {
3059
0
      JCOPY_OPTION copyOption = t[i].options & TJXOPT_COPYNONE ?
3060
0
                                JCOPYOPT_NONE :
3061
0
                                (JCOPY_OPTION)this->saveMarkers;
3062
3063
0
      jpeg_write_coefficients(cinfo, dstcoefs);
3064
0
      jcopy_markers_execute(dinfo, cinfo, copyOption);
3065
0
      if (this->iccBuf != NULL && this->iccSize != 0 &&
3066
0
          copyOption != JCOPYOPT_ALL && copyOption != JCOPYOPT_ICC)
3067
0
        jpeg_write_icc_profile(cinfo, this->iccBuf,
3068
0
                               (unsigned int)this->iccSize);
3069
0
    } else
3070
0
      jinit_c_master_control(cinfo, TRUE);
3071
0
    jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
3072
0
    if (t[i].customFilter) {
3073
0
      int ci, y;
3074
0
      JDIMENSION by;
3075
3076
0
      for (ci = 0; ci < cinfo->num_components; ci++) {
3077
0
        jpeg_component_info *compptr = &cinfo->comp_info[ci];
3078
0
        tjregion arrayRegion = { 0, 0, 0, 0 };
3079
0
        tjregion planeRegion = { 0, 0, 0, 0 };
3080
3081
0
        arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
3082
0
        arrayRegion.h = DCTSIZE;
3083
0
        planeRegion.w = compptr->width_in_blocks * DCTSIZE;
3084
0
        planeRegion.h = compptr->height_in_blocks * DCTSIZE;
3085
3086
0
        for (by = 0; by < compptr->height_in_blocks;
3087
0
             by += compptr->v_samp_factor) {
3088
0
          JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
3089
0
            ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
3090
0
             TRUE);
3091
3092
0
          for (y = 0; y < compptr->v_samp_factor; y++) {
3093
0
            if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
3094
0
                                  i, (tjtransform *)&t[i]) == -1)
3095
0
              THROW("Error in custom filter");
3096
0
            arrayRegion.y += DCTSIZE;
3097
0
          }
3098
0
        }
3099
0
      }
3100
0
    }
3101
0
    if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
3102
0
  }
3103
3104
0
  jpeg_finish_decompress(dinfo);
3105
3106
0
bailout:
3107
0
  if (cinfo->global_state > CSTATE_START) {
3108
0
    if (alloc) (*cinfo->dest->term_destination) (cinfo);
3109
0
    jpeg_abort_compress(cinfo);
3110
0
  }
3111
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
3112
0
  free(xinfo);
3113
0
  if (this->jerr.warning) retval = -1;
3114
0
  return retval;
3115
3116
#else /* TRANSFORMS_SUPPORTED */
3117
3118
  GET_TJINSTANCE(handle, -1)
3119
  THROW("Lossless transformations were disabled at build time")
3120
bailout:
3121
  return retval;
3122
3123
#endif
3124
0
}
3125
3126
/* TurboJPEG 1.2+ */
3127
DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
3128
                          unsigned long jpegSize, int n,
3129
                          unsigned char **dstBufs, unsigned long *dstSizes,
3130
                          tjtransform *t, int flags)
3131
0
{
3132
0
  static const char FUNCTION_NAME[] = "tjTransform";
3133
0
  int i, retval = 0, srcSubsamp = -1;
3134
0
  size_t *sizes = NULL;
3135
3136
0
  GET_DINSTANCE(handle);
3137
0
  if ((this->init & DECOMPRESS) == 0)
3138
0
    THROW("Instance has not been initialized for decompression");
3139
3140
0
  if (n < 1 || dstSizes == NULL)
3141
0
    THROW("Invalid argument");
3142
3143
0
  CATCH_LIBJPEG(this);
3144
3145
0
  processFlags(handle, flags, COMPRESS);
3146
3147
0
  if (this->noRealloc) {
3148
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
3149
0
    jpeg_read_header(dinfo, TRUE);
3150
0
    srcSubsamp = getSubsamp(this);
3151
0
  }
3152
3153
0
  if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL)
3154
0
    THROW("Memory allocation failure");
3155
0
  for (i = 0; i < n; i++) {
3156
0
    sizes[i] = (size_t)dstSizes[i];
3157
0
    if (this->noRealloc) {
3158
0
      int dstWidth = dinfo->image_width, dstHeight = dinfo->image_height;
3159
0
      int dstSubsamp = srcSubsamp;
3160
3161
0
      if (getTransformedSpecs(handle, &dstWidth, &dstHeight, &dstSubsamp,
3162
0
                              &t[i], FUNCTION_NAME) == -1) {
3163
0
        retval = -1;
3164
0
        goto bailout;
3165
0
      }
3166
0
      sizes[i] = tj3JPEGBufSize(dstWidth, dstHeight, dstSubsamp);
3167
0
    }
3168
0
  }
3169
0
  retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes,
3170
0
                        t);
3171
0
  for (i = 0; i < n; i++)
3172
0
    dstSizes[i] = (unsigned long)sizes[i];
3173
3174
0
bailout:
3175
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
3176
0
  if (this->jerr.warning) retval = -1;
3177
0
  free(sizes);
3178
0
  return retval;
3179
0
}
3180
3181
3182
/*************************** Packed-Pixel Image I/O **************************/
3183
3184
/* tj3LoadImage*() is implemented in turbojpeg-mp.c */
3185
3186
/* TurboJPEG 2.0+ */
3187
DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
3188
                                     int align, int *height,
3189
                                     int *pixelFormat, int flags)
3190
0
{
3191
0
  tjhandle handle = NULL;
3192
0
  unsigned char *dstBuf = NULL;
3193
3194
0
  if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL;
3195
3196
0
  processFlags(handle, flags, COMPRESS);
3197
3198
0
  dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat);
3199
3200
0
  tj3Destroy(handle);
3201
0
  return dstBuf;
3202
0
}
3203
3204
3205
/* tj3SaveImage*() is implemented in turbojpeg-mp.c */
3206
3207
/* TurboJPEG 2.0+ */
3208
DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
3209
                          int width, int pitch, int height, int pixelFormat,
3210
                          int flags)
3211
0
{
3212
0
  tjhandle handle = NULL;
3213
0
  int retval = -1;
3214
3215
0
  if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1;
3216
3217
0
  processFlags(handle, flags, DECOMPRESS);
3218
3219
0
  retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height,
3220
0
                         pixelFormat);
3221
3222
0
  tj3Destroy(handle);
3223
0
  return retval;
3224
0
}