Skip to content

Commit

Permalink
Merge 816c74a into bbfc7b0
Browse files Browse the repository at this point in the history
  • Loading branch information
wiredfool committed Nov 22, 2016
2 parents bbfc7b0 + 816c74a commit 0239922
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 75 deletions.
11 changes: 6 additions & 5 deletions PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,16 @@ def load(self):

if use_mmap:
# try memory mapping
d, e, o, a = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
decoder_name, extents, offset, args = self.tile[0]
if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \
and args[0] in Image._MAPMODES:
try:
if hasattr(Image.core, "map"):
# use built-in mapper WIN32 only
self.map = Image.core.map(self.filename)
self.map.seek(o)
self.map.seek(offset)
self.im = self.map.readimage(
self.mode, self.size, a[1], a[2]
self.mode, self.size, args[1], args[2]
)
else:
# use mmap, if possible
Expand All @@ -167,7 +168,7 @@ def load(self):
size = os.path.getsize(self.filename)
self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ)
self.im = Image.core.map_buffer(
self.map, self.size, d, e, o, a
self.map, self.size, decoder_name, extents, offset, args
)
readonly = 1
# After trashing self.im, we might need to reload the palette data.
Expand Down
89 changes: 73 additions & 16 deletions PIL/SunImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ class SunImageFile(ImageFile.ImageFile):

def _open(self):

# The Sun Raster file header is 32 bytes in length and has the following format:

# typedef struct _SunRaster
# {
# DWORD MagicNumber; /* Magic (identification) number */
# DWORD Width; /* Width of image in pixels */
# DWORD Height; /* Height of image in pixels */
# DWORD Depth; /* Number of bits per pixel */
# DWORD Length; /* Size of image data in bytes */
# DWORD Type; /* Type of raster file */
# DWORD ColorMapType; /* Type of color map */
# DWORD ColorMapLength; /* Size of the color map in bytes */
# } SUNRASTER;


# HEAD
s = self.fp.read(32)
if i32(s) != 0x59a66a95:
Expand All @@ -48,31 +63,73 @@ def _open(self):
self.size = i32(s[4:8]), i32(s[8:12])

depth = i32(s[12:16])
data_length = i32(s[16:20]) # unreliable, ignore.
file_type = i32(s[20:24])
palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary
palette_length = i32(s[28:32])

if depth == 1:
self.mode, rawmode = "1", "1;I"
elif depth == 4:
self.mode, rawmode = "L", "L;4"
elif depth == 8:
self.mode = rawmode = "L"
elif depth == 24:
self.mode, rawmode = "RGB", "BGR"
if file_type == 3:
self.mode, rawmode = "RGB", "RGB"
else:
self.mode, rawmode = "RGB", "BGR"
elif depth == 32:
if file_type == 3:
self.mode, rawmode = 'RGB', 'RGBX'
else:
self.mode, rawmode = 'RGB', 'BGRX'
else:
raise SyntaxError("unsupported mode")

compression = i32(s[20:24])

if i32(s[24:28]) != 0:
length = i32(s[28:32])
offset = offset + length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
raise SyntaxError("Unsupported Mode/Bit Depth")



if palette_length:
if palette_length > 1024:
raise SyntaxError("Unsupported Color Palette Length")

if palette_type != 1:
raise SyntaxError("Unsupported Palette Type")

offset = offset + palette_length
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
if self.mode == "L":
self.mode = rawmode = "P"

stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)

if compression == 1:
self.mode = "P"
rawmode = rawmode.replace('L', 'P')

# 16 bit boundaries on stride
stride = ((self.size[0] * depth + 15) // 16) * 2

# file type: Type is the version (or flavor) of the bitmap
# file. The following values are typically found in the Type
# field:
# 0000h Old
# 0001h Standard
# 0002h Byte-encoded
# 0003h RGB format
# 0004h TIFF format
# 0005h IFF format
# FFFFh Experimental

# Old and standard are the same, except for the length tag.
# byte-encoded is run-length-encoded
# RGB looks similar to standard, but RGB byte order
# TIFF and IFF mean that they were converted from T/IFF
# Experimental means that it's something else.
# (http://www.fileformat.info/format/sunraster/egff.htm)

if file_type in (0, 1, 3, 4, 5):
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
elif compression == 2:
elif file_type == 2:
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]

else:
raise SyntaxError('Unsupported Sun Raster file type')

#
# registry

