All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.DDSReader.kt Maven / Gradle / Ivy

The newest version!
package org.openrndr.dds

/**
 * DDS reader
 * This started as a copy of https://github.com/Mudbill/dds-lwjgl @ c16616c07c79c38a552cbbb4f46011c3ee601223
 * I subsequently converted it to Kotlin, simplified loader flow and added partial support for DXGI formats
 * E. Jakobs
 */

import org.openrndr.draw.*
import org.openrndr.utils.buffer.MPPBuffer
import kotlin.jvm.JvmRecord

private const val DDPF_ALPHAPIXELS = 0x1
private const val DDPF_ALPHA = 0x2
private const val DDPF_FOURCC = 0x4
private const val DDPF_RGB = 0x40
private const val DDPF_YUV = 0x200
private const val DDPF_LUMINANCE = 0x20000


class DDSPixelFormat(header: MPPBuffer) {
    var dwSize: Int = 0
    var dwFlags: Int = 0
    var dwFourCC: Int = 0
    var dwRGBBitCount: Int = 0
    var dwRBitMask: Int = 0
    var dwGBitMask: Int = 0
    var dwBBitMask: Int = 0
    var dwABitMask: Int = 0
    var sFourCC: String
    var isCompressed: Boolean = false
    var hasFlagAlphaPixels: Boolean = false
    var hasFlagAlpha: Boolean = false
    var hasFlagFourCC: Boolean = false
    var hasFlagRgb: Boolean = false
    var hasFlagYuv: Boolean = false
    var hasFlagLuminance: Boolean = false

    init {
        dwSize = header.int
        dwFlags = header.int
        dwFourCC = header.int
        dwRGBBitCount = header.int
        dwRBitMask = header.int
        dwGBitMask = header.int
        dwBBitMask = header.int
        dwABitMask = header.int

        if (dwSize != 32) throw RuntimeException("size is not 32 bytes (is $dwSize bytes)")

        hasFlagAlphaPixels = dwFlags and DDPF_ALPHAPIXELS == DDPF_ALPHAPIXELS
        hasFlagAlpha = dwFlags and DDPF_ALPHA == DDPF_ALPHA
        hasFlagFourCC = (dwFlags and DDPF_FOURCC) == DDPF_FOURCC
        hasFlagRgb = dwFlags and DDPF_RGB == DDPF_RGB
        hasFlagYuv = dwFlags and DDPF_YUV == DDPF_YUV
        hasFlagLuminance = dwFlags and DDPF_LUMINANCE == DDPF_LUMINANCE

        sFourCC = if (hasFlagFourCC) createFourCCString(dwFourCC) else ""

        if (hasFlagFourCC) {
            isCompressed = true
        } else if (hasFlagRgb) {
            isCompressed = false
        }
    }

    private fun createFourCCString(fourCC: Int): String {
        val fourCCString = CharArray(DDPF_FOURCC)
        for (i in fourCCString.indices) fourCCString[i] = ((fourCC shr i * 8) and 0xff).toChar()
        return fourCCString.concatToString()
    }
}


private const val DDS_RESOURCE_MISC_TEXTURECUBE = 0x4

private const val DDS_DIMENSION_TEXTURE1D = 2
private const val DDS_DIMENSION_TEXTURE2D = 3
private const val DDS_DIMENSION_TEXTURE3D = 4

private const val DDS_ALPHA_MODE_UNKNOWN = 0x0
private const val DDS_ALPHA_MODE_STRAIGHT = 0x1
private const val DDS_ALPHA_MODE_PREMULTIPLIED = 0x2
private const val DDS_ALPHA_MODE_OPAQUE = 0x3
private const val DDS_ALPHA_MODE_CUSTOM = 0x4

