Coverage Report

Created: 2024-08-27 12:18

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