c++ - 加载 24 位 TGA

标签 c++ tga

我写了一个 TGA 加载器来加载 TGA 文件。它可以很好地加载和保存 32 位 TGA 文件,但在加载和保存 24 位时,它就搞砸了。

来自 Photoshop 的 TGA 24 位文件示例:

enter image description here

我的输出:

enter image description here

知道它有什么问题吗?我用与我的位图加载器相同的方式进行填充,它可以工作,但 TGA 没有..:S 下面的代码可以编译和加载 TGA,以防万一有人想测试它。

#include <iostream>
#include <vector>
#include <stdexcept>
#include <fstream>
#include <cstring>

typedef union RGB
{
    std::uint32_t Color;
    struct
    {
        std::uint8_t B, G, R, A;
    } RGBA;
} *PRGB;

class Tga
{
    private:
        std::vector<RGB> Pixels;
        bool ImageCompressed;
        std::uint32_t width, height, size, BitsPerPixel;

    public:
        Tga(const char* FilePath);
        void Save(const char* FilePath);
};

Tga::Tga(const char* FilePath)
{
    std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
    if (!hFile.is_open()){throw std::invalid_argument("File Not Found.");}

    std::uint8_t Header[18] = {0};
    std::vector<std::uint8_t> ImageData;
    static std::uint8_t DeCompressed[12] = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    static std::uint8_t IsCompressed[12] = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

    hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));

    if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
    {
        BitsPerPixel = Header[16];
        width  = Header[13] * 0xFF + Header[12];
        height = Header[15] * 0xFF + Header[14];
        size  = ((width * BitsPerPixel + 31) / 32) * 4 * height;

        if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
        {
            hFile.close();
            throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
        }

        ImageData.resize(size);
        ImageCompressed = false;
        hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
    }
    else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
    {
        BitsPerPixel = Header[16];
        width  = Header[13] * 0xFF + Header[12];
        height = Header[15] * 0xFF + Header[14];
        size  = ((width * BitsPerPixel + 31) / 32) * 4 * height;

        if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
        {
            hFile.close();
            throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
        }

        RGB Pixel = {0};
        int CurrentByte = 0;
        std::size_t CurrentPixel = 0;
        ImageCompressed = true;
        std::uint8_t ChunkHeader = {0};
        int BytesPerPixel = (BitsPerPixel / 8);
        ImageData.resize(width * height * sizeof(RGB));

        do
        {
            hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(ChunkHeader));

            if(ChunkHeader < 128)
            {
                ++ChunkHeader;
                for(int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
                {
                    hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);

                    ImageData[CurrentByte++] = Pixel.RGBA.B;
                    ImageData[CurrentByte++] = Pixel.RGBA.G;
                    ImageData[CurrentByte++] = Pixel.RGBA.R;
                    if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.RGBA.A;
                }
            }
            else
            {
                ChunkHeader -= 127;
                hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);

                for(int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
                {
                    ImageData[CurrentByte++] = Pixel.RGBA.B;
                    ImageData[CurrentByte++] = Pixel.RGBA.G;
                    ImageData[CurrentByte++] = Pixel.RGBA.R;
                    if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.RGBA.A;
                }
            }
        } while(CurrentPixel < (width * height));
    }
    else
    {
        hFile.close();
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
    }

    hFile.close();
    std::uint8_t* BuffPos = ImageData.data();
    Pixels.resize(width * height);

    //Flip the pixels and store them in my vector..

    for (std::size_t I = 0; I < height; ++I)
    {
        for (std::size_t J = 0; J < width; ++J)
        {
            Pixels[(height - 1 - I) * width + J].RGBA.B = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffPos++) : 0xFF);
        }
        if(BitsPerPixel == 24)
            BuffPos += (-width * 3) & 3;
    }
}

void Tga::Save(const char* FilePath)
{
    std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
    if (!hFile.is_open()) {throw std::invalid_argument("Cannot open file for writing.");}

    std::vector<std::uint8_t> ImageData(size);
    std::uint8_t* BuffPos = ImageData.data();


    //Flip it back to how it was when we loaded it.. 
    for (std::size_t I = 0; I < height; ++I)
    {
        for (std::size_t J = 0; J < width; ++J)
        {                                                                   //Flip The ScanLines/Rows back to normal.
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.B;
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.G;
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.R;

            if (BitsPerPixel > 24)
                *(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.A;
        }
        if(BitsPerPixel == 24)
            BuffPos += (-width * 3) & 3;
    }

    static std::uint8_t DeCompressed[12] = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    static std::uint8_t IsCompressed[12] = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

    if (!ImageCompressed)
    {
        hFile.write(reinterpret_cast<char*>(&DeCompressed), sizeof(DeCompressed));
        hFile.put((width & 0xFF));
        hFile.put((width & 0xFF) / 0xFF);
        hFile.put((height & 0xFF));
        hFile.put(((height & 0xFF) / 0xFF));
        hFile.put(BitsPerPixel);
        hFile.put(0x0);
        hFile.write(reinterpret_cast<char*>(ImageData.data()), ImageData.size());
        hFile.close();
    }
    else
    {
        hFile.write(reinterpret_cast<char*>(&IsCompressed), sizeof(IsCompressed));
        hFile.put((width & 0xFF));
        hFile.put((width & 0xFF) / 0xFF);
        hFile.put((height & 0xFF));
        hFile.put(((height & 0xFF) / 0xFF));
        hFile.put(BitsPerPixel);
        hFile.put(0x0);
    }
    hFile.close();
}



int main()
{
}

最佳答案

您的代码肯定至少有几个问题:

width  = Header[13] * 0xFF + Header[12];

这不是读取两个字节值的正确方法...0xFF 是 255 而不是 256:正确的方法是

width  = (Header[13] << 8) + Header[12];

您的代码在编写时在同一区域也有不同的问题:

hFile.put((width & 0xFF));
hFile.put((width & 0xFF) / 0xFF);

代码是错误的(注意例如你只考虑了width的低8位)。一个正确的版本应该是

hFile.put(width & 0xFF);
hFile.put((width >> 8) & 0xFF);

关于c++ - 加载 24 位 TGA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14095604/

相关文章:

debugging - 如何调试 OpenGL 灰色/黑色纹理框?

c++ - 将 Windows Dll 转换为 C++ Visual Studio 2008 的 .lib

c++ - 'void' 编译器 : TDM-GCC 4. 6.1 64 位之前的预期主表达式

c++ - 加载PVR,纹理问题

c++ - 在 OpenGl 中读取 TGA 文件以创建 3d use

image - 在 TGA 文件格式中,x 原点和 y 原点的用途是什么?

Python 代码比相同的 C++ 代码慢得多?

C++关于纯虚函数的设计原则?

c++ - 在 C++ 中按列将数据写入文本文件