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.

435 lines
14 KiB

4 years ago
  1. """
  2. Blizzard Mipmap Format (.blp)
  3. Jerome Leclanche <jerome@leclan.ch>
  4. The contents of this file are hereby released in the public domain (CC0)
  5. Full text of the CC0 license:
  6. https://creativecommons.org/publicdomain/zero/1.0/
  7. BLP1 files, used mostly in Warcraft III, are not fully supported.
  8. All types of BLP2 files used in World of Warcraft are supported.
  9. The BLP file structure consists of a header, up to 16 mipmaps of the
  10. texture
  11. Texture sizes must be powers of two, though the two dimensions do
  12. not have to be equal; 512x256 is valid, but 512x200 is not.
  13. The first mipmap (mipmap #0) is the full size image; each subsequent
  14. mipmap halves both dimensions. The final mipmap should be 1x1.
  15. BLP files come in many different flavours:
  16. * JPEG-compressed (type == 0) - only supported for BLP1.
  17. * RAW images (type == 1, encoding == 1). Each mipmap is stored as an
  18. array of 8-bit values, one per pixel, left to right, top to bottom.
  19. Each value is an index to the palette.
  20. * DXT-compressed (type == 1, encoding == 2):
  21. - DXT1 compression is used if alpha_encoding == 0.
  22. - An additional alpha bit is used if alpha_depth == 1.
  23. - DXT3 compression is used if alpha_encoding == 1.
  24. - DXT5 compression is used if alpha_encoding == 7.
  25. """
  26. import struct
  27. from io import BytesIO
  28. from . import Image, ImageFile
  29. BLP_FORMAT_JPEG = 0
  30. BLP_ENCODING_UNCOMPRESSED = 1
  31. BLP_ENCODING_DXT = 2
  32. BLP_ENCODING_UNCOMPRESSED_RAW_BGRA = 3
  33. BLP_ALPHA_ENCODING_DXT1 = 0
  34. BLP_ALPHA_ENCODING_DXT3 = 1
  35. BLP_ALPHA_ENCODING_DXT5 = 7
  36. def unpack_565(i):
  37. return (
  38. ((i >> 11) & 0x1f) << 3,
  39. ((i >> 5) & 0x3f) << 2,
  40. (i & 0x1f) << 3
  41. )
  42. def decode_dxt1(data, alpha=False):
  43. """
  44. input: one "row" of data (i.e. will produce 4*width pixels)
  45. """
  46. blocks = len(data) // 8 # number of blocks in row
  47. ret = (bytearray(), bytearray(), bytearray(), bytearray())
  48. for block in range(blocks):
  49. # Decode next 8-byte block.
  50. idx = block * 8
  51. color0, color1, bits = struct.unpack_from("<HHI", data, idx)
  52. r0, g0, b0 = unpack_565(color0)
  53. r1, g1, b1 = unpack_565(color1)
  54. # Decode this block into 4x4 pixels
  55. # Accumulate the results onto our 4 row accumulators
  56. for j in range(4):
  57. for i in range(4):
  58. # get next control op and generate a pixel
  59. control = bits & 3
  60. bits = bits >> 2
  61. a = 0xFF
  62. if control == 0:
  63. r, g, b = r0, g0, b0
  64. elif control == 1:
  65. r, g, b = r1, g1, b1
  66. elif control == 2:
  67. if color0 > color1:
  68. r = (2 * r0 + r1) // 3
  69. g = (2 * g0 + g1) // 3
  70. b = (2 * b0 + b1) // 3
  71. else:
  72. r = (r0 + r1) // 2
  73. g = (g0 + g1) // 2
  74. b = (b0 + b1) // 2
  75. elif control == 3:
  76. if color0 > color1:
  77. r = (2 * r1 + r0) // 3
  78. g = (2 * g1 + g0) // 3
  79. b = (2 * b1 + b0) // 3
  80. else:
  81. r, g, b, a = 0, 0, 0, 0
  82. if alpha:
  83. ret[j].extend([r, g, b, a])
  84. else:
  85. ret[j].extend([r, g, b])
  86. return ret
  87. def decode_dxt3(data):
  88. """
  89. input: one "row" of data (i.e. will produce 4*width pixels)
  90. """
  91. blocks = len(data) // 16 # number of blocks in row
  92. ret = (bytearray(), bytearray(), bytearray(), bytearray())
  93. for block in range(blocks):
  94. idx = block * 16
  95. block = data[idx:idx + 16]
  96. # Decode next 16-byte block.
  97. bits = struct.unpack_from("<8B", block)
  98. color0, color1 = struct.unpack_from("<HH", block, 8)
  99. code, = struct.unpack_from("<I", block, 12)
  100. r0, g0, b0 = unpack_565(color0)
  101. r1, g1, b1 = unpack_565(color1)
  102. for j in range(4):
  103. high = False # Do we want the higher bits?
  104. for i in range(4):
  105. alphacode_index = (4 * j + i) // 2
  106. a = bits[alphacode_index]
  107. if high:
  108. high = False
  109. a >>= 4
  110. else:
  111. high = True
  112. a &= 0xf
  113. a *= 17 # We get a value between 0 and 15
  114. color_code = (code >> 2 * (4 * j + i)) & 0x03
  115. if color_code == 0:
  116. r, g, b = r0, g0, b0
  117. elif color_code == 1:
  118. r, g, b = r1, g1, b1
  119. elif color_code == 2:
  120. r = (2 * r0 + r1) // 3
  121. g = (2 * g0 + g1) // 3
  122. b = (2 * b0 + b1) // 3
  123. elif color_code == 3:
  124. r = (2 * r1 + r0) // 3
  125. g = (2 * g1 + g0) // 3
  126. b = (2 * b1 + b0) // 3
  127. ret[j].extend([r, g, b, a])
  128. return ret
  129. def decode_dxt5(data):
  130. """
  131. input: one "row" of data (i.e. will produce 4 * width pixels)
  132. """
  133. blocks = len(data) // 16 # number of blocks in row
  134. ret = (bytearray(), bytearray(), bytearray(), bytearray())
  135. for block in range(blocks):
  136. idx = block * 16
  137. block = data[idx:idx + 16]
  138. # Decode next 16-byte block.
  139. a0, a1 = struct.unpack_from("<BB", block)
  140. bits = struct.unpack_from("<6B", block, 2)
  141. alphacode1 = (
  142. bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
  143. )
  144. alphacode2 = bits[0] | (bits[1] << 8)
  145. color0, color1 = struct.unpack_from("<HH", block, 8)
  146. code, = struct.unpack_from("<I", block, 12)
  147. r0, g0, b0 = unpack_565(color0)
  148. r1, g1, b1 = unpack_565(color1)
  149. for j in range(4):
  150. for i in range(4):
  151. # get next control op and generate a pixel
  152. alphacode_index = 3 * (4 * j + i)
  153. if alphacode_index <= 12:
  154. alphacode = (alphacode2 >> alphacode_index) & 0x07
  155. elif alphacode_index == 15:
  156. alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06)
  157. else: # alphacode_index >= 18 and alphacode_index <= 45
  158. alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07
  159. if alphacode == 0:
  160. a = a0
  161. elif alphacode == 1:
  162. a = a1
  163. elif a0 > a1:
  164. a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7
  165. elif alphacode == 6:
  166. a = 0
  167. elif alphacode == 7:
  168. a = 255
  169. else:
  170. a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5
  171. color_code = (code >> 2 * (4 * j + i)) & 0x03
  172. if color_code == 0:
  173. r, g, b = r0, g0, b0
  174. elif color_code == 1:
  175. r, g, b = r1, g1, b1
  176. elif color_code == 2:
  177. r = (2 * r0 + r1) // 3
  178. g = (2 * g0 + g1) // 3
  179. b = (2 * b0 + b1) // 3
  180. elif color_code == 3:
  181. r = (2 * r1 + r0) // 3
  182. g = (2 * g1 + g0) // 3
  183. b = (2 * b1 + b0) // 3
  184. ret[j].extend([r, g, b, a])
  185. return ret
  186. class BLPFormatError(NotImplementedError):
  187. pass
  188. class BlpImageFile(ImageFile.ImageFile):
  189. """
  190. Blizzard Mipmap Format
  191. """
  192. format = "BLP"
  193. format_description = "Blizzard Mipmap Format"
  194. def _open(self):
  195. self.magic = self.fp.read(4)
  196. self._read_blp_header()
  197. if self.magic == b"BLP1":
  198. decoder = "BLP1"
  199. self.mode = "RGB"
  200. elif self.magic == b"BLP2":
  201. decoder = "BLP2"
  202. self.mode = "RGBA" if self._blp_alpha_depth else "RGB"
  203. else:
  204. raise BLPFormatError("Bad BLP magic %r" % (self.magic))
  205. self.tile = [
  206. (decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
  207. ]
  208. def _read_blp_header(self):
  209. self._blp_compression, = struct.unpack("<i", self.fp.read(4))
  210. self._blp_encoding, = struct.unpack("<b", self.fp.read(1))
  211. self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1))
  212. self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
  213. self._blp_mips, = struct.unpack("<b", self.fp.read(1))
  214. self._size = struct.unpack("<II", self.fp.read(8))
  215. if self.magic == b"BLP1":
  216. # Only present for BLP1
  217. self._blp_encoding, = struct.unpack("<i", self.fp.read(4))
  218. self._blp_subtype, = struct.unpack("<i", self.fp.read(4))
  219. self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
  220. self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
  221. class _BLPBaseDecoder(ImageFile.PyDecoder):
  222. _pulls_fd = True
  223. def decode(self, buffer):
  224. try:
  225. self.fd.seek(0)
  226. self.magic = self.fd.read(4)
  227. self._read_blp_header()
  228. self._load()
  229. except struct.error:
  230. raise IOError("Truncated Blp file")
  231. return 0, 0
  232. def _read_palette(self):
  233. ret = []
  234. for i in range(256):
  235. try:
  236. b, g, r, a = struct.unpack("<4B", self.fd.read(4))
  237. except struct.error:
  238. break
  239. ret.append((b, g, r, a))
  240. return ret
  241. def _read_blp_header(self):
  242. self._blp_compression, = struct.unpack("<i", self.fd.read(4))
  243. self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
  244. self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
  245. self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
  246. self._blp_mips, = struct.unpack("<b", self.fd.read(1))
  247. self.size = struct.unpack("<II", self.fd.read(8))
  248. if self.magic == b"BLP1":
  249. # Only present for BLP1
  250. self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
  251. self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
  252. self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
  253. self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
  254. class BLP1Decoder(_BLPBaseDecoder):
  255. def _load(self):
  256. if self._blp_compression == BLP_FORMAT_JPEG:
  257. self._decode_jpeg_stream()
  258. elif self._blp_compression == 1:
  259. if self._blp_encoding in (4, 5):
  260. data = bytearray()
  261. palette = self._read_palette()
  262. _data = BytesIO(self.fd.read(self._blp_lengths[0]))
  263. while True:
  264. try:
  265. offset, = struct.unpack("<B", _data.read(1))
  266. except struct.error:
  267. break
  268. b, g, r, a = palette[offset]
  269. data.extend([r, g, b])
  270. self.set_as_raw(bytes(data))
  271. else:
  272. raise BLPFormatError(
  273. "Unsupported BLP encoding %r" % (self._blp_encoding)
  274. )
  275. else:
  276. raise BLPFormatError(
  277. "Unsupported BLP compression %r" % (self._blp_encoding)
  278. )
  279. def _decode_jpeg_stream(self):
  280. from PIL.JpegImagePlugin import JpegImageFile
  281. jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
  282. jpeg_header = self.fd.read(jpeg_header_size)
  283. self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
  284. data = self.fd.read(self._blp_lengths[0])
  285. data = jpeg_header + data
  286. data = BytesIO(data)
  287. image = JpegImageFile(data)
  288. self.tile = image.tile # :/
  289. self.fd = image.fp
  290. self.mode = image.mode
  291. class BLP2Decoder(_BLPBaseDecoder):
  292. def _load(self):
  293. palette = self._read_palette()
  294. data = bytearray()
  295. self.fd.seek(self._blp_offsets[0])
  296. if self._blp_compression == 1:
  297. # Uncompressed or DirectX compression
  298. if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
  299. _data = BytesIO(self.fd.read(self._blp_lengths[0]))
  300. while True:
  301. try:
  302. offset, = struct.unpack("<B", _data.read(1))
  303. except struct.error:
  304. break
  305. b, g, r, a = palette[offset]
  306. data.extend((r, g, b))
  307. elif self._blp_encoding == BLP_ENCODING_DXT:
  308. if self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT1:
  309. linesize = (self.size[0] + 3) // 4 * 8
  310. for yb in range((self.size[1] + 3) // 4):
  311. for d in decode_dxt1(
  312. self.fd.read(linesize),
  313. alpha=bool(self._blp_alpha_depth)
  314. ):
  315. data += d
  316. elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
  317. linesize = (self.size[0] + 3) // 4 * 16
  318. for yb in range((self.size[1] + 3) // 4):
  319. for d in decode_dxt3(self.fd.read(linesize)):
  320. data += d
  321. elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
  322. linesize = (self.size[0] + 3) // 4 * 16
  323. for yb in range((self.size[1] + 3) // 4):
  324. for d in decode_dxt5(self.fd.read(linesize)):
  325. data += d
  326. else:
  327. raise BLPFormatError("Unsupported alpha encoding %r" % (
  328. self._blp_alpha_encoding
  329. ))
  330. else:
  331. raise BLPFormatError(
  332. "Unknown BLP encoding %r" % (self._blp_encoding)
  333. )
  334. else:
  335. raise BLPFormatError(
  336. "Unknown BLP compression %r" % (self._blp_compression)
  337. )
  338. self.set_as_raw(bytes(data))
  339. Image.register_open(
  340. BlpImageFile.format, BlpImageFile, lambda p: p[:4] in (b"BLP1", b"BLP2")
  341. )
  342. Image.register_extension(BlpImageFile.format, ".blp")
  343. Image.register_decoder("BLP1", BLP1Decoder)
  344. Image.register_decoder("BLP2", BLP2Decoder)