Coverage Report

Created: 2026-07-16 06:46

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
49.8k
#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
37.8k
{
82
37.8k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
83
84
37.8k
  (*cinfo->err->output_message) (cinfo);
85
37.8k
  longjmp(myerr->setjmp_buffer, 1);
86
37.8k
}
87
88
static void my_emit_message(j_common_ptr cinfo, int msg_level)
89
30.9k
{
90
30.9k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
91
92
30.9k
  myerr->emit_message(cinfo, msg_level);
93
30.9k
  if (msg_level < 0) {
94
0
    myerr->warning = TRUE;
95
0
    if (myerr->stopOnWarning) longjmp(myerr->setjmp_buffer, 1);
96
0
  }
97
30.9k
}
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
37.8k
{
150
37.8k
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
151
37.8k
  tjinstance *this = (tjinstance *)myerr->tjHandle;
152
153
37.8k
  if (this) {
154
37.8k
    this->isInstanceError = TRUE;
155
37.8k
    (*cinfo->err->format_message) (cinfo, this->errStr);
156
37.8k
  } else
157
0
    (*cinfo->err->format_message) (cinfo, errStr);
158
37.8k
}
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
581
  SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
256
0
  retval = rv;  goto bailout; \
257
581
}
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
581
  SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \
281
581
  this->isInstanceError = TRUE;  THROWG(m, rv) \
282
581
}
283
581
#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
77.0k
#define CATCH_LIBJPEG(this) { \
294
77.0k
  if (setjmp(this->jerr.setjmp_buffer)) { \
295
10.0k
    /* If we get here, the JPEG code has signaled an error. */ \
296
10.0k
    retval = -1;  goto bailout; \
297
10.0k
  } \
298
77.0k
}
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
17.0k
  tjinstance *this = (tjinstance *)handle; \
315
17.0k
  j_compress_ptr cinfo = NULL; \
316
17.0k
  \
317
17.0k
  if (!this) { \
318
0
    SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
319
0
    return -1; \
320
0
  } \
321
17.0k
  cinfo = &this->cinfo; \
322
17.0k
  this->jerr.warning = FALSE; \
323
17.0k
  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
331k
  tjinstance *this = (tjinstance *)handle; \
339
331k
  \
340
331k
  if (!this) { \
341
0
    SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \
342
0
    return errorReturn; \
343
0
  } \
344
331k
  this->jerr.warning = FALSE; \
345
331k
  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
17.0k
{
368
17.0k
  int colorspace = yuv ? TJCS_DEFAULT : this->colorspace;
369
370
17.0k
  this->cinfo.in_color_space = pf2cs[pixelFormat];
371
17.0k
  this->cinfo.input_components = tjPixelSize[pixelFormat];
372
17.0k
  jpeg_set_defaults(&this->cinfo);
373
374
17.0k
  this->cinfo.restart_interval = this->restartIntervalBlocks;
375
17.0k
  this->cinfo.restart_in_rows = this->restartIntervalRows;
376
17.0k
  this->cinfo.X_density = (UINT16)this->xDensity;
377
17.0k
  this->cinfo.Y_density = (UINT16)this->yDensity;
378
17.0k
  this->cinfo.density_unit = (UINT8)this->densityUnits;
379
17.0k
  this->cinfo.mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
380
381
17.0k
  if (this->lossless && !yuv) {
382
17.0k
#ifdef C_LOSSLESS_SUPPORTED
383
17.0k
    jpeg_enable_lossless(&this->cinfo, this->losslessPSV, this->losslessPt);
384
17.0k
#endif
385
17.0k
    return;
386
17.0k
  }
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
50.5k
{
585
50.5k
  static const char FUNCTION_NAME[] = "tj3Init";
586
50.5k
  tjinstance *this = NULL;
587
50.5k
  tjhandle retval = NULL;
588
589
50.5k
  if (initType < 0 || initType >= TJ_NUMINIT || apiVersion < 1000000 ||
590
50.5k
      apiVersion > 999999999)
591
50.5k
    THROWG("Invalid argument", NULL);
592
593
50.5k
  if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL)
594
50.5k
    THROWG("Memory allocation failure", NULL);
595
50.5k
  memset(this, 0, sizeof(tjinstance));
596
50.5k
  SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error");
597
598
50.5k
  this->quality = -1;
599
50.5k
  this->subsamp = TJSAMP_UNKNOWN;
600
50.5k
  this->jpegWidth = -1;
601
50.5k
  this->jpegHeight = -1;
602
50.5k
  this->precision = 8;
603
50.5k
  this->colorspace = TJCS_DEFAULT;
604
50.5k
  this->losslessPSV = 1;
605
50.5k
  this->xDensity = 1;
606
50.5k
  this->yDensity = 1;
607
50.5k
  this->scalingFactor = TJUNSCALED;
608
50.5k
  this->saveMarkers = 2;
609
610
50.5k
  this->apiVersion = apiVersion;
611
50.5k
  this->numSamp = apiVersion >= 3002000 ? TJ_NUMSAMP : 7;
612
613
50.5k
  switch (initType) {
614
50.5k
  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
50.5k
  }
622
623
0
bailout:
624
0
  return retval;
625
50.5k
}
626
627
/* TurboJPEG 3.0+ */
628
DLLEXPORT tjhandle tj3Init(int initType)
629
44.1k
{
630
44.1k
  return tj3InitVersion(initType, 3001000);
631
44.1k
}
632
633
634
/* TurboJPEG 3.0+ */
635
DLLEXPORT void tj3Destroy(tjhandle handle)
636
50.5k
{
637
50.5k
  tjinstance *this = (tjinstance *)handle;
638
50.5k
  j_compress_ptr cinfo = NULL;
639
50.5k
  j_decompress_ptr dinfo = NULL;
640
641
50.5k
  if (!this) return;
642
643
50.5k
  cinfo = &this->cinfo;  dinfo = &this->dinfo;
644
50.5k
  this->jerr.warning = FALSE;
645
50.5k
  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
50.5k
  if (setjmp(this->jerr.setjmp_buffer)) goto destroy_decompress;
650
50.5k
  if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
651
50.5k
destroy_decompress:
652
50.5k
  if (setjmp(this->jerr.setjmp_buffer)) goto bailout;
653
50.5k
  if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo);
654
50.5k
bailout:
655
50.5k
  free(this->iccBuf);
656
50.5k
  free(this->decompICCBuf);
657
50.5k
  free(this);
658
50.5k
}
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
166k
#define SET_PARAM(field, minValue, maxValue) { \
719
166k
  if (value < minValue || (maxValue > 0 && value > maxValue)) \
720
166k
    THROW("Parameter value out of range"); \
721
166k
  this->field = value; \
722
166k
}
723
724
105k
#define SET_BOOL_PARAM(field) { \
725
105k
  if (value < 0 || value > 1) \
726
105k
    THROW("Parameter value out of range"); \
727
105k
  this->field = (boolean)value; \
728
105k
}
729
730
/* TurboJPEG 3.0+ */
731
DLLEXPORT int tj3Set(tjhandle handle, int param, int value)
732
271k
{
733
271k
  static const char FUNCTION_NAME[] = "tj3Set";
734
271k
  int retval = 0;
735
736
271k
  GET_TJINSTANCE(handle, -1);
737
738
271k
  switch (param) {
739
0
  case TJPARAM_STOPONWARNING:
740
0
    SET_BOOL_PARAM(jerr.stopOnWarning);
741
0
    break;
742
44.1k
  case TJPARAM_BOTTOMUP:
743
44.1k
    SET_BOOL_PARAM(bottomUp);
744
44.1k
    break;
745
44.1k
  case TJPARAM_NOREALLOC:
746
44.1k
    if (!(this->init & COMPRESS))
747
44.1k
      THROW("TJPARAM_NOREALLOC is not applicable to decompression instances.");
748
44.1k
    SET_BOOL_PARAM(noRealloc);
749
44.1k
    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
44.1k
  case TJPARAM_PRECISION:
769
44.1k
    SET_PARAM(precision, 2, 16);
770
44.1k
    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
17.0k
  case TJPARAM_LOSSLESS:
805
17.0k
    if (!(this->init & COMPRESS))
806
17.0k
      THROW("TJPARAM_LOSSLESS is read-only in decompression instances.");
807
17.0k
    SET_BOOL_PARAM(lossless);
808
17.0k
    break;
809
17.0k
  case TJPARAM_LOSSLESSPSV:
810
17.0k
    if (!(this->init & COMPRESS))
811
17.0k
      THROW("TJPARAM_LOSSLESSPSV is read-only in decompression instances.");
812
17.0k
    SET_PARAM(losslessPSV, 1, 7);
813
17.0k
    break;
814
17.0k
  case TJPARAM_LOSSLESSPT:
815
17.0k
    if (!(this->init & COMPRESS))
816
17.0k
      THROW("TJPARAM_LOSSLESSPT is read-only in decompression instances.");
817
17.0k
    SET_PARAM(losslessPt, 0, 15);
818
17.0k
    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
44.1k
  case TJPARAM_RESTARTROWS:
826
44.1k
    if (!(this->init & COMPRESS))
827
44.1k
      THROW("TJPARAM_RESTARTROWS is not applicable to decompression instances.");
828
44.1k
    SET_PARAM(restartIntervalRows, 0, 65535);
829
44.1k
    if (value != 0) this->restartIntervalBlocks = 0;
830
44.1k
    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
44.1k
  case TJPARAM_MAXPIXELS:
850
44.1k
    SET_PARAM(maxPixels, 0, -1);
851
44.1k
    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
271k
  }
858
859
271k
bailout:
860
271k
  return retval;
861
271k
}
862
863
864
/* TurboJPEG 3.0+ */
865
DLLEXPORT int tj3Get(tjhandle handle, int param)
866
17.0k
{
867
17.0k
  tjinstance *this = (tjinstance *)handle;
868
17.0k
  if (!this) return -1;
869
870
17.0k
  switch (param) {
871
0
  case TJPARAM_STOPONWARNING:
872
0
    return this->jerr.stopOnWarning;
873
0
  case TJPARAM_BOTTOMUP:
874
0
    return this->bottomUp;
875
17.0k
  case TJPARAM_NOREALLOC:
876
17.0k
    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
17.0k
  }
924
925
0
  return -1;
926
17.0k
}
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
14.7k
{
937
14.7k
  return MALLOC(bytes);
938
14.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
46.6k
{
950
46.6k
  free(buf);
951
46.6k
}
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
17.0k
{
963
17.0k
  static const char FUNCTION_NAME[] = "tj3JPEGBufSize";
964
17.0k
  unsigned long long retval = 0;
965
17.0k
  int mcuw, mcuh, chromasf;
966
967
17.0k
  if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN ||
968
17.0k
      jpegSubsamp >= TJ_NUMSAMP)
969
17.0k
    THROWG("Invalid argument", 0);
970
971
17.0k
  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
17.0k
  mcuw = tjMCUWidth[jpegSubsamp];
978
17.0k
  mcuh = tjMCUHeight[jpegSubsamp];
979
17.0k
  chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
980
17.0k
  retval = (unsigned long long)PAD(width, mcuw) * PAD(height, mcuh) *
981
17.0k
           (2ULL + chromasf) + 2048ULL;
982
#if ULLONG_MAX > ULONG_MAX
983
  if (retval > (unsigned long long)((unsigned long)-1))
984
    THROWG("Image is too large", 0);
985
#endif
986
987
17.0k
bailout:
988
17.0k
  return (size_t)retval;
989
17.0k
}
990
991
/* TurboJPEG 1.2+ */
992
DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
993
0
{
994
0
  static const char FUNCTION_NAME[] = "tjBufSize";
995
0
  size_t retval;
996
997
0
  if (jpegSubsamp < 0)
998
0
    THROWG("Invalid argument", 0);
999
1000
0
  retval = tj3JPEGBufSize(width, height, jpegSubsamp);
1001
1002
0
bailout:
1003
0
  return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
1004
0
}
1005
1006
/* TurboJPEG 1.0+ */
1007
DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
1008
0
{
1009
0
  static const char FUNCTION_NAME[] = "TJBUFSIZE";
1010
0
  unsigned long long retval = 0;
1011
1012
0
  if (width < 1 || height < 1)
1013
0
    THROWG("Invalid argument", (unsigned long)-1);
1014
1015
  /* This allows for rare corner cases in which a JPEG image can actually be
1016
     larger than the uncompressed input (we wouldn't mention it if it hadn't
1017
     happened before.) */
1018
0
  retval = (unsigned long long)PAD(width, 16) * PAD(height, 16) * 6ULL +
1019
0
           2048ULL;
1020
#if ULLONG_MAX > ULONG_MAX
1021
  if (retval > (unsigned long long)((unsigned long)-1))
1022
    THROWG("Image is too large", (unsigned long)-1);
1023
#endif
1024
1025
0
bailout:
1026
0
  return (unsigned long)retval;
1027
0
}
1028
1029
1030
/* TurboJPEG 3.0+ */
1031
DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp)
1032
0
{
1033
0
  static const char FUNCTION_NAME[] = "tj3YUVBufSize";
1034
0
  unsigned long long retval = 0;
1035
0
  int nc, i;
1036
1037
0
  if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1038
0
    THROWG("Invalid argument", 0);
1039
1040
0
  nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1041
1042
0
  for (i = 0; i < nc; i++) {
1043
0
    int pw = tj3YUVPlaneWidth(i, width, subsamp);
1044
0
    unsigned long long stride = PAD((unsigned long long)pw, align);
1045
0
    int ph = tj3YUVPlaneHeight(i, height, subsamp);
1046
1047
0
    if (pw == 0 || ph == 0) return 0;
1048
0
    else retval += stride * ph;
1049
0
  }
1050
#if ULLONG_MAX > ULONG_MAX
1051
  if (retval > (unsigned long long)((unsigned long)-1))
1052
    THROWG("Image is too large", 0);
1053
#endif
1054
1055
0
bailout:
1056
0
  return (size_t)retval;
1057
0
}
1058
1059
/* TurboJPEG 1.4+ */
1060
DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height,
1061
                                      int subsamp)
