Coverage Report

Created: 2026-07-16 06:46

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