Coverage Report

Created: 2025-11-09 06:51

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