private const val DXGI_FORMAT_UNKNOWN = 0
private const val DXGI_FORMAT_R32G32B32A32_TYPELESS = 1
private const val DXGI_FORMAT_R32G32B32A32_FLOAT = 2
private const val DXGI_FORMAT_R32G32B32A32_UINT = 3
private const val DXGI_FORMAT_R32G32B32A32_SINT = 4
private const val DXGI_FORMAT_R32G32B32_TYPELESS = 5
private const val DXGI_FORMAT_R32G32B32_FLOAT = 6
private const val DXGI_FORMAT_R32G32B32_UINT = 7
private const val DXGI_FORMAT_R32G32B32_SINT = 8
private const val DXGI_FORMAT_R16G16B16A16_TYPELESS = 9
private const val DXGI_FORMAT_R16G16B16A16_FLOAT = 10
private const val DXGI_FORMAT_R16G16B16A16_UNORM = 11
private const val DXGI_FORMAT_R16G16B16A16_UINT = 12
private const val DXGI_FORMAT_R16G16B16A16_SNORM = 13
private const val DXGI_FORMAT_R16G16B16A16_SINT = 14
private const val DXGI_FORMAT_R32G32_TYPELESS = 15
private const val DXGI_FORMAT_R32G32_FLOAT = 16
private const val DXGI_FORMAT_R32G32_UINT = 17
private const val DXGI_FORMAT_R32G32_SINT = 18
private const val DXGI_FORMAT_R32G8X24_TYPELESS = 19
private const val DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20
private const val DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21
private const val DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22
private const val DXGI_FORMAT_R10G10B10A2_TYPELESS = 23
private const val DXGI_FORMAT_R10G10B10A2_UNORM = 24
private const val DXGI_FORMAT_R10G10B10A2_UINT = 25
private const val DXGI_FORMAT_R11G11B10_FLOAT = 26
private const val DXGI_FORMAT_R8G8B8A8_TYPELESS = 27
private const val DXGI_FORMAT_R8G8B8A8_UNORM = 28
private const val DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29
private const val DXGI_FORMAT_R8G8B8A8_UINT = 30
private const val DXGI_FORMAT_R8G8B8A8_SNORM = 31
private const val DXGI_FORMAT_R8G8B8A8_SINT = 32
private const val DXGI_FORMAT_R16G16_TYPELESS = 33
private const val DXGI_FORMAT_R16G16_FLOAT = 34
private const val DXGI_FORMAT_R16G16_UNORM = 35
private const val DXGI_FORMAT_R16G16_UINT = 36
private const val DXGI_FORMAT_R16G16_SNORM = 37
private const val DXGI_FORMAT_R16G16_SINT = 38
private const val DXGI_FORMAT_R32_TYPELESS = 39
private const val DXGI_FORMAT_D32_FLOAT = 40
private const val DXGI_FORMAT_R32_FLOAT = 41
private const val DXGI_FORMAT_R32_UINT = 42
private const val DXGI_FORMAT_R32_SINT = 43
private const val DXGI_FORMAT_R24G8_TYPELESS = 44
private const val DXGI_FORMAT_D24_UNORM_S8_UINT = 45
private const val DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46
private const val DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47
private const val DXGI_FORMAT_R8G8_TYPELESS = 48
private const val DXGI_FORMAT_R8G8_UNORM = 49
private const val DXGI_FORMAT_R8G8_UINT = 50
private const val DXGI_FORMAT_R8G8_SNORM = 51
private const val DXGI_FORMAT_R8G8_SINT = 52
private const val DXGI_FORMAT_R16_TYPELESS = 53
private const val DXGI_FORMAT_R16_FLOAT = 54
private const val DXGI_FORMAT_D16_UNORM = 55
private const val DXGI_FORMAT_R16_UNORM = 56
private const val DXGI_FORMAT_R16_UINT = 57
private const val DXGI_FORMAT_R16_SNORM = 58
private const val DXGI_FORMAT_R16_SINT = 59
private const val DXGI_FORMAT_R8_TYPELESS = 60
private const val DXGI_FORMAT_R8_UNORM = 61
private const val DXGI_FORMAT_R8_UINT = 62
private const val DXGI_FORMAT_R8_SNORM = 63
private const val DXGI_FORMAT_R8_SINT = 64
private const val DXGI_FORMAT_A8_UNORM = 65
private const val DXGI_FORMAT_R1_UNORM = 66
private const val DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67
private const val DXGI_FORMAT_R8G8_B8G8_UNORM = 68
private const val DXGI_FORMAT_G8R8_G8B8_UNORM = 69
private const val DXGI_FORMAT_BC1_TYPELESS = 70
private const val DXGI_FORMAT_BC1_UNORM = 71
private const val DXGI_FORMAT_BC1_UNORM_SRGB = 72
private const val DXGI_FORMAT_BC2_TYPELESS = 73
private const val DXGI_FORMAT_BC2_UNORM = 74
private const val DXGI_FORMAT_BC2_UNORM_SRGB = 75
private const val DXGI_FORMAT_BC3_TYPELESS = 76
private const val DXGI_FORMAT_BC3_UNORM = 77
private const val DXGI_FORMAT_BC3_UNORM_SRGB = 78
private const val DXGI_FORMAT_BC4_TYPELESS = 79
private const val DXGI_FORMAT_BC4_UNORM = 80
private const val DXGI_FORMAT_BC4_SNORM = 81
private const val DXGI_FORMAT_BC5_TYPELESS = 82
private const val DXGI_FORMAT_BC5_UNORM = 83
private const val DXGI_FORMAT_BC5_SNORM = 84
private const val DXGI_FORMAT_B5G6R5_UNORM = 85
private const val DXGI_FORMAT_B5G5R5A1_UNORM = 86
private const val DXGI_FORMAT_B8G8R8A8_UNORM = 87
private const val DXGI_FORMAT_B8G8R8X8_UNORM = 88
private const val DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89
private const val DXGI_FORMAT_B8G8R8A8_TYPELESS = 90
private const val DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91
private const val DXGI_FORMAT_B8G8R8X8_TYPELESS = 92
private const val DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93
private const val DXGI_FORMAT_BC6H_TYPELESS = 94
private const val DXGI_FORMAT_BC6H_UF16 = 95
private const val DXGI_FORMAT_BC6H_SF16 = 96
private const val DXGI_FORMAT_BC7_TYPELESS = 97
private const val DXGI_FORMAT_BC7_UNORM = 98
private const val DXGI_FORMAT_BC7_UNORM_SRGB = 99
private const val DXGI_FORMAT_AYUV = 100
private const val DXGI_FORMAT_Y410 = 101
private const val DXGI_FORMAT_Y416 = 102
private const val DXGI_FORMAT_NV12 = 103
private const val DXGI_FORMAT_P010 = 104
private const val DXGI_FORMAT_P016 = 105
private const val DXGI_FORMAT_420_OPAQUE = 106
private const val DXGI_FORMAT_YUY2 = 107
private const val DXGI_FORMAT_Y210 = 108
private const val DXGI_FORMAT_Y216 = 109
private const val DXGI_FORMAT_NV11 = 110
private const val DXGI_FORMAT_AI44 = 111
private const val DXGI_FORMAT_IA44 = 112
private const val DXGI_FORMAT_P8 = 113
private const val DXGI_FORMAT_A8P8 = 114
private const val DXGI_FORMAT_B4G4R4A4_UNORM = 115
private const val DXGI_FORMAT_P208 = 130
private const val DXGI_FORMAT_V208 = 131
private const val DXGI_FORMAT_V408 = 132
private const val DXGI_FORMAT_FORCE_UINT = 0xffffffff

