我写了一个TGA加载器来加载TGA文件。它加载和保存32位TGA文件很好,但当它加载和保存24位时,它搞砸了。
来自Photoshop的TGA 24位文件示例:

我的输出:

你知道它出了什么问题吗?我做填充的方式和我的位图加载器一样,它可以工作,但是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()
{
}发布于 2012-12-31 08:08:24
我认为你遇到的问题是,你假设一个TGA文件是填充的,但它不是。
因此,您的缓冲区大小错误,并且您错误地对它们进行索引。对称地输入和输出意味着它几乎可以工作,但填充字节最终在图像中,这(因为它是每行一个字节)导致图像上的对角线条纹,在颜色通道中交替。
您的读取将返回比预期更少的字节数,但是您没有进行检查。
(尽管6502关于你错误地处理2字节域的说法是完全正确的-但是这个特殊的图像宽/高小于255像素,所以不会受到影响)。
发布于 2012-12-31 07:14:26
你的代码肯定至少有几个问题:
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);https://stackoverflow.com/questions/14095604
复制相似问题