diff options
Diffstat (limited to 'PIL/ArgImagePlugin.py')
-rw-r--r-- | PIL/ArgImagePlugin.py | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py new file mode 100644 index 0000000..48582d9 --- /dev/null +++ b/PIL/ArgImagePlugin.py @@ -0,0 +1,498 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id: ArgImagePlugin.py 2309 2005-03-02 15:06:34Z fredrik $ +# +# ARG animation support code +# +# history: +# 1996-12-30 fl Created +# 1996-01-06 fl Added safe scripting environment +# 1996-01-10 fl Added JHDR, UHDR and sYNC support +# 2005-03-02 fl Removed AAPP and ARUN support +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.4" + +import marshal, string + +import Image, ImageFile, ImagePalette + +from PngImagePlugin import i16, i32, ChunkStream, _MODES + +MAGIC = "\212ARG\r\n\032\n" + +# -------------------------------------------------------------------- +# ARG parser + +class ArgStream(ChunkStream): + "Parser callbacks for ARG data" + + def __init__(self, fp): + + ChunkStream.__init__(self, fp) + + self.eof = 0 + + self.im = None + self.palette = None + + self.__reset() + + def __reset(self): + + # reset decoder state (called on init and sync) + + self.count = 0 + self.id = None + self.action = ("NONE",) + + self.images = {} + self.names = {} + + + def chunk_AHDR(self, offset, bytes): + "AHDR -- animation header" + + # assertions + if self.count != 0: + raise SyntaxError, "misplaced AHDR chunk" + + s = self.fp.read(bytes) + self.size = i32(s), i32(s[4:]) + try: + self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))] + except: + raise SyntaxError, "unknown ARG mode" + + if Image.DEBUG: + print "AHDR size", self.size + print "AHDR mode", self.mode, self.rawmode + + return s + + def chunk_AFRM(self, offset, bytes): + "AFRM -- next frame follows" + + # assertions + if self.count != 0: + raise SyntaxError, "misplaced AFRM chunk" + + self.show = 1 + self.id = 0 + self.count = 1 + self.repair = None + + s = self.fp.read(bytes) + if len(s) >= 2: + self.id = i16(s) + if len(s) >= 4: + self.count = i16(s[2:4]) + if len(s) >= 6: + self.repair = i16(s[4:6]) + else: + self.repair = None + + if Image.DEBUG: + print "AFRM", self.id, self.count + + return s + + def chunk_ADEF(self, offset, bytes): + "ADEF -- store image" + + # assertions + if self.count != 0: + raise SyntaxError, "misplaced ADEF chunk" + + self.show = 0 + self.id = 0 + self.count = 1 + self.repair = None + + s = self.fp.read(bytes) + if len(s) >= 2: + self.id = i16(s) + if len(s) >= 4: + self.count = i16(s[2:4]) + + if Image.DEBUG: + print "ADEF", self.id, self.count + + return s + + def chunk_NAME(self, offset, bytes): + "NAME -- name the current image" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced NAME chunk" + + name = self.fp.read(bytes) + self.names[self.id] = name + + return name + + def chunk_AEND(self, offset, bytes): + "AEND -- end of animation" + + if Image.DEBUG: + print "AEND" + + self.eof = 1 + + raise EOFError, "end of ARG file" + + def __getmodesize(self, s, full=1): + + size = i32(s), i32(s[4:]) + + try: + mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))] + except: + raise SyntaxError, "unknown image mode" + + if full: + if ord(s[12]): + pass # interlace not yet supported + if ord(s[11]): + raise SyntaxError, "unknown filter category" + + return size, mode, rawmode + + def chunk_PAST(self, offset, bytes): + "PAST -- paste one image into another" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced PAST chunk" + + if self.repair is not None: + # we must repair the target image before we + # start pasting + + # brute force; a better solution would be to + # update only the dirty rectangles in images[id]. + # note that if images[id] doesn't exist, it must + # be created + + self.images[self.id] = self.images[self.repair].copy() + self.repair = None + + s = self.fp.read(bytes) + im = self.images[i16(s)] + x, y = i32(s[2:6]), i32(s[6:10]) + bbox = x, y, im.size[0]+x, im.size[1]+y + + if im.mode in ["RGBA"]: + # paste with transparency + # FIXME: should handle P+transparency as well + self.images[self.id].paste(im, bbox, im) + else: + # paste without transparency + self.images[self.id].paste(im, bbox) + + self.action = ("PAST",) + self.__store() + + return s + + def chunk_BLNK(self, offset, bytes): + "BLNK -- create blank image" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced BLNK chunk" + + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # store image (FIXME: handle colour) + self.action = ("BLNK",) + self.im = Image.core.fill(mode, size, 0) + self.__store() + + return s + + def chunk_IHDR(self, offset, bytes): + "IHDR -- full image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced IHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s) + + # decode and store image + self.action = ("IHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.zip_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_DHDR(self, offset, bytes): + "DHDR -- delta image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced DHDR chunk" + + s = self.fp.read(bytes) + + size, mode, rawmode = self.__getmodesize(s) + + # delta header + diff = ord(s[13]) + offs = i32(s[14:18]), i32(s[18:22]) + + bbox = offs + (offs[0]+size[0], offs[1]+size[1]) + + if Image.DEBUG: + print "DHDR", diff, bbox + + # FIXME: decode and apply image + self.action = ("DHDR", diff, bbox) + + # setup decoder + self.im = Image.core.new(mode, size) + + self.decoder = Image.core.zip_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + + self.data = "" + + return s + + def chunk_JHDR(self, offset, bytes): + "JHDR -- JPEG image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced JHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # decode and store image + self.action = ("JHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.jpeg_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_UHDR(self, offset, bytes): + "UHDR -- uncompressed image data follows (EXPERIMENTAL)" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced UHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # decode and store image + self.action = ("UHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.raw_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_IDAT(self, offset, bytes): + "IDAT -- image data block" + + # pass compressed chunks through the decoder + s = self.fp.read(bytes) + self.data = self.data + s + n, e = self.decoder.decode(self.data) + if n < 0: + # end of image + if e < 0: + raise IOError, "decoder error %d" % e + else: + self.data = self.data[n:] + + return s + + def chunk_DEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_JEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_UEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_IEND(self, offset, bytes): + "IEND -- end of image" + + # we now have a new image. carry out the operation + # defined by the image header. + + # won't need these anymore + del self.decoder + del self.data + + self.__store() + + return self.fp.read(bytes) + + def __store(self): + + # apply operation + cid = self.action[0] + + if cid in ["BLNK", "IHDR", "JHDR", "UHDR"]: + # store + self.images[self.id] = self.im + + elif cid == "DHDR": + # paste + cid, mode, bbox = self.action + im0 = self.images[self.id] + im1 = self.im + if mode == 0: + im1 = im1.chop_add_modulo(im0.crop(bbox)) + im0.paste(im1, bbox) + + self.count = self.count - 1 + + if self.count == 0 and self.show: + self.im = self.images[self.id] + raise EOFError # end of this frame + + def chunk_PLTE(self, offset, bytes): + "PLTE -- palette data" + + s = self.fp.read(bytes) + if self.mode == "P": + self.palette = ImagePalette.raw("RGB", s) + return s + + def chunk_sYNC(self, offset, bytes): + "SYNC -- reset decoder" + + if self.count != 0: + raise SyntaxError, "misplaced sYNC chunk" + + s = self.fp.read(bytes) + self.__reset() + return s + + +# -------------------------------------------------------------------- +# ARG reader + +def _accept(prefix): + return prefix[:8] == MAGIC + +## +# Image plugin for the experimental Animated Raster Graphics format. + +class ArgImageFile(ImageFile.ImageFile): + + format = "ARG" + format_description = "Animated raster graphics" + + def _open(self): + + if self.fp.read(8) != MAGIC: + raise SyntaxError, "not an ARG file" + + self.arg = ArgStream(self.fp) + + # read and process the first chunk (AHDR) + + cid, offset, bytes = self.arg.read() + + if cid != "AHDR": + raise SyntaxError, "expected an AHDR chunk" + + s = self.arg.call(cid, offset, bytes) + + self.arg.crc(cid, s) + + # image characteristics + self.mode = self.arg.mode + self.size = self.arg.size + + def load(self): + + if self.arg.im is None: + self.seek(0) + + # image data + self.im = self.arg.im + self.palette = self.arg.palette + + # set things up for further processing + Image.Image.load(self) + + def seek(self, frame): + + if self.arg.eof: + raise EOFError, "end of animation" + + self.fp = self.arg.fp + + while 1: + + # + # process chunks + + cid, offset, bytes = self.arg.read() + + if self.arg.eof: + raise EOFError, "end of animation" + + try: + s = self.arg.call(cid, offset, bytes) + except EOFError: + break + + except "glurk": # AttributeError + if Image.DEBUG: + print cid, bytes, "(unknown)" + s = self.fp.read(bytes) + + self.arg.crc(cid, s) + + self.fp.read(4) # ship extra CRC + + def tell(self): + return 0 + + def verify(self): + "Verify ARG file" + + # back up to first chunk + self.fp.seek(8) + + self.arg.verify(self) + self.arg.close() + + self.fp = None + +# +# -------------------------------------------------------------------- + +Image.register_open("ARG", ArgImageFile, _accept) + +Image.register_extension("ARG", ".arg") + +Image.register_mime("ARG", "video/x-arg") |