Files
CavesOfSwift/Sources/JolkEngine/Loaders/DDSLoader.swift

240 lines
7.3 KiB
Swift

import Foundation
struct DDSLoader: LoaderProtocol
{
typealias T = Image
func load(url: URL) -> T?
{
guard let image = try? DDSLoader.read(url: url) else { return nil }
return image
}
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
static func read(url: URL) throws -> T
{
let file = try FileHandle(forReadingFrom: url)
let header = DDSHeader(
magic: .init(try file.read(upToCount: 4)),
size: try file.read(as: UInt32.self).littleEndian,
flags: .init(rawValue: try file.read(as: UInt32.self).littleEndian),
height: try file.read(as: UInt32.self).littleEndian,
width: try file.read(as: UInt32.self).littleEndian,
pitch: try file.read(as: UInt32.self).littleEndian,
depth: try file.read(as: UInt32.self).littleEndian,
mipNum: try file.read(as: UInt32.self).littleEndian,
reserved1: try (0..<11).map { _ in try file.read(as: UInt32.self) },
pixFmt: DDSPixelFormat(
size: try file.read(as: UInt32.self).littleEndian,
flags: .init(rawValue: try file.read(as: UInt32.self).littleEndian),
fourCC: .init(try file.read(upToCount: 4)),
bits: try file.read(as: UInt32.self).littleEndian,
rMask: try file.read(as: UInt32.self).littleEndian,
gMask: try file.read(as: UInt32.self).littleEndian,
bMask: try file.read(as: UInt32.self).littleEndian,
aMask: try file.read(as: UInt32.self).littleEndian),
caps1: try file.read(as: UInt32.self).littleEndian,
caps2: try file.read(as: UInt32.self).littleEndian,
caps3: try file.read(as: UInt32.self).littleEndian,
caps4: try file.read(as: UInt32.self).littleEndian,
reserved2: try file.read(as: UInt32.self))
guard header.magic == FourCC("DDS "),
header.size == 124 // MemoryLayout<DDSHeader>.size
else { throw NSError() /* ("DDS Header size mismatch") */ }
//#if MemoryLayout<DDSHeaderDX10>.size != 20
//#error("DDS DX10 Extended Header size mismatch")
//#endif
let format = try Image.Format.resolve(pixFmt: header.pixFmt)
let size = (0...header.mipNum).reduce(0) { size, i in
let width = header.width &>> i, height = header.height &>> i
guard width > 0 && height > 0 else { return size }
return size + format.computeSize(width: width, height: height)
}
guard let data = try file.read(upToCount: size)
else { throw NSError() }
return .init(data,
format: format,
width: Int(header.width), height: Int(header.height),
mipLevels: Int(header.mipNum))
}
}
fileprivate extension Image.Format
{
static func resolve(pixFmt: DDSPixelFormat) throws -> Self
{
switch pixFmt
{
case DDSPixelFormat(.rgb, bits: 24, r: 0xFF0000, g: 0x00FF00, b: 0x0000FF): .rgb888
case DDSPixelFormat(.rgb, bits: 24, r: 0x0000FF, g: 0x00FF00, b: 0xFF0000): .bgr888
case DDSPixelFormat(.rgba, bits: 32, r: 0x00FF0000, g: 0x0000FF00, b: 0x000000FF, a: 0xFF000000): .argb8888
case DDSPixelFormat(.rgba, bits: 32, r: 0x000000FF, g: 0x0000FF00, b: 0x00FF0000, a: 0xFF000000): .abgr8888
//case DDSPixelFormat(.luminance, bits: 8, r: 0xFF): .l8
//case DDSPixelFormat(.alpha, bits: 8, a: 0xFF): .a8
//case DDSPixelFormat(.luminance, bits: 16, a: 0xFFFF): .l16
//case DDSPixelFormat(.luminanceA, bits: 16, r: 0x00FF, a: 0xFF00): .al88
case DDSPixelFormat(.fourCC, fourCC: .init("DXT1")): .s3tc_bc1
case DDSPixelFormat(.fourCC, fourCC: .init("DXT2")): .s3tc_bc2_premul
case DDSPixelFormat(.fourCC, fourCC: .init("DXT3")): .s3tc_bc2
case DDSPixelFormat(.fourCC, fourCC: .init("DXT4")): .s3tc_bc3_premul
case DDSPixelFormat(.fourCC, fourCC: .init("DXT5")): .s3tc_bc3
case DDSPixelFormat(.fourCC, fourCC: .init("ATI1")): .rgtc_bc4
case DDSPixelFormat(.fourCC, fourCC: .init("ATI2")): .rgtc_bc5_3dc
default: throw NSError()
}
}
}
internal extension Image.Format
{
private func dxtcSize(_ w: UInt32, _ h: UInt32) -> Int
{
let mul = [.s3tc_bc1, .rgtc_bc4].contains(self) ? 8 : 16
let cw = Int(w + 3) / 4
let ch = Int(h + 3) / 4
return cw * ch * mul
}
func computeSize(width w: UInt32, height h: UInt32) -> Int
{
switch self
{
//case .l8, .a8: Int(w) * Int(h)
//case .l16, .al88: Int(w) * Int(h) * 2
case .rgb888, .bgr888: Int(w) * Int(h) * 3
case .argb8888, .abgr8888: Int(w) * Int(h) * 4
case .s3tc_bc1, .s3tc_bc2_premul, .s3tc_bc2, .s3tc_bc3_premul, .s3tc_bc3, .rgtc_bc4, .rgtc_bc5_3dc:
dxtcSize(w, h)
}
}
}
struct DDSPixelFormat: Equatable
{
let size: UInt32
let flags: Flags
let fourCC: FourCC
let bits: UInt32
let rMask: UInt32, gMask: UInt32, bMask: UInt32, aMask: UInt32
struct Flags: OptionSet
{
let rawValue: UInt32
static let alphaPix = Self(rawValue: 0x00000001)
static let alpha = Self(rawValue: 0x00000002)
static let fourCC = Self(rawValue: 0x00000004)
static let rgb = Self(rawValue: 0x00000040)
static let yuv = Self(rawValue: 0x00000200)
static let indexed1 = Self(rawValue: 0x00000800)
static let indexed2 = Self(rawValue: 0x00001000)
static let indexed4 = Self(rawValue: 0x00000008)
static let indexed8 = Self(rawValue: 0x00000020)
static let luminance = Self(rawValue: 0x00020000)
static let premult = Self(rawValue: 0x00008000)
static let nvNormal = Self(rawValue: 0x80000000)
static let nvSrgb = Self(rawValue: 0x40000000)
static let rgba: Self = [ .rgb, .alphaPix ]
static let luminanceA: Self = [ .luminance, .alphaPix ]
}
}
extension DDSPixelFormat
{
init(
_ flags: Flags,
fourCC: FourCC = .init(),
bits: UInt32 = 0,
r: UInt32 = 0, g: UInt32 = 0, b: UInt32 = 0, a: UInt32 = 0)
{
self.size = UInt32(MemoryLayout<DDSPixelFormat>.size)
assert(self.size == 32, "DDS pixel format size mismatch")
self.flags = flags
self.fourCC = fourCC
self.bits = bits
self.rMask = r
self.gMask = g
self.bMask = b
self.aMask = a
}
}
struct FourCC: Equatable
{
let rawValue: UInt32
init()
{
self.rawValue = 0
}
init(rawValue: UInt32)
{
self.rawValue = rawValue.bigEndian
}
init(_ data: Data?)
{
assert(data != nil)
assert(data!.count == 4)
self.rawValue = data!.withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }
}
init(_ a: Character, _ b: Character, _ c: Character, _ d: Character)
{
assert(a.isASCII && b.isASCII && c.isASCII && d.isASCII)
var u = UInt32(a.asciiValue!)
u |= UInt32(b.asciiValue!) << 8
u |= UInt32(c.asciiValue!) << 16
u |= UInt32(d.asciiValue!) << 24
self.rawValue = u.bigEndian
}
init(_ abcd: String)
{
assert(abcd.count == 4)
self.init(
abcd[abcd.index(abcd.startIndex, offsetBy: 0)],
abcd[abcd.index(abcd.startIndex, offsetBy: 1)],
abcd[abcd.index(abcd.startIndex, offsetBy: 2)],
abcd[abcd.index(abcd.startIndex, offsetBy: 3)])
}
}
struct DDSHeader
{
let magic: FourCC
let size: UInt32
let flags: Flags
let height: UInt32, width: UInt32, pitch: UInt32, depth: UInt32
let mipNum: UInt32
let reserved1: [UInt32]
let pixFmt: DDSPixelFormat
let caps1: UInt32, caps2: UInt32, caps3: UInt32, caps4: UInt32
let reserved2: UInt32
struct Flags: OptionSet
{
let rawValue: UInt32
static let caps = Self(rawValue: 0x000001)
static let height = Self(rawValue: 0x000002)
static let width = Self(rawValue: 0x000004)
static let pitch = Self(rawValue: 0x000008)
static let pixFmt = Self(rawValue: 0x001000)
static let mipNum = Self(rawValue: 0x020000)
static let linSz = Self(rawValue: 0x080000)
static let depth = Self(rawValue: 0x800000)
}
}