加载24位TGA

时间:2012-12-30 23:03:10

标签: 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()
{
}

2 个答案:

答案 0 :(得分:5)

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

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);

答案 1 :(得分:2)

我认为你遇到的问题是你假设TGA文件是填充的,而不是。

所以你的缓冲区大小错误,你错误地索引它们。对输入和输出进行对称操作意味着它几乎可以正常工作,但填充字节最终会出现在图像中(因为它每行输出一个字节)会导致图像的对角条纹,通过颜色通道交替出现

您读取的返回字节数将少于预期,但您不会检查。

(虽然6502对于错误处理2字节字段完全正确 - 但是这个特定图像的宽度/高度小于255像素,所以不会受到影响。