1062
0
{
1063
0
  size_t retval = tj3YUVBufSize(width, align, height, subsamp);
1064
0
  return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval;
1065
0
}
1066
1067
/* TurboJPEG 1.2+ */
1068
DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
1069
0
{
1070
0
  return tjBufSizeYUV2(width, 4, height, subsamp);
1071
0
}
1072
1073
/* TurboJPEG 1.1+ */
1074
DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp)
1075
0
{
1076
0
  return tjBufSizeYUV(width, height, subsamp);
1077
0
}
1078
1079
1080
/* TurboJPEG 3.0+ */
1081
DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride,
1082
                                 int height, int subsamp)
1083
0
{
1084
0
  static const char FUNCTION_NAME[] = "tj3YUVPlaneSize";
1085
0
  unsigned long long retval = 0;
1086
0
  int pw, ph;
1087
1088
0
  if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1089
0
    THROWG("Invalid argument", 0);
1090
1091
0
  pw = tj3YUVPlaneWidth(componentID, width, subsamp);
1092
0
  ph = tj3YUVPlaneHeight(componentID, height, subsamp);
1093
0
  if (pw == 0 || ph == 0) return 0;
1094
1095
0
  if (stride == 0) stride = pw;
1096
0
  else stride = abs(stride);
1097
1098
0
  retval = (unsigned long long)stride * (ph - 1) + pw;
1099
#if ULLONG_MAX > ULONG_MAX
1100
  if (retval > (unsigned long long)((unsigned long)-1))
1101
    THROWG("Image is too large", 0);
1102
#endif
1103
1104
0
bailout:
1105
0
  return (size_t)retval;
1106
0
}
1107
1108
/* TurboJPEG 1.4+ */
1109
DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
1110
                                       int height, int subsamp)
1111
0
{
1112
0
  size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
1113
0
  return (retval == 0) ? -1 : (unsigned long)retval;
1114
0
}
1115
1116
1117
/* TurboJPEG 3.0+ */
1118
DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp)
1119
0
{
1120
0
  static const char FUNCTION_NAME[] = "tj3YUVPlaneWidth";
1121
0
  unsigned long long pw, retval = 0;
1122
0
  int nc;
1123
1124
0
  if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1125
0
    THROWG("Invalid argument", 0);
1126
0
  nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1127
0
  if (componentID < 0 || componentID >= nc)
1128
0
    THROWG("Invalid argument", 0);
1129
1130
0
  pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8);
1131
0
  if (componentID == 0)
1132
0
    retval = pw;
1133
0
  else
1134
0
    retval = pw * 8 / tjMCUWidth[subsamp];
1135
1136
0
  if (retval > (unsigned long long)INT_MAX)
1137
0
    THROWG("Width is too large", 0);
1138
1139
0
bailout:
1140
0
  return (int)retval;
1141
0
}
1142
1143
/* TurboJPEG 1.4+ */
1144
DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp)
1145
0
{
1146
0
  int retval = tj3YUVPlaneWidth(componentID, width, subsamp);
1147
0
  return (retval == 0) ? -1 : retval;
1148
0
}
1149
1150
1151
/* TurboJPEG 3.0+ */
1152
DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp)
1153
0
{
1154
0
  static const char FUNCTION_NAME[] = "tj3YUVPlaneHeight";
1155
0
  unsigned long long ph, retval = 0;
1156
0
  int nc;
1157
1158
0
  if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
1159
0
    THROWG("Invalid argument", 0);
1160
0
  nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
1161
0
  if (componentID < 0 || componentID >= nc)
1162
0
    THROWG("Invalid argument", 0);
1163
1164
0
  ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8);
1165
0
  if (componentID == 0)
1166
0
    retval = ph;
1167
0
  else
1168
0
    retval = ph * 8 / tjMCUHeight[subsamp];
1169
1170
0
  if (retval > (unsigned long long)INT_MAX)
1171
0
    THROWG("Height is too large", 0);
1172
1173
0
bailout:
1174
0
  return (int)retval;
1175
0
}
1176
1177
/* TurboJPEG 1.4+ */
1178
DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
1179
0
{
1180
0
  int retval = tj3YUVPlaneHeight(componentID, height, subsamp);
1181
0
  return (retval == 0) ? -1 : retval;
1182
0
}
1183
1184
1185
/******************************** Compressor *********************************/
1186
1187
static tjhandle _tjInitCompress(tjinstance *this)
1188
50.5k
{
1189
50.5k
  static unsigned char buffer[1];
1190
50.5k
  unsigned char *buf = buffer;
1191
50.5k
  size_t size = 1;
1192
1193
  /* This is also straight out of example.c */
1194
50.5k
  this->cinfo.err = jpeg_std_error(&this->jerr.pub);
1195
50.5k
  this->jerr.pub.error_exit = my_error_exit;
1196
50.5k
  this->jerr.pub.output_message = my_output_message;
1197
50.5k
  this->jerr.emit_message = this->jerr.pub.emit_message;
1198
50.5k
  this->jerr.pub.emit_message = my_emit_message;
1199
50.5k
  this->jerr.pub.addon_message_table = turbojpeg_message_table;
1200
50.5k
  this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1201
50.5k
  this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1202
50.5k
  this->jerr.tjHandle = (tjhandle)this;
1203
1204
50.5k
  if (setjmp(this->jerr.setjmp_buffer)) {
1205
    /* If we get here, the JPEG code has signaled an error. */
1206
0
    free(this);
1207
0
    return NULL;
1208
0
  }
1209
1210
50.5k
  jpeg_create_compress(&this->cinfo);
1211
  /* Make an initial call so it will create the destination manager */
1212
50.5k
  jpeg_mem_dest_tj(&this->cinfo, &buf, &size, 0);
1213
1214
50.5k
  this->init |= COMPRESS;
1215
50.5k
  return (tjhandle)this;
1216
50.5k
}
1217
1218
/* TurboJPEG 1.0+ */
1219
DLLEXPORT tjhandle tjInitCompress(void)
1220
0
{
1221
0
  return tj3Init(TJINIT_COMPRESS);
1222
0
}
1223
1224
1225
/* TurboJPEG 3.1+ */
1226
DLLEXPORT int tj3SetICCProfile(tjhandle handle, unsigned char *iccBuf,
1227
                               size_t iccSize)
1228
15.6k
{
1229
15.6k
  static const char FUNCTION_NAME[] = "tj3SetICCProfile";
1230
15.6k
  int retval = 0;
1231
1232
15.6k
  GET_TJINSTANCE(handle, -1)
1233
15.6k
  if ((this->init & COMPRESS) == 0)
1234
15.6k
    THROW("Instance has not been initialized for compression");
1235
1236
15.6k
  if (iccBuf == this->iccBuf && iccSize == this->iccSize)
1237
0
    return 0;
1238
1239
15.6k
  free(this->iccBuf);
1240
15.6k
  this->iccBuf = NULL;
1241
15.6k
  this->iccSize = 0;
1242
15.6k
  if (iccBuf && iccSize) {
1243
15.6k
    if ((this->iccBuf = (unsigned char *)malloc(iccSize)) == NULL)
1244
15.6k
      THROW("Memory allocation failure");
1245
15.6k
    memcpy(this->iccBuf, iccBuf, iccSize);
1246
15.6k
    this->iccSize = iccSize;
1247
15.6k
  }
1248
1249
15.6k
bailout:
1250
15.6k
  return retval;
1251
15.6k
}
1252
1253
1254
/* tj3Compress*() is implemented in turbojpeg-mp.c */
1255
0
#define BITS_IN_JSAMPLE  8
1256
#include "turbojpeg-mp.c"
1257
#undef BITS_IN_JSAMPLE
1258
182k
#define BITS_IN_JSAMPLE  12
1259
#include "turbojpeg-mp.c"
1260
#undef BITS_IN_JSAMPLE
1261
0
#define BITS_IN_JSAMPLE  16
1262
#include "turbojpeg-mp.c"
1263
#undef BITS_IN_JSAMPLE
1264
1265
/* TurboJPEG 1.2+ */
1266
DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
1267
                          int width, int pitch, int height, int pixelFormat,
1268
                          unsigned char **jpegBuf, unsigned long *jpegSize,
1269
                          int jpegSubsamp, int jpegQual, int flags)
1270
0
{
1271
0
  static const char FUNCTION_NAME[] = "tjCompress2";
1272
0
  int retval = 0;
1273
0
  size_t size;
1274
1275
0
  GET_TJINSTANCE(handle, -1);
1276
1277
0
  if (jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= this->numSamp ||
1278
0
      jpegQual < 0 || jpegQual > 100)
1279
0
    THROW("Invalid argument");
1280
1281
0
  this->quality = jpegQual;
1282
0
  this->subsamp = jpegSubsamp;
1283
0
  processFlags(handle, flags, COMPRESS);
1284
1285
0
  size = (size_t)(*jpegSize);
1286
0
  if (this->noRealloc)
1287
0
    size = tj3JPEGBufSize(width, height, this->subsamp);
1288
0
  retval = tj3Compress8(handle, srcBuf, width, pitch, height, pixelFormat,
1289
0
                        jpegBuf, &size);
1290
0
  *jpegSize = (unsigned long)size;
1291
1292
0
bailout:
1293
0
  return retval;
1294
0
}
1295
1296
/* TurboJPEG 1.0+ */
1297
DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width,
1298
                         int pitch, int height, int pixelSize,
1299
                         unsigned char *jpegBuf, unsigned long *jpegSize,
1300
                         int jpegSubsamp, int jpegQual, int flags)
1301
0
{
1302
0
  int retval = 0;
1303
0
  unsigned long size = jpegSize ? *jpegSize : 0;
1304
1305
0
  if (flags & TJ_YUV) {
1306
0
    size = tjBufSizeYUV(width, height, jpegSubsamp);
1307
0
    retval = tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1308
0
                          getPixelFormat(pixelSize, flags), jpegBuf,
1309
0
                          jpegSubsamp, flags);
1310
0
  } else {
1311
0
    retval = tjCompress2(handle, srcBuf, width, pitch, height,
1312
0
                         getPixelFormat(pixelSize, flags), &jpegBuf, &size,
1313
0
                         jpegSubsamp, jpegQual, flags | TJFLAG_NOREALLOC);
1314
0
  }
1315
0
  *jpegSize = size;
1316
0
  return retval;
1317
0
}
1318
1319
1320
/* TurboJPEG 3.0+ */
1321
DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle,
1322
                                        const unsigned char * const *srcPlanes,
1323
                                        int width, const int *strides,
1324
                                        int height, unsigned char **jpegBuf,
1325
                                        size_t *jpegSize)
1326
0
{
1327
0
  static const char FUNCTION_NAME[] = "tj3CompressFromYUVPlanes8";
1328
0
  int i, row, retval = 0;
1329
0
  boolean alloc = TRUE;
1330
0
  int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
1331
0
    tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
1332
0
  JSAMPLE *_tmpbuf = NULL, *ptr;
1333
0
  JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
1334
1335
0
  GET_CINSTANCE(handle)
1336
1337
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
1338
0
    tmpbuf[i] = NULL;  inbuf[i] = NULL;
1339
0
  }
1340
1341
0
  if ((this->init & COMPRESS) == 0)
1342
0
    THROW("Instance has not been initialized for compression");
1343
1344
0
  if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
1345
0
      jpegBuf == NULL || jpegSize == NULL)
1346
0
    THROW("Invalid argument");
1347
0
  if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
1348
0
    THROW("Invalid argument");
1349
1350
0
  if (this->quality == -1)
1351
0
    THROW("TJPARAM_QUALITY must be specified");
1352
0
  if (this->subsamp == TJSAMP_UNKNOWN)
1353
0
    THROW("TJPARAM_SUBSAMP must be specified");
1354
1355
0
  CATCH_LIBJPEG(this);
1356
1357
0
  cinfo->image_width = width;
1358
0
  cinfo->image_height = height;
1359
0
  cinfo->data_precision = 8;
1360
1361
0
  if (this->noRealloc) alloc = FALSE;
1362
0
  jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
1363
0
  setCompDefaults(this, TJPF_RGB, TRUE);
1364
0
  cinfo->raw_data_in = TRUE;
1365
1366
0
  jpeg_start_compress(cinfo, TRUE);
1367
0
  if (this->iccBuf != NULL && this->iccSize != 0)
1368
0
    jpeg_write_icc_profile(cinfo, this->iccBuf, (unsigned int)this->iccSize);
1369
0
  for (i = 0; i < cinfo->num_components; i++) {
1370
0
    jpeg_component_info *compptr = &cinfo->comp_info[i];
1371
0
    int ih;
1372
1373
0
    iw[i] = compptr->width_in_blocks * DCTSIZE;
1374
0
    ih = compptr->height_in_blocks * DCTSIZE;
1375
0
    pw[i] = PAD(cinfo->image_width, cinfo->max_h_samp_factor) *
1376
0
            compptr->h_samp_factor / cinfo->max_h_samp_factor;
1377
0
    if (strides && strides[i] != 0 && strides[i] < pw[i])
1378
0
      THROW("Invalid argument");
1379
0
    ph[i] = PAD(cinfo->image_height, cinfo->max_v_samp_factor) *
1380
0
            compptr->v_samp_factor / cinfo->max_v_samp_factor;
1381
0
    if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
1382
0
    th[i] = compptr->v_samp_factor * DCTSIZE;
1383
0
    tmpbufsize += iw[i] * th[i];
1384
0
    if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
1385
0
      THROW("Memory allocation failure");
1386
0
    ptr = (JSAMPLE *)srcPlanes[i];
1387
0
    for (row = 0; row < ph[i]; row++) {
1388
0
      inbuf[i][row] = ptr;
1389
0
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1390
0
    }
1391
0
  }
1392
0
  if (usetmpbuf) {
1393
0
    if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
1394
0
      THROW("Memory allocation failure");
1395
0
    ptr = _tmpbuf;
1396
0
    for (i = 0; i < cinfo->num_components; i++) {
1397
0
      if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
1398
0
        THROW("Memory allocation failure");
1399
0
      for (row = 0; row < th[i]; row++) {
1400
0
        tmpbuf[i][row] = ptr;
1401
0
        ptr += iw[i];
1402
0
      }
1403
0
    }
1404
0
  }
1405
1406
0
  CATCH_LIBJPEG(this);
1407
1408
0
  for (row = 0; row < (int)cinfo->image_height;
1409
0
       row += cinfo->max_v_samp_factor * DCTSIZE) {
1410
0
    JSAMPARRAY yuvptr[MAX_COMPONENTS];
1411
0
    int crow[MAX_COMPONENTS];
1412
1413
0
    for (i = 0; i < cinfo->num_components; i++) {
1414
0
      jpeg_component_info *compptr = &cinfo->comp_info[i];
1415
1416
0
      crow[i] = row * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1417
0
      if (usetmpbuf) {
1418
0
        int j, k;
1419
1420
0
        for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
1421
0
          memcpy(tmpbuf[i][j], inbuf[i][crow[i] + j], pw[i]);
1422
          /* Duplicate last sample in row to fill out MCU */
1423
0
          for (k = pw[i]; k < iw[i]; k++)
1424
0
            tmpbuf[i][j][k] = tmpbuf[i][j][pw[i] - 1];
1425
0
        }
1426
        /* Duplicate last row to fill out MCU */
1427
0
        for (j = ph[i] - crow[i]; j < th[i]; j++)
1428
0
          memcpy(tmpbuf[i][j], tmpbuf[i][ph[i] - crow[i] - 1], iw[i]);
1429
0
        yuvptr[i] = tmpbuf[i];
1430
0
      } else
1431
0
        yuvptr[i] = &inbuf[i][crow[i]];
1432
0
    }
1433
0
    jpeg_write_raw_data(cinfo, yuvptr, cinfo->max_v_samp_factor * DCTSIZE);
1434
0
  }
1435
0
  jpeg_finish_compress(cinfo);
1436
1437
0
bailout:
1438
0
  if (cinfo->global_state > CSTATE_START && alloc)
1439
0
    (*cinfo->dest->term_destination) (cinfo);
1440
0
  if (cinfo->global_state > CSTATE_START || retval == -1)
1441
0
    jpeg_abort_compress(cinfo);
1442
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
1443
0
    free(tmpbuf[i]);
1444
0
    free(inbuf[i]);
1445
0
  }
1446
0
  free(_tmpbuf);
1447
0
  if (this->jerr.warning) retval = -1;
1448
0
  return retval;
1449
0
}
1450
1451
/* TurboJPEG 1.4+ */
1452
DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle,
1453
                                      const unsigned char **srcPlanes,
1454
                                      int width, const int *strides,
1455
                                      int height, int subsamp,
1456
                                      unsigned char **jpegBuf,
1457
                                      unsigned long *jpegSize, int jpegQual,
1458
                                      int flags)
1459
0
{
1460
0
  static const char FUNCTION_NAME[] = "tjCompressFromYUVPlanes";
1461
0
  int retval = 0;
1462
0
  size_t size;
1463
1464
0
  GET_TJINSTANCE(handle, -1);
1465
1466
0
  if (subsamp < 0 || subsamp >= this->numSamp || jpegSize == NULL ||
1467
0
      jpegQual < 0 || jpegQual > 100)
1468
0
    THROW("Invalid argument");
1469
1470
0
  this->quality = jpegQual;
1471
0
  this->subsamp = subsamp;
1472
0
  processFlags(handle, flags, COMPRESS);
1473
1474
0
  size = (size_t)(*jpegSize);
1475
0
  if (this->noRealloc)
1476
0
    size = tj3JPEGBufSize(width, height, this->subsamp);
1477
0
  retval = tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1478
0
                                     jpegBuf, &size);
1479
0
  *jpegSize = (unsigned long)size;
1480
1481
0
bailout:
1482
0
  return retval;
1483
0
}
1484
1485
1486
/* TurboJPEG 3.0+ */
1487
DLLEXPORT int tj3CompressFromYUV8(tjhandle handle,
1488
                                  const unsigned char *srcBuf, int width,
1489
                                  int align, int height,
1490
                                  unsigned char **jpegBuf, size_t *jpegSize)
1491
0
{
1492
0
  static const char FUNCTION_NAME[] = "tj3CompressFromYUV8";
1493
0
  const unsigned char *srcPlanes[3];
1494
0
  int pw0, ph0, strides[3], retval = -1;
1495
1496
0
  GET_TJINSTANCE(handle, -1);
1497
1498
0
  if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) ||
1499
0
      height <= 0)
1500
0
    THROW("Invalid argument");
1501
1502
0
  if (this->subsamp == TJSAMP_UNKNOWN)
1503
0
    THROW("TJPARAM_SUBSAMP must be specified");
1504
1505
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1506
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1507
0
  srcPlanes[0] = srcBuf;
1508
0
  strides[0] = (int)PAD((unsigned long long)pw0, align);
1509
0
  if (this->subsamp == TJSAMP_GRAY) {
1510
0
    strides[1] = strides[2] = 0;
1511
0
    srcPlanes[1] = srcPlanes[2] = NULL;
1512
0
  } else {
1513
0
    int pw1 = tjPlaneWidth(1, width, this->subsamp);
1514
0
    int ph1 = tjPlaneHeight(1, height, this->subsamp);
1515
1516
0
    strides[1] = strides[2] = (int)PAD((unsigned long long)pw1, align);
1517
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
1518
0
        (unsigned long long)INT_MAX ||
1519
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
1520
0
        (unsigned long long)INT_MAX)
1521
0
      THROW("Image or row alignment is too large");
1522
0
    srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
1523
0
    srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
1524
0
  }
1525
1526
0
  return tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height,
1527
0
                                   jpegBuf, jpegSize);
1528
1529
0
bailout:
1530
0
  return retval;
1531
0
}
1532
1533
/* TurboJPEG 1.4+ */
1534
DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf,
1535
                                int width, int align, int height, int subsamp,
1536
                                unsigned char **jpegBuf,
1537
                                unsigned long *jpegSize, int jpegQual,
1538
                                int flags)
1539
0
{
1540
0
  static const char FUNCTION_NAME[] = "tjCompressFromYUV";
1541
0
  int retval = -1;
1542
0
  size_t size;
1543
1544
0
  GET_TJINSTANCE(handle, -1);
1545
1546
0
  if (subsamp < 0 || subsamp >= this->numSamp)
1547
0
    THROW("Invalid argument");
1548
1549
0
  this->quality = jpegQual;
1550
0
  this->subsamp = subsamp;
1551
0
  processFlags(handle, flags, COMPRESS);
1552
1553
0
  size = (size_t)(*jpegSize);
1554
0
  if (this->noRealloc)
1555
0
    size = tj3JPEGBufSize(width, height, this->subsamp);
1556
0
  retval = tj3CompressFromYUV8(handle, srcBuf, width, align, height, jpegBuf,
1557
0
                               &size);
1558
0
  *jpegSize = (unsigned long)size;
1559
1560
0
bailout:
1561
0
  return retval;
1562
0
}
1563
1564
1565
/* TurboJPEG 3.0+ */
1566
DLLEXPORT int tj3EncodeYUVPlanes8(tjhandle handle, const unsigned char *srcBuf,
1567
                                  int width, int pitch, int height,
1568
                                  int pixelFormat, unsigned char **dstPlanes,
1569
                                  int *strides)
1570
0
{
1571
0
  static const char FUNCTION_NAME[] = "tj3EncodeYUVPlanes8";
1572
0
  JSAMPROW *row_pointer = NULL;
1573
0
  JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS];
1574
0
  JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
1575
0
  JSAMPROW *outbuf[MAX_COMPONENTS];
1576
0
  int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
1577
0
  JSAMPLE *ptr;
1578
0
  jpeg_component_info *compptr;
1579
1580
0
  GET_CINSTANCE(handle)
1581
1582
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
1583
0
    tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;
1584
0
    tmpbuf2[i] = NULL;  _tmpbuf2[i] = NULL;  outbuf[i] = NULL;
1585
0
  }
1586
1587
0
  if ((this->init & COMPRESS) == 0)
1588
0
    THROW("Instance has not been initialized for compression");
1589
1590
0
  if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
1591
0
      pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
1592
0
      !dstPlanes[0])
1593
0
    THROW("Invalid argument");
1594
0
  if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
1595
0
    THROW("Invalid argument");
1596
1597
0
  if (this->subsamp == TJSAMP_UNKNOWN)
1598
0
    THROW("TJPARAM_SUBSAMP must be specified");
1599
0
  if (pixelFormat == TJPF_CMYK)
1600
0
    THROW("Cannot generate YUV images from packed-pixel CMYK images");
1601
1602
0
  if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
1603
0
  else if (pitch < width * tjPixelSize[pixelFormat])
1604
0
    THROW("Invalid argument");
1605
1606
0
  CATCH_LIBJPEG(this);
1607
1608
0
  cinfo->image_width = width;
1609
0
  cinfo->image_height = height;
1610
0
  cinfo->data_precision = 8;
1611
1612
0
  setCompDefaults(this, pixelFormat, TRUE);
1613
1614
  /* Execute only the parts of jpeg_start_compress() that we need.  If we
1615
     were to call the whole jpeg_start_compress() function, then it would try
1616
     to write the file headers, which could overflow the output buffer if the
1617
     YUV image were very small. */
1618
0
  if (cinfo->global_state != CSTATE_START)
1619
0
    THROW("libjpeg API is in the wrong state");
