Imported Upstream version 3.0.1
[platform/upstream/libjpeg-turbo.git] / turbojpeg-mp.c
1 /*
2  * Copyright (C)2009-2023 D. R. Commander.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of the libjpeg-turbo Project nor the names of its
13  *   contributors may be used to endorse or promote products derived from this
14  *   software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /* TurboJPEG API functions that must be compiled for multiple data
30    precisions */
31
32 #if BITS_IN_JSAMPLE == 8
33 #define _JSAMPLE  JSAMPLE
34 #define _JSAMPROW  JSAMPROW
35 #define _buffer  buffer
36 #define _jinit_read_ppm  jinit_read_ppm
37 #define _jinit_write_ppm  jinit_write_ppm
38 #define _jpeg_crop_scanline  jpeg_crop_scanline
39 #define _jpeg_read_scanlines  jpeg_read_scanlines
40 #define _jpeg_skip_scanlines  jpeg_skip_scanlines
41 #define _jpeg_write_scanlines  jpeg_write_scanlines
42 #elif BITS_IN_JSAMPLE == 12
43 #define _JSAMPLE  J12SAMPLE
44 #define _JSAMPROW  J12SAMPROW
45 #define _buffer  buffer12
46 #define _jinit_read_ppm  j12init_read_ppm
47 #define _jinit_write_ppm  j12init_write_ppm
48 #define _jpeg_crop_scanline  jpeg12_crop_scanline
49 #define _jpeg_read_scanlines  jpeg12_read_scanlines
50 #define _jpeg_skip_scanlines  jpeg12_skip_scanlines
51 #define _jpeg_write_scanlines  jpeg12_write_scanlines
52 #elif BITS_IN_JSAMPLE == 16
53 #define _JSAMPLE  J16SAMPLE
54 #define _JSAMPROW  J16SAMPROW
55 #define _buffer  buffer16
56 #define _jinit_read_ppm  j16init_read_ppm
57 #define _jinit_write_ppm  j16init_write_ppm
58 #define _jpeg_read_scanlines  jpeg16_read_scanlines
59 #define _jpeg_write_scanlines  jpeg16_write_scanlines
60 #endif
61
62 #define _GET_NAME(name, suffix)  name##suffix
63 #define GET_NAME(name, suffix)  _GET_NAME(name, suffix)
64 #define _GET_STRING(name, suffix)  #name #suffix
65 #define GET_STRING(name, suffix)  _GET_STRING(name, suffix)
66
67
68 /******************************** Compressor *********************************/
69
70 /* TurboJPEG 3+ */
71 DLLEXPORT int GET_NAME(tj3Compress, BITS_IN_JSAMPLE)
72   (tjhandle handle, const _JSAMPLE *srcBuf, int width, int pitch, int height,
73    int pixelFormat, unsigned char **jpegBuf, size_t *jpegSize)
74 {
75   static const char FUNCTION_NAME[] = GET_STRING(tj3Compress, BITS_IN_JSAMPLE);
76   int i, retval = 0;
77   boolean alloc = TRUE;
78   _JSAMPROW *row_pointer = NULL;
79
80   GET_CINSTANCE(handle)
81   if ((this->init & COMPRESS) == 0)
82     THROW("Instance has not been initialized for compression");
83
84   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
85       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
86       jpegSize == NULL)
87     THROW("Invalid argument");
88
89   if (!this->lossless && this->quality == -1)
90     THROW("TJPARAM_QUALITY must be specified");
91   if (!this->lossless && this->subsamp == TJSAMP_UNKNOWN)
92     THROW("TJPARAM_SUBSAMP must be specified");
93
94   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
95
96   if ((row_pointer = (_JSAMPROW *)malloc(sizeof(_JSAMPROW) * height)) == NULL)
97     THROW("Memory allocation failure");
98
99   if (setjmp(this->jerr.setjmp_buffer)) {
100     /* If we get here, the JPEG code has signaled an error. */
101     retval = -1;  goto bailout;
102   }
103
104   cinfo->image_width = width;
105   cinfo->image_height = height;
106   cinfo->data_precision = BITS_IN_JSAMPLE;
107
108   setCompDefaults(this, pixelFormat);
109   if (this->noRealloc) {
110     alloc = FALSE;
111     *jpegSize = tj3JPEGBufSize(width, height, this->subsamp);
112   }
113   jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
114
115   jpeg_start_compress(cinfo, TRUE);
116   for (i = 0; i < height; i++) {
117     if (this->bottomUp)
118       row_pointer[i] = (_JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
119     else
120       row_pointer[i] = (_JSAMPROW)&srcBuf[i * (size_t)pitch];
121   }
122   while (cinfo->next_scanline < cinfo->image_height)
123     _jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
124                           cinfo->image_height - cinfo->next_scanline);
125   jpeg_finish_compress(cinfo);
126
127 bailout:
128   if (cinfo->global_state > CSTATE_START) {
129     if (alloc) (*cinfo->dest->term_destination) (cinfo);
130     jpeg_abort_compress(cinfo);
131   }
132   free(row_pointer);
133   if (this->jerr.warning) retval = -1;
134   return retval;
135 }
136
137
138 /******************************* Decompressor ********************************/
139
140 /* TurboJPEG 3+ */
141 DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE)
142   (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize,
143    _JSAMPLE *dstBuf, int pitch, int pixelFormat)
144 {
145   static const char FUNCTION_NAME[] =
146     GET_STRING(tj3Decompress, BITS_IN_JSAMPLE);
147   _JSAMPROW *row_pointer = NULL;
148   int croppedHeight, i, retval = 0;
149 #if BITS_IN_JSAMPLE != 16
150   int scaledWidth;
151 #endif
152   struct my_progress_mgr progress;
153
154   GET_DINSTANCE(handle);
155   if ((this->init & DECOMPRESS) == 0)
156     THROW("Instance has not been initialized for decompression");
157
158   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || pitch < 0 ||
159       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
160     THROW("Invalid argument");
161
162   if (this->scanLimit) {
163     memset(&progress, 0, sizeof(struct my_progress_mgr));
164     progress.pub.progress_monitor = my_progress_monitor;
165     progress.this = this;
166     dinfo->progress = &progress.pub;
167   } else
168     dinfo->progress = NULL;
169
170   if (setjmp(this->jerr.setjmp_buffer)) {
171     /* If we get here, the JPEG code has signaled an error. */
172     retval = -1;  goto bailout;
173   }
174
175   if (dinfo->global_state <= DSTATE_START) {
176     jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
177     jpeg_read_header(dinfo, TRUE);
178   }
179   setDecompParameters(this);
180   this->dinfo.out_color_space = pf2cs[pixelFormat];
181 #if BITS_IN_JSAMPLE != 16
182   scaledWidth = TJSCALED(dinfo->image_width, this->scalingFactor);
183 #endif
184   dinfo->do_fancy_upsampling = !this->fastUpsample;
185   this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW;
186
187   dinfo->scale_num = this->scalingFactor.num;
188   dinfo->scale_denom = this->scalingFactor.denom;
189
190   jpeg_start_decompress(dinfo);
191
192 #if BITS_IN_JSAMPLE != 16
193   if (this->croppingRegion.x != 0 ||
194       (this->croppingRegion.w != 0 && this->croppingRegion.w != scaledWidth)) {
195     JDIMENSION crop_x = this->croppingRegion.x;
196     JDIMENSION crop_w = this->croppingRegion.w;
197
198     _jpeg_crop_scanline(dinfo, &crop_x, &crop_w);
199     if ((int)crop_x != this->croppingRegion.x)
200       THROWI("Unexplained mismatch between specified (%d) and\n"
201              "actual (%d) cropping region left boundary",
202              this->croppingRegion.x, (int)crop_x);
203     if ((int)crop_w != this->croppingRegion.w)
204       THROWI("Unexplained mismatch between specified (%d) and\n"
205              "actual (%d) cropping region width",
206              this->croppingRegion.w, (int)crop_w);
207   }
208 #endif
209
210   if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
211
212   croppedHeight = dinfo->output_height;
213 #if BITS_IN_JSAMPLE != 16
214   if (this->croppingRegion.y != 0 || this->croppingRegion.h != 0)
215     croppedHeight = this->croppingRegion.h;
216 #endif
217   if ((row_pointer =
218        (_JSAMPROW *)malloc(sizeof(_JSAMPROW) * croppedHeight)) == NULL)
219     THROW("Memory allocation failure");
220   if (setjmp(this->jerr.setjmp_buffer)) {
221     /* If we get here, the JPEG code has signaled an error. */
222     retval = -1;  goto bailout;
223   }
224   for (i = 0; i < (int)croppedHeight; i++) {
225     if (this->bottomUp)
226       row_pointer[i] = &dstBuf[(croppedHeight - i - 1) * (size_t)pitch];
227     else
228       row_pointer[i] = &dstBuf[i * (size_t)pitch];
229   }
230
231 #if BITS_IN_JSAMPLE != 16
232   if (this->croppingRegion.y != 0 || this->croppingRegion.h != 0) {
233     if (this->croppingRegion.y != 0) {
234       JDIMENSION lines = _jpeg_skip_scanlines(dinfo, this->croppingRegion.y);
235
236       if ((int)lines != this->croppingRegion.y)
237         THROWI("Unexplained mismatch between specified (%d) and\n"
238                "actual (%d) cropping region upper boundary",
239                this->croppingRegion.y, (int)lines);
240     }
241     while ((int)dinfo->output_scanline <
242            this->croppingRegion.y + this->croppingRegion.h)
243       _jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline -
244                                                this->croppingRegion.y],
245                            this->croppingRegion.y + this->croppingRegion.h -
246                            dinfo->output_scanline);
247     if (this->croppingRegion.y + this->croppingRegion.h !=
248         (int)dinfo->output_height) {
249       JDIMENSION lines = _jpeg_skip_scanlines(dinfo, dinfo->output_height -
250                                                      this->croppingRegion.y -
251                                                      this->croppingRegion.h);
252
253       if (lines != dinfo->output_height - this->croppingRegion.y -
254                    this->croppingRegion.h)
255         THROWI("Unexplained mismatch between specified (%d) and\n"
256                "actual (%d) cropping region lower boundary",
257                this->croppingRegion.y + this->croppingRegion.h,
258                (int)(dinfo->output_height - lines));
259     }
260   } else
261 #endif
262   {
263     while (dinfo->output_scanline < dinfo->output_height)
264       _jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
265                            dinfo->output_height - dinfo->output_scanline);
266   }
267   jpeg_finish_decompress(dinfo);
268
269 bailout:
270   if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
271   free(row_pointer);
272   if (this->jerr.warning) retval = -1;
273   return retval;
274 }
275
276
277 /*************************** Packed-Pixel Image I/O **************************/
278
279 /* TurboJPEG 3+ */
280 DLLEXPORT _JSAMPLE *GET_NAME(tj3LoadImage, BITS_IN_JSAMPLE)
281   (tjhandle handle, const char *filename, int *width, int align, int *height,
282    int *pixelFormat)
283 {
284   static const char FUNCTION_NAME[] =
285     GET_STRING(tj3LoadImage, BITS_IN_JSAMPLE);
286
287 #if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)
288
289   int retval = 0, tempc;
290   size_t pitch;
291   tjhandle handle2 = NULL;
292   tjinstance *this2;
293   j_compress_ptr cinfo = NULL;
294   cjpeg_source_ptr src;
295   _JSAMPLE *dstBuf = NULL;
296   FILE *file = NULL;
297   boolean invert;
298
299   GET_TJINSTANCE(handle, NULL)
300
301   if (!filename || !width || align < 1 || !height || !pixelFormat ||
302       *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
303     THROW("Invalid argument");
304   if ((align & (align - 1)) != 0)
305     THROW("Alignment must be a power of 2");
306
307   /* The instance handle passed to this function is used only for parameter
308      retrieval.  Create a new temporary instance to avoid interfering with the
309      libjpeg state of the primary instance. */
310   if ((handle2 = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL;
311   this2 = (tjinstance *)handle2;
312   cinfo = &this2->cinfo;
313
314 #ifdef _MSC_VER
315   if (fopen_s(&file, filename, "rb") || file == NULL)
316 #else
317   if ((file = fopen(filename, "rb")) == NULL)
318 #endif
319     THROW_UNIX("Cannot open input file");
320
321   if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
322     THROW_UNIX("Could not read input file")
323   else if (tempc == EOF)
324     THROW("Input file contains no data");
325
326   if (setjmp(this2->jerr.setjmp_buffer)) {
327     /* If we get here, the JPEG code has signaled an error. */
328     retval = -1;  goto bailout;
329   }
330
331   cinfo->data_precision = BITS_IN_JSAMPLE;
332   if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN;
333   else cinfo->in_color_space = pf2cs[*pixelFormat];
334   if (tempc == 'B') {
335     if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
336       THROW("Could not initialize bitmap loader");
337     invert = !this->bottomUp;
338   } else if (tempc == 'P') {
339     if ((src = _jinit_read_ppm(cinfo)) == NULL)
340       THROW("Could not initialize PPM loader");
341     invert = this->bottomUp;
342   } else
343     THROW("Unsupported file type");
344
345   src->input_file = file;
346 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
347   /* Refuse to load images larger than 1 Megapixel when fuzzing. */
348   src->max_pixels = this->maxPixels;
349 #endif
350   (*src->start_input) (cinfo, src);
351   if (tempc == 'B') {
352     if (cinfo->X_density && cinfo->Y_density) {
353       this->xDensity = cinfo->X_density;
354       this->yDensity = cinfo->Y_density;
355       this->densityUnits = cinfo->density_unit;
356     }
357   }
358   (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo);
359
360   *width = cinfo->image_width;  *height = cinfo->image_height;
361   *pixelFormat = cs2pf[cinfo->in_color_space];
362
363   pitch = PAD((*width) * tjPixelSize[*pixelFormat], align);
364   if ((unsigned long long)pitch * (unsigned long long)(*height) >
365       (unsigned long long)((size_t)-1) ||
366       (dstBuf = (_JSAMPLE *)malloc(pitch * (*height) *
367                                    sizeof(_JSAMPLE))) == NULL)
368     THROW("Memory allocation failure");
369
370   if (setjmp(this2->jerr.setjmp_buffer)) {
371     /* If we get here, the JPEG code has signaled an error. */
372     retval = -1;  goto bailout;
373   }
374
375   while (cinfo->next_scanline < cinfo->image_height) {
376     int i, nlines = (*src->get_pixel_rows) (cinfo, src);
377
378     for (i = 0; i < nlines; i++) {
379       _JSAMPLE *dstptr;
380       int row;
381
382       row = cinfo->next_scanline + i;
383       if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch];
384       else dstptr = &dstBuf[row * pitch];
385       memcpy(dstptr, src->_buffer[i],
386              (*width) * tjPixelSize[*pixelFormat] * sizeof(_JSAMPLE));
387     }
388     cinfo->next_scanline += nlines;
389   }
390
391   (*src->finish_input) (cinfo, src);
392
393 bailout:
394   tj3Destroy(handle2);
395   if (file) fclose(file);
396   if (retval < 0) { free(dstBuf);  dstBuf = NULL; }
397   return dstBuf;
398
399 #else /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */
400
401   static const char ERROR_MSG[] =
402     "16-bit data precision requires lossless JPEG,\n"
403     "which was disabled at build time.";
404   _JSAMPLE *retval = NULL;
405
406   GET_TJINSTANCE(handle, NULL)
407   SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME,
408            ERROR_MSG);
409   this->isInstanceError = TRUE;  THROWG(ERROR_MSG, NULL)
410
411 bailout:
412   return retval;
413
414 #endif
415 }
416
417
418 /* TurboJPEG 3+ */
419 DLLEXPORT int GET_NAME(tj3SaveImage, BITS_IN_JSAMPLE)
420   (tjhandle handle, const char *filename, const _JSAMPLE *buffer, int width,
421    int pitch, int height, int pixelFormat)
422 {
423   static const char FUNCTION_NAME[] =
424     GET_STRING(tj3SaveImage, BITS_IN_JSAMPLE);
425   int retval = 0;
426
427 #if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)
428
429   tjhandle handle2 = NULL;
430   tjinstance *this2;
431   j_decompress_ptr dinfo = NULL;
432   djpeg_dest_ptr dst;
433   FILE *file = NULL;
434   char *ptr = NULL;
435   boolean invert;
436
437   GET_TJINSTANCE(handle, -1)
438
439   if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
440       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
441     THROW("Invalid argument");
442
443   /* The instance handle passed to this function is used only for parameter
444      retrieval.  Create a new temporary instance to avoid interfering with the
445      libjpeg state of the primary instance. */
446   if ((handle2 = tj3Init(TJINIT_DECOMPRESS)) == NULL)
447     return -1;
448   this2 = (tjinstance *)handle2;
449   dinfo = &this2->dinfo;
450
451 #ifdef _MSC_VER
452   if (fopen_s(&file, filename, "wb") || file == NULL)
453 #else
454   if ((file = fopen(filename, "wb")) == NULL)
455 #endif
456     THROW_UNIX("Cannot open output file");
457
458   if (setjmp(this2->jerr.setjmp_buffer)) {
459     /* If we get here, the JPEG code has signaled an error. */
460     retval = -1;  goto bailout;
461   }
462
463   this2->dinfo.out_color_space = pf2cs[pixelFormat];
464   dinfo->image_width = width;  dinfo->image_height = height;
465   dinfo->global_state = DSTATE_READY;
466   dinfo->scale_num = dinfo->scale_denom = 1;
467   dinfo->data_precision = BITS_IN_JSAMPLE;
468
469   ptr = strrchr(filename, '.');
470   if (ptr && !strcasecmp(ptr, ".bmp")) {
471     if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
472       THROW("Could not initialize bitmap writer");
473     invert = !this->bottomUp;
474     dinfo->X_density = (UINT16)this->xDensity;
475     dinfo->Y_density = (UINT16)this->yDensity;
476     dinfo->density_unit = (UINT8)this->densityUnits;
477   } else {
478     if ((dst = _jinit_write_ppm(dinfo)) == NULL)
479       THROW("Could not initialize PPM writer");
480     invert = this->bottomUp;
481   }
482
483   dst->output_file = file;
484   (*dst->start_output) (dinfo, dst);
485   (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo);
486
487   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
488
489   while (dinfo->output_scanline < dinfo->output_height) {
490     _JSAMPLE *rowptr;
491
492     if (invert)
493       rowptr =
494         (_JSAMPLE *)&buffer[(height - dinfo->output_scanline - 1) * pitch];
495     else
496       rowptr = (_JSAMPLE *)&buffer[dinfo->output_scanline * pitch];
497     memcpy(dst->_buffer[0], rowptr,
498            width * tjPixelSize[pixelFormat] * sizeof(_JSAMPLE));
499     (*dst->put_pixel_rows) (dinfo, dst, 1);
500     dinfo->output_scanline++;
501   }
502
503   (*dst->finish_output) (dinfo, dst);
504
505 bailout:
506   tj3Destroy(handle2);
507   if (file) fclose(file);
508   return retval;
509
510 #else /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */
511
512   GET_TJINSTANCE(handle, -1)
513   THROW("16-bit data precision requires lossless JPEG,\n"
514         "which was disabled at build time.")
515 bailout:
516   return retval;
517
518 #endif
519 }
520
521
522 #undef _JSAMPLE
523 #undef _JSAMPROW
524 #undef _buffer
525 #undef _jinit_read_ppm
526 #undef _jinit_write_ppm
527 #undef _jpeg_crop_scanline
528 #undef _jpeg_read_scanlines
529 #undef _jpeg_skip_scanlines
530 #undef _jpeg_write_scanlines