Need urgent help: TGA file read write and then Smoothing



  • @tampere2021
    Your code has some issues in how it treats 24 vs. 32 bit images. The write function also contains a mistake in how it handles 32 bit alignment (it doesn't).

    E.g. in the read function (ctor), you always compute the size as ((width * BitsPerPixel + 31) / 32) * 4 * height, i.e. with 4 bytes per pixel. Then you allocate an array of that size. That's OK if you want to always use 4 bytes per pixel in memory - makes things easier.

    But then, in the uncompressed case, you read the file data into the buffer without any adjustment. Which won't work for 24 bit images, after all they only have 3 bytes per pixel in the file. The first thing I'd do is to check the return value of the read function. It's always a good idea to check return values. And in this case, I think the read function will return that it has read less data than what you requested it to read. (You request 4 bytes per pixel but the file probably only contains 3 -- unless you really have a TGA with an alpha channel.)

    To fix this, you first need to decide how you want to hold the data in memory. Either as it is in the file (3 or 4 bytes per pixel, depending on the file). Or always use 4 bytes per pixel in memory.

    Holding the data in memory as it is in the file means the read and write code becomes a bit easier, but everything else becomes more complicated.

    Holding the data in memory with fixed 4 bytes per pixel makes the read and write code a bit more complicated, but everything else becomes a lot easier, because it can always deal with the same format in memory. Personally I'd prefer this version.

    Once you have made a choice, go over your read and write code again, and make sure it does what it's supposed to do. Because as I already wrote, there are some mistakes.



  • @tampere2021 sagte in Need urgent help: TGA file read write and then Smoothing:

    @wob Hi Wob, I had mentioned here already that this is CPU intensive application and so want to stick onto to using only c++ std libraries and not use any 3rd party tools or libraries.

    I don't understand that reasoning. Using third-party libraries will not make things slower, it will probably make things faster. Probably by a lot when we're talking about stuff like blur filters ("smoothing").
    Of course you can implement a blur filter with good performance yourself. But it's really hard. A naiive implementation will probably be over an order of magnitude (10x) slower than a good one from a library.


  • Gesperrt

    @Th69 Hi Th69, can I know what is C&P error in line 130? what changes needed here in my code? please type modified code.


  • Gesperrt

    @hustbaer Hi hustbaer
    I am not sure how to deal with both 32 and 24 bit TGA image files. Please suggest some logic so that it can support for both 32 and 24 bit TGA files.

    For DeCompressed case here, I modified the code as below. Is it correct?

    if (!std::memcmp(UnCompressedTGAcompare, &Header, sizeof(UnCompressedTGAcompare)))
        {
            BitsPerPixel = Header[16];
            //width = Header[13] * 256 + Header[12];
            width = (Header[13] << 8) + Header[12];  //modified code
            //height = Header[15] * 256 + Header[14];
            height = (Header[15] << 8) + Header[14]; //modified code
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
    
    

    For Compressed case, I have modified the below code as follows . Please feel free to modify my code here.

    else if (!std::memcmp(IsCompressedTGAcompare, &Header, sizeof(IsCompressedTGAcompare)))
        {
            BitsPerPixel = Header[16];
            //width = Header[13] * 256 + Header[12];
            width = (Header[13] << 8) + Header[12];  //modified code
            //height = Header[15] * 256 + Header[14];
            height = (Header[15] << 8) + Header[14]; //modified code
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
    

    This code change done with assumption that 0xFF is 255 and not 256.


  • Gesperrt

    @hustbaer Hi hustbaer
    I can use the C/C++ standard libraries, but no external libraries beyond those. So I need to do image parsing and writing needs to be fully implemented within the program itself. Also it must support for both 24 and 32 bit images.


  • Gesperrt

    @hustbaer Do you suggest to use any specific blur filters? Are there any c++ code references?



  • @tampere2021 sagte in Need urgent help: TGA file read write and then Smoothing:

    @hustbaer Hi hustbaer
    I am not sure how to deal with both 32 and 24 bit TGA image files. Please suggest some logic so that it can support for both 32 and 24 bit TGA files.

    I already described the logic.

    For DeCompressed case here, I modified the code as below. Is it correct?

    if (!std::memcmp(UnCompressedTGAcompare, &Header, sizeof(UnCompressedTGAcompare)))
        {
            BitsPerPixel = Header[16];
            //width = Header[13] * 256 + Header[12];
            width = (Header[13] << 8) + Header[12];  //modified code
            //height = Header[15] * 256 + Header[14];
            height = (Header[15] << 8) + Header[14]; //modified code
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
    
    

    For Compressed case, I have modified the below code as follows . Please feel free to modify my code here.

    else if (!std::memcmp(IsCompressedTGAcompare, &Header, sizeof(IsCompressedTGAcompare)))
        {
            BitsPerPixel = Header[16];
            //width = Header[13] * 256 + Header[12];
            width = (Header[13] << 8) + Header[12];  //modified code
            //height = Header[15] * 256 + Header[14];
            height = (Header[15] << 8) + Header[14]; //modified code
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
    

    This code change done with assumption that 0xFF is 255 and not 256.

    Those changes don't make a difference, those lines were not the problem.

    Also this smells a lot like homework, and I'm not going to do your homework for you.


  • Gesperrt

    @hustbaer With those code changes, I don't see weird glitchy patterns on the TGA image file anymore, rather I can see overlapping of two images and doesnot retain the actual color of the original image. Somehow the logic seems to be not working for me. Did you mean to modify read function with conditional check?What are other options here? Any way to attach images here?


  • Gesperrt

    @tampere2021 Below is my updated code for the read function. Please review it.

    
    typedef union PixelInfo
    {
    std::uint32_t Colour;
    struct
    {
    std::uint8_t R, G, B, A;
    }RGBA;
    } *PPixelInfo;
    
    //For DeCompressed case here
    if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
        {
            BitsPerPixel = Header[16];
            width = Header[13] * 256 + Header[12];
            height = Header[15] * 256 + Header[14];
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
           //modified code
            if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
            
                    {
            
                        hFile.close();
            
                        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image..");
            
                    }
                     std::vector<unsigned char> ImageData(size);  //this is for image data
                    
                     hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
                     
                     unsigned char* BuffP = ImageData.data();
                     Pixels.resize(width * height); 
                     for (int I = 0; I < height; I++)
            {
                for (int J = 0; J < width; J++)
                {
                    Pixels[(height - 1 - I) * width + J].RGBA.B = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffP++) : 0xFF);
                }
                if(BitsPerPixel == 24)  //Has padding?
                    BuffP += (4 - ((width * 3) % 4)) % 4;
            }
            
            ImageData.resize(size);
            ImageCompressed = false;
            hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
    }
    
    //For Compressed case
    else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
    {
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
        BitsPerPixel = Header[16];
        width = Header[13] * 256 + Header[12];
        height = Header[15] * 256 + Header[14];
        size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
        
        if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
        {
            hFile.close();
            throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
        }
    
    
        PixelInfo Pixel = { 0 };
        char* BuffP = reinterpret_cast<char*>(&Pixel);  // added now
        int CurrentByte = 0;
        std::size_t CurrentPixel = 0;
        ImageCompressed = true;
        std::uint8_t ChunkHeader = { 0 };
        int BytesPerPixel = (BitsPerPixel / 8);
        ImageData.resize(static_cast<size_t>(width) * static_cast<size_t>(height) * sizeof(PixelInfo));
        
        do
        {
            ChunkHeader = 0;
            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);
                    hFile.read(BuffP, BytesPerPixel); //added now
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
    
                    //added now
                    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(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
                 //added now
                 hFile.read(BuffP, BytesPerPixel);  
                 for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
                {
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
                    //added now
                    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 < (static_cast<size_t>(width) * static_cast<size_t>(height)));
    }
    else
    hFile.close();
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
    }
    
    hFile.close();
    this->Pixels = ImageData;
    }
    
    
    

    Also in lines 38,39,40,41 and 95,96,97,98 and 116,117,118,119. I got the below errors. Any idea what could be wrong here?
    [Error] request for member 'RGBA' in '((Tga*)this)->Tga::Pixels.std::vector<_Tp, _Alloc>::operator[]<unsigned char, std::allocator<unsigned char> >(((std::vector<unsigned char>::size_type)((((((Tga*)this)->Tga::height - ((uint32_t)I)) + 4294967295u) * ((Tga*)this)->Tga::width) + ((uint32_t)J))))', which is of non-class type '__gnu_cxx::__alloc_traits<std::allocator<unsigned char> >::value_type {aka unsigned char}'



  • @tampere2021 Somehow I got a bit lost during all the changes, but your Pixels was a vector<std::uint8_t> in an earlier version . Given that's still the case your access to RGBA cannot work.


  • Gesperrt

    @Schlangenmensch Hi Schlangenmensch, I have posted new code with changes in the read function. Can you please review it once? My Pixels is a vectorstd::uint8_t only now also. See the code below.

    //=========================================
    // Code for loading a TGA-file
    //=========================================
    typedef union PixelInfo
    {
    std::uint32_t Colour;
    struct
    {
    std::uint8_t R, G, B, A;
    }RGBA;
    } *PPixelInfo;
    
    class Tga
    {
    private:
    std::vector<std::uint8_t> Pixels;
    bool ImageCompressed;
    std::uint32_t width, height, size, BitsPerPixel;
    
    public:
    Tga(const char* FilePath);
    std::vector<std::uint8_t> GetPixels() { return this->Pixels; }
    std::uint32_t GetWidth() const { return this->width; }
    std::uint32_t GetHeight() const { return this->height; }
    std::uint32_t GetBitsPerPixel() const { return this->BitsPerPixel; }
    bool HasAlphaChannel() { return BitsPerPixel == 32; }
    };
    
    //For DeCompressed case here
    if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
        {
            BitsPerPixel = Header[16];
            width = Header[13] * 256 + Header[12];
            height = Header[15] * 256 + Header[14];
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
           //modified code
            if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
            
                    {
            
                        hFile.close();
            
                        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image..");
            
                    }
                     std::vector<unsigned char> ImageData(size);  //this is for image data
                    
                     hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
                     
                     unsigned char* BuffP = ImageData.data();
                     Pixels.resize(width * height); 
                     for (int I = 0; I < height; I++)
            {
                for (int J = 0; J < width; J++)
                {
                    Pixels[(height - 1 - I) * width + J].RGBA.B = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffP++) : 0xFF);
                }
                if(BitsPerPixel == 24)  //Has padding?
                    BuffP += (4 - ((width * 3) % 4)) % 4;
            }
            
            ImageData.resize(size);
            ImageCompressed = false;
            hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
    }
    
    //For Compressed case
    else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
    {
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
        BitsPerPixel = Header[16];
        width = Header[13] * 256 + Header[12];
        height = Header[15] * 256 + Header[14];
        size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
        
        if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
        {
            hFile.close();
            throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
        }
    
    
        PixelInfo Pixel = { 0 };
        char* BuffP = reinterpret_cast<char*>(&Pixel);  // added now
        int CurrentByte = 0;
        std::size_t CurrentPixel = 0;
        ImageCompressed = true;
        std::uint8_t ChunkHeader = { 0 };
        int BytesPerPixel = (BitsPerPixel / 8);
        ImageData.resize(static_cast<size_t>(width) * static_cast<size_t>(height) * sizeof(PixelInfo));
        
        do
        {
            ChunkHeader = 0;
            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);
                    hFile.read(BuffP, BytesPerPixel); //added now
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
    
                    //added now
                    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(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
                 //added now
                 hFile.read(BuffP, BytesPerPixel);  
                 for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
                {
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
                    //added now
                    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 < (static_cast<size_t>(width) * static_cast<size_t>(height)));
    }
    else
    hFile.close();
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
    }
    
    hFile.close();
    this->Pixels = ImageData;
    }
    
    

    Also in lines 38,39,40,41 and 95,96,97,98 and 116,117,118,119. I got the below errors. Any idea what could be wrong here?
    [Error] request for member 'RGBA' in '((Tga*)this)->Tga::Pixels.std::vector<_Tp, _Alloc>::operator[]<unsigned char, std::allocator<unsigned char> >(((std::vector<unsigned char>::size_type)((((((Tga*)this)->Tga::height - ((uint32_t)I)) + 4294967295u) * ((Tga*)this)->Tga::width) + ((uint32_t)J))))', which is of non-class type


  • Gesperrt

    @tampere2021 Below is complete code after modify both read and write functions. Please check and feel free to modify the code.

    #include <vector>
    #include <fstream>
    
    //=========================================
    // Code for loading a TGA-file
    //=========================================
    typedef union PixelInfo
    {
    std::uint32_t Colour;
    struct
    {
    std::uint8_t R, G, B, A;
    }RGBA;
    } *PPixelInfo;
    
    class Tga
    {
    private:
    std::vector<std::uint8_t> Pixels;
    bool ImageCompressed;
    std::uint32_t width, height, size, BitsPerPixel;
    
    public:
    Tga(const char* FilePath);
    std::vector<std::uint8_t> GetPixels() { return this->Pixels; }
    std::uint32_t GetWidth() const { return this->width; }
    std::uint32_t GetHeight() const { return this->height; }
    std::uint32_t GetBitsPerPixel() const { return this->BitsPerPixel; }
    bool HasAlphaChannel() { return BitsPerPixel == 32; }
    };
    
    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));
    
    //For DeCompressed case here
    if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
        {
            BitsPerPixel = Header[16];
            width = Header[13] * 256 + Header[12];
            height = Header[15] * 256 + Header[14];
            size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
           //modified code
            if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
            
                    {
            
                        hFile.close();
            
                        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image..");
            
                    }
                     std::vector<unsigned char> ImageData(size);  //this is for image data
                    
                     hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
                     
                     unsigned char* BuffP = ImageData.data();
                     Pixels.resize(width * height); 
                     for (int I = 0; I < height; I++)
            {
                for (int J = 0; J < width; J++)
                {
                    Pixels[(height - 1 - I) * width + J].RGBA.B = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffP++);
                    Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffP++) : 0xFF);
                }
                if(BitsPerPixel == 24)  //Has padding?
                    BuffP += (4 - ((width * 3) % 4)) % 4;
            }
            
            ImageData.resize(size);
            ImageCompressed = false;
            hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
    }
    
    //For Compressed case
    else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
    {
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
        BitsPerPixel = Header[16];
        width = Header[13] * 256 + Header[12];
        height = Header[15] * 256 + Header[14];
        size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
        
        if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
        {
            hFile.close();
            throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
        }
    
    
        PixelInfo Pixel = { 0 };
        char* BuffP = reinterpret_cast<char*>(&Pixel);  // added now
        int CurrentByte = 0;
        std::size_t CurrentPixel = 0;
        ImageCompressed = true;
        std::uint8_t ChunkHeader = { 0 };
        int BytesPerPixel = (BitsPerPixel / 8);
        ImageData.resize(static_cast<size_t>(width) * static_cast<size_t>(height) * sizeof(PixelInfo));
        
        do
        {
            ChunkHeader = 0;
            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);
                    hFile.read(BuffP, BytesPerPixel); //added now
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
    
                    //added now
                    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(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
                 //added now
                 hFile.read(BuffP, BytesPerPixel);  
                 for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
                {
                    //ImageData[CurrentByte++] = Pixel.B;
                    //ImageData[CurrentByte++] = Pixel.G;
                    //ImageData[CurrentByte++] = Pixel.R;
                    //if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
                    //added now
                    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 < (static_cast<size_t>(width) * static_cast<size_t>(height)));
    }
    else
    hFile.close();
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
    }
    
    hFile.close();
    this->Pixels = ImageData;
    }
    
    
    //=========================================
    // code for writing a TGA-file
    //=========================================
    void writeTGA(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 back how it got loaded
        for (int I = 0; I < height; ++I)
            {
                for (int J = 0; J < width; ++J)
                {
                    *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.B;
                    *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.G;
                    *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.R;
                    if(BitsPerPixel > 24)
                       *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.A;
                }
                if(BitsPerPixel == 24)  //Has padding?
                    BuffP += (4 - ((width * 3) % 4)) % 4;
            }
            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();
    }
    
    
    //=========================================
    // main
    //=========================================
    int main()
    {
    Tga oTga("tile.tga");
    writeTGA("tile_new.tga", oTga);
    
    }
    
    


  • You doesn't seem to understand your own code.
    You have declared PixelInfo, but it isn't used in your code, it uses vector<std::uint8_t> (instead of vector<PixelInfo>).

    But I would recommend to use the sources of the Aseprite TGA Library (it is under the MIT license) - just copy the whole files to your project and compile them.

    PS: The C(opy)&P(aste) error in (previous) line 130 is obvious:

    unsigned short height = static_cast<unsigned short>(refTGA.GetWidth()); // <- GetWidth???
    

    (but this is just a small bug, compared to your whole program logic)



  • That's simply no valid c++ code.

    @tampere2021 sagte in Need urgent help: TGA file read write and then Smoothing:

    Pixels[(height - 1 - I) * width + J]

    That is from type uint8_t but you use it, like it should has a member RGBA. Do you want to use a vector<PixelInfo>?

    To be honest, it looks like you aren't very familiar with c++


  • Gesperrt

    @Th69 Hi Th69, i have updated write function now. Please check it.


  • Gesperrt

    @Schlangenmensch Hi Schlangenmensch, I need to use vector<PixelInfo> instead of vectorstd::uint8_t. I am confused with the memory alignment for 32bit here. Need your help here.


  • Gesperrt

    Hi Th69, Contortionist, Schlangenmensch

    I have rewritten entire code, please review it now. I have tested the code but it creates only partial image in tile_new.tga file.
    I am not sure what is wrong here? Please help me.

    #include <vector>
    #include <fstream>
    
    //=========================================
    // Code for loading a TGA-file 
    //=========================================
    typedef union PixelInfo
    {
        std::uint32_t Color;
        struct
        {
            std::uint8_t B, G, R, A;
        } RGBA;
    } *PPixelInfo;
    
    class Tga
    {
        private:
            std::vector<PixelInfo> Pixels;
            bool ImageCompressed;
            std::uint32_t width, height, size, BitsPerPixel;
    
        public:
            Tga(const char* FilePath);
            void writeTGA(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.");
            }
    
            PixelInfo 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(PixelInfo));
    
            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* BuffP = ImageData.data();
        Pixels.resize(width * height);
    
        //Flip 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 = *(BuffP++);
                Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffP++);
                Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffP++);
                Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffP++) : 0xFF);
            }
            if(BitsPerPixel == 24)
                BuffP += (-width * 3) & 3;
        }
    }
    
    void Tga::writeTGA(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.
                *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.B;
                *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.G;
                *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.R;
    
                if (BitsPerPixel > 24)
                    *(BuffP++) = Pixels[(height - 1 - I) * width + J].RGBA.A;
            }
            if(BitsPerPixel == 24)
                BuffP += (-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();
    }
    
    
    
    //=========================================
    // main
    //=========================================
    int main(int argc, char** argv) {
    	
    	Tga oTga("tile.tga");
            oTga.writeTGA("tile_new.tga");
    	return 0;
    }
    
    
    


  • @tampere2021 sagte in Need urgent help: TGA file read write and then Smoothing:

    @hustbaer With those code changes, I don't see weird glitchy patterns on the TGA image file anymore, rather I can see overlapping of two images and doesnot retain the actual color of the original image.

    That's strange. Header[13] * 256 + Header[12] and (Header[13] << 8) + Header[12] should be equivalent.

    Any way to attach images here?

    Unfortunately, no. But it would indeed be good to have the images you are using (both, the original and the one that your program creates). Maybe you can find some free image/file hosting service and post a link here?


    Aside from that: you are trying to solve 6 different things here:

    • Reading uncompressed 24 bit images
    • Reading uncompressed 32 bit images
    • Reading compressed 24 bit images
    • Reading compressed 32 bit images
    • Writing uncompressed 24 bit images
    • Writing uncompressed 32 bit images

    This is a bit much all at once, and clearly it's too much for you. Also I'm quite sure you're also not testing all of those 6 things but only two, and those only in combination instead of one at a time.

    What makes matters worse is that for 24 bit, there's an additional complication with images that have a width which is not divisible by 4.

    So...

    I would suggest you start with ONE of those things, e.g. reading uncompressed 24 bit images. Write the code for just that, and test it using two different test images: one that has a width which is divisible by 4 and one that has a width which is NOT divisible by 4. Prepare two small test images in some paint program with known values. E.g. pick 9 different colors and then make small test images that use those colors, one color for each of the 4 corners, one for each of the 4 sides and the last one for the middle section. Choose the colors so all the color components are different.

    Then let your program read the test images and print them to screen ala

    (R,G,B)(R,G,B)(R,G,B)(R,G,B)(R,G,B)
    (R,G,B)(R,G,B)(R,G,B)(R,G,B)(R,G,B)
    (R,G,B)(R,G,B)(R,G,B)(R,G,B)(R,G,B)
    (R,G,B)(R,G,B)(R,G,B)(R,G,B)(R,G,B)
    (R,G,B)(R,G,B)(R,G,B)(R,G,B)(R,G,B)
    

    Use fixed width format for R, G and B so it's easier to check. And then check the output of the program to what it should be. If it is, then you know that part is working and you can start implementing the next part. E.g. writing uncompressed 24 bit images. Or reading compressed 24 bit images - the order is up to you.

    Once you have all that implemented & checked using your test images, you can be reasonable sure that your code works. Then you can start modifying the image.


  • Gesperrt

    @hustbaer Hi hustbaer, I would focus on Uncompressed Targa image format first. Then would want to apply smoothing technique. Blur filter needs to be applied to the images. Any idea on this?



  • I give up.
    Good luck with this.


Anmelden zum Antworten