1620
0
  (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
1621
0
  jinit_c_master_control(cinfo, FALSE);
1622
0
  jinit_color_converter(cinfo);
1623
0
  jinit_downsampler(cinfo);
1624
0
  (*cinfo->cconvert->start_pass) (cinfo);
1625
1626
0
  pw0 = PAD(width, cinfo->max_h_samp_factor);
1627
0
  ph0 = PAD(height, cinfo->max_v_samp_factor);
1628
1629
0
  if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
1630
0
    THROW("Memory allocation failure");
1631
0
  for (i = 0; i < height; i++) {
1632
0
    if (this->bottomUp)
1633
0
      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
1634
0
    else
1635
0
      row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
1636
0
  }
1637
0
  if (height < ph0)
1638
0
    for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
1639
1640
0
  for (i = 0; i < cinfo->num_components; i++) {
1641
0
    compptr = &cinfo->comp_info[i];
1642
0
    _tmpbuf[i] = (JSAMPLE *)MALLOC(
1643
0
      PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1644
0
          compptr->h_samp_factor, 32) *
1645
0
      cinfo->max_v_samp_factor + 32);
1646
0
    if (!_tmpbuf[i])
1647
0
      THROW("Memory allocation failure");
1648
0
    tmpbuf[i] =
1649
0
      (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
1650
0
    if (!tmpbuf[i])
1651
0
      THROW("Memory allocation failure");
1652
0
    for (row = 0; row < cinfo->max_v_samp_factor; row++) {
1653
0
      unsigned char *_tmpbuf_aligned =
1654
0
        (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
1655
1656
0
      tmpbuf[i][row] = &_tmpbuf_aligned[
1657
0
        PAD((compptr->width_in_blocks * cinfo->max_h_samp_factor * DCTSIZE) /
1658
0
            compptr->h_samp_factor, 32) * row];
1659
0
    }
1660
0
    _tmpbuf2[i] =
1661
0
      (JSAMPLE *)MALLOC(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
1662
0
                        compptr->v_samp_factor + 32);
1663
0
    if (!_tmpbuf2[i])
1664
0
      THROW("Memory allocation failure");
1665
0
    tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
1666
0
    if (!tmpbuf2[i])
1667
0
      THROW("Memory allocation failure");
1668
0
    for (row = 0; row < compptr->v_samp_factor; row++) {
1669
0
      unsigned char *_tmpbuf2_aligned =
1670
0
        (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32);
1671
1672
0
      tmpbuf2[i][row] =
1673
0
        &_tmpbuf2_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
1674
0
    }
1675
0
    pw[i] = pw0 * compptr->h_samp_factor / cinfo->max_h_samp_factor;
1676
0
    if (strides && strides[i] != 0 && strides[i] < pw[i])
1677
0
      THROW("Invalid argument");
1678
0
    ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
1679
0
    outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
1680
0
    if (!outbuf[i])
1681
0
      THROW("Memory allocation failure");
1682
0
    ptr = dstPlanes[i];
1683
0
    for (row = 0; row < ph[i]; row++) {
1684
0
      outbuf[i][row] = ptr;
1685
0
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
1686
0
    }
1687
0
  }
1688
1689
0
  CATCH_LIBJPEG(this);
1690
1691
0
  for (row = 0; row < ph0; row += cinfo->max_v_samp_factor) {
1692
0
    (*cinfo->cconvert->color_convert) (cinfo, &row_pointer[row], tmpbuf, 0,
1693
0
                                       cinfo->max_v_samp_factor);
1694
0
    (cinfo->downsample->downsample) (cinfo, tmpbuf, 0, tmpbuf2, 0);
1695
0
    for (i = 0, compptr = cinfo->comp_info; i < cinfo->num_components;
1696
0
         i++, compptr++)
1697
0
      jcopy_sample_rows(tmpbuf2[i], 0, outbuf[i],
1698
0
        row * compptr->v_samp_factor / cinfo->max_v_samp_factor,
1699
0
        compptr->v_samp_factor, pw[i]);
1700
0
  }
1701
0
  cinfo->next_scanline += height;
1702
0
  jpeg_abort_compress(cinfo);
1703
1704
0
bailout:
1705
0
  if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
1706
0
  free(row_pointer);
1707
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
1708
0
    free(tmpbuf[i]);
1709
0
    free(_tmpbuf[i]);
1710
0
    free(tmpbuf2[i]);
1711
0
    free(_tmpbuf2[i]);
1712
0
    free(outbuf[i]);
1713
0
  }
1714
0
  if (this->jerr.warning) retval = -1;
1715
0
  return retval;
1716
0
}
1717
1718
/* TurboJPEG 1.4+ */
1719
DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf,
1720
                                int width, int pitch, int height,
1721
                                int pixelFormat, unsigned char **dstPlanes,
1722
                                int *strides, int subsamp, int flags)
1723
0
{
1724
0
  static const char FUNCTION_NAME[] = "tjEncodeYUVPlanes";
1725
0
  int retval = 0;
1726
1727
0
  GET_TJINSTANCE(handle, -1);
1728
1729
0
  if (subsamp < 0 || subsamp >= this->numSamp)
1730
0
    THROW("Invalid argument");
1731
1732
0
  this->subsamp = subsamp;
1733
0
  processFlags(handle, flags, COMPRESS);
1734
1735
0
  return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1736
0
                             dstPlanes, strides);
1737
1738
0
bailout:
1739
0
  return retval;
1740
0
}
1741
1742
1743
/* TurboJPEG 3.0+ */
1744
DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf,
1745
                            int width, int pitch, int height, int pixelFormat,
1746
                            unsigned char *dstBuf, int align)
1747
0
{
1748
0
  static const char FUNCTION_NAME[] = "tj3EncodeYUV8";
1749
0
  unsigned char *dstPlanes[3];
1750
0
  int pw0, ph0, strides[3], retval = -1;
1751
1752
0
  GET_TJINSTANCE(handle, -1);
1753
1754
0
  if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 ||
1755
0
      !IS_POW2(align))
1756
0
    THROW("Invalid argument");
1757
1758
0
  if (this->subsamp == TJSAMP_UNKNOWN)
1759
0
    THROW("TJPARAM_SUBSAMP must be specified");
1760
1761
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
1762
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
1763
0
  dstPlanes[0] = dstBuf;
1764
0
  strides[0] = (int)PAD((unsigned long long)pw0, align);
1765
0
  if (this->subsamp == TJSAMP_GRAY) {
1766
0
    strides[1] = strides[2] = 0;
1767
0
    dstPlanes[1] = dstPlanes[2] = NULL;
1768
0
  } else {
1769
0
    int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
1770
0
    int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
1771
1772
0
    strides[1] = strides[2] = (int)PAD((unsigned long long)pw1, align);
1773
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
1774
0
        (unsigned long long)INT_MAX ||
1775
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
1776
0
        (unsigned long long)INT_MAX)
1777
0
      THROW("Image or row alignment is too large");
1778
0
    dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
1779
0
    dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
1780
0
  }
1781
1782
0
  return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat,
1783
0
                             dstPlanes, strides);
1784
1785
0
bailout:
1786
0
  return retval;
1787
0
}
1788
1789
/* TurboJPEG 1.4+ */
1790
DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf,
1791
                           int width, int pitch, int height, int pixelFormat,
1792
                           unsigned char *dstBuf, int align, int subsamp,
1793
                           int flags)
1794
0
{
1795
0
  static const char FUNCTION_NAME[] = "tjEncodeYUV3";
1796
0
  int retval = 0;
1797
1798
0
  GET_TJINSTANCE(handle, -1);
1799
1800
0
  if (subsamp < 0 || subsamp >= this->numSamp)
1801
0
    THROW("Invalid argument");
1802
1803
0
  this->subsamp = subsamp;
1804
0
  processFlags(handle, flags, COMPRESS);
1805
1806
0
  return tj3EncodeYUV8(handle, srcBuf, width, pitch, height, pixelFormat,
1807
0
                       dstBuf, align);
1808
1809
0
bailout:
1810
0
  return retval;
1811
0
}
1812
1813
/* TurboJPEG 1.2+ */
1814
DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width,
1815
                           int pitch, int height, int pixelFormat,
1816
                           unsigned char *dstBuf, int subsamp, int flags)
1817
0
{
1818
0
  return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat,
1819
0
                      dstBuf, 4, subsamp, flags);
1820
0
}
1821
1822
/* TurboJPEG 1.1+ */
1823
DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width,
1824
                          int pitch, int height, int pixelSize,
1825
                          unsigned char *dstBuf, int subsamp, int flags)
1826
0
{
1827
0
  return tjEncodeYUV2(handle, srcBuf, width, pitch, height,
1828
0
                      getPixelFormat(pixelSize, flags), dstBuf, subsamp,
1829
0
                      flags);
1830
0
}
1831
1832
1833
/******************************* Decompressor ********************************/
1834
1835
static tjhandle _tjInitDecompress(tjinstance *this)
1836
0
{
1837
0
  static unsigned char buffer[1];
1838
1839
  /* This is also straight out of example.c */
1840
0
  this->dinfo.err = jpeg_std_error(&this->jerr.pub);
1841
0
  this->jerr.pub.error_exit = my_error_exit;
1842
0
  this->jerr.pub.output_message = my_output_message;
1843
0
  this->jerr.emit_message = this->jerr.pub.emit_message;
1844
0
  this->jerr.pub.emit_message = my_emit_message;
1845
0
  this->jerr.pub.addon_message_table = turbojpeg_message_table;
1846
0
  this->jerr.pub.first_addon_message = JMSG_FIRSTADDONCODE;
1847
0
  this->jerr.pub.last_addon_message = JMSG_LASTADDONCODE;
1848
0
  this->jerr.tjHandle = (tjhandle)this;
1849
1850
0
  if (setjmp(this->jerr.setjmp_buffer)) {
1851
    /* If we get here, the JPEG code has signaled an error. */
1852
0
    free(this);
1853
0
    return NULL;
1854
0
  }
1855
1856
0
  jpeg_create_decompress(&this->dinfo);
1857
  /* Make an initial call so it will create the source manager */
1858
0
  jpeg_mem_src_tj(&this->dinfo, buffer, 1);
1859
1860
0
  this->init |= DECOMPRESS;
1861
0
  return (tjhandle)this;
1862
0
}
1863
1864
/* TurboJPEG 1.0+ */
1865
DLLEXPORT tjhandle tjInitDecompress(void)
1866
0
{
1867
0
  return tj3Init(TJINIT_DECOMPRESS);
1868
0
}
1869
1870
1871
/* TurboJPEG 3.0+ */
1872
DLLEXPORT int tj3DecompressHeader(tjhandle handle,
1873
                                  const unsigned char *jpegBuf,
1874
                                  size_t jpegSize)
1875
0
{
1876
0
  static const char FUNCTION_NAME[] = "tj3DecompressHeader";
1877
0
  int retval = 0;
1878
0
  unsigned char *iccPtr = NULL;
1879
0
  unsigned int iccLen = 0;
1880
1881
0
  GET_DINSTANCE(handle);
1882
0
  if ((this->init & DECOMPRESS) == 0)
1883
0
    THROW("Instance has not been initialized for decompression");
1884
1885
0
  if (jpegBuf == NULL || jpegSize <= 0)
1886
0
    THROW("Invalid argument");
1887
1888
0
  CATCH_LIBJPEG(this);
1889
1890
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
1891
1892
  /* Extract ICC profile if TJPARAM_SAVEMARKERS is 2 or 4.  (We could
1893
     eventually reuse this mechanism to save other markers, if needed.)
1894
     Because ICC profiles can be large, we extract them by default but allow
1895
     the user to override that behavior. */
1896
0
#ifdef SAVE_MARKERS_SUPPORTED
1897
0
  if (this->saveMarkers == 2 || this->saveMarkers == 4)
1898
0
    jpeg_save_markers(dinfo, JPEG_APP0 + 2, 0xFFFF);
1899
0
#endif
1900
  /* jpeg_read_header() calls jpeg_abort() and returns JPEG_HEADER_TABLES_ONLY
1901
     if the datastream is a tables-only datastream.  Since we aren't using a
1902
     suspending data source, the only other value it can return is
1903
     JPEG_HEADER_OK. */
1904
0
  if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY)
1905
0
    return 0;
1906
1907
0
  setDecompParameters(this);
1908
1909
0
  if (this->saveMarkers == 2 || this->saveMarkers == 4) {
1910
0
    if (jpeg_read_icc_profile(dinfo, &iccPtr, &iccLen)) {
1911
0
      free(this->decompICCBuf);
1912
0
      this->decompICCBuf = iccPtr;
1913
0
      this->decompICCSize = (size_t)iccLen;
1914
0
    }
1915
0
  }
1916
1917
0
  jpeg_abort_decompress(dinfo);
1918
1919
0
  if (this->colorspace == TJCS_DEFAULT)
1920
0
    THROW("Could not determine colorspace of JPEG image");
1921
0
  if (this->jpegWidth < 1 || this->jpegHeight < 1)
1922
0
    THROW("Invalid data returned in header");
1923
1924
0
bailout:
1925
0
  if (this->jerr.warning) retval = -1;
1926
0
  return retval;
1927
0
}
1928
1929
/* TurboJPEG 1.4+ */
1930
DLLEXPORT int tjDecompressHeader3(tjhandle handle,
1931
                                  const unsigned char *jpegBuf,
1932
                                  unsigned long jpegSize, int *width,
1933
                                  int *height, int *jpegSubsamp,
1934
                                  int *jpegColorspace)
1935
0
{
1936
0
  static const char FUNCTION_NAME[] = "tjDecompressHeader3";
1937
0
  int retval = 0;
1938
1939
0
  GET_TJINSTANCE(handle, -1);
1940
1941
0
  if (width == NULL || height == NULL || jpegSubsamp == NULL ||
1942
0
      jpegColorspace == NULL)
1943
0
    THROW("Invalid argument");
1944
1945
0
  retval = tj3DecompressHeader(handle, jpegBuf, jpegSize);
1946
1947
0
  *width = tj3Get(handle, TJPARAM_JPEGWIDTH);
1948
0
  *height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
1949
0
  *jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
1950
0
  if (*jpegSubsamp == TJSAMP_UNKNOWN)
1951
0
    THROW("Could not determine subsampling level of JPEG image");
1952
0
  *jpegColorspace = tj3Get(handle, TJPARAM_COLORSPACE);
1953
1954
0
bailout:
1955
0
  return retval;
1956
0
}
1957
1958
/* TurboJPEG 1.1+ */
1959
DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf,
1960
                                  unsigned long jpegSize, int *width,
1961
                                  int *height, int *jpegSubsamp)
1962
0
{
1963
0
  int jpegColorspace;
1964
1965
0
  return tjDecompressHeader3(handle, jpegBuf, jpegSize, width, height,
1966
0
                             jpegSubsamp, &jpegColorspace);
1967
0
}
1968
1969
/* TurboJPEG 1.0+ */
1970
DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf,
1971
                                 unsigned long jpegSize, int *width,
1972
                                 int *height)
1973
0
{
1974
0
  int jpegSubsamp;
1975
1976
0
  return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
1977
0
                             &jpegSubsamp);
1978
0
}
1979
1980
1981
/* TurboJPEG 3.1+ */
1982
DLLEXPORT int tj3GetICCProfile(tjhandle handle, unsigned char **iccBuf,
1983
                               size_t *iccSize)
