Merge pull request #219 from wiredfool/maxblock

Fixing buffer size for JPEGs with large exif
This commit is contained in:
wiredfool 2013-05-15 21:28:52 -07:00
commit 5c51ae02f3
3 changed files with 77 additions and 58 deletions

View File

@ -554,6 +554,7 @@ def _save(im, fp, filename):
info.get("exif", b"") info.get("exif", b"")
) )
# if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot. # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot.
# Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this
# is a value that's been used in a django patch. # is a value that's been used in a django patch.
@ -562,6 +563,10 @@ def _save(im, fp, filename):
if "optimize" in info: if "optimize" in info:
bufsize = im.size[0]*im.size[1] bufsize = im.size[0]*im.size[1]
# The exif info needs to be written as one block, + APP1, + one spare byte.
# Ensure that our buffer is big enough
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 )
ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize)
def _save_cjpeg(im, fp, filename): def _save_cjpeg(im, fp, filename):

View File

@ -120,6 +120,12 @@ def test_optimize_large_buffer():
im = Image.new("RGB", (4096,4096), 0xff3333) im = Image.new("RGB", (4096,4096), 0xff3333)
im.save(f, format="JPEG", optimize=True) im.save(f, format="JPEG", optimize=True)
def test_large_exif():
#https://github.com/python-imaging/Pillow/issues/148
f = tempfile('temp.jpg')
im = lena()
im.save(f,'JPEG', quality=90, exif=b"1"*65532)
def test_progressive(): def test_progressive():
im1 = roundtrip(lena()) im1 = roundtrip(lena())
im2 = roundtrip(lena(), progressive=1) im2 = roundtrip(lena(), progressive=1)

View File

@ -22,7 +22,7 @@
#include "Imaging.h" #include "Imaging.h"
#ifdef HAVE_LIBJPEG #ifdef HAVE_LIBJPEG
#undef HAVE_PROTOTYPES #undef HAVE_PROTOTYPES
#undef HAVE_STDLIB_H #undef HAVE_STDLIB_H
@ -36,7 +36,7 @@
#include "Jpeg.h" #include "Jpeg.h"
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* Suspending output handler */ /* Suspending output handler */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
METHODDEF(void) METHODDEF(void)
@ -64,16 +64,16 @@ jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination)
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* Error handler */ /* Error handler */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
METHODDEF(void) METHODDEF(void)
error(j_common_ptr cinfo) error(j_common_ptr cinfo)
{ {
JPEGERROR* error; JPEGERROR* error;
error = (JPEGERROR*) cinfo->err; error = (JPEGERROR*) cinfo->err;
(*cinfo->err->output_message) (cinfo); (*cinfo->err->output_message) (cinfo);
longjmp(error->setjmp_buffer, 1); longjmp(error->setjmp_buffer, 1);
} }
@ -146,59 +146,59 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* Use custom quantization tables */ /* Use custom quantization tables */
if (context->qtables) { if (context->qtables) {
int i; int i;
int quality = 100; int quality = 100;
if (context->quality > 0) { if (context->quality > 0) {
quality = context->quality; quality = context->quality;
} }
for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) { for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) {
// TODO: Should add support for none baseline // TODO: Should add support for none baseline
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
quality, TRUE); quality, TRUE);
} }
} else if (context->quality > 0) { } else if (context->quality > 0) {
jpeg_set_quality(&context->cinfo, context->quality, 1); jpeg_set_quality(&context->cinfo, context->quality, 1);
} }
/* Set subsampling options */ /* Set subsampling options */
switch (context->subsampling) switch (context->subsampling)
{ {
case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ case 0: /* 1x1 1x1 1x1 (4:4:4) : None */
{ {
context->cinfo.comp_info[0].h_samp_factor = 1; context->cinfo.comp_info[0].h_samp_factor = 1;
context->cinfo.comp_info[0].v_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].h_samp_factor = 1;
context->cinfo.comp_info[1].v_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].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1; context->cinfo.comp_info[2].v_samp_factor = 1;
break; break;
} }
case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */
{ {
context->cinfo.comp_info[0].h_samp_factor = 2; context->cinfo.comp_info[0].h_samp_factor = 2;
context->cinfo.comp_info[0].v_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].h_samp_factor = 1;
context->cinfo.comp_info[1].v_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].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1; context->cinfo.comp_info[2].v_samp_factor = 1;
break; break;
} }
case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */
{ {
context->cinfo.comp_info[0].h_samp_factor = 2; context->cinfo.comp_info[0].h_samp_factor = 2;
context->cinfo.comp_info[0].v_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].h_samp_factor = 1;
context->cinfo.comp_info[1].v_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].h_samp_factor = 1;
context->cinfo.comp_info[2].v_samp_factor = 1; context->cinfo.comp_info[2].v_samp_factor = 1;
break; break;
} }
default: default:
{ {
/* Use the lib's default */ /* Use the lib's default */
break; break;
} }
} }
if (context->progressive) if (context->progressive)
jpeg_simple_progression(&context->cinfo); jpeg_simple_progression(&context->cinfo);
context->cinfo.smoothing_factor = context->smooth; context->cinfo.smoothing_factor = context->smooth;
@ -219,24 +219,29 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
jpeg_start_compress(&context->cinfo, FALSE); jpeg_start_compress(&context->cinfo, FALSE);
/* suppress extra section */ /* suppress extra section */
context->extra_offset = context->extra_size; context->extra_offset = context->extra_size;
//add exif header
if (context->rawExifLen > 0)
jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen);
break; break;
default: default:
/* interchange stream */ /* interchange stream */
jpeg_start_compress(&context->cinfo, TRUE); jpeg_start_compress(&context->cinfo, TRUE);
//add exif header break;
if (context->rawExifLen > 0)
jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen);
break;
} }
state->state++; state->state++;
/* fall through */ /* fall through */
case 2: case 2:
// check for exif len + 'APP1' header bytes
if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){
break;
}
//add exif header
if (context->rawExifLen > 0){
jpeg_write_marker(&context->cinfo, JPEG_APP0+1,
(unsigned char*)context->rawExif, context->rawExifLen);
}
state->state++;
/* fall through */
case 3:
if (context->extra) { if (context->extra) {
/* copy extra buffer to output buffer */ /* copy extra buffer to output buffer */
@ -253,9 +258,12 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
else else
break; break;
} else } else
state->state++; state->state++;
case 3: case 4:
if (1024 > context->destination.pub.free_in_buffer){
break;
}
ok = 1; ok = 1;
while (state->y < state->ysize) { while (state->y < state->ysize) {
@ -273,7 +281,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->state++; state->state++;
/* fall through */ /* fall through */
case 4: case 5:
/* Finish compression */ /* Finish compression */
if (context->destination.pub.free_in_buffer < 100) if (context->destination.pub.free_in_buffer < 100)