reading 24 bit per pixel bitmaps issue

Started by
6 comments, last by iMalc 11 years, 3 months ago

I'm currently trying to read bitmaps without using an image library.

My current implementation can read bitmaps ,however, issues occur when both the width and height are not of base 2.

To be more specific, when I tired to render a 922x910 bitmap ,the colors didn't come in correctly. Sometimes bitmaps of different resolutions turnout looking "stretched" and without the correct colors.

To get my bitmaps I let microsoft paint convert png's, jpegs and other formats to .bmp.

The ultimate purpose for all of this is to implement the marching squares method so I can generate polygons from sprites.

How do I deal with this?

My bitmap code so far:


#ifndef COLOR_VECTOR
#define COLOR_VECTOR
struct ColorVector{
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
};
#endif


class uBitmap{
    public:
        uBitmap(std::string path,BYTE redColorKey, BYTE greenColorKey,BYTE blueColorKey);
        ~uBitmap();
        unsigned int getWidth();
        unsigned int getHeight();
        long int getPixel(int x, int y);
        ColorVector getPixelVector(int x, int y);
        void resizeBuffer(unsigned int size);
        void clearBuffer();
        void putPixel(int x, int y, BYTE r, BYTE g, BYTE b);
        void saveBitmap(std::string filename);
        void renderImage();
    private:
        BITMAPFILEHEADER bitmapHeader_;
        BITMAPINFOHEADER bitmapInfoHeader_;
        BYTE* rgbPixelData_;
        BYTE* rgbaPixelData_;
        ColorVector colorKey_;
};


uBitmap::uBitmap(std::string path,BYTE redColorKey, BYTE greenColorKey,BYTE blueColorKey){
    BITMAPFILEHEADER& fileHeader = bitmapHeader_;
    BITMAPINFOHEADER& infoHeader = bitmapInfoHeader_;
    BYTE*& pixelData = rgbPixelData_;

    bitmapInfoHeader_.biWidth = 0;
    bitmapInfoHeader_.biHeight = 0;
    rgbPixelData_ = NULL;
    rgbaPixelData_ = NULL;

    std::ifstream file(path.data(),std::ios::binary);
    if(file.is_open()){
        file.read((char*)&bitmapHeader_    ,sizeof(BITMAPFILEHEADER));
        file.read((char*)&bitmapInfoHeader_,sizeof(BITMAPINFOHEADER));
        rgbPixelData_ = new BYTE[infoHeader.biSizeImage];
        file.read((char*)rgbPixelData_,infoHeader.biSizeImage);
    }
    file.close();
}

uBitmap::~uBitmap(){
    if(rgbPixelData_ != NULL){
        delete[] rgbPixelData_;
    }

    if(rgbaPixelData_ != NULL){
        delete[] rgbaPixelData_;
    }
}

unsigned int uBitmap::getWidth(){
    return bitmapInfoHeader_.biWidth;
}

unsigned int uBitmap::getHeight(){
    return bitmapInfoHeader_.biHeight;
}

/*long int uBitmap::getPixel(int x, int y){// attempting to pack color components into an integer
    ColorVector pix = getPixelVector(x,y);
    return  (pix.b << 24) | (pix.g << 16)  | (pix.r << 8) ;
}*/

ColorVector uBitmap::getPixelVector(int x, int y){
    ColorVector curElement;
    unsigned int firstComponent = (y*getWidth()+x)*3;//which is the blue color vector component
    curElement.b = *(rgbPixelData_+firstComponent);  //reading first component (blue)
    curElement.g = *(rgbPixelData_+firstComponent+1);//reading second component (green)
    curElement.r = *(rgbPixelData_+firstComponent+2);//reading third component (red)
    return curElement;
}

void uBitmap::renderImage(){
    glBegin(GL_POINTS);
    for(unsigned int y = 0; y < getHeight();y++){
        for(unsigned int x = 0; x < getWidth();x++){
            ColorVector c = getPixelVector(x,getHeight() - y);
            glColor3ub(c.r,c.g ,c.b);
            glVertex2i(x,y);
        }
    }
    glEnd();
}

Please note:I realize that using immediate mode to render a bitmap is a horrible idea,however, its more a debugging function than anything else. Just putting that out there.

*EDIT*

I seemed to have fixed the problem. You guys were completely right about the padding. All I did was slightly modify the constructor to fix my problem:


uBitmap::uBitmap(std::string path,BYTE redColorKey, BYTE greenColorKey,BYTE blueColorKey{
    BITMAPFILEHEADER& fileHeader = bitmapHeader_;
    BITMAPINFOHEADER& infoHeader = bitmapInfoHeader_;
    BYTE*& pixelData = rgbPixelData_;
    bitmapInfoHeader_.biWidth = 0;
    bitmapInfoHeader_.biHeight = 0;
    rgbPixelData_ = NULL;
    rgbaPixelData_ = NULL;

    std::ifstream file(path.data(),std::ios::binary);
    if(file.is_open()){
        file.read((char*)&bitmapHeader_    ,sizeof(BITMAPFILEHEADER));
        file.read((char*)&bitmapInfoHeader_,sizeof(BITMAPINFOHEADER));
        
        int desiredRow = int(ceilf((((float)getWidth())*3.0f) / 4.0f) * 4.0f);
        int rowPadding = desiredRow - (getWidth()*3);
        rgbPixelData_ = new BYTE[ (getWidth()*getHeight()*3) + (rowPadding*getHeight());
        for(unsigned int I = 0; I < getHeight();I++){
            file.read(((char*)rgbPixelData_)+(I*getWidth()*3),desiredRow);
        }

    }
    file.close();
}
Advertisement
I don't know the details of the bmp format, but you may want to check to see if it employs a stride in non-pow2 images, which would effect the offset of texels within the data. 'Stride' is essentially used for handling a 'padding' space at the end of each row which aligns the data to the next nearest pow2. The stride value should be either the width of the padding or else the width of a row with the padding included.

Can you post an image showing the kind of distortion you're getting?
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
I don't know the details of the bmp format, but you may want to check to see if it employs a stride in non-pow2 images, which would effect the offset of texels within the data. 'Stride' is essentially used for handling a 'padding' space at the end of each row which aligns the data to the next nearest pow2. The stride value should be either the width of the padding or else the width of a row with the padding included.

Can you post an image showing the kind of distortion you're getting?

This is what I get when i try to render a 305x298 bitmap

Bitmap images do have a padding. Every row of pixel data must be padded to a full multiple of 32 bit.<br /><br />So for your example, with 305 pixels, 305 * 3 (24bit) = 915 bytes per row. The next full multiple is at 916, so you have to read one extra padding byte.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

I remember struggling with this a few years ago. I wrote a class and demo that demonstrate specifically rendering to bitmaps. I think you will find the source code helpful for understanding bitmaps better.

Note that while the class supports working with 24-bit bitmaps, you will find the performance when working with 32-bit bitmaps much better.

Find the demo and blog post here: http://code-section.com/entry/20/gdi-memory-bitmap

I hope this helps.

Once a few years ago I wanted to write bmp load and safe routines too. What I found out was that the format is a badly documented, actually 5+ different incompatible formats with one file extension and if you pick one to write your data out you depend on the mercy and undocumented quirks of the loading routine of some other program you try to get your data into, to support this exact format and actually recognize the version you used and reads all headerfields exactly in the way you wrote them out and not just ignore some and read data at some offset itself always uses for writing.
You better just get some tested image library that can handle better file formats like for example png than rolling your own quirky bmp loader that works on some files but silently garbles others. Or if you really want to load a bmp at least use some Windows function so you can have halfway compatible quirks in your loading routine.
...snip...

Yeah, BMP is a bad format to use. Bloated, poorly documented, difficult to load, etc. I prefer TGA. There is tons of information on the format, it is easy to load and not protected by patents like PNG.
Psst: http://www.flipcode.com/archives/24-bit_BGR_Windows_DIB_Class.shtml

Also, don't bother checking pointers for NULL before deleting them. It has the check inside delete itself.

Consider making your class non-copyable so that you catch anywhere where you accidentally pass by value instead of by reference.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement