mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-30 20:06:17 +03:00
316 lines
9.1 KiB
C
316 lines
9.1 KiB
C
/*
|
|
* The Python Imaging Library.
|
|
* $Id$
|
|
*
|
|
* decoder for JPEG image data.
|
|
*
|
|
* history:
|
|
* 1996-05-02 fl Created
|
|
* 1996-05-05 fl Handle small JPEG files correctly
|
|
* 1996-05-28 fl Added "draft mode" support
|
|
* 1997-01-25 fl Added colour conversion override
|
|
* 1998-01-31 fl Adapted to libjpeg 6a
|
|
* 1998-07-12 fl Extended YCbCr support
|
|
* 1998-12-29 fl Added new state to handle suspension in multipass modes
|
|
* 2000-10-12 fl Suppress warnings
|
|
* 2000-12-04 fl Suppress errors beyond end of image data
|
|
*
|
|
* Copyright (c) 1998-2000 Secret Labs AB
|
|
* Copyright (c) 1996-2000 Fredrik Lundh
|
|
*
|
|
* See the README file for details on usage and redistribution.
|
|
*/
|
|
|
|
|
|
#include "Imaging.h"
|
|
|
|
#ifdef HAVE_LIBJPEG
|
|
|
|
#undef HAVE_PROTOTYPES
|
|
#undef HAVE_STDLIB_H
|
|
#undef HAVE_STDDEF_H
|
|
#undef UINT8
|
|
#undef UINT16
|
|
#undef UINT32
|
|
#undef INT16
|
|
#undef INT32
|
|
|
|
#include "Jpeg.h"
|
|
|
|
|
|
#define STRINGIFY(x) #x
|
|
#define TOSTRING(x) STRINGIFY(x)
|
|
|
|
// There is no way to compare versions on compile time,
|
|
// so we have to do that in runtime.
|
|
#ifdef LIBJPEG_TURBO_VERSION
|
|
char *libjpeg_turbo_version = TOSTRING(LIBJPEG_TURBO_VERSION);
|
|
#else
|
|
char *libjpeg_turbo_version = NULL;
|
|
#endif
|
|
|
|
int
|
|
ImagingJpegUseJCSExtensions()
|
|
{
|
|
int use_jcs_extensions = 0;
|
|
#ifdef JCS_EXTENSIONS
|
|
#if defined(LIBJPEG_TURBO_VERSION_NUMBER)
|
|
#if LIBJPEG_TURBO_VERSION_NUMBER >= 1002010
|
|
use_jcs_extensions = 1;
|
|
#endif
|
|
#else
|
|
if (libjpeg_turbo_version) {
|
|
use_jcs_extensions = strcmp(libjpeg_turbo_version, "1.2.1") >= 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
return use_jcs_extensions;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Suspending input handler */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
METHODDEF(void)
|
|
stub(j_decompress_ptr cinfo)
|
|
{
|
|
/* empty */
|
|
}
|
|
|
|
METHODDEF(boolean)
|
|
fill_input_buffer(j_decompress_ptr cinfo)
|
|
{
|
|
/* Suspension */
|
|
return FALSE;
|
|
}
|
|
|
|
METHODDEF(void)
|
|
skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
JPEGSOURCE* source = (JPEGSOURCE*) cinfo->src;
|
|
|
|
if (num_bytes > (long) source->pub.bytes_in_buffer) {
|
|
/* We need to skip more data than we have in the buffer.
|
|
This will force the JPEG library to suspend decoding. */
|
|
source->skip = num_bytes - source->pub.bytes_in_buffer;
|
|
source->pub.next_input_byte += source->pub.bytes_in_buffer;
|
|
source->pub.bytes_in_buffer = 0;
|
|
} else {
|
|
/* Skip portion of the buffer */
|
|
source->pub.bytes_in_buffer -= num_bytes;
|
|
source->pub.next_input_byte += num_bytes;
|
|
source->skip = 0;
|
|
}
|
|
}
|
|
|
|
|
|
GLOBAL(void)
|
|
jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source)
|
|
{
|
|
cinfo->src = (void*) source;
|
|
|
|
/* Prepare for suspending reader */
|
|
source->pub.init_source = stub;
|
|
source->pub.fill_input_buffer = fill_input_buffer;
|
|
source->pub.skip_input_data = skip_input_data;
|
|
source->pub.resync_to_restart = jpeg_resync_to_restart;
|
|
source->pub.term_source = stub;
|
|
source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
|
|
|
source->skip = 0;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Error handler */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
METHODDEF(void)
|
|
error(j_common_ptr cinfo)
|
|
{
|
|
JPEGERROR* error;
|
|
error = (JPEGERROR*) cinfo->err;
|
|
longjmp(error->setjmp_buffer, 1);
|
|
}
|
|
|
|
METHODDEF(void)
|
|
output(j_common_ptr cinfo)
|
|
{
|
|
/* nothing */
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Decoder */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
int
|
|
ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|
{
|
|
JPEGSTATE* context = (JPEGSTATE*) state->context;
|
|
int ok;
|
|
|
|
if (setjmp(context->error.setjmp_buffer)) {
|
|
/* JPEG error handler */
|
|
jpeg_destroy_decompress(&context->cinfo);
|
|
state->errcode = IMAGING_CODEC_BROKEN;
|
|
return -1;
|
|
}
|
|
|
|
if (!state->state) {
|
|
|
|
/* Setup decompression context */
|
|
context->cinfo.err = jpeg_std_error(&context->error.pub);
|
|
context->error.pub.error_exit = error;
|
|
context->error.pub.output_message = output;
|
|
jpeg_create_decompress(&context->cinfo);
|
|
jpeg_buffer_src(&context->cinfo, &context->source);
|
|
|
|
/* Ready to decode */
|
|
state->state = 1;
|
|
|
|
}
|
|
|
|
/* Load the source buffer */
|
|
context->source.pub.next_input_byte = buf;
|
|
context->source.pub.bytes_in_buffer = bytes;
|
|
|
|
if (context->source.skip > 0) {
|
|
skip_input_data(&context->cinfo, context->source.skip);
|
|
if (context->source.skip > 0)
|
|
return context->source.pub.next_input_byte - buf;
|
|
}
|
|
|
|
switch (state->state) {
|
|
|
|
case 1:
|
|
|
|
/* Read JPEG header, until we find an image body. */
|
|
do {
|
|
|
|
/* Note that we cannot return unless we have decoded
|
|
as much data as possible. */
|
|
ok = jpeg_read_header(&context->cinfo, FALSE);
|
|
|
|
} while (ok == JPEG_HEADER_TABLES_ONLY);
|
|
|
|
if (ok == JPEG_SUSPENDED)
|
|
break;
|
|
|
|
/* Decoder settings */
|
|
|
|
/* jpegmode indicates whats in the file; if not set, we'll
|
|
trust the decoder */
|
|
if (strcmp(context->jpegmode, "L") == 0)
|
|
context->cinfo.jpeg_color_space = JCS_GRAYSCALE;
|
|
else if (strcmp(context->jpegmode, "RGB") == 0)
|
|
context->cinfo.jpeg_color_space = JCS_RGB;
|
|
else if (strcmp(context->jpegmode, "CMYK") == 0)
|
|
context->cinfo.jpeg_color_space = JCS_CMYK;
|
|
else if (strcmp(context->jpegmode, "YCbCr") == 0)
|
|
context->cinfo.jpeg_color_space = JCS_YCbCr;
|
|
else if (strcmp(context->jpegmode, "YCbCrK") == 0) {
|
|
context->cinfo.jpeg_color_space = JCS_YCCK;
|
|
}
|
|
|
|
/* rawmode indicates what we want from the decoder. if not
|
|
set, conversions are disabled */
|
|
if (strcmp(context->rawmode, "L") == 0)
|
|
context->cinfo.out_color_space = JCS_GRAYSCALE;
|
|
else if (strcmp(context->rawmode, "RGB") == 0)
|
|
context->cinfo.out_color_space = JCS_RGB;
|
|
#ifdef JCS_EXTENSIONS
|
|
else if (strcmp(context->rawmode, "RGBX") == 0)
|
|
context->cinfo.out_color_space = JCS_EXT_RGBX;
|
|
#endif
|
|
else if (strcmp(context->rawmode, "CMYK") == 0 ||
|
|
strcmp(context->rawmode, "CMYK;I") == 0)
|
|
context->cinfo.out_color_space = JCS_CMYK;
|
|
else if (strcmp(context->rawmode, "YCbCr") == 0)
|
|
context->cinfo.out_color_space = JCS_YCbCr;
|
|
else if (strcmp(context->rawmode, "YCbCrK") == 0)
|
|
context->cinfo.out_color_space = JCS_YCCK;
|
|
else {
|
|
/* Disable decoder conversions */
|
|
context->cinfo.jpeg_color_space = JCS_UNKNOWN;
|
|
context->cinfo.out_color_space = JCS_UNKNOWN;
|
|
}
|
|
|
|
if (context->scale > 1) {
|
|
context->cinfo.scale_num = 1;
|
|
context->cinfo.scale_denom = context->scale;
|
|
}
|
|
if (context->draft) {
|
|
context->cinfo.do_fancy_upsampling = FALSE;
|
|
context->cinfo.dct_method = JDCT_FASTEST;
|
|
}
|
|
|
|
state->state++;
|
|
/* fall through */
|
|
|
|
case 2:
|
|
|
|
/* Set things up for decompression (this processes the entire
|
|
file if necessary to return data line by line) */
|
|
if (!jpeg_start_decompress(&context->cinfo))
|
|
break;
|
|
|
|
state->state++;
|
|
/* fall through */
|
|
|
|
case 3:
|
|
|
|
/* Decompress a single line of data */
|
|
ok = 1;
|
|
while (state->y < state->ysize) {
|
|
ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1);
|
|
if (ok != 1)
|
|
break;
|
|
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
|
|
state->xoff * im->pixelsize, state->buffer,
|
|
state->xsize);
|
|
state->y++;
|
|
}
|
|
if (ok != 1)
|
|
break;
|
|
state->state++;
|
|
/* fall through */
|
|
|
|
case 4:
|
|
|
|
/* Finish decompression */
|
|
if (!jpeg_finish_decompress(&context->cinfo)) {
|
|
/* FIXME: add strictness mode test */
|
|
if (state->y < state->ysize)
|
|
break;
|
|
}
|
|
|
|
/* Clean up */
|
|
jpeg_destroy_decompress(&context->cinfo);
|
|
/* if (jerr.pub.num_warnings) return BROKEN; */
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Return number of bytes consumed */
|
|
return context->source.pub.next_input_byte - buf;
|
|
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
int ImagingJpegDecodeCleanup(ImagingCodecState state){
|
|
/* called to free the decompression engine when the decode terminates
|
|
due to a corrupt or truncated image
|
|
*/
|
|
JPEGSTATE* context = (JPEGSTATE*) state->context;
|
|
|
|
/* Clean up */
|
|
jpeg_destroy_decompress(&context->cinfo);
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|