1555 DXT1 解压缩给出不正确的图像输出

1555 DXT1 decompression giving incorrect image output

我正在尝试在 C++ 中将 1555 个 DXT1 纹理解压缩为 RGBA 8888,将输出存储到 std::string。

我已经使用 squish 库成功将 565 DXT1 解压缩为 RGBA 8888,但似乎无法使 1555 工作。

程序没有崩溃,输出图像看起来几乎是正确的,但是在随机位置有几个像素是奇怪的颜色,如下面的输出图像所示。

这是代码。

using namespace std;

string              CTexture::extractRGBAData(void)
{
    string strPixels;
    strPixels.resize(m_usImageSize[0] * m_usImageSize[1] * 4);
    for (unsigned long i = 0, j = m_usImageSize[0] * m_usImageSize[1] * 4; i < j; i++)
    {
        strPixels[i] = 0;
    }

    if (m_strImageData.length() == 0)
    {
        return strPixels;
    }

    unsigned long uiDXTCompressionType;
    if (m_uiPlatformId == 8) // GTA III, VC
    {
        uiDXTCompressionType = m_ucDXTCompressionType;
    }
    else if (m_uiPlatformId == 9) // SA
    {
        //uiDXTCompressionType = m_uiAlpha;
        uiDXTCompressionType = m_ucDXTCompressionType;
    }
    else if (m_uiPlatformId == 5) // XBOX, Android
    {
        uiDXTCompressionType = m_uiAlpha;
    }

    if (uiDXTCompressionType == DXT1)
    {
        unsigned long uiWidth = m_usImageSize[0];
        unsigned long uiHeight = m_usImageSize[1];

        if (m_uiRasterFormat == FORMAT_1555)
        {
            unsigned long
                uiPixelKey = 0,
                uiTexelSeek = 0;
            for (unsigned long y = 0; y < uiHeight; y += 4)
            {
                for (unsigned long x = 0; x < uiWidth; x += 4)
                {
                    string strTexel = m_strImageData.substr(uiTexelSeek, 8);

                    unsigned char *pPixels = new unsigned char[16 * 4];
                    unsigned char *pBlock = new unsigned char[8];
                    memcpy(pBlock, strTexel.c_str(), 8);

                    decompress_DXT1_1555(pPixels, pBlock);

                    for (unsigned long yOffset = 0; yOffset < 4; yOffset++)
                    {
                        for (unsigned long xOffset = 0; xOffset < 4; xOffset++)
                        {
                            unsigned long uiPixelKey = (y * uiWidth) + x + (yOffset * uiWidth) + xOffset;
                            //CDebugger::log("uiPixelKey: " + CStringUtility::toString(uiPixelKey) + ", x: " + CStringUtility::toString(x) + ", y: " + CStringUtility::toString(y) + ", xOffset: " + CStringUtility::toString(xOffset) + ", yOffset: " + CStringUtility::toString(yOffset));
                            uiPixelKey *= 4;

                            if (uiPixelKey < strPixels.size()) // this checks if the height has a remainder when dividing by 4 (as the iteration does 4x4 block of pixels)
                            {
                                strPixels[uiPixelKey + 0] = pPixels[(((yOffset * 4) + xOffset) * 4) + 2] & 0xFF;
                                strPixels[uiPixelKey + 1] = pPixels[(((yOffset * 4) + xOffset) * 4) + 1] & 0xFF;
                                strPixels[uiPixelKey + 2] = pPixels[(((yOffset * 4) + xOffset) * 4) + 0] & 0xFF;
                                strPixels[uiPixelKey + 3] = 255;// pPixels[(((yOffset * 4) + xOffset) * 4) + 3] & 0xFF;
                            }
                        }
                    }

                    delete[] pPixels;
                    delete[] pBlock;
                    uiTexelSeek += 8;
                }
            }

        }
    }
}

