I'm diving into various Image reading and writing methods and writing my own. I decided to tackle TGA/Targa. I got reading working flawlessly. It reads both compressed and decompressed .TGA files. I got writing decompressed .TGA files working.
However, I cannot figure out how to write Compressed .TGA files. I read it like:
If a 1-byte chunk header "X" is less than 127, the amount of pixels to read is: (X - 1) before I hit another 1-byte header. If X is above 127, then it is the amount of times a pixel is repeated consecutively. So (X - 127) is the amount of times I copy that pixel into the buffer. I minused 127 from X inorder to get rid of the 1-byte header.
Now, I have a problem reversing this algorithm and writing it back to the file. I have no clue where to start. I read wikipedia and it stated that the algorithm is the RLE-Packbits algorithm. I'm not sure how true that is, but I do know it is RLE because it repeats?
I put the whole source code below so any one willing to help can compile it. The Loading/Reading works fine. I only need help "saving as Compressed" (Line 84).
Any help is appreciated.
#include <iostream>
#include <vector>
#include <fstream>
#include <cstring>
#include <stdexcept>
typedef union RGB
{
uint32_t Color;
struct
{
unsigned char B, G, R, A;
} RGBA;
} *PRGB;
class Tga
{
private:
std::vector<unsigned char> ImageData;
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.");}
unsigned char Header[18] = {0};
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
RGB Pixel = {0};
int CurrentByte = 0;
size_t CurrentPixel = 0;
unsigned char 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));
hFile.close();
}
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.");}
static unsigned char IsCompressed[12] = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
//Need help writing this part.. I wrote out the header and the information needed. I only need to write the pixels compressed. The pixels are stored in: ImageData.
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);
//Write the ImageData here.. Compress it with RLE..
hFile.close();
}
int main()
{
//Tga F("C:/Users/Brandon/Desktop/Foo.tga");
//F.Save("C:/Users/Brandon/Desktop/Boo.tga");
}