Coverage Report

Created: 2026-04-12 06:05

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