Coverage Report

Created: 2026-03-12 07:02

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