1984
0
{
1985
0
  static const char FUNCTION_NAME[] = "tj3GetICCProfile";
1986
0
  int retval = 0;
1987
1988
0
  GET_TJINSTANCE(handle, -1);
1989
1990
0
  if (iccSize == NULL)
1991
0
    THROW("Invalid argument");
1992
1993
0
  if (this->init & DECOMPRESS) {
1994
0
    if (!this->decompICCBuf || !this->decompICCSize) {
1995
0
      if (iccBuf) *iccBuf = NULL;
1996
0
      *iccSize = 0;
1997
0
      this->jerr.warning = TRUE;
1998
0
      THROW("No ICC profile data has been extracted");
1999
0
    }
2000
0
    *iccSize = this->decompICCSize;
2001
0
  } else
2002
0
    *iccSize = this->iccSize;
2003
0
  if (iccBuf == NULL)
2004
0
    return 0;
2005
0
  if (*iccSize) {
2006
0
    if ((*iccBuf = (unsigned char *)malloc(*iccSize)) == NULL)
2007
0
      THROW("Memory allocation failure");
2008
0
    if (this->init & DECOMPRESS)
2009
0
      memcpy(*iccBuf, this->decompICCBuf, *iccSize);
2010
0
    else
2011
0
      memcpy(*iccBuf, this->iccBuf, *iccSize);
2012
0
  } else
2013
0
    *iccBuf = NULL;
2014
2015
0
bailout:
2016
0
  return retval;
2017
0
}
2018
2019
2020
/* TurboJPEG 3.0+ */
2021
DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors)
2022
0
{
2023
0
  static const char FUNCTION_NAME[] = "tj3GetScalingFactors";
2024
0
  tjscalingfactor *retval = (tjscalingfactor *)sf;
2025
2026
0
  if (numScalingFactors == NULL)
2027
0
    THROWG("Invalid argument", NULL);
2028
2029
0
  *numScalingFactors = NUMSF;
2030
2031
0
bailout:
2032
0
  return retval;
2033
0
}
2034
2035
/* TurboJPEG 1.2+ */
2036
DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors)
2037
0
{
2038
0
  return tj3GetScalingFactors(numScalingFactors);
2039
0
}
2040
2041
2042
/* TurboJPEG 3.0+ */
2043
DLLEXPORT int tj3SetScalingFactor(tjhandle handle,
2044
                                  tjscalingfactor scalingFactor)
2045
0
{
2046
0
  static const char FUNCTION_NAME[] = "tj3SetScalingFactor";
2047
0
  int i, retval = 0;
2048
2049
0
  GET_TJINSTANCE(handle, -1);
2050
0
  if ((this->init & DECOMPRESS) == 0)
2051
0
    THROW("Instance has not been initialized for decompression");
2052
2053
0
  for (i = 0; i < NUMSF; i++) {
2054
0
    if (scalingFactor.num == sf[i].num && scalingFactor.denom == sf[i].denom)
2055
0
      break;
2056
0
  }
2057
0
  if (i >= NUMSF)
2058
0
    THROW("Unsupported scaling factor");
2059
2060
0
  this->scalingFactor = scalingFactor;
2061
2062
0
bailout:
2063
0
  return retval;
2064
0
}
2065
2066
2067
/* TurboJPEG 3.0+ */
2068
DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion)
2069
0
{
2070
0
  static const char FUNCTION_NAME[] = "tj3SetCroppingRegion";
2071
0
  int retval = 0, scaledWidth, scaledHeight;
2072
2073
0
  GET_TJINSTANCE(handle, -1);
2074
0
  if ((this->init & DECOMPRESS) == 0)
2075
0
    THROW("Instance has not been initialized for decompression");
2076
2077
0
  if (croppingRegion.x == 0 && croppingRegion.y == 0 &&
2078
0
      croppingRegion.w == 0 && croppingRegion.h == 0) {
2079
0
    this->croppingRegion = croppingRegion;
2080
0
    return 0;
2081
0
  }
2082
2083
0
  if (croppingRegion.x < 0 || croppingRegion.y < 0 || croppingRegion.w < 0 ||
2084
0
      croppingRegion.h < 0)
2085
0
    THROW("Invalid cropping region");
2086
0
  if (this->jpegWidth < 0 || this->jpegHeight < 0)
2087
0
    THROW("JPEG header has not yet been read");
2088
0
  if ((this->precision != 8 && this->precision != 12) || this->lossless)
2089
0
    THROW("Cannot partially decompress lossless JPEG images");
2090
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2091
0
    THROW("Could not determine subsampling level of JPEG image");
2092
2093
0
  scaledWidth = TJSCALED(this->jpegWidth, this->scalingFactor);
2094
0
  scaledHeight = TJSCALED(this->jpegHeight, this->scalingFactor);
2095
2096
0
  if (croppingRegion.x %
2097
0
      TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor) != 0)
2098
0
    THROWI("The left boundary of the cropping region (%d) is not\n"
2099
0
           "divisible by the scaled iMCU width (%d)",
2100
0
           croppingRegion.x,
2101
0
           TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor));
2102
0
  if (croppingRegion.w == 0)
2103
0
    croppingRegion.w = scaledWidth - croppingRegion.x;
2104
0
  if (croppingRegion.h == 0)
2105
0
    croppingRegion.h = scaledHeight - croppingRegion.y;
2106
0
  if (croppingRegion.w <= 0 || croppingRegion.h <= 0 ||
2107
0
      croppingRegion.x + croppingRegion.w > scaledWidth ||
2108
0
      croppingRegion.y + croppingRegion.h > scaledHeight)
2109
0
    THROW("The cropping region exceeds the scaled image dimensions");
2110
2111
0
  this->croppingRegion = croppingRegion;
2112
2113
0
bailout:
2114
0
  return retval;
2115
0
}
2116
2117
2118
/* tj3Decompress*() is implemented in turbojpeg-mp.c */
2119
2120
/* TurboJPEG 1.2+ */
2121
DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
2122
                            unsigned long jpegSize, unsigned char *dstBuf,
2123
                            int width, int pitch, int height, int pixelFormat,
2124
                            int flags)
2125
0
{
2126
0
  static const char FUNCTION_NAME[] = "tjDecompress2";
2127
0
  int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2128
2129
0
  GET_DINSTANCE(handle);
2130
0
  if ((this->init & DECOMPRESS) == 0)
2131
0
    THROW("Instance has not been initialized for decompression");
2132
2133
0
  if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2134
0
    THROW("Invalid argument");
2135
2136
0
  CATCH_LIBJPEG(this);
2137
2138
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2139
0
  jpeg_read_header(dinfo, TRUE);
2140
0
  jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2141
0
  if (width == 0) width = jpegwidth;
2142
0
  if (height == 0) height = jpegheight;
2143
0
  for (i = 0; i < NUMSF; i++) {
2144
0
    scaledw = TJSCALED(jpegwidth, sf[i]);
2145
0
    scaledh = TJSCALED(jpegheight, sf[i]);
2146
0
    if (scaledw <= width && scaledh <= height)
2147
0
      break;
2148
0
  }
2149
0
  if (i >= NUMSF)
2150
0
    THROW("Could not scale down to desired image dimensions");
2151
0
  if (dinfo->master->lossless && ((JDIMENSION)scaledw != dinfo->image_width ||
2152
0
                                  (JDIMENSION)scaledh != dinfo->image_height))
2153
0
    THROW("Cannot use decompression scaling with lossless JPEG images");
2154
2155
0
  processFlags(handle, flags, DECOMPRESS);
2156
2157
0
  if (tj3SetScalingFactor(handle, sf[i]) == -1)
2158
0
    return -1;
2159
0
  if (tj3SetCroppingRegion(handle, TJUNCROPPED) == -1)
2160
0
    return -1;
2161
0
  return tj3Decompress8(handle, jpegBuf, jpegSize, dstBuf, pitch, pixelFormat);
2162
2163
0
bailout:
2164
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2165
0
  if (this->jerr.warning) retval = -1;
2166
0
  return retval;
2167
0
}
2168
2169
/* TurboJPEG 1.0+ */
2170
DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf,
2171
                           unsigned long jpegSize, unsigned char *dstBuf,
2172
                           int width, int pitch, int height, int pixelSize,
2173
                           int flags)
2174
0
{
2175
0
  if (flags & TJ_YUV)
2176
0
    return tjDecompressToYUV(handle, jpegBuf, jpegSize, dstBuf, flags);
2177
0
  else
2178
0
    return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
2179
0
                         height, getPixelFormat(pixelSize, flags), flags);
2180
0
}
2181
2182
2183
/* TurboJPEG 3.0+ */
2184
DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle,
2185
                                        const unsigned char *jpegBuf,
2186
                                        size_t jpegSize,
2187
                                        unsigned char **dstPlanes,
2188
                                        int *strides)
2189
0
{
2190
0
  static const char FUNCTION_NAME[] = "tj3DecompressToYUVPlanes8";
2191
0
  int i, row, retval = 0;
2192
0
  int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS],
2193
0
    tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS];
2194
0
  JSAMPLE *_tmpbuf = NULL, *ptr;
2195
0
  JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
2196
0
  int dctsize;
2197
0
  struct my_progress_mgr progress;
2198
2199
0
  GET_DINSTANCE(handle);
2200
2201
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2202
0
    tmpbuf[i] = NULL;  outbuf[i] = NULL;
2203
0
  }
2204
2205
0
  if ((this->init & DECOMPRESS) == 0)
2206
0
    THROW("Instance has not been initialized for decompression");
2207
2208
0
  if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0])
2209
0
    THROW("Invalid argument");
2210
2211
0
  if (this->scanLimit) {
2212
0
    memset(&progress, 0, sizeof(struct my_progress_mgr));
2213
0
    progress.pub.progress_monitor = my_progress_monitor;
2214
0
    progress.this = this;
2215
0
    dinfo->progress = &progress.pub;
2216
0
  } else
2217
0
    dinfo->progress = NULL;
2218
2219
0
  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2220
2221
0
  CATCH_LIBJPEG(this);
2222
2223
0
  if (dinfo->global_state <= DSTATE_INHEADER) {
2224
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2225
0
    jpeg_read_header(dinfo, TRUE);
2226
0
  }
2227
0
  setDecompParameters(this);
2228
0
  if (this->maxPixels &&
2229
0
      (unsigned long long)this->jpegWidth * this->jpegHeight >
2230
0
      (unsigned long long)this->maxPixels)
2231
0
    THROW("Image is too large");
2232
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2233
0
    THROW("Could not determine subsampling level of JPEG image");
2234
2235
0
  if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
2236
0
    THROW("Invalid argument");
2237
2238
0
  if (dinfo->num_components > 3)
2239
0
    THROW("JPEG image must have 3 or fewer components");
2240
2241
0
  dinfo->scale_num = this->scalingFactor.num;
2242
0
  dinfo->scale_denom = this->scalingFactor.denom;
2243
0
  jpeg_calc_output_dimensions(dinfo);
2244
2245
0
  dctsize = DCTSIZE * this->scalingFactor.num / this->scalingFactor.denom;
2246
2247
0
  for (i = 0; i < dinfo->num_components; i++) {
2248
0
    jpeg_component_info *compptr = &dinfo->comp_info[i];
2249
0
    int ih;
2250
2251
0
    iw[i] = compptr->width_in_blocks * dctsize;
2252
0
    ih = compptr->height_in_blocks * dctsize;
2253
0
    pw[i] = tj3YUVPlaneWidth(i, dinfo->output_width, this->subsamp);
2254
0
    if (strides && strides[i] != 0 && strides[i] < pw[i])
2255
0
      THROW("Invalid argument");
2256
0
    ph[i] = tj3YUVPlaneHeight(i, dinfo->output_height, this->subsamp);
2257
0
    if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1;
2258
0
    th[i] = compptr->v_samp_factor * dctsize;
2259
0
    tmpbufsize += iw[i] * th[i];
2260
0
    if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
2261
0
      THROW("Memory allocation failure");
2262
0
    ptr = dstPlanes[i];
2263
0
    for (row = 0; row < ph[i]; row++) {
2264
0
      outbuf[i][row] = ptr;
2265
0
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2266
0
    }
2267
0
  }
