""" S3TC DXT1/DXT5 Texture Decompression Adapted from https://github.com/leamsii/Python-DXT-Decompress to add types and take in bytes instead of file pointers. Inspired by Benjamin Dobell. Original C++ code https://github.com/Benjamin-Dobell/s3tc-dxt-decompression """ import io import struct from typing import List, Optional, Tuple class DXTBuffer: def __init__(self, width: int, height: int): self.width = width self.height = height self.block_countx = self.width // 4 self.block_county = self.height // 4 self.decompressed_buffer: List[Optional[bytes]] = [None] * ((width * height) * 2) def unpackRGB(self, packed: int) -> Tuple[int, int, int]: # This function converts RGB565 format to raw pixels R = (packed >> 11) & 0x1F G = (packed >> 5) & 0x3F B = (packed) & 0x1F R = (R << 3) | (R >> 2) G = (G << 2) | (G >> 4) B = (B << 3) | (B >> 2) return (R, G, B) def swapbytes(self, data: bytes, swap: bool) -> bytes: if swap: return b"".join([ data[(x + 1):(x + 2)] + data[x:(x + 1)] for x in range(0, len(data), 2) ]) return data def DXT5Decompress(self, filedata: bytes, swap: bool = False) -> bytes: # Loop through each block and decompress it file = io.BytesIO(filedata) for row in range(self.block_county): for col in range(self.block_countx): # Get the alpha values, and color lookup table a0, a1, acode0, acode1, c0, c1, ctable = struct.unpack(" bytes: # Loop through each block and decompress it file = io.BytesIO(filedata) for row in range(self.block_county): for col in range(self.block_countx): # Color 1 color 2, color look up table c0, c1, ctable = struct.unpack(" None: code = (ctable >> (2 * ((4 * j) + i))) & 0x03 # Get the color of the current pixel pixel_color = None r0, g0, b0 = self.unpackRGB(c0) r1, g1, b1 = self.unpackRGB(c1) # Sliding scale between colors. if code == 0: pixel_color = (r0, g0, b0, alpha) if code == 1: pixel_color = (r1, g1, b1, alpha) if code == 2: if c0 > c1: pixel_color = ((2 * r0 + r1) // 3, (2 * g0 + g1) // 3, (2 * b0 + b1) // 3, alpha) else: pixel_color = ((r0 + r1) // 2, (g0 + g1) // 2, (b0 + b1) // 2, alpha) if code == 3: if c0 > c1: pixel_color = ((r0 + 2 * r1) // 3, (g0 + 2 * g1) // 3, (b0 + 2 * b1) // 3, alpha) else: pixel_color = (0, 0, 0, alpha) # While not surpassing the image dimensions, assign pixels the colors. if pixel_color is not None and (x + i) < self.width and (y + j) < self.height: self.decompressed_buffer[(y + j) * self.width + (x + i)] = ( struct.pack(' int: # Using the same method as the colors calculate the alpha values alpha_code = (acode >> (3 * ((4 * j) + i))) & 0x07 if alpha_code == 0: alpha = a0 elif alpha_code == 1: alpha = a1 else: if a0 > a1: alpha = ((8 - alpha_code) * a0 + (alpha_code - 1) * a1) // 7 else: if alpha_code == 6: alpha = 0 elif alpha_code == 7: alpha = 255 else: alpha = ((6 - alpha_code) * a0 + (alpha_code - 1) * a1) // 5 return alpha