Pillow
391 строка · 13.4 Кб
1/*
2* The Python Imaging Library.
3* $Id$
4*
5* coder for JPEG data
6*
7* history:
8* 1996-05-06 fl created
9* 1996-07-16 fl don't drop last block of encoded data
10* 1996-12-30 fl added quality and progressive settings
11* 1997-01-08 fl added streamtype settings
12* 1998-01-31 fl adapted to libjpeg 6a
13* 1998-07-12 fl added YCbCr support
14* 2001-04-16 fl added DPI write support
15*
16* Copyright (c) 1997-2001 by Secret Labs AB
17* Copyright (c) 1995-1997 by Fredrik Lundh
18*
19* See the README file for details on usage and redistribution.
20*/
21
22#include "Imaging.h"23
24#ifdef HAVE_LIBJPEG25
26#undef HAVE_PROTOTYPES27#undef HAVE_STDLIB_H28#undef HAVE_STDDEF_H29#undef UINT830#undef UINT1631#undef UINT3232#undef INT1633#undef INT3234
35#include "Jpeg.h"36
37/* -------------------------------------------------------------------- */
38/* Suspending output handler */
39/* -------------------------------------------------------------------- */
40
41METHODDEF(void)42stub(j_compress_ptr cinfo) { /* empty */ }43
44METHODDEF(boolean)45empty_output_buffer(j_compress_ptr cinfo) {46/* Suspension */47return FALSE;48}
49
50GLOBAL(void)51jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION *destination) {52cinfo->dest = (void *)destination;53
54destination->pub.init_destination = stub;55destination->pub.empty_output_buffer = empty_output_buffer;56destination->pub.term_destination = stub;57}
58
59/* -------------------------------------------------------------------- */
60/* Error handler */
61/* -------------------------------------------------------------------- */
62
63METHODDEF(void)64error(j_common_ptr cinfo) {65JPEGERROR *error;66error = (JPEGERROR *)cinfo->err;67(*cinfo->err->output_message)(cinfo);68longjmp(error->setjmp_buffer, 1);69}
70
71/* -------------------------------------------------------------------- */
72/* Encoder */
73/* -------------------------------------------------------------------- */
74
75int
76ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {77JPEGENCODERSTATE *context = (JPEGENCODERSTATE *)state->context;78int ok;79
80if (setjmp(context->error.setjmp_buffer)) {81/* JPEG error handler */82jpeg_destroy_compress(&context->cinfo);83state->errcode = IMAGING_CODEC_BROKEN;84return -1;85}86
87if (!state->state) {88/* Setup compression context (very similar to the decoder) */89context->cinfo.err = jpeg_std_error(&context->error.pub);90context->error.pub.error_exit = error;91jpeg_create_compress(&context->cinfo);92jpeg_buffer_dest(&context->cinfo, &context->destination);93
94context->extra_offset = 0;95
96/* Ready to encode */97state->state = 1;98}99
100/* Load the destination buffer */101context->destination.pub.next_output_byte = buf;102context->destination.pub.free_in_buffer = bytes;103
104switch (state->state) {105case 1:106
107context->cinfo.image_width = state->xsize;108context->cinfo.image_height = state->ysize;109
110switch (state->bits) {111case 8:112context->cinfo.input_components = 1;113context->cinfo.in_color_space = JCS_GRAYSCALE;114break;115case 24:116context->cinfo.input_components = 3;117if (strcmp(im->mode, "YCbCr") == 0) {118context->cinfo.in_color_space = JCS_YCbCr;119} else {120context->cinfo.in_color_space = JCS_RGB;121}122break;123case 32:124context->cinfo.input_components = 4;125context->cinfo.in_color_space = JCS_CMYK;126#ifdef JCS_EXTENSIONS127if (strcmp(context->rawmode, "RGBX") == 0) {128context->cinfo.in_color_space = JCS_EXT_RGBX;129}130#endif131break;132default:133state->errcode = IMAGING_CODEC_CONFIG;134return -1;135}136
137/* Compressor configuration */138jpeg_set_defaults(&context->cinfo);139
140/* Prevent RGB -> YCbCr conversion */141if (context->keep_rgb) {142switch (context->cinfo.in_color_space) {143case JCS_RGB:144#ifdef JCS_EXTENSIONS145case JCS_EXT_RGBX:146#endif147switch (context->subsampling) {148case -1: /* Default */149case 0: /* No subsampling */150break;151default:152/* Would subsample the green and blue153channels, which doesn't make sense */
154state->errcode = IMAGING_CODEC_CONFIG;155return -1;156}157jpeg_set_colorspace(&context->cinfo, JCS_RGB);158break;159default:160break;161}162}163
164/* Use custom quantization tables */165if (context->qtables) {166int i;167int quality = 100;168int last_q = 0;169if (context->quality != -1) {170quality = context->quality;171}172for (i = 0; i < context->qtablesLen; i++) {173jpeg_add_quant_table(174&context->cinfo,175i,176&context->qtables[i * DCTSIZE2],177quality,178FALSE
179);180context->cinfo.comp_info[i].quant_tbl_no = i;181last_q = i;182}183if (context->qtablesLen == 1) {184// jpeg_set_defaults created two qtables internally, but we only185// wanted one.186jpeg_add_quant_table(187&context->cinfo, 1, &context->qtables[0], quality, FALSE188);189}190for (i = last_q; i < context->cinfo.num_components; i++) {191context->cinfo.comp_info[i].quant_tbl_no = last_q;192}193} else if (context->quality != -1) {194jpeg_set_quality(&context->cinfo, context->quality, TRUE);195}196
197/* Set subsampling options */198switch (context->subsampling) {199case 0: /* 1x1 1x1 1x1 (4:4:4) : None */200{201context->cinfo.comp_info[0].h_samp_factor = 1;202context->cinfo.comp_info[0].v_samp_factor = 1;203context->cinfo.comp_info[1].h_samp_factor = 1;204context->cinfo.comp_info[1].v_samp_factor = 1;205context->cinfo.comp_info[2].h_samp_factor = 1;206context->cinfo.comp_info[2].v_samp_factor = 1;207break;208}209case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */210{211context->cinfo.comp_info[0].h_samp_factor = 2;212context->cinfo.comp_info[0].v_samp_factor = 1;213context->cinfo.comp_info[1].h_samp_factor = 1;214context->cinfo.comp_info[1].v_samp_factor = 1;215context->cinfo.comp_info[2].h_samp_factor = 1;216context->cinfo.comp_info[2].v_samp_factor = 1;217break;218}219case 2: /* 2x2, 1x1, 1x1 (4:2:0) : High */220{221context->cinfo.comp_info[0].h_samp_factor = 2;222context->cinfo.comp_info[0].v_samp_factor = 2;223context->cinfo.comp_info[1].h_samp_factor = 1;224context->cinfo.comp_info[1].v_samp_factor = 1;225context->cinfo.comp_info[2].h_samp_factor = 1;226context->cinfo.comp_info[2].v_samp_factor = 1;227break;228}229default: {230/* Use the lib's default */231break;232}233}234if (context->progressive) {235jpeg_simple_progression(&context->cinfo);236}237context->cinfo.smoothing_factor = context->smooth;238context->cinfo.optimize_coding = (boolean)context->optimize;239context->cinfo.restart_interval = context->restart_marker_blocks;240context->cinfo.restart_in_rows = context->restart_marker_rows;241if (context->xdpi > 0 && context->ydpi > 0) {242context->cinfo.write_JFIF_header = TRUE;243context->cinfo.density_unit = 1; /* dots per inch */244context->cinfo.X_density = context->xdpi;245context->cinfo.Y_density = context->ydpi;246}247switch (context->streamtype) {248case 1:249/* tables only */250jpeg_write_tables(&context->cinfo);251goto cleanup;252case 2:253/* image only */254jpeg_suppress_tables(&context->cinfo, TRUE);255jpeg_start_compress(&context->cinfo, FALSE);256/* suppress extra section */257context->extra_offset = context->extra_size;258break;259default:260/* interchange stream */261jpeg_start_compress(&context->cinfo, TRUE);262break;263}264state->state++;265/* fall through */266
267case 2:268// check for exif len + 'APP1' header bytes269if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer) {270break;271}272// add exif header273if (context->rawExifLen > 0) {274jpeg_write_marker(275&context->cinfo,276JPEG_APP0 + 1,277(unsigned char *)context->rawExif,278context->rawExifLen279);280}281
282state->state++;283/* fall through */284case 3:285
286if (context->extra) {287/* copy extra buffer to output buffer */288unsigned int n = context->extra_size - context->extra_offset;289if (n > context->destination.pub.free_in_buffer) {290n = context->destination.pub.free_in_buffer;291}292memcpy(293context->destination.pub.next_output_byte,294context->extra + context->extra_offset,295n
296);297context->destination.pub.next_output_byte += n;298context->destination.pub.free_in_buffer -= n;299context->extra_offset += n;300if (context->extra_offset >= context->extra_size) {301state->state++;302} else {303break;304}305} else {306state->state++;307}308
309case 4:310
311if (context->comment) {312jpeg_write_marker(313&context->cinfo,314JPEG_COM,315(unsigned char *)context->comment,316context->comment_size317);318}319state->state++;320
321case 5:322if (1024 > context->destination.pub.free_in_buffer) {323break;324}325
326ok = 1;327while (state->y < state->ysize) {328state->shuffle(329state->buffer,330(UINT8 *)im->image[state->y + state->yoff] +331state->xoff * im->pixelsize,332state->xsize333);334ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1);335if (ok != 1) {336break;337}338state->y++;339}340
341if (ok != 1) {342break;343}344state->state++;345/* fall through */346
347case 6:348
349/* Finish compression */350if (context->destination.pub.free_in_buffer < 100) {351break;352}353jpeg_finish_compress(&context->cinfo);354
355cleanup:356/* Clean up */357if (context->comment) {358free(context->comment);359context->comment = NULL;360}361if (context->extra) {362free(context->extra);363context->extra = NULL;364}365if (context->rawExif) {366free(context->rawExif);367context->rawExif = NULL;368}369if (context->qtables) {370free(context->qtables);371context->qtables = NULL;372}373
374jpeg_destroy_compress(&context->cinfo);375/* if (jerr.pub.num_warnings) return BROKEN; */376state->errcode = IMAGING_CODEC_END;377break;378}379
380/* Return number of bytes in output buffer */381return context->destination.pub.next_output_byte - buf;382}
383
384const char *385ImagingJpegVersion(void) {386static char version[20];387sprintf(version, "%d.%d", JPEG_LIB_VERSION / 10, JPEG_LIB_VERSION % 10);388return version;389}
390
391#endif392