""" Read-only Softimage PICT (.pic) image decoder for PIL. """ import Image, ImageFile import struct from array import array __all__ = [] def _decompose(i, bits = 8): return filter(None, map(lambda (i, j): i & (1 << j), zip((i,) * bits, range(bits)) )) class SoftimageImageFile(ImageFile.ImageFile): format = "PICT" format_description = "SoftImage PICT File" CHANNELDICT = { 0x80: 'B', 0x40: 'G', 0x20: 'R', 0x10: 'A', 0x08: 'X', # shadow 0x04: 'X', # depth 0x02: 'X', # aux 1 0x01: 'X', # aux 2 } COMPRESSIONTYPES = { 0x00: 'raw', 0x01: 'rle', 0x02: 'mrle', } DATATYPES = { 0x00: 'uint', 0x10: 'int', 0x20: 'float', } def _open(self): FMT, SIZE = '>If80s4sHHfHH', 104 magic, version, comment, ident, width, height, ratio, fields, padding = struct.unpack(FMT, self.fp.read(SIZE)) if magic != 0x5380f634: raise SyntaxError, "Invalid magic" if ident != 'PICT': raise SyntaxError, "Invalid id string" comment = comment.replace('\x00','') self.size = width, height chained = 1 self.info['comment'] = comment self.info['ratio'] = ratio self.info['fields'] = fields self.info['version'] = version clist = self.info['channels'] = [] tile = self.tile = [] self.mode = 'RGBX' while chained: chained, size, typ, channels = struct.unpack('4B', self.fp.read(4)) # we don't support chained channels mode = ''.join([self.CHANNELDICT[channel] for channel in _decompose(channels)]) compression = self.COMPRESSIONTYPES[typ & 0x0f] datatype = self.DATATYPES[typ & 0xf0] clist.append((mode, compression, datatype, size)) if datatype != 'uint' or size != 8: raise SyntaxError, "We only handle uint8" if 'A' in mode: self.mode = 'RGBA' self.tile = ('',) def load(self): """ This is, of course, not the most elegant way to do it... However, it works, and doesn't require any particular module or codec """ Image.Image.load(self) if not self.tile: return self.load_prepare() width, height = self.size decoders = [ (getattr(self, 'scanline_' + compression), (array('c'), mode, width)) for mode, compression, datatype, size in self.info['channels'] ] for y in xrange(height): for decoder, args in decoders: decoder(*args) im = self.im for fn, (buff, mode, width) in decoders: for i,c in enumerate(mode): if c == 'X' or c not in self.mode: continue tmp = Image.frombuffer('L', self.size, buff[i::len(mode)]).im im.putband(tmp, self.mode.index(c)) self.readonly = 1 self.fp = None self.tile = () self.load_end() def scanline_raw(self, buff, mode, width): modelen = len(mode) buff.fromfile(self.fp, width * len(mode)) def scanline_rle(self, buff, mode, width): modelen = len(mode) fromstring = buff.fromstring read = self.fp.read while width > 0: count = min(ord(read(1)), width) fromstring(read(modelen) * count) width -= count def scanline_mrle(self, buff, mode, width): modelen = len(mode) fromstring = buff.fromstring fromfile = buff.fromfile read = self.fp.read fp = self.fp unpack = struct.unpack while width > 0: count = ord(read(1)) if count & 128: if count == 128: count, = unpack('>H', read(2)) else: count -= 127 fromstring(read(modelen) * count) else: count += 1 fromfile(fp, count * modelen) width -= count Image.register_open("PICT", SoftimageImageFile) Image.register_extension("PICT", ".pic") if __name__ == '__main__': import os, sys fn = sys.argv[1] Image.open(fn).save(os.path.splitext(fn)[0]+'.png')