Pillow/src/libImaging/JpegEncode.c

355 lines
12 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library.
* $Id$
*
* coder for JPEG data
*
* history:
* 1996-05-06 fl created
* 1996-07-16 fl don't drop last block of encoded data
* 1996-12-30 fl added quality and progressive settings
* 1997-01-08 fl added streamtype settings
* 1998-01-31 fl adapted to libjpeg 6a
* 1998-07-12 fl added YCbCr support
* 2001-04-16 fl added DPI write support
*
* Copyright (c) 1997-2001 by Secret Labs AB
* Copyright (c) 1995-1997 by Fredrik Lundh
*
* See the README file for details on usage and redistribution.
*/
#include "Imaging.h"
2021-01-03 06:17:51 +03:00
#ifdef HAVE_LIBJPEG
2010-07-31 06:52:47 +04:00
#undef HAVE_PROTOTYPES
#undef HAVE_STDLIB_H
#undef HAVE_STDDEF_H
2010-07-31 06:52:47 +04:00
#undef UINT8
#undef UINT16
#undef UINT32
#undef INT16
#undef INT32
#include "Jpeg.h"
/* -------------------------------------------------------------------- */
2013-05-15 07:45:07 +04:00
/* Suspending output handler */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
METHODDEF(void)
2021-01-03 06:17:51 +03:00
stub(j_compress_ptr cinfo) { /* empty */ }
2010-07-31 06:52:47 +04:00
METHODDEF(boolean)
2021-01-03 06:17:51 +03:00
empty_output_buffer(j_compress_ptr cinfo) {
2010-07-31 06:52:47 +04:00
/* Suspension */
return FALSE;
}
GLOBAL(void)
2021-01-03 06:17:51 +03:00
jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION *destination) {
cinfo->dest = (void *)destination;
2010-07-31 06:52:47 +04:00
destination->pub.init_destination = stub;
destination->pub.empty_output_buffer = empty_output_buffer;
destination->pub.term_destination = stub;
}
/* -------------------------------------------------------------------- */
2013-05-15 07:45:07 +04:00
/* Error handler */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
METHODDEF(void)
2021-01-03 06:17:51 +03:00
error(j_common_ptr cinfo) {
JPEGERROR *error;
error = (JPEGERROR *)cinfo->err;
(*cinfo->err->output_message)(cinfo);
2013-05-15 07:45:07 +04:00
longjmp(error->setjmp_buffer, 1);
2010-07-31 06:52:47 +04:00
}
/* -------------------------------------------------------------------- */
2020-05-01 15:08:57 +03:00
/* Encoder */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
int
2021-01-03 06:17:51 +03:00
ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
JPEGENCODERSTATE *context = (JPEGENCODERSTATE *)state->context;
2010-07-31 06:52:47 +04:00
int ok;
if (setjmp(context->error.setjmp_buffer)) {
2020-05-01 15:08:57 +03:00
/* JPEG error handler */
jpeg_destroy_compress(&context->cinfo);
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
2010-07-31 06:52:47 +04:00
}
if (!state->state) {
2020-05-01 15:08:57 +03:00
/* Setup compression context (very similar to the decoder) */
context->cinfo.err = jpeg_std_error(&context->error.pub);
context->error.pub.error_exit = error;
jpeg_create_compress(&context->cinfo);
jpeg_buffer_dest(&context->cinfo, &context->destination);
2010-07-31 06:52:47 +04:00
context->extra_offset = 0;
2020-05-01 15:08:57 +03:00
/* Ready to encode */
state->state = 1;
2010-07-31 06:52:47 +04:00
}
/* Load the destination buffer */
context->destination.pub.next_output_byte = buf;
context->destination.pub.free_in_buffer = bytes;
switch (state->state) {
2020-05-01 15:08:57 +03:00
case 1:
context->cinfo.image_width = state->xsize;
context->cinfo.image_height = state->ysize;
switch (state->bits) {
case 8:
context->cinfo.input_components = 1;
context->cinfo.in_color_space = JCS_GRAYSCALE;
break;
case 24:
context->cinfo.input_components = 3;
2020-05-10 12:56:36 +03:00
if (strcmp(im->mode, "YCbCr") == 0) {
2020-05-01 15:08:57 +03:00
context->cinfo.in_color_space = JCS_YCbCr;
2020-05-10 12:56:36 +03:00
} else {
2020-05-01 15:08:57 +03:00
context->cinfo.in_color_space = JCS_RGB;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
break;
case 32:
context->cinfo.input_components = 4;
context->cinfo.in_color_space = JCS_CMYK;
2021-01-03 06:17:51 +03:00
#ifdef JCS_EXTENSIONS
2020-05-10 12:56:36 +03:00
if (strcmp(context->rawmode, "RGBX") == 0) {
2020-05-01 15:08:57 +03:00
context->cinfo.in_color_space = JCS_EXT_RGBX;
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
#endif
2020-05-01 15:08:57 +03:00
break;
default:
state->errcode = IMAGING_CODEC_CONFIG;
return -1;
}
/* Compressor configuration */
jpeg_set_defaults(&context->cinfo);
/* Use custom quantization tables */
if (context->qtables) {
int i;
int quality = 100;
int last_q = 0;
if (context->quality != -1) {
quality = context->quality;
}
for (i = 0; i < context->qtablesLen; i++) {
2021-01-03 06:17:51 +03:00
jpeg_add_quant_table(
&context->cinfo,
i,
&context->qtables[i * DCTSIZE2],
quality,
FALSE);
2020-05-01 15:08:57 +03:00
context->cinfo.comp_info[i].quant_tbl_no = i;
last_q = i;
}
if (context->qtablesLen == 1) {
2021-01-03 06:17:51 +03:00
// jpeg_set_defaults created two qtables internally, but we only
// wanted one.
jpeg_add_quant_table(
&context->cinfo, 1, &context->qtables[0], quality, FALSE);
2020-05-01 15:08:57 +03:00
}
for (i = last_q; i < context->cinfo.num_components; i++) {
context->cinfo.comp_info[i].quant_tbl_no = last_q;
}
} else if (context->quality != -1) {
jpeg_set_quality(&context->cinfo, context->quality, TRUE);
2020-05-01 15:08:57 +03:00
}
/* Set subsampling options */
2021-01-03 06:17:51 +03:00
switch (context->subsampling) {
case 0: /* 1x1 1x1 1x1 (4:4:4) : None */
2020-05-01 15:08:57 +03:00
{
context->cinfo.comp_info[0].h_samp_factor = 1;
context->cinfo.comp_info[0].v_samp_factor = 1;
context->cinfo.comp_info[1].h_samp_factor = 1;
context->cinfo.comp_info[1].v_samp_factor = 1;
context->cinfo.comp_info[2].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1;
break;
}
2021-01-03 06:17:51 +03:00
case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */
2020-05-01 15:08:57 +03:00
{
context->cinfo.comp_info[0].h_samp_factor = 2;
context->cinfo.comp_info[0].v_samp_factor = 1;
context->cinfo.comp_info[1].h_samp_factor = 1;
context->cinfo.comp_info[1].v_samp_factor = 1;
context->cinfo.comp_info[2].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1;
break;
}
2021-01-03 06:17:51 +03:00
case 2: /* 2x2, 1x1, 1x1 (4:2:0) : High */
2020-05-01 15:08:57 +03:00
{
context->cinfo.comp_info[0].h_samp_factor = 2;
context->cinfo.comp_info[0].v_samp_factor = 2;
context->cinfo.comp_info[1].h_samp_factor = 1;
context->cinfo.comp_info[1].v_samp_factor = 1;
context->cinfo.comp_info[2].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1;
break;
}
2021-01-03 06:17:51 +03:00
default: {
2020-05-01 15:08:57 +03:00
/* Use the lib's default */
break;
}
2021-01-03 06:17:51 +03:00
}
if (context->progressive) {
jpeg_simple_progression(&context->cinfo);
}
context->cinfo.smoothing_factor = context->smooth;
context->cinfo.optimize_coding = (boolean)context->optimize;
if (context->xdpi > 0 && context->ydpi > 0) {
context->cinfo.write_JFIF_header = TRUE;
context->cinfo.density_unit = 1; /* dots per inch */
context->cinfo.X_density = context->xdpi;
context->cinfo.Y_density = context->ydpi;
}
2020-05-01 15:08:57 +03:00
switch (context->streamtype) {
case 1:
/* tables only -- not yet implemented */
state->errcode = IMAGING_CODEC_CONFIG;
return -1;
case 2:
/* image only */
jpeg_suppress_tables(&context->cinfo, TRUE);
jpeg_start_compress(&context->cinfo, FALSE);
/* suppress extra section */
context->extra_offset = context->extra_size;
break;
default:
/* interchange stream */
jpeg_start_compress(&context->cinfo, TRUE);
break;
}
state->state++;
/* fall through */
case 2:
// check for exif len + 'APP1' header bytes
2021-01-03 06:17:51 +03:00
if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer) {
2020-05-01 15:08:57 +03:00
break;
}
2021-01-03 06:17:51 +03:00
// add exif header
if (context->rawExifLen > 0) {
jpeg_write_marker(
&context->cinfo,
JPEG_APP0 + 1,
(unsigned char *)context->rawExif,
context->rawExifLen);
2020-05-01 15:08:57 +03:00
}
state->state++;
/* fall through */
case 3:
if (context->extra) {
/* copy extra buffer to output buffer */
unsigned int n = context->extra_size - context->extra_offset;
2020-05-10 12:56:36 +03:00
if (n > context->destination.pub.free_in_buffer) {
2020-05-01 15:08:57 +03:00
n = context->destination.pub.free_in_buffer;
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
memcpy(
context->destination.pub.next_output_byte,
context->extra + context->extra_offset,
n);
2020-05-01 15:08:57 +03:00
context->destination.pub.next_output_byte += n;
context->destination.pub.free_in_buffer -= n;
context->extra_offset += n;
2020-05-10 12:56:36 +03:00
if (context->extra_offset >= context->extra_size) {
2020-05-01 15:08:57 +03:00
state->state++;
2020-05-10 12:56:36 +03:00
} else {
2020-05-01 15:08:57 +03:00
break;
2020-05-10 12:56:36 +03:00
}
} else {
2010-07-31 06:52:47 +04:00
state->state++;
}
2020-05-01 15:08:57 +03:00
case 4:
2022-12-05 05:57:26 +03:00
if (context->comment_size > 0) {
jpeg_write_marker(&context->cinfo, JPEG_COM, (unsigned char *)context->comment, context->comment_size);
}
state->state++;
case 5:
2021-01-03 06:17:51 +03:00
if (1024 > context->destination.pub.free_in_buffer) {
2020-05-01 15:08:57 +03:00
break;
}
ok = 1;
while (state->y < state->ysize) {
2021-01-03 06:17:51 +03:00
state->shuffle(
state->buffer,
(UINT8 *)im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->xsize);
2020-05-01 15:08:57 +03:00
ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1);
2020-05-10 12:56:36 +03:00
if (ok != 1) {
2020-05-01 15:08:57 +03:00
break;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
state->y++;
}
2020-05-10 12:56:36 +03:00
if (ok != 1) {
2010-07-31 06:52:47 +04:00
break;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
state->state++;
/* fall through */
2010-07-31 06:52:47 +04:00
2022-12-05 05:57:26 +03:00
case 6:
2020-05-01 15:08:57 +03:00
/* Finish compression */
2020-05-10 12:56:36 +03:00
if (context->destination.pub.free_in_buffer < 100) {
2020-05-01 15:08:57 +03:00
break;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
jpeg_finish_compress(&context->cinfo);
/* Clean up */
2022-12-05 05:57:26 +03:00
if (context->comment) {
free(context->comment);
context->comment = NULL;
}
2020-05-01 15:08:57 +03:00
if (context->extra) {
free(context->extra);
context->extra = NULL;
}
if (context->rawExif) {
free(context->rawExif);
context->rawExif = NULL;
}
if (context->qtables) {
free(context->qtables);
context->qtables = NULL;
}
jpeg_destroy_compress(&context->cinfo);
/* if (jerr.pub.num_warnings) return BROKEN; */
state->errcode = IMAGING_CODEC_END;
break;
2010-07-31 06:52:47 +04:00
}
/* Return number of bytes in output buffer */
return context->destination.pub.next_output_byte - buf;
}
2021-01-03 06:17:51 +03:00
const char *
ImagingJpegVersion(void) {
2010-07-31 06:52:47 +04:00
static char version[20];
sprintf(version, "%d.%d", JPEG_LIB_VERSION / 10, JPEG_LIB_VERSION % 10);
return version;
}
#endif