Repository navigation
Expand file tree
/
Copy pathdds_loader.cpp
More file actions
104 lines (89 loc) · 3.64 KB
/
Copy pathdds_loader.cpp
File metadata and controls
104 lines (89 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "dds_loader.hpp"
#include <fstream>
#include <iostream>
#include <algorithm>
namespace {
// 16-bit 565 color to 32-bit RGBA
uint32_t rgb565ToRgba(uint16_t c, uint8_t a = 255) {
uint8_t r = ((c >> 11) & 0x1F) * 255 / 31;
uint8_t g = ((c >> 5) & 0x3F) * 255 / 63;
uint8_t b = (c & 0x1F) * 255 / 31;
return (uint32_t)r | ((uint32_t)g << 8) | ((uint32_t)b << 16) | ((uint32_t)a << 24);
}
void decodeDXT1Block(const uint8_t* block, uint32_t* out, uint32_t stride) {
uint16_t c0 = block[0] | (block[1] << 8);
uint16_t c1 = block[2] | (block[3] << 8);
uint32_t code = block[4] | (block[5] << 8) | (block[6] << 16) | (block[7] << 24);
uint8_t r0 = ((c0 >> 11) & 0x1F) * 255 / 31;
uint8_t g0 = ((c0 >> 5) & 0x3F) * 255 / 63;
uint8_t b0 = (c0 & 0x1F) * 255 / 31;
uint8_t r1 = ((c1 >> 11) & 0x1F) * 255 / 31;
uint8_t g1 = ((c1 >> 5) & 0x3F) * 255 / 63;
uint8_t b1 = (c1 & 0x1F) * 255 / 31;
uint32_t colors[4];
colors[0] = rgb565ToRgba(c0);
colors[1] = rgb565ToRgba(c1);
if (c0 > c1) {
colors[2] = ((2*r0 + r1)/3) | (((2*g0 + g1)/3) << 8) | (((2*b0 + b1)/3) << 16) | (0xFF << 24);
colors[3] = ((r0 + 2*r1)/3) | (((g0 + 2*g1)/3) << 8) | (((b0 + 2*b1)/3) << 16) | (0xFF << 24);
} else {
colors[2] = ((r0 + r1)/2) | (((g0 + g1)/2) << 8) | (((b0 + b1)/2) << 16) | (0xFF << 24);
colors[3] = 0; // transparent black
}
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
uint8_t idx = (code >> (2 * (4 * y + x))) & 0x03;
out[y * stride + x] = colors[idx];
}
}
}
void decodeDXT3Block(const uint8_t* block, uint32_t* out, uint32_t stride) {
decodeDXT1Block(block + 8, out, stride);
for (int y = 0; y < 4; ++y) {
uint16_t aRow = block[y * 2] | (block[y * 2 + 1] << 8);
for (int x = 0; x < 4; ++x) {
uint8_t a = ((aRow >> (4 * x)) & 0x0F) * 17;
out[y * stride + x] = (out[y * stride + x] & 0x00FFFFFF) | ((uint32_t)a << 24);
}
}
}
} // namespace
DecodedImage loadDDS(const std::string& path) {
DecodedImage img;
std::ifstream f(path, std::ios::binary);
if (!f.is_open()) return img;
DDS_HEADER hdr;
f.read(reinterpret_cast<char*>(&hdr), sizeof(hdr));
if (hdr.dwMagic != 0x20534444) return img; // 'DDS '
img.width = hdr.dwWidth;
img.height = hdr.dwHeight;
img.rgba.resize(img.width * img.height, 0);
// Read compressed data
std::vector<uint8_t> data((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
const uint32_t DXT1 = 0x31545844;
const uint32_t DXT3 = 0x33545844;
const uint32_t DXT5 = 0x35545844;
uint32_t fourCC = hdr.ddspf.dwFourCC;
if (fourCC == DXT1 || fourCC == DXT3 || fourCC == DXT5) {
uint32_t blockSize = (fourCC == DXT1) ? 8 : 16;
size_t srcOffset = 0;
for (uint32_t y = 0; y < img.height; y += 4) {
for (uint32_t x = 0; x < img.width; x += 4) {
if (srcOffset + blockSize > data.size()) break;
uint32_t blockPixels[16];
if (fourCC == DXT1) {
decodeDXT1Block(&data[srcOffset], blockPixels, 4);
} else {
decodeDXT3Block(&data[srcOffset], blockPixels, 4);
}
srcOffset += blockSize;
for (uint32_t by = 0; by < 4 && (y + by) < img.height; ++by) {
for (uint32_t bx = 0; bx < 4 && (x + bx) < img.width; ++bx) {
img.rgba[(y + by) * img.width + (x + bx)] = blockPixels[by * 4 + bx];
}
}
}
}
}
return img;
}