aboutsummaryrefslogtreecommitdiff
path: root/libs/preview_dds_native/src/ddsfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/preview_dds_native/src/ddsfile.cpp')
-rw-r--r--libs/preview_dds_native/src/ddsfile.cpp132
1 files changed, 132 insertions, 0 deletions
diff --git a/libs/preview_dds_native/src/ddsfile.cpp b/libs/preview_dds_native/src/ddsfile.cpp
new file mode 100644
index 0000000..91dc627
--- /dev/null
+++ b/libs/preview_dds_native/src/ddsfile.cpp
@@ -0,0 +1,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();
+}