private class DDSHeaderDXT10(header: MPPBuffer) {
    var dxgiFormat: Int = 0
    var resourceDimension: Int = 0
    var miscFlag: Int = 0
    var arraySize: Int = 0
    var miscFlags2: Int = 0

    init {
        dxgiFormat = header.int
        resourceDimension = header.int
        miscFlag = header.int
        arraySize = header.int
        miscFlags2 = header.int
    }
}

private const val DDSD_CAPS = 0x000001
private const val DDSD_HEIGHT = 0x000002
private const val DDSD_WIDTH = 0x000004
private const val DDSD_PITCH = 0x000008
private const val DDSD_PIXELFORMAT = 0x001000
private const val DDSD_MIPMAPCOUNT = 0x020000
private const val DDSD_LINEARSIZE = 0x080000
private const val DDSD_DEPTH = 0x800000

private const val DDSCAPS_COMPLEX = 0x8
private const val DDSCAPS_MIPMAP = 0x400000
private const val DDSCAPS_TEXTURE = 0x1000

private const val DDSCAPS2_CUBEMAP = 0x200
private const val DDSCAPS2_CUBEMAP_POSITIVEX = 0x400
private const val DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800
private const val DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000
private const val DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000
private const val DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000
private const val DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000
private const val DDSCAPS2_VOLUME = 0x200000