2268
0
  if (usetmpbuf) {
2269
0
    if ((_tmpbuf = (JSAMPLE *)MALLOC(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
2270
0
      THROW("Memory allocation failure");
2271
0
    ptr = _tmpbuf;
2272
0
    for (i = 0; i < dinfo->num_components; i++) {
2273
0
      if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
2274
0
        THROW("Memory allocation failure");
2275
0
      for (row = 0; row < th[i]; row++) {
2276
0
        tmpbuf[i][row] = ptr;
2277
0
        ptr += iw[i];
2278
0
      }
2279
0
    }
2280
0
  }
2281
2282
0
  CATCH_LIBJPEG(this);
2283
2284
0
  dinfo->do_fancy_upsampling = !this->fastUpsample;
2285
0
  dinfo->dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2286
0
  dinfo->raw_data_out = TRUE;
2287
2288
0
  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2289
2290
0
  jpeg_start_decompress(dinfo);
2291
0
  for (row = 0; row < (int)dinfo->output_height;
2292
0
       row += dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size) {
2293
0
    JSAMPARRAY yuvptr[MAX_COMPONENTS];
2294
0
    int crow[MAX_COMPONENTS];
2295
2296
0
    for (i = 0; i < dinfo->num_components; i++) {
2297
0
      jpeg_component_info *compptr = &dinfo->comp_info[i];
2298
2299
0
      if (this->subsamp == TJSAMP_420 || this->subsamp == TJSAMP_410 ||
2300
0
          this->subsamp == TJSAMP_24) {
2301
        /* When 4:2:0, 4:1:0, or 2:4 subsampling is used with IDCT scaling,
2302
           libjpeg will try to be clever and use the IDCT to perform upsampling
2303
           on the U and V planes.  For instance, if the output image is to be
2304
           scaled by 1/2 relative to the JPEG image, then the scaling factor
2305
           and upsampling effectively cancel each other, so a normal 8x8 IDCT
2306
           can be used. However, this is not desirable when using the
2307
           decompress-to-YUV functionality in TurboJPEG, since we want to
2308
           output the U and V planes in their subsampled form.  Thus, we have
2309
           to override some internal libjpeg parameters to force it to use the
2310
           "scaled" IDCT functions on the U and V planes. */
2311
0
        compptr->_DCT_scaled_size = dctsize;
2312
0
        compptr->MCU_sample_width = tjMCUWidth[this->subsamp] *
2313
0
          this->scalingFactor.num / this->scalingFactor.denom *
2314
0
          compptr->h_samp_factor / dinfo->max_h_samp_factor;
2315
0
        dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0];
2316
0
      }
2317
0
      crow[i] = row * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2318
0
      if (usetmpbuf) yuvptr[i] = tmpbuf[i];
2319
0
      else yuvptr[i] = &outbuf[i][crow[i]];
2320
0
    }
2321
0
    jpeg_read_raw_data(dinfo, yuvptr,
2322
0
                       dinfo->max_v_samp_factor * dinfo->_min_DCT_scaled_size);
2323
0
    if (usetmpbuf) {
2324
0
      int j;
2325
2326
0
      for (i = 0; i < dinfo->num_components; i++) {
2327
0
        for (j = 0; j < MIN(th[i], ph[i] - crow[i]); j++) {
2328
0
          memcpy(outbuf[i][crow[i] + j], tmpbuf[i][j], pw[i]);
2329
0
        }
2330
0
      }
2331
0
    }
2332
0
  }
2333
0
  jpeg_finish_decompress(dinfo);
2334
2335
0
bailout:
2336
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2337
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2338
0
    free(tmpbuf[i]);
2339
0
    free(outbuf[i]);
2340
0
  }
2341
0
  free(_tmpbuf);
2342
0
  if (this->jerr.warning) retval = -1;
2343
0
  return retval;
2344
0
}
2345
2346
/* TurboJPEG 1.4+ */
2347
DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle,
2348
                                      const unsigned char *jpegBuf,
2349
                                      unsigned long jpegSize,
2350
                                      unsigned char **dstPlanes, int width,
2351
                                      int *strides, int height, int flags)
2352
0
{
2353
0
  static const char FUNCTION_NAME[] = "tjDecompressToYUVPlanes";
2354
0
  int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2355
2356
0
  GET_DINSTANCE(handle);
2357
0
  if ((this->init & DECOMPRESS) == 0)
2358
0
    THROW("Instance has not been initialized for decompression");
2359
2360
0
  if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2361
0
    THROW("Invalid argument");
2362
2363
0
  CATCH_LIBJPEG(this);
2364
2365
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2366
0
  jpeg_read_header(dinfo, TRUE);
2367
0
  jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2368
0
  if (width == 0) width = jpegwidth;
2369
0
  if (height == 0) height = jpegheight;
2370
0
  for (i = 0; i < NUMSF; i++) {
2371
0
    scaledw = TJSCALED(jpegwidth, sf[i]);
2372
0
    scaledh = TJSCALED(jpegheight, sf[i]);
2373
0
    if (scaledw <= width && scaledh <= height)
2374
0
      break;
2375
0
  }
2376
0
  if (i >= NUMSF)
2377
0
    THROW("Could not scale down to desired image dimensions");
2378
2379
0
  processFlags(handle, flags, DECOMPRESS);
2380
2381
0
  if (tj3SetScalingFactor(handle, sf[i]) == -1)
2382
0
    return -1;
2383
0
  return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2384
0
                                   strides);
2385
2386
0
bailout:
2387
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2388
0
  if (this->jerr.warning) retval = -1;
2389
0
  return retval;
2390
0
}
2391
2392
2393
/* TurboJPEG 3.0+ */
2394
DLLEXPORT int tj3DecompressToYUV8(tjhandle handle,
2395
                                  const unsigned char *jpegBuf,
2396
                                  size_t jpegSize,
2397
                                  unsigned char *dstBuf, int align)
2398
0
{
2399
0
  static const char FUNCTION_NAME[] = "tj3DecompressToYUV8";
2400
0
  unsigned char *dstPlanes[3];
2401
0
  int pw0, ph0, strides[3], retval = -1;
2402
0
  int width, height;
2403
2404
0
  GET_DINSTANCE(handle);
2405
2406
0
  if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || align < 1 ||
2407
0
      !IS_POW2(align))
2408
0
    THROW("Invalid argument");
2409
2410
0
  CATCH_LIBJPEG(this);
2411
2412
0
  if (dinfo->global_state <= DSTATE_INHEADER) {
2413
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2414
0
    jpeg_read_header(dinfo, TRUE);
2415
0
  }
2416
0
  setDecompParameters(this);
2417
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2418
0
    THROW("Could not determine subsampling level of JPEG image");
2419
2420
0
  width = TJSCALED(dinfo->image_width, this->scalingFactor);
2421
0
  height = TJSCALED(dinfo->image_height, this->scalingFactor);
2422
2423
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2424
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2425
0
  dstPlanes[0] = dstBuf;
2426
0
  strides[0] = PAD(pw0, align);
2427
0
  if (this->subsamp == TJSAMP_GRAY) {
2428
0
    strides[1] = strides[2] = 0;
2429
0
    dstPlanes[1] = dstPlanes[2] = NULL;
2430
0
  } else {
2431
0
    int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2432
0
    int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2433
2434
0
    strides[1] = strides[2] = PAD(pw1, align);
2435
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
2436
0
        (unsigned long long)INT_MAX ||
2437
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
2438
0
        (unsigned long long)INT_MAX)
2439
0
      THROW("Image or row alignment is too large");
2440
0
    dstPlanes[1] = dstPlanes[0] + strides[0] * ph0;
2441
0
    dstPlanes[2] = dstPlanes[1] + strides[1] * ph1;
2442
0
  }
2443
2444
0
  return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes,
2445
0
                                   strides);
2446
2447
0
bailout:
2448
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2449
0
  if (this->jerr.warning) retval = -1;
2450
0
  return retval;
2451
0
}
2452
2453
/* TurboJPEG 1.4+ */
2454
DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf,
2455
                                 unsigned long jpegSize, unsigned char *dstBuf,
2456
                                 int width, int align, int height, int flags)
2457
0
{
2458
0
  static const char FUNCTION_NAME[] = "tjDecompressToYUV2";
2459
0
  int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
2460
2461
0
  GET_DINSTANCE(handle);
2462
0
  if ((this->init & DECOMPRESS) == 0)
2463
0
    THROW("Instance has not been initialized for decompression");
2464
2465
0
  if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0)
2466
0
    THROW("Invalid argument");
2467
2468
0
  CATCH_LIBJPEG(this);
2469
2470
0
  jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2471
0
  jpeg_read_header(dinfo, TRUE);
2472
0
  jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
2473
0
  if (width == 0) width = jpegwidth;
2474
0
  if (height == 0) height = jpegheight;
2475
0
  for (i = 0; i < NUMSF; i++) {
2476
0
    scaledw = TJSCALED(jpegwidth, sf[i]);
2477
0
    scaledh = TJSCALED(jpegheight, sf[i]);
2478
0
    if (scaledw <= width && scaledh <= height)
2479
0
      break;
2480
0
  }
2481
0
  if (i >= NUMSF)
2482
0
    THROW("Could not scale down to desired image dimensions");
2483
2484
0
  width = scaledw;  height = scaledh;
2485
2486
0
  processFlags(handle, flags, DECOMPRESS);
2487
2488
0
  if (tj3SetScalingFactor(handle, sf[i]) == -1)
2489
0
    return -1;
2490
0
  return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align);
2491
2492
0
bailout:
2493
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2494
0
  if (this->jerr.warning) retval = -1;
2495
0
  return retval;
2496
0
}
2497
2498
/* TurboJPEG 1.1+ */
2499
DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf,
2500
                                unsigned long jpegSize, unsigned char *dstBuf,
2501
                                int flags)
2502
0
{
2503
0
  return tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, 0, 4, 0, flags);
2504
0
}
2505
2506
2507
static void setDecodeDefaults(tjinstance *this, int pixelFormat)
2508
0
{
2509
0
  int i;
2510
2511
0
  this->dinfo.scale_num = this->dinfo.scale_denom = 1;
2512
2513
0
  if (this->subsamp == TJSAMP_GRAY) {
2514
0
    this->dinfo.num_components = this->dinfo.comps_in_scan = 1;
2515
0
    this->dinfo.jpeg_color_space = JCS_GRAYSCALE;
2516
0
  } else {
2517
0
    this->dinfo.num_components = this->dinfo.comps_in_scan = 3;
2518
0
    this->dinfo.jpeg_color_space = JCS_YCbCr;
2519
0
  }
2520
2521
0
  this->dinfo.comp_info = (jpeg_component_info *)
2522
0
    (*this->dinfo.mem->alloc_small) ((j_common_ptr)&this->dinfo, JPOOL_IMAGE,
2523
0
                                     this->dinfo.num_components *
2524
0
                                     sizeof(jpeg_component_info));
2525
2526
0
  for (i = 0; i < this->dinfo.num_components; i++) {
2527
0
    jpeg_component_info *compptr = &this->dinfo.comp_info[i];
2528
2529
0
    compptr->h_samp_factor = (i == 0) ? tjMCUWidth[this->subsamp] / 8 : 1;
2530
0
    compptr->v_samp_factor = (i == 0) ? tjMCUHeight[this->subsamp] / 8 : 1;
2531
0
    compptr->component_index = i;
2532
0
    compptr->component_id = i + 1;
2533
0
    compptr->quant_tbl_no = compptr->dc_tbl_no =
2534
0
      compptr->ac_tbl_no = (i == 0) ? 0 : 1;
2535
0
    this->dinfo.cur_comp_info[i] = compptr;
2536
0
  }
2537
0
  this->dinfo.data_precision = 8;
2538
0
  for (i = 0; i < 2; i++) {
2539
0
    if (this->dinfo.quant_tbl_ptrs[i] == NULL)
2540
0
      this->dinfo.quant_tbl_ptrs[i] =
2541
0
        jpeg_alloc_quant_table((j_common_ptr)&this->dinfo);
2542
0
  }
2543
2544
0
  this->dinfo.mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2545
0
}
2546
2547
2548
static int my_read_markers(j_decompress_ptr dinfo)
2549
0
{
2550
0
  return JPEG_REACHED_SOS;
2551
0
}
2552
2553
static void my_reset_marker_reader(j_decompress_ptr dinfo)
2554
0
{
2555
0
}
2556
2557
/* TurboJPEG 3.0+ */
2558
DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle,
2559
                                  const unsigned char * const *srcPlanes,
2560
                                  const int *strides, unsigned char *dstBuf,
2561
                                  int width, int pitch, int height,
2562
                                  int pixelFormat)
2563
0
{
2564
0
  static const char FUNCTION_NAME[] = "tj3DecodeYUVPlanes8";
2565
0
  JSAMPROW *row_pointer = NULL;
2566
0
  JSAMPLE *_tmpbuf[MAX_COMPONENTS];
2567
0
  JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
2568
0
  int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
2569
0
  JSAMPLE *ptr;
2570
0
  jpeg_component_info *compptr;
2571
0
  int (*old_read_markers) (j_decompress_ptr) = NULL;
2572
0
  void (*old_reset_marker_reader) (j_decompress_ptr) = NULL;
2573
2574
0
  GET_DINSTANCE(handle);
2575
2576
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2577
0
    tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;  inbuf[i] = NULL;
2578
0
  }
2579
2580
0
  if ((this->init & DECOMPRESS) == 0)
2581
0
    THROW("Instance has not been initialized for decompression");
