TgaImagePlugin.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # TGA file handling
  6. #
  7. # History:
  8. # 95-09-01 fl created (reads 24-bit files only)
  9. # 97-01-04 fl support more TGA versions, including compressed images
  10. # 98-07-04 fl fixed orientation and alpha layer bugs
  11. # 98-09-11 fl fixed orientation for runlength decoder
  12. #
  13. # Copyright (c) Secret Labs AB 1997-98.
  14. # Copyright (c) Fredrik Lundh 1995-97.
  15. #
  16. # See the README file for information on usage and redistribution.
  17. #
  18. import warnings
  19. from . import Image, ImageFile, ImagePalette
  20. from ._binary import i8
  21. from ._binary import i16le as i16
  22. from ._binary import o8
  23. from ._binary import o16le as o16
  24. #
  25. # --------------------------------------------------------------------
  26. # Read RGA file
  27. MODES = {
  28. # map imagetype/depth to rawmode
  29. (1, 8): "P",
  30. (3, 1): "1",
  31. (3, 8): "L",
  32. (3, 16): "LA",
  33. (2, 16): "BGR;5",
  34. (2, 24): "BGR",
  35. (2, 32): "BGRA",
  36. }
  37. ##
  38. # Image plugin for Targa files.
  39. class TgaImageFile(ImageFile.ImageFile):
  40. format = "TGA"
  41. format_description = "Targa"
  42. def _open(self):
  43. # process header
  44. s = self.fp.read(18)
  45. id_len = i8(s[0])
  46. colormaptype = i8(s[1])
  47. imagetype = i8(s[2])
  48. depth = i8(s[16])
  49. flags = i8(s[17])
  50. self._size = i16(s[12:]), i16(s[14:])
  51. # validate header fields
  52. if (
  53. colormaptype not in (0, 1)
  54. or self.size[0] <= 0
  55. or self.size[1] <= 0
  56. or depth not in (1, 8, 16, 24, 32)
  57. ):
  58. raise SyntaxError("not a TGA file")
  59. # image mode
  60. if imagetype in (3, 11):
  61. self.mode = "L"
  62. if depth == 1:
  63. self.mode = "1" # ???
  64. elif depth == 16:
  65. self.mode = "LA"
  66. elif imagetype in (1, 9):
  67. self.mode = "P"
  68. elif imagetype in (2, 10):
  69. self.mode = "RGB"
  70. if depth == 32:
  71. self.mode = "RGBA"
  72. else:
  73. raise SyntaxError("unknown TGA mode")
  74. # orientation
  75. orientation = flags & 0x30
  76. if orientation == 0x20:
  77. orientation = 1
  78. elif not orientation:
  79. orientation = -1
  80. else:
  81. raise SyntaxError("unknown TGA orientation")
  82. self.info["orientation"] = orientation
  83. if imagetype & 8:
  84. self.info["compression"] = "tga_rle"
  85. if id_len:
  86. self.info["id_section"] = self.fp.read(id_len)
  87. if colormaptype:
  88. # read palette
  89. start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])
  90. if mapdepth == 16:
  91. self.palette = ImagePalette.raw(
  92. "BGR;16", b"\0" * 2 * start + self.fp.read(2 * size)
  93. )
  94. elif mapdepth == 24:
  95. self.palette = ImagePalette.raw(
  96. "BGR", b"\0" * 3 * start + self.fp.read(3 * size)
  97. )
  98. elif mapdepth == 32:
  99. self.palette = ImagePalette.raw(
  100. "BGRA", b"\0" * 4 * start + self.fp.read(4 * size)
  101. )
  102. # setup tile descriptor
  103. try:
  104. rawmode = MODES[(imagetype & 7, depth)]
  105. if imagetype & 8:
  106. # compressed
  107. self.tile = [
  108. (
  109. "tga_rle",
  110. (0, 0) + self.size,
  111. self.fp.tell(),
  112. (rawmode, orientation, depth),
  113. )
  114. ]
  115. else:
  116. self.tile = [
  117. (
  118. "raw",
  119. (0, 0) + self.size,
  120. self.fp.tell(),
  121. (rawmode, 0, orientation),
  122. )
  123. ]
  124. except KeyError:
  125. pass # cannot decode
  126. #
  127. # --------------------------------------------------------------------
  128. # Write TGA file
  129. SAVE = {
  130. "1": ("1", 1, 0, 3),
  131. "L": ("L", 8, 0, 3),
  132. "LA": ("LA", 16, 0, 3),
  133. "P": ("P", 8, 1, 1),
  134. "RGB": ("BGR", 24, 0, 2),
  135. "RGBA": ("BGRA", 32, 0, 2),
  136. }
  137. def _save(im, fp, filename):
  138. try:
  139. rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
  140. except KeyError as e:
  141. raise OSError(f"cannot write mode {im.mode} as TGA") from e
  142. if "rle" in im.encoderinfo:
  143. rle = im.encoderinfo["rle"]
  144. else:
  145. compression = im.encoderinfo.get("compression", im.info.get("compression"))
  146. rle = compression == "tga_rle"
  147. if rle:
  148. imagetype += 8
  149. id_section = im.encoderinfo.get("id_section", im.info.get("id_section", ""))
  150. id_len = len(id_section)
  151. if id_len > 255:
  152. id_len = 255
  153. id_section = id_section[:255]
  154. warnings.warn("id_section has been trimmed to 255 characters")
  155. if colormaptype:
  156. colormapfirst, colormaplength, colormapentry = 0, 256, 24
  157. else:
  158. colormapfirst, colormaplength, colormapentry = 0, 0, 0
  159. if im.mode in ("LA", "RGBA"):
  160. flags = 8
  161. else:
  162. flags = 0
  163. orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1))
  164. if orientation > 0:
  165. flags = flags | 0x20
  166. fp.write(
  167. o8(id_len)
  168. + o8(colormaptype)
  169. + o8(imagetype)
  170. + o16(colormapfirst)
  171. + o16(colormaplength)
  172. + o8(colormapentry)
  173. + o16(0)
  174. + o16(0)
  175. + o16(im.size[0])
  176. + o16(im.size[1])
  177. + o8(bits)
  178. + o8(flags)
  179. )
  180. if id_section:
  181. fp.write(id_section)
  182. if colormaptype:
  183. fp.write(im.im.getpalette("RGB", "BGR"))
  184. if rle:
  185. ImageFile._save(
  186. im, fp, [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))]
  187. )
  188. else:
  189. ImageFile._save(
  190. im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))]
  191. )
  192. # write targa version 2 footer
  193. fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000")
  194. #
  195. # --------------------------------------------------------------------
  196. # Registry
  197. Image.register_open(TgaImageFile.format, TgaImageFile)
  198. Image.register_save(TgaImageFile.format, _save)
  199. Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"])
  200. Image.register_mime(TgaImageFile.format, "image/x-tga")