Coverage Report

Created: 2024-01-20 12:28

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