Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/PIL/ArgImagePlugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'PIL/ArgImagePlugin.py')
-rw-r--r--PIL/ArgImagePlugin.py498
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")