private class DDSHeader(header: MPPBuffer) {
    var size: Int = 0
    var flags: Int = 0
    var height: Int = 0
    var width: Int = 0
    var pitchOrLinearSize: Int = 0
    var depth: Int = 0
    var mipmapCount: Int = 0
    var reserved = IntArray(11)
    var pixelFormat: DDSPixelFormat
    var caps: Int = 0
    var caps2: Int = 0
    var caps3: Int = 0
    var caps4: Int = 0
    var reserved2: Int = 0
    var hasFlagMipMapCount: Boolean = false
    var hasFlagCaps: Boolean = false
    var hasFlagHeight: Boolean = false
    var hasFlagWidth: Boolean = false
    var hasFlagPitch: Boolean = false
    var hasFlagPixelFormat: Boolean = false
    var hasFlagLinearSize: Boolean = false
    var hasFlagDepth: Boolean = false
    var hasCapsComplex: Boolean = false
    var hasCapsMipMap: Boolean = false
    var hasCapsTexture: Boolean = false
    var hasCaps2CubeMap: Boolean = false
    var hasCaps2CubeMapPX: Boolean = false
    var hasCaps2CubeMapNX: Boolean = false
    var hasCaps2CubeMapPY: Boolean = false
    var hasCaps2CubeMapNY: Boolean = false
    var hasCaps2CubeMapPZ: Boolean = false
    var hasCaps2CubeMapNZ: Boolean = false
    var hasCaps2Volume: Boolean = false

    init {
        if (header.capacity() != 124) {
            throw RuntimeException("Expected header size of 124 bytes (is ${header.capacity()} bytes")
        }

        size = header.int
        flags = header.int
        height = header.int
        width = header.int
        pitchOrLinearSize = header.int
        depth = header.int
        mipmapCount = header.int

        for (i in reserved.indices) {
            reserved[i] = header.int
        }

        pixelFormat = DDSPixelFormat(header)

        caps = header.int
        caps2 = header.int
        caps3 = header.int
        caps4 = header.int
        reserved2 = header.int

        hasFlagCaps = flags and DDSD_CAPS == DDSD_CAPS
        hasFlagHeight = flags and DDSD_HEIGHT == DDSD_HEIGHT
        hasFlagWidth = flags and DDSD_WIDTH == DDSD_WIDTH
        hasFlagPitch = flags and DDSD_PITCH == DDSD_PITCH
        hasFlagPixelFormat = flags and DDSD_PIXELFORMAT == DDSD_PIXELFORMAT
        hasFlagMipMapCount = flags and DDSD_MIPMAPCOUNT == DDSD_MIPMAPCOUNT
        hasFlagLinearSize = flags and DDSD_LINEARSIZE == DDSD_LINEARSIZE
        hasFlagDepth = flags and DDSD_DEPTH == DDSD_DEPTH

        hasCapsComplex = caps and DDSCAPS_COMPLEX == DDSCAPS_COMPLEX
        hasCapsMipMap = caps and DDSCAPS_MIPMAP == DDSCAPS_MIPMAP
        hasCapsTexture = caps and DDSCAPS_TEXTURE == DDSCAPS_TEXTURE

        hasCaps2CubeMap = caps2 and DDSCAPS2_CUBEMAP == DDSCAPS2_CUBEMAP
        hasCaps2CubeMapPX = caps2 and DDSCAPS2_CUBEMAP_POSITIVEX == DDSCAPS2_CUBEMAP_POSITIVEX
        hasCaps2CubeMapNX = caps2 and DDSCAPS2_CUBEMAP_NEGATIVEX == DDSCAPS2_CUBEMAP_NEGATIVEX
        hasCaps2CubeMapPY = caps2 and DDSCAPS2_CUBEMAP_POSITIVEY == DDSCAPS2_CUBEMAP_POSITIVEY
        hasCaps2CubeMapNY = caps2 and DDSCAPS2_CUBEMAP_NEGATIVEY == DDSCAPS2_CUBEMAP_NEGATIVEY
        hasCaps2CubeMapPZ = caps2 and DDSCAPS2_CUBEMAP_POSITIVEZ == DDSCAPS2_CUBEMAP_POSITIVEZ
        hasCaps2CubeMapNZ = caps2 and DDSCAPS2_CUBEMAP_NEGATIVEZ == DDSCAPS2_CUBEMAP_NEGATIVEZ
        hasCaps2Volume = caps2 and DDSCAPS2_VOLUME == DDSCAPS2_VOLUME

        if (!hasFlagCaps || !hasFlagHeight || !hasFlagWidth || !hasFlagPixelFormat) {
            throw  RuntimeException("missing required flags")
        }
        if (!hasCapsTexture) {
            throw RuntimeException("missing required caps")
        }
    }

