mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 17:54:32 +03:00
commit
6fa7f3fc67
|
@ -41,6 +41,10 @@ install:
|
|||
|
||||
# libimagequant
|
||||
- pushd depends && ./install_imagequant.sh && popd
|
||||
|
||||
# extra test images
|
||||
- pushd depends && ./install_extra_test_images.sh && popd
|
||||
|
||||
|
||||
before_script:
|
||||
# Qt needs a display for some of the tests, and it's only run on the system site packages install
|
||||
|
|
|
@ -150,15 +150,16 @@ class ImageFile(Image.Image):
|
|||
|
||||
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
|
||||
|
@ -167,7 +168,7 @@ class ImageFile(Image.Image):
|
|||
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.
|
||||
|
|
|
@ -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:
|
||||
|
@ -48,31 +63,71 @@ class SunImageFile(ImageFile.ImageFile):
|
|||
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")
|
||||
raise SyntaxError("Unsupported Mode/Bit Depth")
|
||||
|
||||
if palette_length:
|
||||
if palette_length > 1024:
|
||||
raise SyntaxError("Unsupported Color Palette Length")
|
||||
|
||||
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))
|
||||
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"
|
||||
self.mode = "P"
|
||||
rawmode = rawmode.replace('L', 'P')
|
||||
|
||||
# 16 bit boundaries on stride
|
||||
stride = ((self.size[0] * depth + 15) // 16) * 2
|
||||
|
||||
stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)
|
||||
# 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
|
||||
|
||||
if compression == 1:
|
||||
# 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
|
||||
|
||||
|
|
BIN
Tests/images/sunraster.im1
Normal file
BIN
Tests/images/sunraster.im1
Normal file
Binary file not shown.
BIN
Tests/images/sunraster.im1.png
Normal file
BIN
Tests/images/sunraster.im1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -1,7 +1,10 @@
|
|||
from helper import unittest, PillowTestCase
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image, SunImagePlugin
|
||||
|
||||
import os
|
||||
|
||||
EXTRA_DIR = 'Tests/images/sunraster'
|
||||
|
||||
class TestFileSun(PillowTestCase):
|
||||
|
||||
|
@ -16,10 +19,32 @@ class TestFileSun(PillowTestCase):
|
|||
# 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)
|
||||
|
||||
|
||||
@unittest.skipIf(not os.path.exists(EXTRA_DIR),
|
||||
"Extra image files not installed")
|
||||
def test_others(self):
|
||||
files = (os.path.join(EXTRA_DIR, f) for f in
|
||||
os.listdir(EXTRA_DIR) if os.path.splitext(f)[1]
|
||||
in ('.sun', '.SUN', '.ras'))
|
||||
for path in files:
|
||||
with Image.open(path) as im:
|
||||
im.load()
|
||||
self.assertIsInstance(im, SunImagePlugin.SunImageFile)
|
||||
target_path = "%s.png" % os.path.splitext(path)[0]
|
||||
#im.save(target_file)
|
||||
with Image.open(target_path) as target:
|
||||
self.assert_image_equal(im, target)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -19,6 +19,7 @@ install:
|
|||
- git clone https://github.com/python-pillow/pillow-depends.git c:\pillow-depends
|
||||
- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\
|
||||
- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\
|
||||
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
|
||||
- cd c:\pillow\winbuild\
|
||||
- c:\python34\python.exe c:\pillow\winbuild\build_dep.py
|
||||
- c:\pillow\winbuild\build_deps.cmd
|
||||
|
|
11
depends/install_extra_test_images.sh
Executable file
11
depends/install_extra_test_images.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
# install extra test images
|
||||
|
||||
if [ ! -f test_images.tar.gz ]; then
|
||||
wget -O 'test_images.tar.gz' 'https://github.com/python-pillow/pillow-depends/blob/master/test_images.tar.gz?raw=true'
|
||||
fi
|
||||
|
||||
rm -r test_images
|
||||
tar -xvzf test_images.tar.gz
|
||||
|
||||
cp -r test_images/* ../Tests/images
|
|
@ -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.
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user