void            CTexture::decompress_DXT1_1555(unsigned char *pixels, unsigned char *block)
{
    string strArea = string((char*)block, 8);
    string strPaletteStr = strArea.substr(0, 4);
    unsigned long uiIndexes = CStringUtility::unpackULong(strArea.substr(4, 4), false);
    unsigned char ucPalette[4][4];
    double fPalette[4][4];

    unsigned short usPaletteInt[2];
    usPaletteInt[0] = CStringUtility::unpackUShort(strPaletteStr.substr(0, 2), false); // 1555
    usPaletteInt[1] = CStringUtility::unpackUShort(strPaletteStr.substr(2, 2), false); // 1555

    // based on: http://www.glassechidna.com.au/2009/devblogs/s3tc-dxt1dxt5-texture-decompression/
    float red, green, blue, alpha;

    alpha = (usPaletteInt[0] >> 15) & 1;

    red = ((float)((usPaletteInt[0] >> 10) & 0x1F) * 255.0 + 16.0);
    red = ((red / 32.0) + red) / 32.0;

    green = ((float)((usPaletteInt[0] >> 5) & 0x1F) * 255.0 + 16.0);
    green = ((green / 32.0) + green) / 32.0;

    blue = ((float)(usPaletteInt[0] & 0x1F)) * 255.0 + 16.0;
    blue = ((blue / 32.0) + blue) / 32.0;

    fPalette[0][0] = red;
    fPalette[0][1] = green;
    fPalette[0][2] = blue;
    fPalette[0][3] = alpha;

    alpha = (usPaletteInt[1] >> 15) & 1;

    red = ((float)((usPaletteInt[1] >> 10) & 0x1F) * 255.0 + 16.0);
    red = ((red / 32.0) + red) / 32.0;

    green = ((float)((usPaletteInt[1] >> 5) & 0x1F) * 255.0 + 16.0);
    green = ((green / 32.0) + green) / 32.0;

    blue = ((float)(usPaletteInt[1] & 0x1F)) * 255.0 + 16.0;
    blue = ((blue / 32.0) + blue) / 32.0;

    fPalette[1][0] = red;
    fPalette[1][1] = green;
    fPalette[1][2] = blue;
    fPalette[1][3] = alpha;

    // fetch other 2 colours in palette, interpolated between min/max colours
    if (usPaletteInt[0] > usPaletteInt[1])
    {
        fPalette[2][0] = (2.0 * fPalette[0][0] + fPalette[1][0]) / 3.0;
        fPalette[2][1] = (2.0 * fPalette[0][1] + fPalette[1][1]) / 3.0;
        fPalette[2][2] = (2.0 * fPalette[0][2] + fPalette[1][2]) / 3.0;
        fPalette[2][3] = 255;

        fPalette[3][0] = (fPalette[0][0] + 2.0 * fPalette[1][0]) / 3.0;
        fPalette[3][1] = (fPalette[0][1] + 2.0 * fPalette[1][1]) / 3.0;
        fPalette[3][2] = (fPalette[0][2] + 2.0 * fPalette[1][2]) / 3.0;
        fPalette[3][3] = 255;
    }
    else
    {
        fPalette[2][0] = (fPalette[0][0] + fPalette[1][0]) / 2.0;
        fPalette[2][1] = (fPalette[0][1] + fPalette[1][1]) / 2.0;
        fPalette[2][2] = (fPalette[0][2] + fPalette[1][2]) / 2.0;
        fPalette[2][3] = 255;

        fPalette[3][0] = 0;
        fPalette[3][1] = 0;
        fPalette[3][2] = 0;
        fPalette[3][3] = 255; // transparent black
    }

    for (unsigned long i5 = 0; i5 < 4; i5++)
    {
        ucPalette[i5][0] = fPalette[i5][0];
        ucPalette[i5][1] = fPalette[i5][1];
        ucPalette[i5][2] = fPalette[i5][2];
        ucPalette[i5][3] = fPalette[i5][3];
    }

    for (unsigned long i2 = 0; i2<16; i2++)
    {
        unsigned char index = (uiIndexes >> (i2 * 2)) & 3;
        unsigned char colour[4];
        colour[0] = ((unsigned char)ucPalette[index][0]) & 0xFF;
        colour[1] = ((unsigned char)ucPalette[index][1]) & 0xFF;
        colour[2] = ((unsigned char)ucPalette[index][2]) & 0xFF;
        colour[3] = ((unsigned char)ucPalette[index][3]) & 0xFF;

        // store colour
        pixels[(i2 * 4) + 0] = colour[0] & 0xFF;
        pixels[(i2 * 4) + 1] = colour[1] & 0xFF;
        pixels[(i2 * 4) + 2] = colour[2] & 0xFF;
        pixels[(i2 * 4) + 3] = colour[3] & 0xFF;
    }
}

我认为您对 DXT1 的工作原理有点误解。

2 种基色中没有任何 alpha。他们都在 5:6:5.

"alpha" 仅来自 c0 <= c1 的情况。如果块符合此条件,则索引为 3 的任何像素都将完全透明(从中推断出 1 位 alpha)。

所以...阅读 5:6:5(并为这些设置 alpha=255)而不是基色中的 1:5:5:5,并将 "transparent black" 案例中的 alpha 从 0,0,0,2550,0,0,0 (实际上是透明的黑色而不是不透明的黑色),你应该会得到更好的结果。