    override fun toString(): String {
        return "DDSHeader(size=$size, flags=$flags, height=$height, width=$width, pitchOrLinearSize=$pitchOrLinearSize, depth=$depth, mipmapCount=$mipmapCount, reserved=${reserved.contentToString()}, pixelFormat=$pixelFormat, caps=$caps, caps2=$caps2, caps3=$caps3, caps4=$caps4, reserved2=$reserved2, hasFlagMipMapCount=$hasFlagMipMapCount, hasFlagCaps=$hasFlagCaps, hasFlagHeight=$hasFlagHeight, hasFlagWidth=$hasFlagWidth, hasFlagPitch=$hasFlagPitch, hasFlagPixelFormat=$hasFlagPixelFormat, hasFlagLinearSize=$hasFlagLinearSize, hasFlagDepth=$hasFlagDepth, hasCapsComplex=$hasCapsComplex, hasCapsMipMap=$hasCapsMipMap, hasCapsTexture=$hasCapsTexture, hasCaps2CubeMap=$hasCaps2CubeMap, hasCaps2CubeMapPX=$hasCaps2CubeMapPX, hasCaps2CubeMapNX=$hasCaps2CubeMapNX, hasCaps2CubeMapPY=$hasCaps2CubeMapPY, hasCaps2CubeMapNY=$hasCaps2CubeMapNY, hasCaps2CubeMapPZ=$hasCaps2CubeMapPZ, hasCaps2CubeMapNZ=$hasCaps2CubeMapNZ, hasCaps2Volume=$hasCaps2Volume)"
    }

}

private const val DDS_MAGIC = 0x20534444

@JvmRecord
data class DDSData(
    val format: ColorFormat,
    val type: ColorType,
    val width: Int,
    val height: Int,
    val mipmaps: Int,
    val cubeMap: Boolean,
    val bdata: List,
    val bdata2: List,
    val flipV: Boolean
) {
    fun image(level: Int): MPPBuffer {
        return if (level == 0) bdata[0] else bdata2[level - 1]
    }

    fun sidePX(level: Int = 0): MPPBuffer = if (level == 0) bdata[0] else bdata2[(level - 1) + 0 * (mipmaps - 1)]
    fun sideNX(level: Int = 0): MPPBuffer = if (level == 0) bdata[1] else bdata2[(level - 1) + 1 * (mipmaps - 1)]
    fun sidePY(level: Int = 0): MPPBuffer = if (level == 0) bdata[2] else bdata2[(level - 1) + 2 * (mipmaps - 1)]
    fun sideNY(level: Int = 0): MPPBuffer = if (level == 0) bdata[3] else bdata2[(level - 1) + 3 * (mipmaps - 1)]
    fun sidePZ(level: Int = 0): MPPBuffer = if (level == 0) bdata[4] else bdata2[(level - 1) + 4 * (mipmaps - 1)]
    fun sideNZ(level: Int = 0): MPPBuffer = if (level == 0) bdata[5] else bdata2[(level - 1) + 5 * (mipmaps - 1)]

    fun side(cubemapSide: CubemapSide, level: Int) =
        when (cubemapSide) {
            CubemapSide.POSITIVE_X -> sidePX(level)
            CubemapSide.POSITIVE_Y -> sidePY(level)
            CubemapSide.POSITIVE_Z -> sidePZ(level)
            CubemapSide.NEGATIVE_X -> sideNX(level)
            CubemapSide.NEGATIVE_Y -> sideNY(level)
            CubemapSide.NEGATIVE_Z -> sideNZ(level)
        }

    fun toColorBuffer(session: Session? = Session.active): ColorBuffer {
        require(!cubeMap)
        val cb = colorBuffer(width, height, 1.0, format, type, levels = mipmaps, session = session).apply {
            this.flipV = [email protected]
        }
        for (level in 0 until mipmaps) {
            val div = 1 shl level
            cb.write(image(0), format, type, width = width / div, height = height / div, level = level)
        }
        return cb
    }

    fun toCubemap(session: Session? = Session.active): Cubemap {
        require(cubeMap)
        val cm = cubemap(width, format, type, mipmaps, session)
        for (level in 0 until mipmaps) {
            val levelWidth = width / (1 shl level)
            for (side in CubemapSide.values()) {
                cm.write(side, side(side, level), format, type, x = 0, y = 0, width = levelWidth, height = levelWidth, level = level)
            }
        }

        if (mipmaps == 1) {
            cm.generateMipmaps()
        }

        cm.filter(MinifyingFilter.LINEAR, MagnifyingFilter.LINEAR)
        return cm
    }
}

fun loadDDS(data: MPPBuffer, bgrIsRgb: Boolean = false): DDSData {
    val primarySurfaces = mutableListOf()
    val secondarySurfaces = mutableListOf()

    run {
        val inputData = data
        var totalByteCount = inputData.capacity()
        val bMagic = ByteArray(4)
        inputData.get(bMagic)

        val magic = MPPBuffer.createFrom(bMagic).int
        if (magic != DDS_MAGIC) {
            throw RuntimeException("mismatch in magic word, not a dds file")
        }

        val bHeader = ByteArray(124)
        inputData.get(bHeader)
        val header = DDSHeader(MPPBuffer.createFrom(bHeader))

        val format: ColorFormat
        val type: ColorType

        if (header.pixelFormat.sFourCC.equals("DXT1", ignoreCase = true)) {
            type = ColorType.DXT1
            format = if (header.pixelFormat.hasFlagAlpha) {
                ColorFormat.RGBa
            } else {
                ColorFormat.RGB
            }
        } else if (header.pixelFormat.sFourCC.equals("DXT3", ignoreCase = true)) {
            type = ColorType.DXT3
            format = ColorFormat.RGBa
        } else if (header.pixelFormat.sFourCC.equals("DXT5", ignoreCase = true)) {
            type = ColorType.DXT5
            format = ColorFormat.RGBa
        } else if (header.pixelFormat.sFourCC.equals("DX10", ignoreCase = true)) {
            val dxt10HeaderArray = ByteArray(20)
            inputData.get(dxt10HeaderArray)
            val dxt10Header = DDSHeaderDXT10(MPPBuffer.createFrom(dxt10HeaderArray))

            when (dxt10Header.dxgiFormat) {
                DXGI_FORMAT_R16G16B16A16_FLOAT -> {
                    format = ColorFormat.RGBa
                    type = ColorType.FLOAT16
                }
                DXGI_FORMAT_R32G32B32A32_FLOAT -> {
                    format = ColorFormat.RGBa
                    type = ColorType.FLOAT32
                }
                else -> {
                    error("unsupported dxgi format: ${dxt10Header.dxgiFormat}")
                }
            }
        } else if (header.pixelFormat.dwRGBBitCount == 24 && header.pixelFormat.dwRBitMask == (0xff0000) && header.pixelFormat.dwGBitMask == 0x00ff00 && header.pixelFormat.dwBBitMask == 0x0000ff) {
            format = if (bgrIsRgb) ColorFormat.RGB else ColorFormat.BGR
            type = ColorType.UINT8
        } else {
            throw RuntimeException("unknown and/or unsupported format ${header.pixelFormat.sFourCC}")
        }

        val surfaceCount: Int
        totalByteCount -= 128

        val isCubeMap: Boolean
        if (header.hasCaps2CubeMap) {
            surfaceCount = 6
            isCubeMap = true
        } else {
            surfaceCount = 1
            isCubeMap = false
        }

        fun size(level: Int): Int {
            val div = (1 shl level)
            val width = header.width / div
            val height = header.height / div
            return when (type) {
                ColorType.DXT1 -> (width * height) / 2
                ColorType.DXT3, ColorType.DXT5 -> (width * height)
                else -> (header.pitchOrLinearSize * header.height) shl level
            }
        }

        val primarySize = size(0)

        require(primarySize > 0) {
            """size of surface is 0 bytes $header"""
        }
        for (i in 0 until surfaceCount) {
            require(inputData.remaining() >= primarySize) {
                "source byte buffer only has ${inputData.remaining()} bytes left, need $primarySize, $format/$type"
            }
            val bytes = ByteArray(primarySize)
            inputData.get(bytes)
            totalByteCount -= bytes.size
            primarySurfaces.add(MPPBuffer.createFrom(bytes))

            if (header.hasFlagMipMapCount) {
                for (level in 1 until header.mipmapCount) {
                    val secondarySize = size(level)
                    val bytes2 = ByteArray(secondarySize)
                    inputData.get(bytes2)
                    totalByteCount -= bytes2.size
                    secondarySurfaces.add(MPPBuffer.createFrom(bytes2))
                }
            }
        }
        return DDSData(
            format,
            type,
            header.width,
            header.height,
            header.mipmapCount,
            isCubeMap,
            primarySurfaces,
            secondarySurfaces,
            true
        )
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy