Pure Python MSP Decoder

This commit is contained in:
wiredfool 2017-02-21 05:09:32 -08:00
parent 069ad8cbb0
commit 949932f45f
5 changed files with 90 additions and 117 deletions

View File

@ -1,6 +1,5 @@
#
# The Python Imaging Library.
# $Id$
#
# MSP file handling
#
@ -9,9 +8,11 @@
# History:
# 95-09-05 fl Created
# 97-01-03 fl Read/write MSP images
# 17-02-21 es Fixed RLE interpretation
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1995-97.
# Copyright (c) Eric Soroos 2017.
#
# See the README file for information on usage and redistribution.
#
@ -20,9 +21,11 @@
# Figure 205. Windows Paint Version 1: "DanM" Format
# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03
#
# See also: http://www.fileformat.info/format/mspaint/egff.htm
from . import Image, ImageFile
from ._binary import i16le as i16, o16le as o16
import struct, io
__version__ = "0.1"
@ -64,7 +67,91 @@ class MspImageFile(ImageFile.ImageFile):
if s[:4] == b"DanM":
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
else:
self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)]
self.tile = [("MSP", (0, 0)+self.size, 32, None)]
class MspDecoder(ImageFile.PyDecoder):
# The algo for the MSP decoder is from
# http://www.fileformat.info/format/mspaint/egff.htm
# cc-by-attribution -- That page references is taken from the
# Encyclopedia of Graphics File Formats and is licensed by
# O'Reilly under the Creative Common/Attribution license
#
# For RLE encoded files, the 32byte header is followed by a scan
# line map, encoded as one 16bit word of encoded byte length per
# line.
#
# NOTE: the encoded length of the line can be 0. This was not
# handled in the previous version of this encoder, and there's no
# mention of how to handle it in the documentation. From the few
# examples I've seen, I've assumed that it is a fill of the
# background color, in this case, white.
#
#
# Pseudocode of the decoder:
# Read a BYTE value as the RunType
# If the RunType value is zero
# Read next byte as the RunCount
# Read the next byte as the RunValue
# Write the RunValue byte RunCount times
# If the RunType value is non-zero
# Use this value as the RunCount
# Read and write the next RunCount bytes literally
#
# e.g.:
# 0x00 03 ff 05 00 01 02 03 04
# would yield the bytes:
# 0xff ff ff 00 01 02 03 04
#
# which are then interpreted as a bit packed mode '1' image
_pulls_fd = True
def decode(self, buffer):
img = io.BytesIO()
blank_line = bytearray((0xff,)*((self.state.xsize+7)//8))
try:
last_pos = 0
self.fd.seek(32)
rowmap = struct.unpack_from("<%dH" % (self.state.ysize),
self.fd.read(self.state.ysize*2))
except struct.error:
raise IOError("Truncated MSP file in row map")
for x, rowlen in enumerate(rowmap):
try:
if rowlen == 0:
img.write(blank_line)
continue
row = self.fd.read(rowlen)
if len(row) != rowlen:
raise IOError("Truncated MSP file, expected %d bytes on row %s",
(rowlen, x))
idx = 0
while idx < rowlen:
runtype = row[idx]
idx += 1
if runtype == 0:
(runcount, runval) = struct.unpack("Bc", row[idx:idx+2])
img.write(runval * runcount)
idx += 2
else:
runcount = runtype
img.write(row[idx:idx+runcount])
idx += runcount
except struct.error:
raise IOError("Corrupted MSP file in row %d" %x)
self.set_as_raw(img.getvalue(), ("1", 0, 1))
return 0,0
Image.register_decoder('MSP', MspDecoder)
#
# write MSP files (uncompressed only)

View File

@ -3290,7 +3290,6 @@ extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcxDecoderNew(PyObject* self, PyObject* args);
@ -3368,7 +3367,6 @@ static PyMethodDef functions[] = {
{"libtiff_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
#endif
{"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
{"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},
{"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, 1},

View File

@ -557,27 +557,6 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
#endif
/* -------------------------------------------------------------------- */
/* MSP */
/* -------------------------------------------------------------------- */
PyObject*
PyImaging_MspDecoderNew(PyObject* self, PyObject* args)
{
ImagingDecoderObject* decoder;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
return NULL;
if (get_unpacker(decoder, "1", "1") < 0)
return NULL;
decoder->decode = ImagingMspDecode;
return (PyObject*) decoder;
}
/* -------------------------------------------------------------------- */
/* PackBits */

View File

@ -1,91 +0,0 @@
/*
* The Python Imaging Library.
* $Id$
*
* decoder for MSP version 2 data.
*
* history:
* 97-01-03 fl Created
*
* Copyright (c) Fredrik Lundh 1997.
* Copyright (c) Secret Labs AB 1997.
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
int
ImagingMspDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
int n;
UINT8* ptr;
ptr = buf;
for (;;) {
if (bytes < 1)
return ptr - buf;
if (ptr[0] == 0) {
/* Run (3 bytes block) */
if (bytes < 3)
break;
n = ptr[1];
if (state->x + n > state->bytes) {
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
memset(state->buffer + state->x, ptr[2], n);
ptr += 3;
bytes -= 3;
} else {
/* Literal (1+n bytes block) */
n = ptr[0];
if (bytes < 1 + n)
break;
if (state->x + n > state->bytes) {
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
memcpy(state->buffer + state->x, ptr + 1, n);
ptr += 1 + n;
bytes -= 1 + n;
}
state->x += n;
if (state->x >= state->bytes) {
/* Got a full line, unpack it */
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->buffer,
state->xsize);
state->x = 0;
if (++state->y >= state->ysize) {
/* End of file (errcode = 0) */
return -1;
}
}
}
return ptr - buf;
}

View File

@ -32,7 +32,7 @@ _LIB_IMAGING = (
"Draw", "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", "Histo",
"JpegDecode", "JpegEncode", "LzwDecode", "Matrix", "ModeFilter",
"MspDecode", "Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste",
"Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste",
"Quant", "QuantOctree", "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode",
"PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage",
"SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask",