Merge pull request #2041 from uploadcare/pcx-encoding

PCX encoding fixes
This commit is contained in:
wiredfool 2016-08-06 22:09:22 +01:00 committed by GitHub
commit 0a8385f5e0
2 changed files with 108 additions and 27 deletions

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image, PcxImagePlugin from PIL import Image, ImageFile, PcxImagePlugin
class TestFilePcx(PillowTestCase): class TestFilePcx(PillowTestCase):
@ -46,6 +46,84 @@ class TestFilePcx(PillowTestCase):
# Make sure all pixels are either 0 or 255. # Make sure all pixels are either 0 or 255.
self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144) self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144)
def test_1px_width(self):
im = Image.new('L', (1, 256))
px = im.load()
for y in range(256):
px[0, y] = y
self._roundtrip(im)
def test_large_count(self):
im = Image.new('L', (256, 1))
px = im.load()
for x in range(256):
px[x, 0] = x // 67 * 67
self._roundtrip(im)
def _test_buffer_overflow(self, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
self._roundtrip(im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_one_in_loop(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_many_in_loop(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
for x in range(8):
px[x, 4] = 16
self._test_buffer_overflow(im)
def test_break_one_at_end(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
self._test_buffer_overflow(im)
def test_break_many_at_end(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
for x in range(4):
px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
self._test_buffer_overflow(im)
def test_break_padding(self):
im = Image.new('L', (257, 5))
px = im.load()
for y in range(5):
for x in range(257):
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
self._test_buffer_overflow(im)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -75,7 +75,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
(UINT8*) im->image[state->y + state->yoff] + (UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->xsize); state->xoff * im->pixelsize, state->xsize);
state->y++; state->y += 1;
state->count = 1; state->count = 1;
state->LAST = state->buffer[0]; state->LAST = state->buffer[0];
@ -91,7 +91,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* when we arrive here, "count" contains the number of /* when we arrive here, "count" contains the number of
bytes having the value of "LAST" that we've already bytes having the value of "LAST" that we've already
seen */ seen */
while (state->x < planes * bytes_per_line) { do {
/* If we're encoding an odd width file, and we've /* If we're encoding an odd width file, and we've
got more than one plane, we need to pad each got more than one plane, we need to pad each
color row with padding bytes at the end. Since color row with padding bytes at the end. Since
@ -103,22 +103,23 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (state->count == 63) { if (state->count == 63) {
/* this run is full; flush it */ /* this run is full; flush it */
if (bytes < 2) if (bytes < 2) {
return ptr - buf; return ptr - buf;
*ptr++ = 0xff; }
*ptr++ = state->LAST; ptr[0] = 0xff;
ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
state->count = 0; state->count = 0;
} }
this = state->buffer[state->x]; this = state->buffer[state->x];
if (this == state->LAST) { if (this == state->LAST) {
/* extend the current run */ /* extend the current run */
state->x++; state->x += 1;
state->count++; state->count += 1;
} else { } else {
/* start a new run */ /* start a new run */
@ -126,15 +127,17 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (bytes < 1) { if (bytes < 1) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = state->LAST; ptr[0] = state->LAST;
bytes--; ptr += 1;
bytes -= 1;
} else { } else {
if (state->count > 0) { if (state->count > 0) {
if (bytes < 2) { if (bytes < 2) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = 0xc0 | state->count; ptr[0] = 0xc0 | state->count;
*ptr++ = state->LAST; ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
} }
} }
@ -142,8 +145,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->LAST = this; state->LAST = this;
state->count = 1; state->count = 1;
state->x++; state->x += 1;
} }
} }
@ -152,33 +154,34 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (bytes < 1 + padding) { if (bytes < 1 + padding) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = state->LAST; ptr[0] = state->LAST;
bytes--; ptr += 1;
bytes -= 1;
} else { } else {
if (state->count > 0) { if (state->count > 0) {
if (bytes < 2 + padding) { if (bytes < 2 + padding) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = 0xc0 | state->count; ptr[0] = 0xc0 | state->count;
*ptr++ = state->LAST; ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
} }
} }
if (bytes < padding) {
return ptr - buf;
}
/* add the padding */ /* add the padding */
for (i=0;i<padding;i++){ for (i = 0; i < padding; i++) {
*ptr++=0; ptr[0] = 0;
bytes--; ptr += 1;
bytes -= 1;
} }
/* reset for the next color plane. */ /* reset for the next color plane. */
if (state->x < planes * bytes_per_line) { if (state->x < planes * bytes_per_line) {
state->count = 1; state->count = 1;
state->LAST = state->buffer[state->x]; state->LAST = state->buffer[state->x];
state->x++; state->x += 1;
} }
} } while (state->x < planes * bytes_per_line);
/* read next line */ /* read next line */
state->state = FETCH; state->state = FETCH;
break; break;