Question

Very very slight problem with my Targa Reader. One pixel is off and 2 extra pixels are black :S

The 24-bit targa file I'm loading looks like: enter image description here

Notice the White pixel in the very top left corner. The very first pixel at the top left is white.

But when I save the file pixels back as a 24-bit bitmap, it looks like: enter image description here

Notice that the white pixel is somehow in the top right as well as 2 black pixels above it.

I cannot for the life of me figure out why everything else is correct except for that.. It's bothering me because I'm so close to being able to load TGA's.

Any idea what is wrong with my code below? I'm writing the TGA reader for 16, 24 and 32 bit TGA files created with Photoshop.

typedef union RGB   //Struct which will hold all pixels.
{
    uint32_t Color;
    struct
    {
        unsigned char B, G, R, A;
    } RGBA;
} *PRGB;



class Tga    //My Targa class that will load the TGA file.
{
    private:
        std::vector<RGB> Pixels;
        uint32_t width, height, size, BitsPerPixel;

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


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

    //The header is 12 bytes. By reading it, I can tell if the TGA is compressed or not.

    byte Header[12] = {0};
    byte DeCompressed[12] = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    byte IsCompressed[12] = {0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};


    hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header)); //Read the header.

    if (memcmp(DeCompressed, &Header, sizeof(Header)) == 0)  //If it is not compressed.
    {
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));

        BitsPerPixel = Header[4];              //Displays correctly.
        width  = Header[1] * 256 + Header[0];  //Displays correctly.
        height = Header[3] * 256 + Header[2];  //Displays correctly.
        size  = ((width * BitsPerPixel + 31) / 32) * 4 * height;  //Same algorithm for bitmaps. width * height * 3 OR width * height * 4. Taken from MSDN.

        if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
        {
            hFile.close();
            throw std::logic_error("Error: Invalid TGA File. Width And Height cannot be less than 0. BitsPerPixel must be 24 or 32 bits.");
        }

        std::vector<unsigned char> ImageData(size);  //An array for holding the image data.
        hFile.read(reinterpret_cast<char*>(ImageData.data()), size);

        unsigned char* BuffPos = ImageData.data();
        Pixels.resize(width * height);  //A vector holding my structs that will hold the pxiels.


        //The following is supposed to flip the Image and copy it into my struct vector.
        for (int I = 0; I < height; I++)
        {
            for (int 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)  //Has padding?
                BuffPos += (4 - ((width * 3) % 4)) % 4;
        }
    }
    else if (memcmp(IsCompressed, &Header, sizeof(Header)) == 0)  //The TGA is compressed..
    {
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));

        BitsPerPixel = Header[4];
        height  = Header[1] * 256 + Header[0];
        width = Header[3] * 256 + Header[2];
        size  = ((width * BitsPerPixel + 31) / 32) * 4 * height;

        if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
        {
            hFile.close();
            throw std::logic_error("Error: Invalid TGA File. Width And Height cannot be less than 0. BitsPerPixel must be 24 or 32 bits.");
        }

        RGB Pixel = {0};
        char* BuffPos = reinterpret_cast<char*>(&Pixel);
        int CurrentByte = 0, CurrentPixel = 0;
        int BytesPerPixel = (BitsPerPixel / 8);
        Pixels.resize(width * height * sizeof(RGB));
        do
        {
            byte ChunkHeader = 0;
            hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(byte));
            if (ChunkHeader < 128)
            {
                ++ChunkHeader;
                for (int I = 0; I < ChunkHeader; ++I)
                {
                    hFile.read(BuffPos, BytesPerPixel);
                    Pixels[CurrentByte].RGBA.B = Pixel.RGBA.B;
                    Pixels[CurrentByte].RGBA.G = Pixel.RGBA.G;
                    Pixels[CurrentByte].RGBA.R = Pixel.RGBA.R;
                    Pixels[CurrentByte].RGBA.A = (BitsPerPixel > 24) ? Pixel.RGBA.A : 0xFF;
                    CurrentByte += BytesPerPixel;
                    ++CurrentPixel;
                }
            }
            else
            {
                ChunkHeader -= 127;
                hFile.read(BuffPos, BytesPerPixel);
                for (int I = 0; I < ChunkHeader; ++I)
                {
                    Pixels[CurrentByte].RGBA.B = Pixel.RGBA.B;
                    Pixels[CurrentByte].RGBA.G = Pixel.RGBA.G;
                    Pixels[CurrentByte].RGBA.R = Pixel.RGBA.R;
                    Pixels[CurrentByte].RGBA.A = (BitsPerPixel > 24) ? Pixel.RGBA.A : 0xFF;
                    CurrentByte += BytesPerPixel;
                    ++CurrentPixel;
                }
            }
        } while(CurrentPixel < (width * height));
    }
    hFile.close();


    //I can guarantee nothing is wrong with the bitmap creator.

    Bitmap BMP(width, height, 24);  //Take the pixels structure and create a 24 bit bitmap. This works 100% of the time for PNG and Bitmap using the same structure.
    BMP.Set(Pixels);
    BMP.Save("C:/Foo.bmp");
}

int main()
{
    Tga F("C:/Foo.tga");
}
Was it helpful?

Solution

I think you have the header size wrong. The various bits are defined as below; the assertions are there just to make sure the compiler packs the structures properly.

typedef uint8_t byte;

enum class color_map_type : byte {
    absent  = 0,
    present = 1,
};

enum class image_type : byte {
    none             = 0,
    color_mapped     = 1,
    true_color       = 2,
    black_white      = 3,
    rle_color_mapped = 9,
    rle_true_color   = 10,
    rle_black_white  = 11,
};

struct color_map_spec {
    uint16_t first_entry_index;
    uint16_t length;
    byte     entry_size;
};
static_assert(sizeof(color_map_spec) == 5, "bad size");

struct image_descriptor {
    byte alpha    : 4;
    byte right    : 1;
    byte top      : 1;
    byte reserved : 2;
};
static_assert(sizeof(image_descriptor) == 1, "bad size");

struct image_spec {
    uint16_t         x_origin;
    uint16_t         y_origin;
    uint16_t         width;
    uint16_t         height;
    byte             depth;
    image_descriptor descriptor;
};
static_assert(sizeof(image_spec) == 10, "bad size");

struct header {
    byte           id_length;
    color_map_type color_map_type;
    image_type     image_type;
    color_map_spec color_map_spec;
    image_spec     image_spec;
};
static_assert(sizeof(header) == 18, "bad size");

struct footer {
    uint32_t ext_area_offset;
    uint32_t dev_dir_offset;
    char     signature[16];
    char     dot_terminator;
    char     null_terminator;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top