Coverage Report

Created: 2026-06-10 06:16

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