DDS: vendoring and using bcdec.h for DDS BC decompression

This can also make it easier to support BC6H in the future.
This commit is contained in:
Wang Zichong
2026-10-27 07:23:35 +01:00
committed by Mirco Miranda
parent 477e9ea542
commit 67ee4cb720
6 changed files with 1574 additions and 367 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -45,7 +45,7 @@ endif()
################################## ##################################
if(KIMAGEFORMATS_DDS) if(KIMAGEFORMATS_DDS)
kimageformats_add_plugin(kimg_dds SOURCES dds.cpp scanlineconverter.cpp) kimageformats_add_plugin(kimg_dds SOURCES dds.cpp bcdec.cpp scanlineconverter.cpp)
endif() endif()
################################## ##################################
+11
View File
@@ -0,0 +1,11 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2022 Sergii "iOrange" Kudlai
SPDX-License-Identifier: MIT
*/
// Single-header BCn decoder used by the DDS plugin (see bcdec.h).
#define BCDEC_IMPLEMENTATION
#include "bcdec.h"
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
SPDX-FileCopyrightText: 2022 Sergii "iOrange" Kudlai
SPDX-License-Identifier: MIT
+27 -366
View File
@@ -9,6 +9,7 @@
// Forked from Qt 5.6 branch // Forked from Qt 5.6 branch
#include "bcdec.h"
#include "dds_p.h" #include "dds_p.h"
#include "util_p.h" #include "util_p.h"
#include "scanlineconverter_p.h" #include "scanlineconverter_p.h"
@@ -965,32 +966,25 @@ static QImage readATI2(QDataStream &s, quint32 width, quint32 height)
return image; return image;
} }
quint8 block[16];
quint8 texel[16 * 2];
for (quint32 i = 0; i < height; i += 4) { for (quint32 i = 0; i < height; i += 4) {
for (quint32 j = 0; j < width; j += 4) { for (quint32 j = 0; j < width; j += 4) {
quint64 alpha1; if (s.readRawData(reinterpret_cast<char *>(block), sizeof(block)) != int(sizeof(block)))
quint64 alpha2;
s >> alpha1;
s >> alpha2;
if (s.status() != QDataStream::Ok)
return QImage(); return QImage();
QRgb arr[16]; // BC5 (ATI2) stores two BC4 blocks: the first one is the second
memset(arr, 0, sizeof(QRgb) * 16); // normal component, the second one is the first normal component.
setAlphaDXT<Five>(arr, alpha1); bcdec_bc5(block, texel, 4 * 2);
for (int k = 0; k < 16; ++k) {
quint8 a = qAlpha(arr[k]);
arr[k] = qRgba(0, 0, a, 0);
}
setAlphaDXT<Five>(arr, alpha2);
const quint32 kMax = qMin<quint32>(4, height - i); const quint32 kMax = qMin<quint32>(4, height - i);
const quint32 lMax = qMin<quint32>(4, width - j); const quint32 lMax = qMin<quint32>(4, width - j);
for (quint32 k = 0; k < kMax; k++) { for (quint32 k = 0; k < kMax; k++) {
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k)); QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
for (quint32 l = 0; l < lMax; l++) { for (quint32 l = 0; l < lMax; l++) {
QRgb pixel = arr[k * 4 + l]; const quint8 *pixel = texel + (k * 4 + l) * 2;
const quint8 nx = qAlpha(pixel); const quint8 nx = pixel[1];
const quint8 ny = qBlue(pixel); const quint8 ny = pixel[0];
const quint8 nz = getNormalZ(nx, ny); const quint8 nz = getNormalZ(nx, ny);
line[j + l] = qRgb(nx, ny, nz); line[j + l] = qRgb(nx, ny, nz);
} }
@@ -1010,23 +1004,21 @@ static QImage readBC4(QDataStream &s, quint32 width, quint32 height)
return image; return image;
} }
quint8 block[8];
quint8 texel[16];
for (quint32 i = 0; i < height; i += 4) { for (quint32 i = 0; i < height; i += 4) {
for (quint32 j = 0; j < width; j += 4) { for (quint32 j = 0; j < width; j += 4) {
quint64 block; if (s.readRawData(reinterpret_cast<char *>(block), sizeof(block)) != int(sizeof(block)))
s >> block;
if (s.status() != QDataStream::Ok)
return QImage(); return QImage();
QRgb arr[16]; bcdec_bc4(block, texel, 4);
memset(arr, 0, sizeof(QRgb) * 16);
setAlphaDXT<Five>(arr, block);
const quint32 kMax = qMin<quint32>(4, height - i); const quint32 kMax = qMin<quint32>(4, height - i);
const quint32 lMax = qMin<quint32>(4, width - j); const quint32 lMax = qMin<quint32>(4, width - j);
for (quint32 k = 0; k < kMax; k++) { for (quint32 k = 0; k < kMax; k++) {
quint8 *line = reinterpret_cast<quint8 *>(image.scanLine(i + k)); quint8 *line = image.scanLine(i + k);
for (quint32 l = 0; l < lMax; l++) { for (quint32 l = 0; l < lMax; l++) {
line[j + l] = qAlpha(arr[k * 4 + l]); line[j + l] = texel[k * 4 + l];
} }
} }
} }
@@ -1035,360 +1027,29 @@ static QImage readBC4(QDataStream &s, quint32 width, quint32 height)
return image; return image;
} }
/* *** BC7 ***
* The decoder is implemented following the "BC7 Format" specification
* (https://learn.microsoft.com/en-us/windows/win32/direct3d11/bc7-format).
* The partition tables are the same described in the OpenGL ARB_texture_compression_bptc
* extension (the ones on the Khronos registry data format page contain errors).
*/
static inline quint64 bc7ReadU64(const quint8 *p)
{
return quint64(p[0]) | (quint64(p[1]) << 8) | (quint64(p[2]) << 16) | (quint64(p[3]) << 24)
| (quint64(p[4]) << 32) | (quint64(p[5]) << 40) | (quint64(p[6]) << 48) | (quint64(p[7]) << 56);
}
struct BC7BitStream {
quint64 low = 0;
quint64 high = 0;
};
static inline int bc7ReadBits(BC7BitStream *bstream, int numBits)
{
const quint64 mask = (quint64(1) << numBits) - 1;
const quint32 bits = bstream->low & mask;
bstream->low >>= numBits;
bstream->low |= (bstream->high & mask) << (64 - numBits);
bstream->high >>= numBits;
return bits;
}
static inline int bc7Interpolate(int a, int b, const int *weights, int index)
{
return (a * (64 - weights[index]) + b * weights[index] + 32) >> 6;
}
static QImage readBC7(QDataStream &s, quint32 width, quint32 height) static QImage readBC7(QDataStream &s, quint32 width, quint32 height)
{ {
// Number of bits used to encode each endpoint per mode (RGB, Alpha).
static const quint8 actualBitsCount[2][8] = {
{ 4, 6, 5, 7, 5, 7, 7, 5 }, // RGBA
{ 0, 0, 0, 0, 6, 8, 7, 5 } // Alpha
};
// Modes that explicitly store P-bits.
static const quint8 modesWithPBits = 0b11001011;
// Interpolation weights for 2, 3 and 4 bit indices.
static const int weight2[] = { 0, 21, 43, 64 };
static const int weight3[] = { 0, 9, 18, 27, 37, 46, 55, 64 };
static const int weight4[] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
// Partition tables for the 2-subset and 3-subset modes. The MSB of each
// value marks the fix-up index of the subset (the subset is in the low bits).
static const quint8 partitionSets[2][64][4][4] = {
{ // 2-subset
{ {128, 0, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 1, 129} },
{ {128, 0, 0, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 129} },
{ {128, 1, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {0, 1, 1, 129} },
{ {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 1, 129} },
{ {128, 0, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 0, 1, 129} },
{ {128, 0, 1, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 1, 1, 129} },
{ {128, 0, 0, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 1, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {1, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 0}, {1, 1, 1, 129} },
{ {128, 0, 0, 0}, {1, 0, 0, 0}, { 1, 1, 1, 0}, {1, 1, 1, 129} },
{ {128, 1, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 1, 0} },
{ {128, 1, 129, 1}, {0, 0, 1, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} },
{ {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} },
{ {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 1, 0, 0}, {1, 1, 1, 0} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} },
{ {128, 1, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 0, 129} },
{ {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} },
{ {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} },
{ {128, 1, 129, 0}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {0, 1, 1, 0} },
{ {128, 0, 129, 1}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {1, 1, 0, 0} },
{ {128, 0, 0, 1}, {0, 1, 1, 1}, {129, 1, 1, 0}, {1, 0, 0, 0} },
{ {128, 0, 0, 0}, {1, 1, 1, 1}, {129, 1, 1, 1}, {0, 0, 0, 0} },
{ {128, 1, 129, 1}, {0, 0, 0, 1}, { 1, 0, 0, 0}, {1, 1, 1, 0} },
{ {128, 0, 129, 1}, {1, 0, 0, 1}, { 1, 0, 0, 1}, {1, 1, 0, 0} },
{ {128, 1, 0, 1}, {0, 1, 0, 1}, { 0, 1, 0, 1}, {0, 1, 0, 129} },
{ {128, 0, 0, 0}, {1, 1, 1, 1}, { 0, 0, 0, 0}, {1, 1, 1, 129} },
{ {128, 1, 0, 1}, {1, 0, 129, 0}, { 0, 1, 0, 1}, {1, 0, 1, 0} },
{ {128, 0, 1, 1}, {0, 0, 1, 1}, {129, 1, 0, 0}, {1, 1, 0, 0} },
{ {128, 0, 129, 1}, {1, 1, 0, 0}, { 0, 0, 1, 1}, {1, 1, 0, 0} },
{ {128, 1, 0, 1}, {0, 1, 0, 1}, {129, 0, 1, 0}, {1, 0, 1, 0} },
{ {128, 1, 1, 0}, {1, 0, 0, 1}, { 0, 1, 1, 0}, {1, 0, 0, 129} },
{ {128, 1, 0, 1}, {1, 0, 1, 0}, { 1, 0, 1, 0}, {0, 1, 0, 129} },
{ {128, 1, 129, 1}, {0, 0, 1, 1}, { 1, 1, 0, 0}, {1, 1, 1, 0} },
{ {128, 0, 0, 1}, {0, 0, 1, 1}, {129, 1, 0, 0}, {1, 0, 0, 0} },
{ {128, 0, 129, 1}, {0, 0, 1, 0}, { 0, 1, 0, 0}, {1, 1, 0, 0} },
{ {128, 0, 129, 1}, {1, 0, 1, 1}, { 1, 1, 0, 1}, {1, 1, 0, 0} },
{ {128, 1, 129, 0}, {1, 0, 0, 1}, { 1, 0, 0, 1}, {0, 1, 1, 0} },
{ {128, 0, 1, 1}, {1, 1, 0, 0}, { 1, 1, 0, 0}, {0, 0, 1, 129} },
{ {128, 1, 1, 0}, {0, 1, 1, 0}, { 1, 0, 0, 1}, {1, 0, 0, 129} },
{ {128, 0, 0, 0}, {0, 1, 129, 0}, { 0, 1, 1, 0}, {0, 0, 0, 0} },
{ {128, 1, 0, 0}, {1, 1, 129, 0}, { 0, 1, 0, 0}, {0, 0, 0, 0} },
{ {128, 0, 129, 0}, {0, 1, 1, 1}, { 0, 0, 1, 0}, {0, 0, 0, 0} },
{ {128, 0, 0, 0}, {0, 0, 129, 0}, { 0, 1, 1, 1}, {0, 0, 1, 0} },
{ {128, 0, 0, 0}, {0, 1, 0, 0}, {129, 1, 1, 0}, {0, 1, 0, 0} },
{ {128, 1, 1, 0}, {1, 1, 0, 0}, { 1, 0, 0, 1}, {0, 0, 1, 129} },
{ {128, 0, 1, 1}, {0, 1, 1, 0}, { 1, 1, 0, 0}, {1, 0, 0, 129} },
{ {128, 1, 129, 0}, {0, 0, 1, 1}, { 1, 0, 0, 1}, {1, 1, 0, 0} },
{ {128, 0, 129, 1}, {1, 0, 0, 1}, { 1, 1, 0, 0}, {0, 1, 1, 0} },
{ {128, 1, 1, 0}, {1, 1, 0, 0}, { 1, 1, 0, 0}, {1, 0, 0, 129} },
{ {128, 1, 1, 0}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {1, 0, 0, 129} },
{ {128, 1, 1, 1}, {1, 1, 1, 0}, { 1, 0, 0, 0}, {0, 0, 0, 129} },
{ {128, 0, 0, 1}, {1, 0, 0, 0}, { 1, 1, 1, 0}, {0, 1, 1, 129} },
{ {128, 0, 0, 0}, {1, 1, 1, 1}, { 0, 0, 1, 1}, {0, 0, 1, 129} },
{ {128, 0, 129, 1}, {0, 0, 1, 1}, { 1, 1, 1, 1}, {0, 0, 0, 0} },
{ {128, 0, 129, 0}, {0, 0, 1, 0}, { 1, 1, 1, 0}, {1, 1, 1, 0} },
{ {128, 1, 0, 0}, {0, 1, 0, 0}, { 0, 1, 1, 1}, {0, 1, 1, 129} }
},
{ // 3-subset
{ {128, 0, 1, 129}, {0, 0, 1, 1}, { 0, 2, 2, 1}, { 2, 2, 2, 130} },
{ {128, 0, 0, 129}, {0, 0, 1, 1}, {130, 2, 1, 1}, { 2, 2, 2, 1} },
{ {128, 0, 0, 0}, {2, 0, 0, 1}, {130, 2, 1, 1}, { 2, 2, 1, 129} },
{ {128, 2, 2, 130}, {0, 0, 2, 2}, { 0, 0, 1, 1}, { 0, 1, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 1, 2, 2}, { 1, 1, 2, 130} },
{ {128, 0, 1, 129}, {0, 0, 1, 1}, { 0, 0, 2, 2}, { 0, 0, 2, 130} },
{ {128, 0, 2, 130}, {0, 0, 2, 2}, { 1, 1, 1, 1}, { 1, 1, 1, 129} },
{ {128, 0, 1, 1}, {0, 0, 1, 1}, {130, 2, 1, 1}, { 2, 2, 1, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 1, 1, 1}, { 2, 2, 2, 130} },
{ {128, 0, 0, 0}, {1, 1, 1, 1}, {129, 1, 1, 1}, { 2, 2, 2, 130} },
{ {128, 0, 0, 0}, {1, 1, 129, 1}, { 2, 2, 2, 2}, { 2, 2, 2, 130} },
{ {128, 0, 1, 2}, {0, 0, 129, 2}, { 0, 0, 1, 2}, { 0, 0, 1, 130} },
{ {128, 1, 1, 2}, {0, 1, 129, 2}, { 0, 1, 1, 2}, { 0, 1, 1, 130} },
{ {128, 1, 2, 2}, {0, 129, 2, 2}, { 0, 1, 2, 2}, { 0, 1, 2, 130} },
{ {128, 0, 1, 129}, {0, 1, 1, 2}, { 1, 1, 2, 2}, { 1, 2, 2, 130} },
{ {128, 0, 1, 129}, {2, 0, 0, 1}, {130, 2, 0, 0}, { 2, 2, 2, 0} },
{ {128, 0, 0, 129}, {0, 0, 1, 1}, { 0, 1, 1, 2}, { 1, 1, 2, 130} },
{ {128, 1, 1, 129}, {0, 0, 1, 1}, {130, 0, 0, 1}, { 2, 2, 0, 0} },
{ {128, 0, 0, 0}, {1, 1, 2, 2}, {129, 1, 2, 2}, { 1, 1, 2, 130} },
{ {128, 0, 2, 130}, {0, 0, 2, 2}, { 0, 0, 2, 2}, { 1, 1, 1, 129} },
{ {128, 1, 1, 129}, {0, 1, 1, 1}, { 0, 2, 2, 2}, { 0, 2, 2, 130} },
{ {128, 0, 0, 129}, {0, 0, 0, 1}, {130, 2, 2, 1}, { 2, 2, 2, 1} },
{ {128, 0, 0, 0}, {0, 0, 129, 1}, { 0, 1, 2, 2}, { 0, 1, 2, 130} },
{ {128, 0, 0, 0}, {1, 1, 0, 0}, {130, 2, 129, 0}, { 2, 2, 1, 0} },
{ {128, 1, 2, 130}, {0, 129, 2, 2}, { 0, 0, 1, 1}, { 0, 0, 0, 0} },
{ {128, 0, 1, 2}, {0, 0, 1, 2}, {129, 1, 2, 2}, { 2, 2, 2, 130} },
{ {128, 1, 1, 0}, {1, 2, 130, 1}, {129, 2, 2, 1}, { 0, 1, 1, 0} },
{ {128, 0, 0, 0}, {0, 1, 129, 0}, { 1, 2, 130, 1}, { 1, 2, 2, 1} },
{ {128, 0, 2, 2}, {1, 1, 0, 2}, {129, 1, 0, 2}, { 0, 0, 2, 130} },
{ {128, 1, 1, 0}, {0, 129, 1, 0}, { 2, 0, 0, 2}, { 2, 2, 2, 130} },
{ {128, 0, 1, 1}, {0, 1, 2, 2}, { 0, 1, 130, 2}, { 0, 0, 1, 129} },
{ {128, 0, 0, 0}, {2, 0, 0, 0}, {130, 2, 1, 1}, { 2, 2, 2, 129} },
{ {128, 0, 0, 0}, {0, 0, 0, 2}, {129, 1, 2, 2}, { 1, 2, 2, 130} },
{ {128, 2, 2, 130}, {0, 0, 2, 2}, { 0, 0, 1, 2}, { 0, 0, 1, 129} },
{ {128, 0, 1, 129}, {0, 0, 1, 2}, { 0, 0, 2, 2}, { 0, 2, 2, 130} },
{ {128, 1, 2, 0}, {0, 129, 2, 0}, { 0, 1, 130, 0}, { 0, 1, 2, 0} },
{ {128, 0, 0, 0}, {1, 1, 129, 1}, { 2, 2, 130, 2}, { 0, 0, 0, 0} },
{ {128, 1, 2, 0}, {1, 2, 0, 1}, {130, 0, 129, 2}, { 0, 1, 2, 0} },
{ {128, 1, 2, 0}, {2, 0, 1, 2}, {129, 130, 0, 1}, { 0, 1, 2, 0} },
{ {128, 0, 1, 1}, {2, 2, 0, 0}, { 1, 1, 130, 2}, { 0, 0, 1, 129} },
{ {128, 0, 1, 1}, {1, 1, 130, 2}, { 2, 2, 0, 0}, { 0, 0, 1, 129} },
{ {128, 1, 0, 129}, {0, 1, 0, 1}, { 2, 2, 2, 2}, { 2, 2, 2, 130} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, {130, 1, 2, 1}, { 2, 1, 2, 129} },
{ {128, 0, 2, 2}, {1, 129, 2, 2}, { 0, 0, 2, 2}, { 1, 1, 2, 130} },
{ {128, 0, 2, 130}, {0, 0, 1, 1}, { 0, 0, 2, 2}, { 0, 0, 1, 129} },
{ {128, 2, 2, 0}, {1, 2, 130, 1}, { 0, 2, 2, 0}, { 1, 2, 2, 129} },
{ {128, 1, 0, 1}, {2, 2, 130, 2}, { 2, 2, 2, 2}, { 0, 1, 0, 129} },
{ {128, 0, 0, 0}, {2, 1, 2, 1}, {130, 1, 2, 1}, { 2, 1, 2, 129} },
{ {128, 1, 0, 129}, {0, 1, 0, 1}, { 0, 1, 0, 1}, { 2, 2, 2, 130} },
{ {128, 2, 2, 130}, {0, 1, 1, 1}, { 0, 2, 2, 2}, { 0, 1, 1, 129} },
{ {128, 0, 0, 2}, {1, 129, 1, 2}, { 0, 0, 0, 2}, { 1, 1, 1, 130} },
{ {128, 0, 0, 0}, {2, 129, 1, 2}, { 2, 1, 1, 2}, { 2, 1, 1, 130} },
{ {128, 2, 2, 2}, {0, 129, 1, 1}, { 0, 1, 1, 1}, { 0, 2, 2, 130} },
{ {128, 0, 0, 2}, {1, 1, 1, 2}, {129, 1, 1, 2}, { 0, 0, 0, 130} },
{ {128, 1, 1, 0}, {0, 129, 1, 0}, { 0, 1, 1, 0}, { 2, 2, 2, 130} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 2, 1, 129, 2}, { 2, 1, 1, 130} },
{ {128, 1, 1, 0}, {0, 129, 1, 0}, { 2, 2, 2, 2}, { 2, 2, 2, 130} },
{ {128, 0, 2, 2}, {0, 0, 1, 1}, { 0, 0, 129, 1}, { 0, 0, 2, 130} },
{ {128, 0, 2, 2}, {1, 1, 2, 2}, {129, 1, 2, 2}, { 0, 0, 2, 130} },
{ {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 0}, { 2, 129, 1, 130} },
{ {128, 0, 0, 130}, {0, 0, 0, 1}, { 0, 0, 0, 2}, { 0, 0, 0, 129} },
{ {128, 2, 2, 2}, {1, 2, 2, 2}, { 0, 2, 2, 2}, {129, 2, 2, 130} },
{ {128, 1, 0, 129}, {2, 2, 2, 2}, { 2, 2, 2, 2}, { 2, 2, 2, 130} },
{ {128, 1, 1, 129}, {2, 0, 1, 1}, {130, 2, 0, 1}, { 2, 2, 2, 0} }
}
};
QImage image = imageAlloc(width, height, QImage::Format_ARGB32); QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
if (image.isNull()) { if (image.isNull()) {
return image; return image;
} }
quint8 block[16]; quint8 block[16];
quint8 texel[4][4][4]; quint8 texel[16 * 4];
for (quint32 by = 0; by < height; by += 4) { for (quint32 i = 0; i < height; i += 4) {
for (quint32 bx = 0; bx < width; bx += 4) { for (quint32 j = 0; j < width; j += 4) {
if (s.readRawData(reinterpret_cast<char *>(block), sizeof(block)) != int(sizeof(block))) if (s.readRawData(reinterpret_cast<char *>(block), sizeof(block)) != int(sizeof(block)))
return QImage(); return QImage();
memset(texel, 0, sizeof(texel)); bcdec_bc7(block, texel, 4 * 4);
BC7BitStream bstream; const quint32 kMax = qMin<quint32>(4, height - i);
bstream.low = bc7ReadU64(block); const quint32 lMax = qMin<quint32>(4, width - j);
bstream.high = bc7ReadU64(block + 8); for (quint32 k = 0; k < kMax; k++) {
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
// The mode is the number of leading zero bits. for (quint32 l = 0; l < lMax; l++) {
int mode = 0; const quint8 *pixel = texel + (k * 4 + l) * 4;
while (mode < 8 && bc7ReadBits(&bstream, 1) == 0) line[j + l] = qRgba(pixel[0], pixel[1], pixel[2], pixel[3]);
++mode;
// Reserved mode: the decompressed block is transparent black.
if (mode < 8) {
int partition = 0;
int numPartitions = 1;
int rotation = 0;
int indexSelectionBit = 0;
if (mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7) {
numPartitions = (mode == 0 || mode == 2) ? 3 : 2;
partition = bc7ReadBits(&bstream, (mode == 0) ? 4 : 6);
}
const int numEndpoints = numPartitions * 2;
if (mode == 4 || mode == 5) {
rotation = bc7ReadBits(&bstream, 2);
if (mode == 4)
indexSelectionBit = bc7ReadBits(&bstream, 1);
}
int endpoints[6][4];
memset(endpoints, 0, sizeof(endpoints));
for (int c = 0; c < 3; ++c)
for (int e = 0; e < numEndpoints; ++e)
endpoints[e][c] = bc7ReadBits(&bstream, actualBitsCount[0][mode]);
if (actualBitsCount[1][mode] > 0)
for (int e = 0; e < numEndpoints; ++e)
endpoints[e][3] = bc7ReadBits(&bstream, actualBitsCount[1][mode]);
if (mode == 0 || mode == 1 || mode == 3 || mode == 6 || mode == 7) {
for (int e = 0; e < numEndpoints; ++e)
for (int c = 0; c < 4; ++c)
endpoints[e][c] <<= 1;
if (mode == 1) {
// The two subsets share one P-bit each.
const int p0 = bc7ReadBits(&bstream, 1);
const int p1 = bc7ReadBits(&bstream, 1);
for (int c = 0; c < 3; ++c) {
endpoints[0][c] |= p0;
endpoints[1][c] |= p0;
endpoints[2][c] |= p1;
endpoints[3][c] |= p1;
}
} else if (modesWithPBits & (1 << mode)) {
// One P-bit per endpoint.
for (int e = 0; e < numEndpoints; ++e) {
const int p = bc7ReadBits(&bstream, 1);
for (int c = 0; c < 4; ++c)
endpoints[e][c] |= p;
}
}
}
for (int e = 0; e < numEndpoints; ++e) {
int bits = actualBitsCount[0][mode] + ((modesWithPBits >> mode) & 1);
for (int c = 0; c < 3; ++c) {
endpoints[e][c] <<= (8 - bits);
endpoints[e][c] |= (endpoints[e][c] >> bits);
}
bits = actualBitsCount[1][mode] + ((modesWithPBits >> mode) & 1);
endpoints[e][3] <<= (8 - bits);
endpoints[e][3] |= (endpoints[e][3] >> bits);
}
// If the mode does not store alpha, then alpha is opaque.
if (actualBitsCount[1][mode] == 0)
for (int e = 0; e < numEndpoints; ++e)
endpoints[e][3] = 0xff;
const int indexBits = (mode == 0 || mode == 1) ? 3 : ((mode == 6) ? 4 : 2);
const int indexBits2 = (mode == 4) ? 3 : ((mode == 5) ? 2 : 0);
const int *weights = (indexBits == 2) ? weight2 : ((indexBits == 3) ? weight3 : weight4);
const int *weights2 = (indexBits2 == 2) ? weight2 : weight3;
// The color indices are stored before the alpha indices.
int indices[4][4];
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
const int partitionSet = (numPartitions == 1) ? ((i | j) ? 0 : 128)
: partitionSets[numPartitions - 2][partition][i][j];
int bits = indexBits;
if (partitionSet & 0x80) // fix-up index
bits--;
indices[i][j] = bc7ReadBits(&bstream, bits);
}
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
const int partitionSet = ((numPartitions == 1) ? ((i | j) ? 0 : 128)
: partitionSets[numPartitions - 2][partition][i][j]) & 0x03;
const int index = indices[i][j];
int r, g, b, a;
if (indexBits2 == 0) {
r = bc7Interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights, index);
g = bc7Interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights, index);
b = bc7Interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights, index);
a = bc7Interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights, index);
} else {
const int index2 = bc7ReadBits(&bstream, (i | j) ? indexBits2 : (indexBits2 - 1));
if (indexSelectionBit == 0) {
r = bc7Interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights, index);
g = bc7Interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights, index);
b = bc7Interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights, index);
a = bc7Interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights2, index2);
} else {
r = bc7Interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights2, index2);
g = bc7Interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights2, index2);
b = bc7Interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights2, index2);
a = bc7Interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights, index);
}
}
switch (rotation) {
case 1: // swap A and R
std::swap(a, r);
break;
case 2: // swap A and G
std::swap(a, g);
break;
case 3: // swap A and B
std::swap(a, b);
break;
}
texel[i][j][0] = quint8(r);
texel[i][j][1] = quint8(g);
texel[i][j][2] = quint8(b);
texel[i][j][3] = quint8(a);
}
}
}
const quint32 kMax = qMin<quint32>(4, height - by);
const quint32 lMax = qMin<quint32>(4, width - bx);
for (quint32 i = 0; i < kMax; i++) {
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(by + i));
for (quint32 j = 0; j < lMax; j++) {
line[bx + j] = qRgba(texel[i][j][0], texel[i][j][1], texel[i][j][2], texel[i][j][3]);
} }
} }
} }