2582
2583
0
  if (!srcPlanes || !srcPlanes[0] || dstBuf == NULL || width <= 0 ||
2584
0
      pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
2585
0
    THROW("Invalid argument");
2586
0
  if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
2587
0
    THROW("Invalid argument");
2588
2589
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2590
0
    THROW("TJPARAM_SUBSAMP must be specified");
2591
0
  if (pixelFormat == TJPF_CMYK)
2592
0
    THROW("Cannot decode YUV images into packed-pixel CMYK images.");
2593
2594
0
  if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
2595
0
  else if (pitch < width * tjPixelSize[pixelFormat])
2596
0
    THROW("Invalid argument");
2597
0
  dinfo->image_width = width;
2598
0
  dinfo->image_height = height;
2599
2600
0
  dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
2601
0
  dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
2602
0
  dinfo->Se = DCTSIZE2 - 1;
2603
0
  setDecodeDefaults(this, pixelFormat);
2604
0
  old_read_markers = dinfo->marker->read_markers;
2605
0
  dinfo->marker->read_markers = my_read_markers;
2606
0
  old_reset_marker_reader = dinfo->marker->reset_marker_reader;
2607
0
  dinfo->marker->reset_marker_reader = my_reset_marker_reader;
2608
0
  CATCH_LIBJPEG(this);
2609
0
  jpeg_read_header(dinfo, TRUE);
2610
0
  dinfo->marker->read_markers = old_read_markers;
2611
0
  dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2612
2613
0
  this->dinfo.out_color_space = pf2cs[pixelFormat];
2614
0
  this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
2615
0
  dinfo->do_fancy_upsampling = FALSE;
2616
0
  dinfo->Se = DCTSIZE2 - 1;
2617
0
  jinit_master_decompress(dinfo);
2618
0
  (*dinfo->upsample->start_pass) (dinfo);
2619
2620
0
  pw0 = PAD(width, dinfo->max_h_samp_factor);
2621
0
  ph0 = PAD(height, dinfo->max_v_samp_factor);
2622
2623
0
  if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
2624
2625
0
  if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
2626
0
    THROW("Memory allocation failure");
2627
0
  for (i = 0; i < height; i++) {
2628
0
    if (this->bottomUp)
2629
0
      row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
2630
0
    else
2631
0
      row_pointer[i] = &dstBuf[i * (size_t)pitch];
2632
0
  }
2633
0
  if (height < ph0)
2634
0
    for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
2635
2636
0
  for (i = 0; i < dinfo->num_components; i++) {
2637
0
    compptr = &dinfo->comp_info[i];
2638
0
    _tmpbuf[i] =
2639
0
      (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
2640
0
                        compptr->v_samp_factor + 32);
2641
0
    if (!_tmpbuf[i])
2642
0
      THROW("Memory allocation failure");
2643
0
    tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
2644
0
    if (!tmpbuf[i])
2645
0
      THROW("Memory allocation failure");
2646
0
    for (row = 0; row < compptr->v_samp_factor; row++) {
2647
0
      unsigned char *_tmpbuf_aligned =
2648
0
        (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32);
2649
2650
0
      tmpbuf[i][row] =
2651
0
        &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
2652
0
    }
2653
0
    pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
2654
0
    if (strides && strides[i] != 0 && strides[i] < pw[i])
2655
0
      THROW("Invalid argument");
2656
0
    ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
2657
0
    inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
2658
0
    if (!inbuf[i])
2659
0
      THROW("Memory allocation failure");
2660
0
    ptr = (JSAMPLE *)srcPlanes[i];
2661
0
    for (row = 0; row < ph[i]; row++) {
2662
0
      inbuf[i][row] = ptr;
2663
0
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
2664
0
    }
2665
0
  }
2666
2667
0
  CATCH_LIBJPEG(this);
2668
2669
0
  for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
2670
0
    JDIMENSION inrow = 0, outrow = 0;
2671
2672
0
    for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
2673
0
         i++, compptr++)
2674
0
      jcopy_sample_rows(inbuf[i],
2675
0
        row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
2676
0
        compptr->v_samp_factor, pw[i]);
2677
0
    (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
2678
0
                                 dinfo->max_v_samp_factor, &row_pointer[row],
2679
0
                                 &outrow, dinfo->max_v_samp_factor);
2680
0
  }
2681
0
  jpeg_abort_decompress(dinfo);
2682
2683
0
bailout:
2684
0
  if (old_read_markers)
2685
0
    dinfo->marker->read_markers = old_read_markers;
2686
0
  if (old_reset_marker_reader)
2687
0
    dinfo->marker->reset_marker_reader = old_reset_marker_reader;
2688
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
2689
0
  free(row_pointer);
2690
0
  for (i = 0; i < MAX_COMPONENTS; i++) {
2691
0
    free(tmpbuf[i]);
2692
0
    free(_tmpbuf[i]);
2693
0
    free(inbuf[i]);
2694
0
  }
2695
0
  if (this->jerr.warning) retval = -1;
2696
0
  return retval;
2697
0
}
2698
2699
/* TurboJPEG 1.4+ */
2700
DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
2701
                                const unsigned char **srcPlanes,
2702
                                const int *strides, int subsamp,
2703
                                unsigned char *dstBuf, int width, int pitch,
2704
                                int height, int pixelFormat, int flags)
2705
0
{
2706
0
  static const char FUNCTION_NAME[] = "tjDecodeYUVPlanes";
2707
0
  int retval = 0;
2708
2709
0
  GET_TJINSTANCE(handle, -1);
2710
2711
0
  if (subsamp < 0 || subsamp >= this->numSamp)
2712
0
    THROW("Invalid argument");
2713
2714
0
  this->subsamp = subsamp;
2715
0
  processFlags(handle, flags, DECOMPRESS);
2716
2717
0
  return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2718
0
                             height, pixelFormat);
2719
2720
0
bailout:
2721
0
  return retval;
2722
0
}
2723
2724
2725
/* TurboJPEG 3.0+ */
2726
DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf,
2727
                            int align, unsigned char *dstBuf, int width,
2728
                            int pitch, int height, int pixelFormat)
2729
0
{
2730
0
  static const char FUNCTION_NAME[] = "tj3DecodeYUV8";
2731
0
  const unsigned char *srcPlanes[3];
2732
0
  int pw0, ph0, strides[3], retval = -1;
2733
2734
0
  GET_TJINSTANCE(handle, -1);
2735
2736
0
  if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 ||
2737
0
      height <= 0)
2738
0
    THROW("Invalid argument");
2739
2740
0
  if (this->subsamp == TJSAMP_UNKNOWN)
2741
0
    THROW("TJPARAM_SUBSAMP must be specified");
2742
2743
0
  pw0 = tj3YUVPlaneWidth(0, width, this->subsamp);
2744
0
  ph0 = tj3YUVPlaneHeight(0, height, this->subsamp);
2745
0
  srcPlanes[0] = srcBuf;
2746
0
  strides[0] = (int)PAD((unsigned long long)pw0, align);
2747
0
  if (this->subsamp == TJSAMP_GRAY) {
2748
0
    strides[1] = strides[2] = 0;
2749
0
    srcPlanes[1] = srcPlanes[2] = NULL;
2750
0
  } else {
2751
0
    int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp);
2752
0
    int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp);
2753
2754
0
    strides[1] = strides[2] = (int)PAD((unsigned long long)pw1, align);
2755
0
    if ((unsigned long long)strides[0] * (unsigned long long)ph0 >
2756
0
        (unsigned long long)INT_MAX ||
2757
0
        (unsigned long long)strides[1] * (unsigned long long)ph1 >
2758
0
        (unsigned long long)INT_MAX)
2759
0
      THROW("Image or row alignment is too large");
2760
0
    srcPlanes[1] = srcPlanes[0] + strides[0] * ph0;
2761
0
    srcPlanes[2] = srcPlanes[1] + strides[1] * ph1;
2762
0
  }
2763
2764
0
  return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch,
2765
0
                             height, pixelFormat);
2766
2767
0
bailout:
2768
0
  return retval;
2769
0
}
2770
2771
/* TurboJPEG 1.4+ */
2772
DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf,
2773
                          int align, int subsamp, unsigned char *dstBuf,
2774
                          int width, int pitch, int height, int pixelFormat,
2775
                          int flags)
2776
0
{
2777
0
  static const char FUNCTION_NAME[] = "tjDecodeYUV";
2778
0
  int retval = -1;
2779
2780
0
  GET_TJINSTANCE(handle, -1);
2781
2782
0
  if (subsamp < 0 || subsamp >= this->numSamp)
2783
0
    THROW("Invalid argument");
2784
2785
0
  this->subsamp = subsamp;
2786
0
  processFlags(handle, flags, DECOMPRESS);
2787
2788
0
  return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height,
2789
0
                       pixelFormat);
2790
2791
0
bailout:
2792
0
  return retval;
2793
0
}
2794
2795
2796
/******************************** Transformer ********************************/
2797
2798
/* TurboJPEG 1.2+ */
2799
DLLEXPORT tjhandle tjInitTransform(void)
2800
0
{
2801
0
  return tj3Init(TJINIT_TRANSFORM);
2802
0
}
2803
2804
2805
static int getDstSubsamp(int srcSubsamp, const tjtransform *transform)
2806
0
{
2807
0
  int dstSubsamp;
2808
2809
0
  if (!transform)
2810
0
    return srcSubsamp;
2811
2812
0
  dstSubsamp = (transform->options & TJXOPT_GRAY) ? TJSAMP_GRAY : srcSubsamp;
2813
2814
0
  if (transform->op == TJXOP_TRANSPOSE || transform->op == TJXOP_TRANSVERSE ||
2815
0
      transform->op == TJXOP_ROT90 || transform->op == TJXOP_ROT270) {
2816
0
    if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
2817
0
    else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
2818
0
    else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
2819
0
    else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
2820
0
    else if (dstSubsamp == TJSAMP_410) dstSubsamp = TJSAMP_24;
2821
0
    else if (dstSubsamp == TJSAMP_24) dstSubsamp = TJSAMP_410;
2822
0
  }
2823
2824
0
  return dstSubsamp;
2825
0
}
2826
2827
static int getTransformedSpecs(tjhandle handle, int *width, int *height,
2828
                               int *subsamp, const tjtransform *transform,
2829
                               const char *FUNCTION_NAME)
2830
0
{
2831
0
  int retval = 0, dstWidth, dstHeight, dstSubsamp;
2832
2833
0
  GET_TJINSTANCE(handle, -1);
2834
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2835
0
    THROW("Instance has not been initialized for transformation");
2836
2837
0
  if (!width || !height || !subsamp || !transform || *width < 1 ||
2838
0
      *height < 1 || *subsamp < TJSAMP_UNKNOWN || *subsamp >= this->numSamp)
2839
0
    THROW("Invalid argument");
2840
2841
0
  dstWidth = *width;  dstHeight = *height;
2842
0
  if (transform->op == TJXOP_TRANSPOSE || transform->op == TJXOP_TRANSVERSE ||
2843
0
      transform->op == TJXOP_ROT90 || transform->op == TJXOP_ROT270) {
2844
0
    dstWidth = *height;  dstHeight = *width;
2845
0
  }
2846
0
  dstSubsamp = getDstSubsamp(*subsamp, transform);
2847
2848
0
  if (transform->options & TJXOPT_CROP) {
2849
0
    int croppedWidth, croppedHeight;
2850
2851
0
    if (transform->r.x < 0 || transform->r.y < 0 || transform->r.w < 0 ||
2852
0
        transform->r.h < 0)
2853
0
      THROW("Invalid cropping region");
2854
0
    if (dstSubsamp == TJSAMP_UNKNOWN)
2855
0
      THROW("Could not determine subsampling level of JPEG image");
2856
0
    if ((transform->r.x % tjMCUWidth[dstSubsamp]) != 0 ||
2857
0
        (transform->r.y % tjMCUHeight[dstSubsamp]) != 0)
2858
0
      THROWI("To crop this JPEG image, x must be a multiple of %d\n"
2859
0
             "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
2860
0
             tjMCUHeight[dstSubsamp]);
2861
0
    if (transform->r.x >= dstWidth || transform->r.y >= dstHeight)
2862
0
      THROW("The cropping region exceeds the destination image dimensions");
2863
0
    croppedWidth = transform->r.w == 0 ? dstWidth - transform->r.x :
2864
0
                                         transform->r.w;
2865
0
    croppedHeight = transform->r.h == 0 ? dstHeight - transform->r.y :
2866
0
                                          transform->r.h;
2867
0
    if (transform->r.x + croppedWidth > dstWidth ||
2868
0
        transform->r.y + croppedHeight > dstHeight)
2869
0
      THROW("The cropping region exceeds the destination image dimensions");
2870
0
    dstWidth = croppedWidth;  dstHeight = croppedHeight;
2871
0
  }
2872
2873
0
  *width = dstWidth;  *height = dstHeight;  *subsamp = dstSubsamp;
2874
2875
0
bailout:
2876
0
  return retval;
2877
0
}
2878
2879
2880
/* TurboJPEG 3.1+ */
2881
DLLEXPORT size_t tj3TransformBufSize(tjhandle handle,
2882
                                     const tjtransform *transform)
2883
0
{
2884
0
  static const char FUNCTION_NAME[] = "tj3TransformBufSize";
2885
0
  size_t retval = 0;
2886
0
  int dstWidth, dstHeight, dstSubsamp;
2887
2888
0
  GET_TJINSTANCE(handle, 0);
2889
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2890
0
    THROWRV("Instance has not been initialized for transformation", 0);
2891
2892
0
  if (transform == NULL)
2893
0
    THROWRV("Invalid argument", 0)
2894
2895
0
  if (this->jpegWidth < 0 || this->jpegHeight < 0)
2896
0
    THROWRV("JPEG header has not yet been read", 0);
2897
2898
0
  dstWidth = this->jpegWidth;
2899
0
  dstHeight = this->jpegHeight;
2900
0
  dstSubsamp = this->subsamp;
2901
0
  if (getTransformedSpecs(handle, &dstWidth, &dstHeight, &dstSubsamp,
2902
0
                          transform, FUNCTION_NAME) == -1) {
2903
0
    retval = 0;
2904
0
    goto bailout;
2905
0
  }
2906
2907
0
  retval = tj3JPEGBufSize(dstWidth, dstHeight, dstSubsamp);
2908
0
  if ((this->saveMarkers == 2 || this->saveMarkers == 4) &&
2909
0
      !(transform->options & TJXOPT_COPYNONE))
2910
0
    retval += this->decompICCSize;
2911
0
  else
2912
0
    retval += this->iccSize;
2913
2914
0
bailout:
2915
0
  return retval;
2916
0
}
2917
2918
2919
/* TurboJPEG 3.0+ */
2920
DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
2921
                           size_t jpegSize, int n, unsigned char **dstBufs,
2922
                           size_t *dstSizes, const tjtransform *t)
2923
0
{
2924
0
  static const char FUNCTION_NAME[] = "tj3Transform";
2925
0
  int retval = 0;
2926
2927
0
#if TRANSFORMS_SUPPORTED
2928
2929
0
  jpeg_transform_info *xinfo = NULL;
2930
0
  jvirt_barray_ptr *srccoefs, *dstcoefs;
2931
0
  int i, saveMarkers = 0, srcSubsamp;
2932
0
  boolean alloc = TRUE;
2933
0
  struct my_progress_mgr progress;
2934
2935
0
  GET_INSTANCE(handle);
2936
0
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
2937
0
    THROW("Instance has not been initialized for transformation");
2938
2939
0
  if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
2940
0
      dstSizes == NULL || t == NULL)
2941
0
    THROW("Invalid argument");
2942
2943
0
  if (this->scanLimit) {
2944
0
    memset(&progress, 0, sizeof(struct my_progress_mgr));
2945
0
    progress.pub.progress_monitor = my_progress_monitor;
2946
0
    progress.this = this;
2947
0
    dinfo->progress = &progress.pub;
2948
0
  } else
2949
0
    dinfo->progress = NULL;
2950
2951
0
  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;
2952
2953
0
  if ((xinfo =
2954
0
       (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
2955
0
    THROW("Memory allocation failure");
2956
0
  memset(xinfo, 0, sizeof(jpeg_transform_info) * n);
2957
2958
0
  CATCH_LIBJPEG(this);
2959
2960
0
  if (dinfo->global_state <= DSTATE_INHEADER)
2961
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
2962
2963
0
  for (i = 0; i < n; i++) {
2964
0
    if (t[i].op < 0 || t[i].op >= TJ_NUMXOP)
2965
0
      THROW("Invalid transform operation");
2966
0
    xinfo[i].transform = xformtypes[t[i].op];
2967
0
    xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
2968
0
    xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
2969
0
    xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
2970
0
    xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
2971
0
    if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
2972
0
    else xinfo[i].slow_hflip = 0;
2973
2974
0
    if (xinfo[i].crop) {
2975
0
      if (t[i].r.x < 0 || t[i].r.y < 0 || t[i].r.w < 0 || t[i].r.h < 0)
2976
0
        THROW("Invalid cropping region");
2977
0
      xinfo[i].crop_xoffset = t[i].r.x;  xinfo[i].crop_xoffset_set = JCROP_POS;
2978
0
      xinfo[i].crop_yoffset = t[i].r.y;  xinfo[i].crop_yoffset_set = JCROP_POS;
2979
0
      if (t[i].r.w != 0) {
2980
0
        xinfo[i].crop_width = t[i].r.w;  xinfo[i].crop_width_set = JCROP_POS;
2981
0
      } else
2982
0
        xinfo[i].crop_width = JCROP_UNSET;
2983
0
      if (t[i].r.h != 0) {
2984
0
        xinfo[i].crop_height = t[i].r.h;  xinfo[i].crop_height_set = JCROP_POS;
2985
0
      } else
2986
0
        xinfo[i].crop_height = JCROP_UNSET;
2987
0
    }
2988
0
    if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
2989
0
  }
2990
2991
0
  jcopy_markers_setup(dinfo, saveMarkers ?
2992
0
                             (JCOPY_OPTION)this->saveMarkers : JCOPYOPT_NONE);
2993
0
  if (dinfo->global_state <= DSTATE_INHEADER)
2994
0
    jpeg_read_header(dinfo, TRUE);
2995
0
  if (this->maxPixels &&
2996
0
      (unsigned long long)dinfo->image_width * dinfo->image_height >
2997
0
      (unsigned long long)this->maxPixels)
2998
0
    THROW("Image is too large");
2999
0
  srcSubsamp = getSubsamp(this);
3000
3001
0
  for (i = 0; i < n; i++) {
3002
0
    if (!jtransform_request_workspace(dinfo, &xinfo[i]))
3003
0
      THROW("Transform is not perfect");
3004
3005
0
    if (xinfo[i].crop) {
3006
0
      int dstSubsamp = getDstSubsamp(srcSubsamp, &t[i]);
3007
3008
0
      if (dstSubsamp == TJSAMP_UNKNOWN)
3009
0
        THROW("Could not determine subsampling level of destination image");
3010
0
      if ((t[i].r.x % tjMCUWidth[dstSubsamp]) != 0 ||
3011
0
          (t[i].r.y % tjMCUHeight[dstSubsamp]) != 0)
3012
0
        THROWI("To crop this JPEG image, x must be a multiple of %d\n"
3013
0
               "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
3014
0
               tjMCUHeight[dstSubsamp]);
3015
0
    }
3016
0
  }
3017
3018
0
  srccoefs = jpeg_read_coefficients(dinfo);
3019
3020
0
  for (i = 0; i < n; i++) {
3021
0
    if (this->noRealloc) alloc = FALSE;
3022
0
    if (!(t[i].options & TJXOPT_NOOUTPUT))
3023
0
      jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
3024
0
    jpeg_copy_critical_parameters(dinfo, cinfo);
3025
0
    dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
3026
0
    if (this->optimize || t[i].options & TJXOPT_OPTIMIZE)
3027
0
      cinfo->optimize_coding = TRUE;
3028
0
#ifdef C_PROGRESSIVE_SUPPORTED
3029
0
    if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE)
3030
0
      jpeg_simple_progression(cinfo);
3031
0
#endif
3032
0
    if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) {
3033
0
      cinfo->arith_code = TRUE;
3034
0
      cinfo->optimize_coding = FALSE;
3035
0
    }
3036
0
    cinfo->restart_interval = this->restartIntervalBlocks;
3037
0
    cinfo->restart_in_rows = this->restartIntervalRows;
3038
0
    if (!(t[i].options & TJXOPT_NOOUTPUT)) {
3039
0
      JCOPY_OPTION copyOption = t[i].options & TJXOPT_COPYNONE ?
3040
0
                                JCOPYOPT_NONE :
3041
0
                                (JCOPY_OPTION)this->saveMarkers;
3042
3043
0
      jpeg_write_coefficients(cinfo, dstcoefs);
3044
0
      jcopy_markers_execute(dinfo, cinfo, copyOption);
3045
0
      if (this->iccBuf != NULL && this->iccSize != 0 &&
3046
0
          copyOption != JCOPYOPT_ALL && copyOption != JCOPYOPT_ICC)
3047
0
        jpeg_write_icc_profile(cinfo, this->iccBuf,
3048
0
                               (unsigned int)this->iccSize);
3049
0
    } else
3050
0
      jinit_c_master_control(cinfo, TRUE);
3051
0
    jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
3052
0
    if (t[i].customFilter) {
3053
0
      int ci, y;
3054
0
      JDIMENSION by;
3055
3056
0
      for (ci = 0; ci < cinfo->num_components; ci++) {
3057
0
        jpeg_component_info *compptr = &cinfo->comp_info[ci];
3058
0
        tjregion arrayRegion = { 0, 0, 0, 0 };
3059
0
        tjregion planeRegion = { 0, 0, 0, 0 };
3060
3061
0
        arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
3062
0
        arrayRegion.h = DCTSIZE;
3063
0
        planeRegion.w = compptr->width_in_blocks * DCTSIZE;
3064
0
        planeRegion.h = compptr->height_in_blocks * DCTSIZE;
3065
3066
0
        for (by = 0; by < compptr->height_in_blocks;
3067
0
             by += compptr->v_samp_factor) {
3068
0
          JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
3069
0
            ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
3070
0
             TRUE);
3071
3072
0
          for (y = 0; y < compptr->v_samp_factor; y++) {
3073
0
            if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
3074
0
                                  i, (tjtransform *)&t[i]) == -1)
3075
0
              THROW("Error in custom filter");
3076
0
            arrayRegion.y += DCTSIZE;
3077
0
          }
3078
0
        }
3079
0
      }
3080
0
    }
3081
0
    if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
3082
0
  }
3083
3084
0
  jpeg_finish_decompress(dinfo);
3085
3086
0
bailout:
3087
0
  if (cinfo->global_state > CSTATE_START) {
3088
0
    if (alloc) (*cinfo->dest->term_destination) (cinfo);
3089
0
    jpeg_abort_compress(cinfo);
3090
0
  }
3091
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
3092
0
  free(xinfo);
3093
0
  if (this->jerr.warning) retval = -1;
3094
0
  return retval;
3095
3096
#else /* TRANSFORMS_SUPPORTED */
3097
3098
  GET_TJINSTANCE(handle, -1)
3099
  THROW("Lossless transformations were disabled at build time")
3100
bailout:
3101
  return retval;
3102
3103
#endif
3104
0
}
3105
3106
/* TurboJPEG 1.2+ */
3107
DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
3108
                          unsigned long jpegSize, int n,
3109
                          unsigned char **dstBufs, unsigned long *dstSizes,
3110
                          tjtransform *t, int flags)
3111
0
{
3112
0
  static const char FUNCTION_NAME[] = "tjTransform";
3113
0
  int i, retval = 0, srcSubsamp = -1;
3114
0
  size_t *sizes = NULL;
3115
3116
0
  GET_DINSTANCE(handle);
3117
0
  if ((this->init & DECOMPRESS) == 0)
3118
0
    THROW("Instance has not been initialized for decompression");
3119
3120
0
  if (n < 1 || dstSizes == NULL)
3121
0
    THROW("Invalid argument");
3122
3123
0
  CATCH_LIBJPEG(this);
3124
3125
0
  processFlags(handle, flags, COMPRESS);
3126
3127
0
  if (this->noRealloc) {
3128
0
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
3129
0
    jpeg_read_header(dinfo, TRUE);
3130
0
    srcSubsamp = getSubsamp(this);
3131
0
  }
3132
3133
0
  if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL)
3134
0
    THROW("Memory allocation failure");
3135
0
  for (i = 0; i < n; i++) {
3136
0
    sizes[i] = (size_t)dstSizes[i];
3137
0
    if (this->noRealloc) {
3138
0
      int dstWidth = dinfo->image_width, dstHeight = dinfo->image_height;
3139
0
      int dstSubsamp = srcSubsamp;
3140
3141
0
      if (getTransformedSpecs(handle, &dstWidth, &dstHeight, &dstSubsamp,
3142
0
                              &t[i], FUNCTION_NAME) == -1) {
3143
0
        retval = -1;
3144
0
        goto bailout;
3145
0
      }
3146
0
      sizes[i] = tj3JPEGBufSize(dstWidth, dstHeight, dstSubsamp);
3147
0
    }
3148
0
  }
3149
0
  retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes,
3150
0
                        t);
3151
0
  for (i = 0; i < n; i++)
3152
0
    dstSizes[i] = (unsigned long)sizes[i];
3153
3154
0
bailout:
3155
0
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
3156
0
  if (this->jerr.warning) retval = -1;
3157
0
  free(sizes);
3158
0
  return retval;
3159
0
}
3160
3161
3162
/*************************** Packed-Pixel Image I/O **************************/
3163
3164
/* tj3LoadImage*() is implemented in turbojpeg-mp.c */
3165
3166
/* TurboJPEG 2.0+ */
3167
DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width,
3168
                                     int align, int *height,
3169
                                     int *pixelFormat, int flags)
3170
0
{
3171
0
  tjhandle handle = NULL;
3172
0
  unsigned char *dstBuf = NULL;
3173
3174
0
  if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL;
3175
3176
0
  processFlags(handle, flags, COMPRESS);
3177
3178
0
  dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat);
3179
3180
0
  tj3Destroy(handle);
3181
0
  return dstBuf;
3182
0
}
3183
3184
3185
/* tj3SaveImage*() is implemented in turbojpeg-mp.c */
3186
3187
/* TurboJPEG 2.0+ */
3188
DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer,
3189
                          int width, int pitch, int height, int pixelFormat,
3190
                          int flags)
3191
0
{
3192
0
  tjhandle handle = NULL;
3193
0
  int retval = -1;
3194
3195
0
  if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1;
3196
3197
0
  processFlags(handle, flags, DECOMPRESS);
3198
3199
0
  retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height,
3200
0
                         pixelFormat);
3201
3202
0
  tj3Destroy(handle);
3203
0
  return retval;
3204
0
}