import dataclasses from enum import IntEnum, IntFlag, auto import struct from typing import ClassVar, List from .glstuff import GL_IMAGE_FORMAT, CompressedGLTextureFormat, UncompressedGLTextureFormat DDS_MAGIC_NUMBER = b"DDS " # b"\x20\x53\x44\x44" class IntEnumFromZero(IntEnum): def _generate_next_value_(name, start, count, last_values): return count class D3D10_RESOURCE_DIMENSION(IntEnumFromZero): D3D10_RESOURCE_DIMENSION_UNKNOWN = auto() D3D10_RESOURCE_DIMENSION_BUFFER = auto() D3D10_RESOURCE_DIMENSION_TEXTURE1D = auto() D3D10_RESOURCE_DIMENSION_TEXTURE2D = auto() D3D10_RESOURCE_DIMENSION_TEXTURE3D = auto() class DXGI_FORMAT(IntEnumFromZero): DXGI_FORMAT_UNKNOWN = auto() DXGI_FORMAT_R32G32B32A32_TYPELESS = auto() DXGI_FORMAT_R32G32B32A32_FLOAT = auto() DXGI_FORMAT_R32G32B32A32_UINT = auto() DXGI_FORMAT_R32G32B32A32_SINT = auto() DXGI_FORMAT_R32G32B32_TYPELESS = auto() DXGI_FORMAT_R32G32B32_FLOAT = auto() DXGI_FORMAT_R32G32B32_UINT = auto() DXGI_FORMAT_R32G32B32_SINT = auto() DXGI_FORMAT_R16G16B16A16_TYPELESS = auto() DXGI_FORMAT_R16G16B16A16_FLOAT = auto() DXGI_FORMAT_R16G16B16A16_UNORM = auto() DXGI_FORMAT_R16G16B16A16_UINT = auto() DXGI_FORMAT_R16G16B16A16_SNORM = auto() DXGI_FORMAT_R16G16B16A16_SINT = auto() DXGI_FORMAT_R32G32_TYPELESS = auto() DXGI_FORMAT_R32G32_FLOAT = auto() DXGI_FORMAT_R32G32_UINT = auto() DXGI_FORMAT_R32G32_SINT = auto() DXGI_FORMAT_R32G8X24_TYPELESS = auto() DXGI_FORMAT_D32_FLOAT_S8X24_UINT = auto() DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = auto() DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = auto() DXGI_FORMAT_R10G10B10A2_TYPELESS = auto() DXGI_FORMAT_R10G10B10A2_UNORM = auto() DXGI_FORMAT_R10G10B10A2_UINT = auto() DXGI_FORMAT_R11G11B10_FLOAT = auto() DXGI_FORMAT_R8G8B8A8_TYPELESS = auto() DXGI_FORMAT_R8G8B8A8_UNORM = auto() DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = auto() DXGI_FORMAT_R8G8B8A8_UINT = auto() DXGI_FORMAT_R8G8B8A8_SNORM = auto() DXGI_FORMAT_R8G8B8A8_SINT = auto() DXGI_FORMAT_R16G16_TYPELESS = auto() DXGI_FORMAT_R16G16_FLOAT = auto() DXGI_FORMAT_R16G16_UNORM = auto() DXGI_FORMAT_R16G16_UINT = auto() DXGI_FORMAT_R16G16_SNORM = auto() DXGI_FORMAT_R16G16_SINT = auto() DXGI_FORMAT_R32_TYPELESS = auto() DXGI_FORMAT_D32_FLOAT = auto() DXGI_FORMAT_R32_FLOAT = auto() DXGI_FORMAT_R32_UINT = auto() DXGI_FORMAT_R32_SINT = auto() DXGI_FORMAT_R24G8_TYPELESS = auto() DXGI_FORMAT_D24_UNORM_S8_UINT = auto() DXGI_FORMAT_R24_UNORM_X8_TYPELESS = auto() DXGI_FORMAT_X24_TYPELESS_G8_UINT = auto() DXGI_FORMAT_R8G8_TYPELESS = auto() DXGI_FORMAT_R8G8_UNORM = auto() DXGI_FORMAT_R8G8_UINT = auto() DXGI_FORMAT_R8G8_SNORM = auto() DXGI_FORMAT_R8G8_SINT = auto() DXGI_FORMAT_R16_TYPELESS = auto() DXGI_FORMAT_R16_FLOAT = auto() DXGI_FORMAT_D16_UNORM = auto() DXGI_FORMAT_R16_UNORM = auto() DXGI_FORMAT_R16_UINT = auto() DXGI_FORMAT_R16_SNORM = auto() DXGI_FORMAT_R16_SINT = auto() DXGI_FORMAT_R8_TYPELESS = auto() DXGI_FORMAT_R8_UNORM = auto() DXGI_FORMAT_R8_UINT = auto() DXGI_FORMAT_R8_SNORM = auto() DXGI_FORMAT_R8_SINT = auto() DXGI_FORMAT_A8_UNORM = auto() DXGI_FORMAT_R1_UNORM = auto() DXGI_FORMAT_R9G9B9E5_SHAREDEXP = auto() DXGI_FORMAT_R8G8_B8G8_UNORM = auto() DXGI_FORMAT_G8R8_G8B8_UNORM = auto() DXGI_FORMAT_BC1_TYPELESS = auto() DXGI_FORMAT_BC1_UNORM = auto() DXGI_FORMAT_BC1_UNORM_SRGB = auto() DXGI_FORMAT_BC2_TYPELESS = auto() DXGI_FORMAT_BC2_UNORM = auto() DXGI_FORMAT_BC2_UNORM_SRGB = auto() DXGI_FORMAT_BC3_TYPELESS = auto() DXGI_FORMAT_BC3_UNORM = auto() DXGI_FORMAT_BC3_UNORM_SRGB = auto() DXGI_FORMAT_BC4_TYPELESS = auto() DXGI_FORMAT_BC4_UNORM = auto() DXGI_FORMAT_BC4_SNORM = auto() DXGI_FORMAT_BC5_TYPELESS = auto() DXGI_FORMAT_BC5_UNORM = auto() DXGI_FORMAT_BC5_SNORM = auto() DXGI_FORMAT_B5G6R5_UNORM = auto() DXGI_FORMAT_B5G5R5A1_UNORM = auto() DXGI_FORMAT_B8G8R8A8_UNORM = auto() DXGI_FORMAT_B8G8R8X8_UNORM = auto() DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = auto() DXGI_FORMAT_B8G8R8A8_TYPELESS = auto() DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = auto() DXGI_FORMAT_B8G8R8X8_TYPELESS = auto() DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = auto() DXGI_FORMAT_BC6H_TYPELESS = auto() DXGI_FORMAT_BC6H_UF16 = auto() DXGI_FORMAT_BC6H_SF16 = auto() DXGI_FORMAT_BC7_TYPELESS = auto() DXGI_FORMAT_BC7_UNORM = auto() DXGI_FORMAT_BC7_UNORM_SRGB = auto() DXGI_FORMAT_AYUV = auto() DXGI_FORMAT_Y410 = auto() DXGI_FORMAT_Y416 = auto() DXGI_FORMAT_NV12 = auto() DXGI_FORMAT_P010 = auto() DXGI_FORMAT_P016 = auto() DXGI_FORMAT_420_OPAQUE = auto() DXGI_FORMAT_YUY2 = auto() DXGI_FORMAT_Y210 = auto() DXGI_FORMAT_Y216 = auto() DXGI_FORMAT_NV11 = auto() DXGI_FORMAT_AI44 = auto() DXGI_FORMAT_IA44 = auto() DXGI_FORMAT_P8 = auto() DXGI_FORMAT_A8P8 = auto() DXGI_FORMAT_B4G4R4A4_UNORM = auto() DXGI_FORMAT_P208 = auto() DXGI_FORMAT_V208 = auto() DXGI_FORMAT_V408 = auto() DXGI_FORMAT_FORCE_UINT = auto() def DataclassFromBytes(dataclass): class LoadableDataclass(dataclass): def __init__(self, bytes=None): super(LoadableDataclass, self).__init__() if bytes: self.fromBytes(bytes) def fromStream(self, byteStream): self.fromBytes(byteStream.read(struct.calcsize(self.structFormat))) def fromBytes(self, bytes): loaded = struct.unpack(self.structFormat, bytes) fields = dataclasses.fields(self) memberIndex = 0 for field in fields: if field.metadata and "count" in field.metadata: # We have a list listed = field.type.__args__[0] myList = [] for i in range(field.metadata["count"]): myList.append(listed(loaded[memberIndex])) memberIndex += 1 self.__dict__[field.name] = myList else: self.__dict__[field.name] = field.type(loaded[memberIndex]) memberIndex += 1 return LoadableDataclass @DataclassFromBytes @dataclasses.dataclass class DDS_PIXELFORMAT: structFormat: ClassVar[str] = "> rShift) * multiplier) // rDivisor else: red = 0 if gIntMask: green = (((gIntMask & pixel) >> gShift) * multiplier) // gDivisor else: green = 0 if bIntMask: blue = (((bIntMask & pixel) >> bShift) * multiplier) // bDivisor else: blue = 0 if aIntMask: alpha = (((aIntMask & pixel) >> aShift) * multiplier) // aDivisor else: alpha = multiplier struct.pack_into(packFormat, repacked, repackIndex, blue, green, red, alpha) repackIndex += biggestComponent * 4 return bytes(repacked) return (convert, glInternalFormat, glFormat, glType) def getGLFormat(pixelFormat, dxt10Header=None): # Half or more of this function may be unreachable or otherwise redundant. glInternalFormat = None dxgiFormat = None if dxt10Header: dxgiFormat = dxt10Header.dxgiFormat flags = pixelFormat.dwFlags if flags & pixelFormat.Flags.DDPF_FOURCC: fourCC = pixelFormat.dwFourCC if fourCC == b"DX10" and not dxt10Header: raise UnsupportedDDSFormatException() dxgiFormat = dxt10Header.dxgiFormat if dxt10Header else fourCCToDXGI(fourCC) if dxgiFormat: return dxgiToGL[dxgiFormat] elif flags & ( pixelFormat.Flags.DDPF_ALPHA | pixelFormat.Flags.DDPF_RGB | pixelFormat.Flags.DDPF_YUV | pixelFormat.Flags.DDPF_LUMINANCE): compressed = False rBitmask = None gBitmask = None bBitmask = None aBitmask = None lumBitmask = None if flags & (pixelFormat.Flags.DDPF_ALPHA | pixelFormat.Flags.DDPF_ALPHAPIXELS): aBitmask = pixelFormat.dwABitMask if flags & (pixelFormat.Flags.DDPF_RGB | pixelFormat.Flags.DDPF_YUV): rBitmask = pixelFormat.dwRBitMask gBitmask = pixelFormat.dwGBitMask bBitmask = pixelFormat.dwBBitMask if flags & pixelFormat.Flags.DDPF_LUMINANCE: lumBitmask = pixelFormat.dwRBitMask def bitCount(theBytes): count = 0 for byte in theBytes: count += bin(byte).count("1") return count def firstBit(theBytes): index = 0 for byte in theBytes: if byte != 0: return index + format(byte, 'b').find("1") index += 8 bitCounts = dict() starts = dict() namedBitmasks = dict() if rBitmask: bitCounts["r"] = bitCount(rBitmask) starts["r"] = firstBit(rBitmask) namedBitmasks["r"] = rBitmask if gBitmask: bitCounts["g"] = bitCount(gBitmask) starts["g"] = firstBit(gBitmask) namedBitmasks["g"] = gBitmask if bBitmask: bitCounts["b"] = bitCount(bBitmask) starts["b"] = firstBit(bBitmask) namedBitmasks["b"] = bBitmask if aBitmask: bitCounts["a"] = bitCount(aBitmask) starts["a"] = firstBit(aBitmask) namedBitmasks["a"] = aBitmask if lumBitmask: bitCounts["luminance"] = bitCount(lumBitmask) starts["luminance"] = firstBit(lumBitmask) namedBitmasks["luminance"] = lumBitmask toSort = [] for key in starts: toSort.append((starts[key], key)) toSort.sort() glInternalFormatName = ["GL"] glRequirements = None desc = "GL_" lastBitCount = -1 usedBitCounts = set() for pos, channel in toSort: if lastBitCount != bitCounts[channel]: if lastBitCount != -1: desc += str(lastBitCount) lastBitCount = bitCounts[channel] usedBitCounts.add(lastBitCount) desc += channel if len(usedBitCounts) != 1: desc += str(lastBitCount) desc = desc.upper() glFormat = GL_IMAGE_FORMAT[desc] if desc in GL_IMAGE_FORMAT.__members__ else None numComponents = len(toSort) byteCount = (pixelFormat.dwRGBBitCount + 7) // 8 needsConversion = False if len(usedBitCounts) == 1: if lastBitCount == 8: glType = GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE elif lastBitCount == 16: glType = GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT elif lastBitCount == 32: glType = GL_IMAGE_FORMAT.GL_UNSIGNED_INT elif lastBitCount == 4: if numComponents == 4: glType = GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT_4_4_4_4_REV else: needsConversion = True if not glFormat or numComponents * lastBitCount != pixelFormat.dwRGBBitCount: needsConversion = True if needsConversion: convert, glInternalFormat, glFormat, glType = buildConverter(byteCount, bitmasks=namedBitmasks) else: convert = None if not glInternalFormat: glInternalFormatName = "_".join(glInternalFormatName) glInternalFormat = GL_IMAGE_FORMAT[ glInternalFormatName] if glInternalFormatName in GL_IMAGE_FORMAT.__members__ else GL_IMAGE_FORMAT.GL_RGBA if compressed: return CompressedGLTextureFormat(glRequirements, glInternalFormat) else: return UncompressedGLTextureFormat(glRequirements, glInternalFormat, glFormat, glType, convert) def sizeFromFormat(dxgiFormat, width, height): blockCompressed = False if dxgiFormat in {DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB, DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM}: blockCompressed = True blockSize = 8 elif dxgiFormat in {DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB, DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB, DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM, DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16, DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16, DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB}: blockCompressed = True blockSize = 16 if blockCompressed: return max(1, ((width + 3) // 4)) * max(1, ((height + 3) // 4)) * blockSize if dxgiFormat <= DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: name = dxgiFormat.name + "_" count = 0 currentNum = "" for char in name: if char.isdecimal(): currentNum += char elif currentNum != "": count += int(currentNum) currentNum = "" pixelSize = (count + 7) // 8 return width * height * pixelSize pass