mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-27 10:26:19 +03:00
Pure Python MSP Decoder
This commit is contained in:
parent
069ad8cbb0
commit
949932f45f
|
@ -1,6 +1,5 @@
|
||||||
#
|
#
|
||||||
# The Python Imaging Library.
|
# The Python Imaging Library.
|
||||||
# $Id$
|
|
||||||
#
|
#
|
||||||
# MSP file handling
|
# MSP file handling
|
||||||
#
|
#
|
||||||
|
@ -9,9 +8,11 @@
|
||||||
# History:
|
# History:
|
||||||
# 95-09-05 fl Created
|
# 95-09-05 fl Created
|
||||||
# 97-01-03 fl Read/write MSP images
|
# 97-01-03 fl Read/write MSP images
|
||||||
|
# 17-02-21 es Fixed RLE interpretation
|
||||||
#
|
#
|
||||||
# Copyright (c) Secret Labs AB 1997.
|
# Copyright (c) Secret Labs AB 1997.
|
||||||
# Copyright (c) Fredrik Lundh 1995-97.
|
# Copyright (c) Fredrik Lundh 1995-97.
|
||||||
|
# Copyright (c) Eric Soroos 2017.
|
||||||
#
|
#
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
@ -20,9 +21,11 @@
|
||||||
# Figure 205. Windows Paint Version 1: "DanM" Format
|
# Figure 205. Windows Paint Version 1: "DanM" Format
|
||||||
# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03
|
# 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 . import Image, ImageFile
|
||||||
from ._binary import i16le as i16, o16le as o16
|
from ._binary import i16le as i16, o16le as o16
|
||||||
|
import struct, io
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
@ -64,7 +67,91 @@ class MspImageFile(ImageFile.ImageFile):
|
||||||
if s[:4] == b"DanM":
|
if s[:4] == b"DanM":
|
||||||
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
|
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
|
||||||
else:
|
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)
|
# write MSP files (uncompressed only)
|
||||||
|
|
|
@ -3290,7 +3290,6 @@ extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_LibTiffDecoderNew(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_PackbitsDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_PcxDecoderNew(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_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
|
||||||
{"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
|
{"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
|
||||||
#endif
|
#endif
|
||||||
{"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
|
|
||||||
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
|
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
|
||||||
{"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},
|
{"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},
|
||||||
{"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, 1},
|
{"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, 1},
|
||||||
|
|
21
decode.c
21
decode.c
|
@ -557,27 +557,6 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
#endif
|
#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 */
|
/* PackBits */
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
2
setup.py
2
setup.py
|
@ -32,7 +32,7 @@ _LIB_IMAGING = (
|
||||||
"Draw", "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
|
"Draw", "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
|
||||||
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", "Histo",
|
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", "Histo",
|
||||||
"JpegDecode", "JpegEncode", "LzwDecode", "Matrix", "ModeFilter",
|
"JpegDecode", "JpegEncode", "LzwDecode", "Matrix", "ModeFilter",
|
||||||
"MspDecode", "Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste",
|
"Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste",
|
||||||
"Quant", "QuantOctree", "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode",
|
"Quant", "QuantOctree", "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode",
|
||||||
"PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage",
|
"PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage",
|
||||||
"SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask",
|
"SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user