ArgImagePlugin.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. #
  2. # THIS IS WORK IN PROGRESS
  3. #
  4. # The Python Imaging Library.
  5. # $Id$
  6. #
  7. # ARG animation support code
  8. #
  9. # history:
  10. # 1996-12-30 fl Created
  11. # 1996-01-06 fl Added safe scripting environment
  12. # 1996-01-10 fl Added JHDR, UHDR and sYNC support
  13. # 2005-03-02 fl Removed AAPP and ARUN support
  14. #
  15. # Copyright (c) Secret Labs AB 1997.
  16. # Copyright (c) Fredrik Lundh 1996-97.
  17. #
  18. # See the README file for information on usage and redistribution.
  19. #
  20. __version__ = "0.4"
  21. import Image, ImageFile, ImagePalette
  22. from PngImagePlugin import i16, i32, ChunkStream, _MODES
  23. MAGIC = "\212ARG\r\n\032\n"
  24. # --------------------------------------------------------------------
  25. # ARG parser
  26. class ArgStream(ChunkStream):
  27. "Parser callbacks for ARG data"
  28. def __init__(self, fp):
  29. ChunkStream.__init__(self, fp)
  30. self.eof = 0
  31. self.im = None
  32. self.palette = None
  33. self.__reset()
  34. def __reset(self):
  35. # reset decoder state (called on init and sync)
  36. self.count = 0
  37. self.id = None
  38. self.action = ("NONE",)
  39. self.images = {}
  40. self.names = {}
  41. def chunk_AHDR(self, offset, bytes):
  42. "AHDR -- animation header"
  43. # assertions
  44. if self.count != 0:
  45. raise SyntaxError, "misplaced AHDR chunk"
  46. s = self.fp.read(bytes)
  47. self.size = i32(s), i32(s[4:])
  48. try:
  49. self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  50. except:
  51. raise SyntaxError, "unknown ARG mode"
  52. if Image.DEBUG:
  53. print "AHDR size", self.size
  54. print "AHDR mode", self.mode, self.rawmode
  55. return s
  56. def chunk_AFRM(self, offset, bytes):
  57. "AFRM -- next frame follows"
  58. # assertions
  59. if self.count != 0:
  60. raise SyntaxError, "misplaced AFRM chunk"
  61. self.show = 1
  62. self.id = 0
  63. self.count = 1
  64. self.repair = None
  65. s = self.fp.read(bytes)
  66. if len(s) >= 2:
  67. self.id = i16(s)
  68. if len(s) >= 4:
  69. self.count = i16(s[2:4])
  70. if len(s) >= 6:
  71. self.repair = i16(s[4:6])
  72. else:
  73. self.repair = None
  74. if Image.DEBUG:
  75. print "AFRM", self.id, self.count
  76. return s
  77. def chunk_ADEF(self, offset, bytes):
  78. "ADEF -- store image"
  79. # assertions
  80. if self.count != 0:
  81. raise SyntaxError, "misplaced ADEF chunk"
  82. self.show = 0
  83. self.id = 0
  84. self.count = 1
  85. self.repair = None
  86. s = self.fp.read(bytes)
  87. if len(s) >= 2:
  88. self.id = i16(s)
  89. if len(s) >= 4:
  90. self.count = i16(s[2:4])
  91. if Image.DEBUG:
  92. print "ADEF", self.id, self.count
  93. return s
  94. def chunk_NAME(self, offset, bytes):
  95. "NAME -- name the current image"
  96. # assertions
  97. if self.count == 0:
  98. raise SyntaxError, "misplaced NAME chunk"
  99. name = self.fp.read(bytes)
  100. self.names[self.id] = name
  101. return name
  102. def chunk_AEND(self, offset, bytes):
  103. "AEND -- end of animation"
  104. if Image.DEBUG:
  105. print "AEND"
  106. self.eof = 1
  107. raise EOFError, "end of ARG file"
  108. def __getmodesize(self, s, full=1):
  109. size = i32(s), i32(s[4:])
  110. try:
  111. mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  112. except:
  113. raise SyntaxError, "unknown image mode"
  114. if full:
  115. if ord(s[12]):
  116. pass # interlace not yet supported
  117. if ord(s[11]):
  118. raise SyntaxError, "unknown filter category"
  119. return size, mode, rawmode
  120. def chunk_PAST(self, offset, bytes):
  121. "PAST -- paste one image into another"
  122. # assertions
  123. if self.count == 0:
  124. raise SyntaxError, "misplaced PAST chunk"
  125. if self.repair is not None:
  126. # we must repair the target image before we
  127. # start pasting
  128. # brute force; a better solution would be to
  129. # update only the dirty rectangles in images[id].
  130. # note that if images[id] doesn't exist, it must
  131. # be created
  132. self.images[self.id] = self.images[self.repair].copy()
  133. self.repair = None
  134. s = self.fp.read(bytes)
  135. im = self.images[i16(s)]
  136. x, y = i32(s[2:6]), i32(s[6:10])
  137. bbox = x, y, im.size[0]+x, im.size[1]+y
  138. if im.mode in ["RGBA"]:
  139. # paste with transparency
  140. # FIXME: should handle P+transparency as well
  141. self.images[self.id].paste(im, bbox, im)
  142. else:
  143. # paste without transparency
  144. self.images[self.id].paste(im, bbox)
  145. self.action = ("PAST",)
  146. self.__store()
  147. return s
  148. def chunk_BLNK(self, offset, bytes):
  149. "BLNK -- create blank image"
  150. # assertions
  151. if self.count == 0:
  152. raise SyntaxError, "misplaced BLNK chunk"
  153. s = self.fp.read(bytes)
  154. size, mode, rawmode = self.__getmodesize(s, 0)
  155. # store image (FIXME: handle colour)
  156. self.action = ("BLNK",)
  157. self.im = Image.core.fill(mode, size, 0)
  158. self.__store()
  159. return s
  160. def chunk_IHDR(self, offset, bytes):
  161. "IHDR -- full image follows"
  162. # assertions
  163. if self.count == 0:
  164. raise SyntaxError, "misplaced IHDR chunk"
  165. # image header
  166. s = self.fp.read(bytes)
  167. size, mode, rawmode = self.__getmodesize(s)
  168. # decode and store image
  169. self.action = ("IHDR",)
  170. self.im = Image.core.new(mode, size)
  171. self.decoder = Image.core.zip_decoder(rawmode)
  172. self.decoder.setimage(self.im, (0,0) + size)
  173. self.data = ""
  174. return s
  175. def chunk_DHDR(self, offset, bytes):
  176. "DHDR -- delta image follows"
  177. # assertions
  178. if self.count == 0:
  179. raise SyntaxError, "misplaced DHDR chunk"
  180. s = self.fp.read(bytes)
  181. size, mode, rawmode = self.__getmodesize(s)
  182. # delta header
  183. diff = ord(s[13])
  184. offs = i32(s[14:18]), i32(s[18:22])
  185. bbox = offs + (offs[0]+size[0], offs[1]+size[1])
  186. if Image.DEBUG:
  187. print "DHDR", diff, bbox
  188. # FIXME: decode and apply image
  189. self.action = ("DHDR", diff, bbox)
  190. # setup decoder
  191. self.im = Image.core.new(mode, size)
  192. self.decoder = Image.core.zip_decoder(rawmode)
  193. self.decoder.setimage(self.im, (0,0) + size)
  194. self.data = ""
  195. return s
  196. def chunk_JHDR(self, offset, bytes):
  197. "JHDR -- JPEG image follows"
  198. # assertions
  199. if self.count == 0:
  200. raise SyntaxError, "misplaced JHDR chunk"
  201. # image header
  202. s = self.fp.read(bytes)
  203. size, mode, rawmode = self.__getmodesize(s, 0)
  204. # decode and store image
  205. self.action = ("JHDR",)
  206. self.im = Image.core.new(mode, size)
  207. self.decoder = Image.core.jpeg_decoder(rawmode)
  208. self.decoder.setimage(self.im, (0,0) + size)
  209. self.data = ""
  210. return s
  211. def chunk_UHDR(self, offset, bytes):
  212. "UHDR -- uncompressed image data follows (EXPERIMENTAL)"
  213. # assertions
  214. if self.count == 0:
  215. raise SyntaxError, "misplaced UHDR chunk"
  216. # image header
  217. s = self.fp.read(bytes)
  218. size, mode, rawmode = self.__getmodesize(s, 0)
  219. # decode and store image
  220. self.action = ("UHDR",)
  221. self.im = Image.core.new(mode, size)
  222. self.decoder = Image.core.raw_decoder(rawmode)
  223. self.decoder.setimage(self.im, (0,0) + size)
  224. self.data = ""
  225. return s
  226. def chunk_IDAT(self, offset, bytes):
  227. "IDAT -- image data block"
  228. # pass compressed chunks through the decoder
  229. s = self.fp.read(bytes)
  230. self.data = self.data + s
  231. n, e = self.decoder.decode(self.data)
  232. if n < 0:
  233. # end of image
  234. if e < 0:
  235. raise IOError, "decoder error %d" % e
  236. else:
  237. self.data = self.data[n:]
  238. return s
  239. def chunk_DEND(self, offset, bytes):
  240. return self.chunk_IEND(offset, bytes)
  241. def chunk_JEND(self, offset, bytes):
  242. return self.chunk_IEND(offset, bytes)
  243. def chunk_UEND(self, offset, bytes):
  244. return self.chunk_IEND(offset, bytes)
  245. def chunk_IEND(self, offset, bytes):
  246. "IEND -- end of image"
  247. # we now have a new image. carry out the operation
  248. # defined by the image header.
  249. # won't need these anymore
  250. del self.decoder
  251. del self.data
  252. self.__store()
  253. return self.fp.read(bytes)
  254. def __store(self):
  255. # apply operation
  256. cid = self.action[0]
  257. if cid in ["BLNK", "IHDR", "JHDR", "UHDR"]:
  258. # store
  259. self.images[self.id] = self.im
  260. elif cid == "DHDR":
  261. # paste
  262. cid, mode, bbox = self.action
  263. im0 = self.images[self.id]
  264. im1 = self.im
  265. if mode == 0:
  266. im1 = im1.chop_add_modulo(im0.crop(bbox))
  267. im0.paste(im1, bbox)
  268. self.count = self.count - 1
  269. if self.count == 0 and self.show:
  270. self.im = self.images[self.id]
  271. raise EOFError # end of this frame
  272. def chunk_PLTE(self, offset, bytes):
  273. "PLTE -- palette data"
  274. s = self.fp.read(bytes)
  275. if self.mode == "P":
  276. self.palette = ImagePalette.raw("RGB", s)
  277. return s
  278. def chunk_sYNC(self, offset, bytes):
  279. "SYNC -- reset decoder"
  280. if self.count != 0:
  281. raise SyntaxError, "misplaced sYNC chunk"
  282. s = self.fp.read(bytes)
  283. self.__reset()
  284. return s
  285. # --------------------------------------------------------------------
  286. # ARG reader
  287. def _accept(prefix):
  288. return prefix[:8] == MAGIC
  289. ##
  290. # Image plugin for the experimental Animated Raster Graphics format.
  291. class ArgImageFile(ImageFile.ImageFile):
  292. format = "ARG"
  293. format_description = "Animated raster graphics"
  294. def _open(self):
  295. if Image.warnings:
  296. Image.warnings.warn(
  297. "The ArgImagePlugin driver is obsolete, and will be removed "
  298. "from a future release of PIL. If you rely on this module, "
  299. "please contact the PIL authors.",
  300. RuntimeWarning
  301. )
  302. if self.fp.read(8) != MAGIC:
  303. raise SyntaxError, "not an ARG file"
  304. self.arg = ArgStream(self.fp)
  305. # read and process the first chunk (AHDR)
  306. cid, offset, bytes = self.arg.read()
  307. if cid != "AHDR":
  308. raise SyntaxError, "expected an AHDR chunk"
  309. s = self.arg.call(cid, offset, bytes)
  310. self.arg.crc(cid, s)
  311. # image characteristics
  312. self.mode = self.arg.mode
  313. self.size = self.arg.size
  314. def load(self):
  315. if self.arg.im is None:
  316. self.seek(0)
  317. # image data
  318. self.im = self.arg.im
  319. self.palette = self.arg.palette
  320. # set things up for further processing
  321. Image.Image.load(self)
  322. def seek(self, frame):
  323. if self.arg.eof:
  324. raise EOFError, "end of animation"
  325. self.fp = self.arg.fp
  326. while 1:
  327. #
  328. # process chunks
  329. cid, offset, bytes = self.arg.read()
  330. if self.arg.eof:
  331. raise EOFError, "end of animation"
  332. try:
  333. s = self.arg.call(cid, offset, bytes)
  334. except EOFError:
  335. break
  336. except "glurk": # AttributeError
  337. if Image.DEBUG:
  338. print cid, bytes, "(unknown)"
  339. s = self.fp.read(bytes)
  340. self.arg.crc(cid, s)
  341. self.fp.read(4) # ship extra CRC
  342. def tell(self):
  343. return 0
  344. def verify(self):
  345. "Verify ARG file"
  346. # back up to first chunk
  347. self.fp.seek(8)
  348. self.arg.verify(self)
  349. self.arg.close()
  350. self.fp = None
  351. #
  352. # --------------------------------------------------------------------
  353. Image.register_open("ARG", ArgImageFile, _accept)
  354. Image.register_extension("ARG", ".arg")
  355. Image.register_mime("ARG", "video/x-arg")