Coverage Report

Created: 2026-08-31 07:17

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