FliImagePlugin.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # FLI/FLC file handling.
  6. #
  7. # History:
  8. # 95-09-01 fl Created
  9. # 97-01-03 fl Fixed parser, setup decoder tile
  10. # 98-07-15 fl Renamed offset attribute to avoid name clash
  11. #
  12. # Copyright (c) Secret Labs AB 1997-98.
  13. # Copyright (c) Fredrik Lundh 1995-97.
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17. from . import Image, ImageFile, ImagePalette
  18. from ._binary import i16le as i16
  19. from ._binary import i32le as i32
  20. from ._binary import o8
  21. #
  22. # decoder
  23. def _accept(prefix):
  24. return len(prefix) >= 6 and i16(prefix, 4) in [0xAF11, 0xAF12]
  25. ##
  26. # Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
  27. # method to load individual frames.
  28. class FliImageFile(ImageFile.ImageFile):
  29. format = "FLI"
  30. format_description = "Autodesk FLI/FLC Animation"
  31. _close_exclusive_fp_after_loading = False
  32. def _open(self):
  33. # HEAD
  34. s = self.fp.read(128)
  35. if not (
  36. _accept(s)
  37. and i16(s, 14) in [0, 3] # flags
  38. and s[20:22] == b"\x00\x00" # reserved
  39. ):
  40. raise SyntaxError("not an FLI/FLC file")
  41. # frames
  42. self.n_frames = i16(s, 6)
  43. self.is_animated = self.n_frames > 1
  44. # image characteristics
  45. self.mode = "P"
  46. self._size = i16(s, 8), i16(s, 10)
  47. # animation speed
  48. duration = i32(s, 16)
  49. magic = i16(s, 4)
  50. if magic == 0xAF11:
  51. duration = (duration * 1000) // 70
  52. self.info["duration"] = duration
  53. # look for palette
  54. palette = [(a, a, a) for a in range(256)]
  55. s = self.fp.read(16)
  56. self.__offset = 128
  57. if i16(s, 4) == 0xF100:
  58. # prefix chunk; ignore it
  59. self.__offset = self.__offset + i32(s)
  60. s = self.fp.read(16)
  61. if i16(s, 4) == 0xF1FA:
  62. # look for palette chunk
  63. s = self.fp.read(6)
  64. if i16(s, 4) == 11:
  65. self._palette(palette, 2)
  66. elif i16(s, 4) == 4:
  67. self._palette(palette, 0)
  68. palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
  69. self.palette = ImagePalette.raw("RGB", b"".join(palette))
  70. # set things up to decode first frame
  71. self.__frame = -1
  72. self.__fp = self.fp
  73. self.__rewind = self.fp.tell()
  74. self.seek(0)
  75. def _palette(self, palette, shift):
  76. # load palette
  77. i = 0
  78. for e in range(i16(self.fp.read(2))):
  79. s = self.fp.read(2)
  80. i = i + s[0]
  81. n = s[1]
  82. if n == 0:
  83. n = 256
  84. s = self.fp.read(n * 3)
  85. for n in range(0, len(s), 3):
  86. r = s[n] << shift
  87. g = s[n + 1] << shift
  88. b = s[n + 2] << shift
  89. palette[i] = (r, g, b)
  90. i += 1
  91. def seek(self, frame):
  92. if not self._seek_check(frame):
  93. return
  94. if frame < self.__frame:
  95. self._seek(0)
  96. for f in range(self.__frame + 1, frame + 1):
  97. self._seek(f)
  98. def _seek(self, frame):
  99. if frame == 0:
  100. self.__frame = -1
  101. self.__fp.seek(self.__rewind)
  102. self.__offset = 128
  103. else:
  104. # ensure that the previous frame was loaded
  105. self.load()
  106. if frame != self.__frame + 1:
  107. raise ValueError(f"cannot seek to frame {frame}")
  108. self.__frame = frame
  109. # move to next frame
  110. self.fp = self.__fp
  111. self.fp.seek(self.__offset)
  112. s = self.fp.read(4)
  113. if not s:
  114. raise EOFError
  115. framesize = i32(s)
  116. self.decodermaxblock = framesize
  117. self.tile = [("fli", (0, 0) + self.size, self.__offset, None)]
  118. self.__offset += framesize
  119. def tell(self):
  120. return self.__frame
  121. def _close__fp(self):
  122. try:
  123. if self.__fp != self.fp:
  124. self.__fp.close()
  125. except AttributeError:
  126. pass
  127. finally:
  128. self.__fp = None
  129. #
  130. # registry
  131. Image.register_open(FliImageFile.format, FliImageFile, _accept)
  132. Image.register_extensions(FliImageFile.format, [".fli", ".flc"])