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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#include "ddsfile.h"
#include <QFile>
#include <algorithm>
#include <cstring>
bool DDSFile::loadFromFile(const QString& path)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
return false;
}
return parse(file.readAll());
}
bool DDSFile::loadFromData(const QByteArray& data)
{
return parse(data);
}
bool DDSFile::parse(const QByteArray& data)
{
const char* ptr = data.constData();
int offset = 0;
int size = data.size();
// Magic number
if (size < 4)
return false;
uint32_t magic;
std::memcpy(&magic, ptr + offset, 4);
offset += 4;
if (magic != DDS_MAGIC)
return false;
// DDS header
if (size - offset < static_cast<int>(sizeof(DDSHeader)))
return false;
DDSHeader header;
std::memcpy(&header, ptr + offset, sizeof(DDSHeader));
offset += sizeof(DDSHeader);
if (header.dwSize != 124)
return false;
// DXT10 extended header
DDSHeaderDXT10 dxt10{};
bool hasDXT10 = false;
if ((header.ddspf.dwFlags & DDPF_FOURCC) &&
header.ddspf.dwFourCC == makeFourCC('D', 'X', '1', '0')) {
if (size - offset < static_cast<int>(sizeof(DDSHeaderDXT10)))
return false;
std::memcpy(&dxt10, ptr + offset, sizeof(DDSHeaderDXT10));
offset += sizeof(DDSHeaderDXT10);
hasDXT10 = true;
}
m_width = header.dwWidth;
m_height = header.dwHeight;
// Determine GL format
m_glFormat = getGLFormat(header.ddspf, hasDXT10 ? &dxt10 : nullptr);
if (!m_glFormat.valid) {
return false;
}
// Determine DXGI format for size calculations
if (hasDXT10) {
m_dxgiFormat = dxt10.dxgiFormat;
} else if (header.ddspf.dwFlags & DDPF_FOURCC) {
m_dxgiFormat = fourCCToDXGI(header.ddspf.dwFourCC);
} else {
m_dxgiFormat = DXGIFormat::UNKNOWN;
}
// Description
m_description = formatDescription(header.ddspf, hasDXT10 ? &dxt10 : nullptr);
m_description +=
QString(" | %1x%2").arg(m_width).arg(m_height);
// Cubemap detection
m_cubemap = false;
int layers = 1;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP) {
m_cubemap = true;
layers = 0;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) layers++;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) layers++;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEY) layers++;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) layers++;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) layers++;
if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) layers++;
m_description += " | Cubemap";
} else {
m_description += " | 2D";
}
// Mipmap count
int mipCount = 1;
if (header.dwFlags & DDSD_MIPMAPCOUNT) {
mipCount = std::max(1u, header.dwMipMapCount);
}
if (mipCount > 1) {
m_description += QString(" | %1 mips").arg(mipCount);
}
// Read pixel data for each face and mip level
m_faces.resize(layers);
for (int face = 0; face < layers; ++face) {
m_faces[face].mips.resize(mipCount);
uint32_t w = m_width;
uint32_t h = m_height;
for (int mip = 0; mip < mipCount; ++mip) {
uint32_t dataSize = mipDataSize(m_dxgiFormat, header.ddspf, w, h);
if (offset + static_cast<int>(dataSize) > size) {
// Truncated file, keep what we have
m_faces[face].mips.resize(mip);
break;
}
m_faces[face].mips[mip].width = w;
m_faces[face].mips[mip].height = h;
m_faces[face].mips[mip].data =
QByteArray(ptr + offset, static_cast<int>(dataSize));
offset += dataSize;
w = std::max(1u, w / 2);
h = std::max(1u, h / 2);
}
}
return !m_faces.isEmpty() && !m_faces[0].mips.isEmpty();
}
|