Coverage Report

Created: 2025-07-14 06:09

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