Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,13 +554,18 @@ def validate_qtables(qtables):
info.get("exif", b"")
)


# 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
# is a value that's been used in a django patch.
# https://github.com/jdriscoll/django-imagekit/issues/50
bufsize=0
if "optimize" in info:
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)

Expand Down
6 changes: 6 additions & 0 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ def test_optimize_large_buffer():
im = Image.new("RGB", (4096,4096), 0xff3333)
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():
im1 = roundtrip(lena())
im2 = roundtrip(lena(), progressive=1)
Expand Down
124 changes: 66 additions & 58 deletions libImaging/JpegEncode.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#include "Imaging.h"

#ifdef HAVE_LIBJPEG
#ifdef HAVE_LIBJPEG

#undef HAVE_PROTOTYPES
#undef HAVE_STDLIB_H
Expand All @@ -36,7 +36,7 @@
#include "Jpeg.h"

/* -------------------------------------------------------------------- */
/* Suspending output handler */
/* Suspending output handler */
/* -------------------------------------------------------------------- */

METHODDEF(void)
Expand Down Expand Up @@ -64,16 +64,16 @@ jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination)


/* -------------------------------------------------------------------- */
/* Error handler */
/* Error handler */
/* -------------------------------------------------------------------- */

METHODDEF(void)
error(j_common_ptr cinfo)
{
JPEGERROR* error;
error = (JPEGERROR*) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(error->setjmp_buffer, 1);
JPEGERROR* error;
error = (JPEGERROR*) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(error->setjmp_buffer, 1);
}


Expand Down Expand Up @@ -146,59 +146,59 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)

/* Use custom quantization tables */
if (context->qtables) {
int i;
int quality = 100;
if (context->quality > 0) {
quality = context->quality;
}
for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) {
// TODO: Should add support for none baseline
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
quality, TRUE);
}
int i;
int quality = 100;
if (context->quality > 0) {
quality = context->quality;
}
for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) {
// TODO: Should add support for none baseline
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
quality, TRUE);
}
} else if (context->quality > 0) {
jpeg_set_quality(&context->cinfo, context->quality, 1);
}

/* Set subsampling options */
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].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;
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;
}
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].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;
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;
}
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].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;
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;
}
default:
default:
{
/* Use the lib's default */
break;
/* Use the lib's default */
break;
}
}
}
if (context->progressive)
jpeg_simple_progression(&context->cinfo);
context->cinfo.smoothing_factor = context->smooth;
Expand All @@ -219,24 +219,29 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
jpeg_start_compress(&context->cinfo, FALSE);
/* suppress extra section */
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;
default:
/* interchange stream */
jpeg_start_compress(&context->cinfo, TRUE);
//add exif header
if (context->rawExifLen > 0)
jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen);

break;
break;
}
state->state++;
/* fall through */

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) {
/* copy extra buffer to output buffer */
Expand All @@ -253,9 +258,12 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
else
break;
} else
state->state++;
state->state++;

case 3:
case 4:
if (1024 > context->destination.pub.free_in_buffer){
break;
}

ok = 1;
while (state->y < state->ysize) {
Expand All @@ -273,7 +281,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->state++;
/* fall through */

case 4:
case 5:

/* Finish compression */
if (context->destination.pub.free_in_buffer < 100)
Expand Down