I wrote a TGA loader to load TGA files. It loads and saves 32-bit TGA files just fine but when it comes to loading and saving 24-bit, it messed up.
Example TGA 24-bit file from Photoshop:
My output:
Any idea what is wrong with it? I did the padding the same way as my bitmap loader and it works but the TGA doesn't.. :S The code below can compile and load TGA's just in case anyone is wanting to test it.
#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()
{
}