add RLE decompression for SGI images

This commit is contained in:
Mickael B 2017-07-22 03:34:06 -04:00 committed by Eric Soroos
parent 7c10f6341f
commit a90dc49100
7 changed files with 217 additions and 19 deletions

View File

@ -9,6 +9,7 @@
#
#
# History:
# 2017-22-07 mb Add RLE decompression
# 2016-16-10 mb Add save method without compression
# 1995-09-10 fl Created
#
@ -44,40 +45,62 @@ class SgiImageFile(ImageFile.ImageFile):
def _open(self):
# HEAD
s = self.fp.read(512)
offset = 512
s = self.fp.read(offset)
# magic number : 474
if i16(s) != 474:
raise ValueError("Not an SGI image file")
# relevant header entries
# compression : verbatim or RLE
compression = i8(s[2])
# bytes, dimension, zsize
layout = i8(s[3]), i16(s[4:]), i16(s[10:])
# depth : 1 or 2 bytes (8bits or 16bits)
depth = i8(s[3]) * 8
# determine mode from bytes/zsize
if layout == (1, 2, 1) or layout == (1, 1, 1):
# dimension : 1, 2 or 3 (depending on xsize, ysize and zsize)
dimension = i16(s[4:])
# xsize : width
xsize = i16(s[6:])
# ysize : height
ysize = i16(s[8:])
# zsize : channels count
zsize = i16(s[10:])
# layout
layout = depth, dimension, zsize
# determine mode from bits/zsize
if layout == (8, 2, 1) or layout == (8, 1, 1):
self.mode = "L"
elif layout == (1, 3, 3):
elif layout == (8, 3, 3):
self.mode = "RGB"
elif layout == (1, 3, 4):
elif layout == (8, 3, 4):
self.mode = "RGBA"
else:
raise ValueError("Unsupported SGI image mode")
# size
self.size = i16(s[6:]), i16(s[8:])
self.size = xsize, ysize
# orientation -1 : scanlines begins at the bottom-left corner
orientation = -1
# decoder info
if compression == 0:
offset = 512
pagesize = self.size[0]*self.size[1]*layout[0]
pagesize = xsize * ysize * (depth / 8)
self.tile = []
for layer in self.mode:
self.tile.append(
("raw", (0, 0)+self.size, offset, (layer, 0, -1)))
("raw", (0, 0) + self.size,
offset, (layer, 0, orientation)))
offset = offset + pagesize
elif compression == 1:
raise ValueError("SGI RLE encoding not supported")
self.tile = [("sgi_rle", (0, 0) + self.size,
offset, (self.mode, orientation, depth))]
# raise ValueError("SGI RLE encoding not supported")
def _save(im, fp, filename):

View File

@ -31,12 +31,13 @@ class TestFileSgi(PillowTestCase):
self.assert_image_equal(im, target)
def test_rle(self):
# Created with ImageMagick:
# convert hopper.ppm hopper.sgi
# We don't support RLE compression, this should throw a value error
test_file = "Tests/images/hopper.sgi"
with self.assertRaises(ValueError):
Image.open(test_file)
im = Image.open(test_file)
target = Image.open('Tests/images/hopper.rgb')
self.assert_image_equal(im, target)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"

View File

@ -3347,6 +3347,7 @@ extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcxDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_RawDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_SgiRleDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_XbmDecoderNew(PyObject* self, PyObject* args);
@ -3426,6 +3427,7 @@ static PyMethodDef functions[] = {
{"pcx_encoder", (PyCFunction)PyImaging_PcxEncoderNew, 1},
{"raw_decoder", (PyCFunction)PyImaging_RawDecoderNew, 1},
{"raw_encoder", (PyCFunction)PyImaging_RawEncoderNew, 1},
{"sgi_rle_decoder", (PyCFunction)PyImaging_SgiRleDecoderNew, 1},
{"sun_rle_decoder", (PyCFunction)PyImaging_SunRleDecoderNew, 1},
{"tga_rle_decoder", (PyCFunction)PyImaging_TgaRleDecoderNew, 1},
{"xbm_decoder", (PyCFunction)PyImaging_XbmDecoderNew, 1},

View File

@ -671,6 +671,38 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args)
}
/* -------------------------------------------------------------------- */
/* SGI RLE */
/* -------------------------------------------------------------------- */
PyObject*
PyImaging_SgiRleDecoderNew(PyObject* self, PyObject* args)
{
ImagingDecoderObject* decoder;
char* mode;
char* rawmode;
int ystep = 1;
int depth = 8;
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth))
return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
return NULL;
decoder->decode = ImagingSgiRleDecode;
decoder->state.ystep = ystep;
decoder->state.count = depth;
return (PyObject*) decoder;
}
/* -------------------------------------------------------------------- */
/* SUN RLE */
/* -------------------------------------------------------------------- */

