From 89cb3c7e85fdd98b54f0f93fdee36806dc86993e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 3 Mar 2014 23:03:36 -0800 Subject: [PATCH] Use even stride when saving odd sized .pcx files --- PIL/PcxImagePlugin.py | 10 +++ libImaging/PcxEncode.c | 149 ++++++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 55 deletions(-) diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index d90a7cf0d..2496af676 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -134,6 +134,16 @@ def _save(im, fp, filename, check=0): # bytes per plane stride = (im.size[0] * bits + 7) // 8 + # stride should be even + stride = stride + (stride % 2) + # Stride needs to be kept in sync with the PcxEncode.c version. + # Ideally it should be passed in in the state, but the bytes value + # gets overwritten. + + + if Image.DEBUG: + print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( + im.size[0], bits, stride)) # under windows, we could determine the current screen size with # "Image.core.display_mode()[1]", but I think that's overkill... diff --git a/libImaging/PcxEncode.c b/libImaging/PcxEncode.c index 8c6272d44..c1f64a33d 100644 --- a/libImaging/PcxEncode.c +++ b/libImaging/PcxEncode.c @@ -26,6 +26,12 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { UINT8* ptr; int this; + int bytes_per_line = 0; + int padding = 0; + int stride = 0; + int bpp = 0; + int planes = 1; + int i; ptr = buf; @@ -35,12 +41,25 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) state->errcode = IMAGING_CODEC_END; return 0; } - - state->bytes = (state->xsize*state->bits + 7) / 8; state->state = FETCH; - } + bpp = state->bits; + if (state->bits == 24){ + planes = 3; + bpp = 8; + } + + bytes_per_line = (state->xsize*bpp + 7) / 8; + /* The stride here needs to be kept in sync with the version in + PcxImagePlugin.py. If it's not, the header and the body of the + image will be out of sync and bad things will happen on decode. + */ + stride = bytes_per_line + (bytes_per_line % 2); + + padding = stride - bytes_per_line; + + for (;;) { switch (state->state) { @@ -72,74 +91,94 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) /* when we arrive here, "count" contains the number of bytes having the value of "LAST" that we've already seen */ + while (state->x < planes * bytes_per_line) { + /* If we're encoding an odd width file, and we've + got more than one plane, we need to pad each + color row with padding bytes at the end. Since + The pixels are stored RRRRRGGGGGBBBBB, so we need + to have the padding be RRRRRPGGGGGPBBBBBP. Hence + the double loop + */ + while (state->x % bytes_per_line) { - while (state->x < state->bytes) { - - if (state->count == 63) { - /* this run is full; flush it */ - if (bytes < 2) - return ptr - buf; - *ptr++ = 0xff; - *ptr++ = state->LAST; - bytes -= 2; - - state->count = 0; - - } - - this = state->buffer[state->x]; - - if (this == state->LAST) { - /* extend the current run */ - state->x++; - state->count++; - - } else { - - /* start a new run */ - if (state->count == 1 && (state->LAST < 0xc0)) { - if (bytes < 1) { + if (state->count == 63) { + /* this run is full; flush it */ + if (bytes < 2) return ptr - buf; - } + *ptr++ = 0xff; *ptr++ = state->LAST; - bytes--; + bytes -= 2; + + state->count = 0; + + } + + this = state->buffer[state->x]; + + if (this == state->LAST) { + /* extend the current run */ + state->x++; + state->count++; + } else { - if (state->count > 0) { - if (bytes < 2) { + /* start a new run */ + if (state->count == 1 && (state->LAST < 0xc0)) { + if (bytes < 1) { return ptr - buf; } - *ptr++ = 0xc0 | state->count; *ptr++ = state->LAST; - bytes -= 2; + bytes--; + } else { + if (state->count > 0) { + if (bytes < 2) { + return ptr - buf; + } + *ptr++ = 0xc0 | state->count; + *ptr++ = state->LAST; + bytes -= 2; + } } + + state->LAST = this; + state->count = 1; + + state->x++; + } - - state->LAST = this; - state->count = 1; - - state->x++; - } - } - /* end of line; flush the current run */ - if (state->count == 1 && (state->LAST < 0xc0)) { - if (bytes < 1) { - return ptr - buf; - } - *ptr++ = state->LAST; - bytes--; - } else { - if (state->count > 0) { - if (bytes < 2) { + /* end of line; flush the current run */ + if (state->count == 1 && (state->LAST < 0xc0)) { + if (bytes < 1 + padding) { return ptr - buf; } - *ptr++ = 0xc0 | state->count; *ptr++ = state->LAST; - bytes -= 2; + bytes--; + } else { + if (state->count > 0) { + if (bytes < 2 + padding) { + return ptr - buf; + } + *ptr++ = 0xc0 | state->count; + *ptr++ = state->LAST; + bytes -= 2; + } + } + if (bytes < padding) { + return ptr - buf; + } + /* add the padding */ + for (i=0;ix < planes * bytes_per_line) { + state->count = 1; + state->LAST = state->buffer[state->x]; + state->x++; } } - /* read next line */ state->state = FETCH; break;