Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PsdImagePlugin.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # Adobe PSD 2.5/3.0 file handling
  6. #
  7. # History:
  8. # 1995-09-01 fl Created
  9. # 1997-01-03 fl Read most PSD images
  10. # 1997-01-18 fl Fixed P and CMYK support
  11. # 2001-10-21 fl Added seek/tell support (for layers)
  12. #
  13. # Copyright (c) 1997-2001 by Secret Labs AB.
  14. # Copyright (c) 1995-2001 by Fredrik Lundh
  15. #
  16. # See the README file for information on usage and redistribution.
  17. #
  18. import io
  19. from . import Image, ImageFile, ImagePalette
  20. from ._binary import i8
  21. from ._binary import i16be as i16
  22. from ._binary import i32be as i32
  23. from ._binary import si16be as si16
  24. MODES = {
  25. # (photoshop mode, bits) -> (pil mode, required channels)
  26. (0, 1): ("1", 1),
  27. (0, 8): ("L", 1),
  28. (1, 8): ("L", 1),
  29. (2, 8): ("P", 1),
  30. (3, 8): ("RGB", 3),
  31. (4, 8): ("CMYK", 4),
  32. (7, 8): ("L", 1), # FIXME: multilayer
  33. (8, 8): ("L", 1), # duotone
  34. (9, 8): ("LAB", 3),
  35. }
  36. # --------------------------------------------------------------------.
  37. # read PSD images
  38. def _accept(prefix):
  39. return prefix[:4] == b"8BPS"
  40. ##
  41. # Image plugin for Photoshop images.
  42. class PsdImageFile(ImageFile.ImageFile):
  43. format = "PSD"
  44. format_description = "Adobe Photoshop"
  45. _close_exclusive_fp_after_loading = False
  46. def _open(self):
  47. read = self.fp.read
  48. #
  49. # header
  50. s = read(26)
  51. if not _accept(s) or i16(s, 4) != 1:
  52. msg = "not a PSD file"
  53. raise SyntaxError(msg)
  54. psd_bits = i16(s, 22)
  55. psd_channels = i16(s, 12)
  56. psd_mode = i16(s, 24)
  57. mode, channels = MODES[(psd_mode, psd_bits)]
  58. if channels > psd_channels:
  59. msg = "not enough channels"
  60. raise OSError(msg)
  61. if mode == "RGB" and psd_channels == 4:
  62. mode = "RGBA"
  63. channels = 4
  64. self.mode = mode
  65. self._size = i32(s, 18), i32(s, 14)
  66. #
  67. # color mode data
  68. size = i32(read(4))
  69. if size:
  70. data = read(size)
  71. if mode == "P" and size == 768:
  72. self.palette = ImagePalette.raw("RGB;L", data)
  73. #
  74. # image resources
  75. self.resources = []
  76. size = i32(read(4))
  77. if size:
  78. # load resources
  79. end = self.fp.tell() + size
  80. while self.fp.tell() < end:
  81. read(4) # signature
  82. id = i16(read(2))
  83. name = read(i8(read(1)))
  84. if not (len(name) & 1):
  85. read(1) # padding
  86. data = read(i32(read(4)))
  87. if len(data) & 1:
  88. read(1) # padding
  89. self.resources.append((id, name, data))
  90. if id == 1039: # ICC profile
  91. self.info["icc_profile"] = data
  92. #
  93. # layer and mask information
  94. self.layers = []
  95. size = i32(read(4))
  96. if size:
  97. end = self.fp.tell() + size
  98. size = i32(read(4))
  99. if size:
  100. _layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size))
  101. self.layers = _layerinfo(_layer_data, size)
  102. self.fp.seek(end)
  103. self.n_frames = len(self.layers)
  104. self.is_animated = self.n_frames > 1
  105. #
  106. # image descriptor
  107. self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels)
  108. # keep the file open
  109. self._fp = self.fp
  110. self.frame = 1
  111. self._min_frame = 1
  112. def seek(self, layer):
  113. if not self._seek_check(layer):
  114. return
  115. # seek to given layer (1..max)
  116. try:
  117. name, mode, bbox, tile = self.layers[layer - 1]
  118. self.mode = mode
  119. self.tile = tile
  120. self.frame = layer
  121. self.fp = self._fp
  122. return name, bbox
  123. except IndexError as e:
  124. msg = "no such layer"
  125. raise EOFError(msg) from e
  126. def tell(self):
  127. # return layer number (0=image, 1..max=layers)
  128. return self.frame
  129. def _layerinfo(fp, ct_bytes):
  130. # read layerinfo block
  131. layers = []
  132. def read(size):
  133. return ImageFile._safe_read(fp, size)
  134. ct = si16(read(2))
  135. # sanity check
  136. if ct_bytes < (abs(ct) * 20):
  137. msg = "Layer block too short for number of layers requested"
  138. raise SyntaxError(msg)
  139. for _ in range(abs(ct)):
  140. # bounding box
  141. y0 = i32(read(4))
  142. x0 = i32(read(4))
  143. y1 = i32(read(4))
  144. x1 = i32(read(4))
  145. # image info
  146. mode = []
  147. ct_types = i16(read(2))
  148. types = list(range(ct_types))
  149. if len(types) > 4:
  150. continue
  151. for _ in types:
  152. type = i16(read(2))
  153. if type == 65535:
  154. m = "A"
  155. else:
  156. m = "RGBA"[type]
  157. mode.append(m)
  158. read(4) # size
  159. # figure out the image mode
  160. mode.sort()
  161. if mode == ["R"]:
  162. mode = "L"
  163. elif mode == ["B", "G", "R"]:
  164. mode = "RGB"
  165. elif mode == ["A", "B", "G", "R"]:
  166. mode = "RGBA"
  167. else:
  168. mode = None # unknown
  169. # skip over blend flags and extra information
  170. read(12) # filler
  171. name = ""
  172. size = i32(read(4)) # length of the extra data field
  173. if size:
  174. data_end = fp.tell() + size
  175. length = i32(read(4))
  176. if length:
  177. fp.seek(length - 16, io.SEEK_CUR)
  178. length = i32(read(4))
  179. if length:
  180. fp.seek(length, io.SEEK_CUR)
  181. length = i8(read(1))
  182. if length:
  183. # Don't know the proper encoding,
  184. # Latin-1 should be a good guess
  185. name = read(length).decode("latin-1", "replace")
  186. fp.seek(data_end)
  187. layers.append((name, mode, (x0, y0, x1, y1)))
  188. # get tiles
  189. for i, (name, mode, bbox) in enumerate(layers):
  190. tile = []
  191. for m in mode:
  192. t = _maketile(fp, m, bbox, 1)
  193. if t:
  194. tile.extend(t)
  195. layers[i] = name, mode, bbox, tile
  196. return layers
  197. def _maketile(file, mode, bbox, channels):
  198. tile = None
  199. read = file.read
  200. compression = i16(read(2))
  201. xsize = bbox[2] - bbox[0]
  202. ysize = bbox[3] - bbox[1]
  203. offset = file.tell()
  204. if compression == 0:
  205. #
  206. # raw compression
  207. tile = []
  208. for channel in range(channels):
  209. layer = mode[channel]
  210. if mode == "CMYK":
  211. layer += ";I"
  212. tile.append(("raw", bbox, offset, layer))
  213. offset = offset + xsize * ysize
  214. elif compression == 1:
  215. #
  216. # packbits compression
  217. i = 0
  218. tile = []
  219. bytecount = read(channels * ysize * 2)
  220. offset = file.tell()
  221. for channel in range(channels):
  222. layer = mode[channel]
  223. if mode == "CMYK":
  224. layer += ";I"
  225. tile.append(("packbits", bbox, offset, layer))
  226. for y in range(ysize):
  227. offset = offset + i16(bytecount, i)
  228. i += 2
  229. file.seek(offset)
  230. if offset & 1:
  231. read(1) # padding
  232. return tile
  233. # --------------------------------------------------------------------
  234. # registry
  235. Image.register_open(PsdImageFile.format, PsdImageFile, _accept)
  236. Image.register_extension(PsdImageFile.format, ".psd")
  237. Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop")