Coverage Report

Created: 2026-05-30 06:16

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