View File

@ -445,6 +445,8 @@ extern int ImagingRawDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingRawEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingSunRleDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingTgaRleDecode(Imaging im, ImagingCodecState state,

138
libImaging/SgiRleDecode.c Normal file
View File

@ -0,0 +1,138 @@
/*
* The Python Imaging Library.
* $Id$
*
* decoder for Sgi RLE data.
*
* history:
* 2017-07-20 mb created
*
* Copyright (c) Mickael Bonfill 2017.
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
#include "stdio.h"
static unsigned long getlong(UINT8 *buf)
{
return (unsigned long)(buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+(buf[3]<<0);
}
static void readlongtab(UINT8** buf, int n, unsigned long *tab)
{
int i;
for (i = 0; i < n; i++) {
tab[i] = getlong(*buf);
*buf += 4;
}
}
static void expandrow(UINT8* optr,UINT8* iptr, int z)
{
UINT8 pixel, count;
optr += z;
while(1) {
pixel = *iptr++;
if ( !(count = (pixel & 0x7f)) )
return;
if(pixel & 0x80) {
while(count--)
*optr++ = *iptr++;
} else {
pixel = *iptr++;
while(count--)
*optr++ = pixel;
}
}
}
int
ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
UINT8* buf, int bytes)
{
int n, depth;
UINT8* ptr;
ptr = buf;
if (state->state == 0) {
/* check image orientation */
if (state->ystep < 0) {
state->y = state->ysize-1;
state->ystep = -1;
} else
state->ystep = 1;
state->state = 1;
}
/* get the channels count */
int zsize = state->bits / state->count;
/* allocate memory for the buffer used for full lines later */
state->buffer = (UINT8*)malloc(sizeof(UINT8) * state->xsize * zsize);
/* get RLE offset and length tabs */
unsigned long *starttab, *lengthtab;
int tablen = state->ysize * zsize * sizeof(unsigned long);
starttab = (unsigned long *)malloc(tablen);
lengthtab = (unsigned long *)malloc(tablen);
readlongtab(&ptr, state->ysize * zsize, starttab);
readlongtab(&ptr, state->ysize * zsize, lengthtab);
/* get scanlines informations */
for (int rowno = 0; rowno < state->ysize; ++rowno) {
for (int channo = 0; channo < zsize; ++channo) {
unsigned long rleoffset = starttab[rowno + channo * state->ysize];
/*
* we also need to substract the file header and RLE tabs length
* from the offset
*/
rleoffset -= 512;
rleoffset -= tablen;
unsigned long rlelength = lengthtab[rowno + channo * state->ysize];
UINT8* rledata;
rledata = (UINT8*)malloc(sizeof(UINT8) * rlelength);
memcpy(rledata, &ptr[rleoffset], rlelength * sizeof(UINT8));
UINT8* scanline;
scanline = (UINT8*)malloc(sizeof(UINT8) * state->xsize);
/* decompress raw data */
expandrow(scanline, rledata, 0);
/* populate the state buffer */
for (int x = 0; x < state->xsize; ++x) {
state->buffer[x * zsize + channo] = scanline[x];
}
free(rledata);
free(scanline);
}
/* Unpack the full line stored in the state buffer */
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->buffer,
state->xsize);
state->y += state->ystep;
}
free(starttab);
free(lengthtab);
return -1;
}

View File

@ -35,8 +35,8 @@ _LIB_IMAGING = (
"Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste",
"Quant", "QuantOctree", "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode",
"PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage",
"SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask",
"XbmDecode", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode",
"SgiRleDecode", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC",
"UnsharpMask", "XbmDecode", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode",
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "QuantPngQuant", "codec_fd")
DEBUG = False