aboutsummaryrefslogtreecommitdiff
path: root/libs/dds_header/src/DDS/DDSFile.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/dds_header/src/DDS/DDSFile.py')
-rw-r--r--libs/dds_header/src/DDS/DDSFile.py220
1 files changed, 0 insertions, 220 deletions
diff --git a/libs/dds_header/src/DDS/DDSFile.py b/libs/dds_header/src/DDS/DDSFile.py
deleted file mode 100644
index 4592074..0000000
--- a/libs/dds_header/src/DDS/DDSFile.py
+++ /dev/null
@@ -1,220 +0,0 @@
-import io
-
-from PyQt6.QtCore import QCoreApplication, qCritical, QFile, QIODeviceBase
-from PyQt6.QtOpenGL import QOpenGLTexture
-
-from . import DDSDefinitions
-from .glstuff import GLTextureFormat
-
-
-class DDSReadException(Exception):
- """Thrown if there was an error reading a DDS file"""
- pass
-
-
-ddsCubemapFaces = {
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEX: QOpenGLTexture.CubeMapFace.CubeMapPositiveX,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEX: QOpenGLTexture.CubeMapFace.CubeMapNegativeX,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEY: QOpenGLTexture.CubeMapFace.CubeMapPositiveY,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEY: QOpenGLTexture.CubeMapFace.CubeMapNegativeY,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEZ: QOpenGLTexture.CubeMapFace.CubeMapPositiveZ,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEZ: QOpenGLTexture.CubeMapFace.CubeMapNegativeZ}
-
-
-class DDSFile:
- def __init__(self, fileData: bytes, fileName: str):
- self.fileName = fileName
- self.header = DDSDefinitions.DDS_HEADER()
- self.dxt10Header = None
- self.glFormat: GLTextureFormat = None
- self.fileData = fileData
- self.data = None
- self.isCubemap = None
-
- @classmethod
- def fromFile(cls, fileName: str):
- file = QFile(fileName)
- if file.open(QIODeviceBase.OpenModeFlag.ReadOnly):
- fileData = file.readAll()
- else:
- raise DDSReadException()
- return cls(fileData.data(), fileName)
-
- def load(self):
- with io.BytesIO(self.fileData) as file:
- magicNumber = file.read(4)
- if magicNumber != DDSDefinitions.DDS_MAGIC_NUMBER:
- qCritical(self.tr("Magic number mismatch."))
- raise DDSReadException()
-
- self.header.fromStream(file)
-
- if self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_FOURCC:
- fourCC = self.header.ddspf.dwFourCC
- if fourCC == b"DX10":
- self.dxt10Header = DDSDefinitions.DDS_HEADER_DXT10()
- self.dxt10Header.fromStream(file)
- else:
- fourCC = None
-
- self.glFormat = DDSDefinitions.getGLFormat(self.header.ddspf, self.dxt10Header)
- self.data = []
- # Do this once per layer/mip level whatever, (times one per scanline if uncompressed). Also, potentially recompute this based on the format and size in case writers lie.
- # self.data.append(file.read(self.header.dwPitchOrLinearSize))
-
- layerCount = 1
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- self.isCubemap = True
- layerCount = 0
- for face in ddsCubemapFaces:
- if self.header.dwCaps2 & face:
- layerCount += 1
- else:
- self.isCubemap = False
-
- for layer in range(layerCount):
- nextWidth = self.header.dwWidth
- nextHeight = self.header.dwHeight
- mipCount = self.mipLevels()
- for level in range(mipCount):
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHA | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_RGB | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_YUV | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_LUMINANCE):
- size = nextWidth * nextHeight * ((self.header.ddspf.dwRGBBitCount + 7) // 8)
- elif fourCC:
- if self.dxt10Header:
- dxgiFormat = self.dxt10Header.dxgiFormat
- else:
- dxgiFormat = DDSDefinitions.fourCCToDXGI(fourCC)
- size = DDSDefinitions.sizeFromFormat(dxgiFormat, nextWidth, nextHeight)
- self.data.append(file.read(size))
- nextWidth = max(nextWidth // 2, 1)
- nextHeight = max(nextHeight // 2, 1)
-
- def getDescription(self):
- format = ""
- # DX10 header says the format enum
- if self.dxt10Header is not None:
- format = self.dxt10Header.dxgiFormat.name.replace("DXGI_FORMAT_", "")
- # Pixel Format says the FourCC
- elif self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_FOURCC:
- fourCC = self.header.ddspf.dwFourCC
- format = self.tr("{0} (equivalent to {1})").format(fourCC.decode('ascii'),
- DDSDefinitions.fourCCToDXGI(fourCC).name.replace(
- "DXGI_FORMAT_", ""))
- # We've got bitmasks for the colour channels
- else:
- # This could be prettier if there was logic to detect that certain common bitmasks represented things more easily represented, like RGBA8
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_RGB | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_YUV):
- format += self.tr("Red bitmask {0}, Green bitmask {1}, Blue bitmask {2}").format(
- self.header.ddspf.dwRBitMask.hex().upper(), self.header.ddspf.dwGBitMask.hex().upper(),
- self.header.ddspf.dwBBitMask.hex().upper())
- if self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_LUMINANCE:
- if format != "":
- format += ", "
- format += self.tr("Luminance bitmask {0}").format(self.header.ddspf.dwRBitMask.hex().upper())
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHA | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHAPIXELS):
- if format != "":
- format += ", "
- format += self.tr("Alpha bitmask {0}").format(self.header.ddspf.dwABitMask.hex().upper())
-
- size = self.tr("{0}×{1}").format(self.header.dwWidth, self.header.dwHeight)
-
- dimensions = self.tr("Cubemap") if self.isCubemap else self.tr("2D")
-
- mipmaps = self.tr("Mipmapped") if self.mipLevels() != 1 else self.tr("No mipmaps")
-
- return self.tr("{0}, {1} {2}, {3}").format(format, size, dimensions, mipmaps)
-
- def mipLevels(self):
- if self.header.dwFlags & DDSDefinitions.DDS_HEADER.Flags.DDSD_MIPMAPCOUNT:
- return self.header.dwMipMapCount
- else:
- return 1
-
- def asQOpenGLTexture(self, gl, context):
- if not self.data:
- return
-
- if self.glFormat.requirements:
- minVersion, extensions = self.glFormat.requirements
- glVersion = (gl.glGetIntegerv(gl.GL_MAJOR_VERSION), gl.glGetIntegerv(gl.GL_MINOR_VERSION))
- if glVersion < minVersion or minVersion < (1, 0):
- compatible = False
- for extension in extensions:
- if context.hasExtension(extension):
- compatible = True
- break
- if not compatible:
- qCritical(self.tr("OpenGL driver incompatible with texture format."))
- return None
-
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- texture = QOpenGLTexture(QOpenGLTexture.Target.TargetCubeMap)
- if self.header.dwWidth != self.header.dwHeight:
- qCritical(self.tr("Cubemap faces must be square"))
- return None
- else:
- # Assume GL_TEXTURE_2D for now
- texture = QOpenGLTexture(QOpenGLTexture.Target.Target2D)
- # Assume single layer for now
- # self.texture.setLayers(1)
- mipCount = self.mipLevels()
- texture.setAutoMipMapGenerationEnabled(False)
- texture.setMipLevels(mipCount)
- texture.setMipLevelRange(0, mipCount - 1)
- texture.setSize(self.header.dwWidth, self.header.dwHeight)
- texture.setFormat(QOpenGLTexture.TextureFormat(self.glFormat.internalFormat))
- texture.allocateStorage()
-
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- # Lisa hasn't whipped David Wang into shape yet. At least there are fewer bugs than under Raja.
- # The specific bug has been reported and AMD "will try to reproduce it soon"
- # MO 2.5.0: Radeon-specific code is causing crashing on the latest drivers
- # Some cubemaps fail to render with or without these modifications
- # noDSA = "Radeon" in gl.glGetString(gl.GL_RENDERER) and self.glFormat.compressed
- noDSA = False
- if noDSA:
- texture.bind()
- faceIndex = 0
- for face in ddsCubemapFaces:
- if self.header.dwCaps2 & face:
- for i in range(mipCount):
- if self.glFormat.compressed:
- if not noDSA:
- texture.setCompressedData(i, 0, ddsCubemapFaces[face],
- len(self.data[faceIndex * mipCount + i]),
- self.data[faceIndex * mipCount + i])
- else:
- gl.glCompressedTexSubImage2D(ddsCubemapFaces[face], i, 0, 0,
- max(self.header.dwWidth // 2 ** i, 1),
- max(self.header.dwHeight // 2 ** i, 1),
- self.glFormat.internalFormat,
- len(self.data[faceIndex * mipCount + i]),
- self.data[faceIndex * mipCount + i])
- else:
- texture.setData(i, 0, ddsCubemapFaces[face], QOpenGLTexture.PixelFormat(self.glFormat.format), QOpenGLTexture.PixelType(self.glFormat.type),
- self.glFormat.converter(self.data[faceIndex * mipCount + i]))
- faceIndex += 1
- if noDSA:
- texture.release()
- else:
- for i in range(mipCount):
- if self.glFormat.compressed:
- texture.setCompressedData(i, 0, len(self.data[i]), self.data[i])
- else:
-
- texture.setData(i, 0, QOpenGLTexture.PixelFormat(self.glFormat.format), QOpenGLTexture.PixelType(self.glFormat.type),
- self.glFormat.converter(self.data[i]))
-
- texture.setWrapMode(QOpenGLTexture.WrapMode.ClampToEdge)
-
- if self.glFormat.samplerType != "F":
- # integer textures can't be filtered
- texture.setMinMagFilters(QOpenGLTexture.Filter.NearestMipMapNearest, QOpenGLTexture.Filter.Nearest)
-
- return texture
-
- def tr(self, str):
- return QCoreApplication.translate("DDSFile", str)