Expand Down
Binary file added Tests/images/sunraster.im1
Binary file not shown.
Binary file added Tests/images/sunraster.im1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions Tests/test_file_sun.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase
from helper import unittest, PillowTestCase, hopper

from PIL import Image, SunImagePlugin

Expand All @@ -16,10 +16,16 @@ def test_sanity(self):
# Assert
self.assertEqual(im.size, (128, 128))

self.assert_image_similar(im, hopper(), 5) # visually verified

invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: SunImagePlugin.SunImageFile(invalid_file))


def test_im1(self):
im = Image.open('Tests/images/sunraster.im1')
target = Image.open('Tests/images/sunraster.im1.png')
self.assert_image_equal(im, target)

if __name__ == '__main__':
unittest.main()
138 changes: 86 additions & 52 deletions libImaging/SunRleDecode.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* decoder for SUN RLE data.
*
* history:
* 97-01-04 fl Created
* 97-01-04 fl Created
*
* Copyright (c) Fredrik Lundh 1997.
* Copyright (c) Secret Labs AB 1997.
Expand All @@ -24,88 +24,122 @@ ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
int n;
UINT8* ptr;
UINT8 extra_data = 0;
UINT8 extra_bytes = 0;

ptr = buf;

for (;;) {

if (bytes < 1)
return ptr - buf;
if (bytes < 1)
return ptr - buf;

if (ptr[0] == 0x80) {
if (ptr[0] == 0x80) {

if (bytes < 2)
break;
if (bytes < 2)
break;

n = ptr[1];
n = ptr[1];

if (n == 0) {

/* Literal 0x80 (2 bytes) */
n = 1;
if (n == 0) {

state->buffer[state->x] = 0x80;
/* Literal 0x80 (2 bytes) */
n = 1;

ptr += 2;
bytes -= 2;
state->buffer[state->x] = 0x80;

} else {
ptr += 2;
bytes -= 2;

/* Run (3 bytes) */
if (bytes < 3)
break;
} else {

if (state->x + n > state->bytes) {
/* FIXME: is this correct? */
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
/* Run (3 bytes) */
if (bytes < 3)
break;

memset(state->buffer + state->x, ptr[2], n);
/* from (http://www.fileformat.info/format/sunraster/egff.htm)
ptr += 3;
bytes -= 3;
For example, a run of 100 pixels with the value of
0Ah would encode as the values 80h 64h 0Ah. A
single pixel value of 80h would encode as the
values 80h 00h. The four unencoded bytes 12345678h
would be stored in the RLE stream as 12h 34h 56h
78h. 100 pixels, n=100, not 100 pixels, n=99.
}
But Wait! There's More!
(http://www.fileformat.info/format/sunraster/spec/598a59c4fac64c52897585d390d86360/view.htm)
} else {
If the first byte is 0x80, and the second byte is
not zero, the record is three bytes long. The
second byte is a count and the third byte is a
value. Output (count+1) pixels of that value.
/* Literal (1+n bytes block) */
n = ptr[0];
2 specs, same site, but Imagemagick and GIMP seem
to agree on the second one.
*/
n += 1;

if (bytes < 1 + n)
break;
if (state->x + n > state->bytes) {
extra_bytes = n; /* full value */
n = state->bytes - state->x;
extra_bytes -= n;
extra_data = ptr[2];
}

if (state->x + n > state->bytes) {
/* FIXME: is this correct? */
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
memset(state->buffer + state->x, ptr[2], n);

memcpy(state->buffer + state->x, ptr + 1, n);
ptr += 3;
bytes -= 3;

ptr += 1 + n;
bytes -= 1 + n;
}

}
} else {

state->x += n;
/* Literal byte */
n = 1;

if (state->x >= state->bytes) {
state->buffer[state->x] = ptr[0];

/* Got a full line, unpack it */
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->buffer,
state->xsize);
ptr += 1;
bytes -= 1;

state->x = 0;
}

if (++state->y >= state->ysize) {
/* End of file (errcode = 0) */
return -1;
}
}
for (;;) {
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;
}
}

if (extra_bytes == 0) {
break;
}

if (state->x > 0) {
break; // assert
}

if (extra_bytes >= state->bytes) {
n = state->bytes;
} else {
n = extra_bytes;
}
memset(state->buffer + state->x, extra_data, n);
extra_bytes -= n;
}
}

return ptr - buf;
Expand Down

0 comments on commit 0239922

Please sign in to comment.