123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- #
- # THIS IS WORK IN PROGRESS
- #
- # The Python Imaging Library.
- # $Id$
- #
- # 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 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 Image.warnings:
- Image.warnings.warn(
- "The ArgImagePlugin driver is obsolete, and will be removed "
- "from a future release of PIL. If you rely on this module, "
- "please contact the PIL authors.",
- RuntimeWarning
- )
- 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")
|