GIDForums  

Go Back   GIDForums > Computer Programming Forums > C++ Forum
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 30-Nov-2005, 06:26
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

reading bmp's


I've written the following code for reading/writing/displaying bmp images. Everything but reading 4 bit encoded mode works. I have been trying to figure out why for two days. I think it's an endianness problem, but I can't figure out what to do about it.

Here's the code:

CPP / C++ / C Code:
#include <fstream>
#include <iostream>
#include <vector>
#include <GL/glut.h>
#include <math>

using namespace std;

struct Color
{
  Color (float r, float g, float b)
  : R(r), G(g), B(b) {}
  Color ()
  : R(0), G(0), B(0) {}
  float R;
  float G;
  float B;
};

class Image
{
  public:
    Image(unsigned short xr, unsigned short yr)
    : xRes(xr), yRes(yr), pixel_data(vector< vector<Color> >(0))
    {
      for (int i=0; i<yr; ++i)
      {
        pixel_data.push_back(vector<Color>(xr, Color()));
      }
    }
    Image()
    : pixel_data(vector< vector<Color> >(0))
    {}
    void write_bmp(char * name)
    {
      ofstream ofs;
      ofs.open(name, ios::binary);
      int endBytes = xRes%4; // extra bytes at end of row
      unsigned int infoHeaderSize = 40; // size of info header
      unsigned int dataOffset = infoHeaderSize + 14; // offset to raster data in bits
      unsigned int fileSize = dataOffset + (endBytes + 3*xRes)*yRes; // size of imagine file
      unsigned int compression = 0; // no compression
      unsigned int compressedFileSize = 0; // 0 because no compression
      unsigned int colorsUsed = 0; // 24 bit bitmap does not use color table
      unsigned int colorsImportant = 0; // 24 bit bitmap does not use color table
      unsigned short bitsPerPixel = 24; // 24 bit bitmap
      unsigned int xPrintRes = 0; // x Pixels per meter
      unsigned int yPrintRes = 0; // y Pixels per meter

      // write file header

      ofs<<"BM";
      write(fileSize, ofs);
      write((unsigned int)0, ofs);
      write(dataOffset, ofs);

      // write info header

      write(infoHeaderSize, ofs);
      write(xRes, ofs);
      write(yRes, ofs);
      write((unsigned short)1, ofs);
      write(bitsPerPixel, ofs);
      write(compression, ofs);
      write(compressedFileSize, ofs);
      write(xPrintRes, ofs);
      write(yPrintRes, ofs);
      write(colorsUsed, ofs);
      write(colorsImportant, ofs);
      for (unsigned int y = 0; y<yRes; ++y)
      {
        for (unsigned int x=0; x<xRes; ++x)
        {
          Color C = pixel_data[y][x];
          write(C.R, C.G, C.B, ofs);
        }
        writeEndBytes(endBytes, ofs);
      }
      ofs.close();
    }
    void read_bmp(char * name)
    {
      ifstream ifs;
      ifs.open(name, ios::binary);

      // read file header

      unsigned short signature;
      read_short(&signature, ifs);
      if (signature != 19778)
      {
        cout<<"invalid file format"<<endl;
        ifs.close();
        return;
      }
      cout<<"Reading "<<name<<endl<<flush;;
      unsigned int filseSize;
      read_int(&filseSize, ifs);
      cout<<"File size: "<<filseSize<<" bytes" <<endl<<flush;

      // read info header

      ifs.ignore(4);
      unsigned int dataOffset;
      read_int(&dataOffset, ifs);
      cout<<"Data offset: "<<dataOffset<<endl<<flush;
      unsigned int infoHeaderSize;
      read_int(&infoHeaderSize, ifs);
      read_int(&xRes, ifs);
      read_int(&yRes, ifs);
      pixel_data = vector< vector<Color> >(0);
      for (unsigned int i=0; i<yRes; ++i)
      {
        pixel_data.push_back(vector<Color>(xRes, Color()));
      }
      cout<<"Dimensions: "<<xRes<<" x "<<yRes<<endl<<flush;
      ifs.ignore(2);
      unsigned short bitsPerPixel;
      read_short(&bitsPerPixel, ifs);
      cout<<"Bits per pixel: "<<bitsPerPixel<<endl<<flush;
      unsigned int compression;
      read_int(&compression, ifs);
      switch (compression)
      {
        case 0: cout<<"No compression"<<endl<<flush;
        break;
        case 1: cout<<"Compression: 8 bit RLE encoding"<<endl<<flush;
        break;
        case 2: cout<<"Compression: 4 bit RLE encoding"<<endl<<flush;
      }
      unsigned int compressedSize;
      read_int(&compressedSize, ifs);
      if (compression != 0)
      {
        cout<<"Compressed image size: "<<compressedSize<<" bytes"<<endl<<flush;
      }
      unsigned int xDim;
      read_int(&xDim, ifs);
      unsigned int yDim;
      read_int(&yDim, ifs);
      if (xDim!=0 && yDim!=0)
      {
        float xPrintD = ((float)xRes/(float)xDim) * 100.0;
        float yPrintD = ((float)yRes/(float)yDim) * 100.0;
        cout<<"Print dimensions: "<<xPrintD<<" x "<<yPrintD<<" centimeters"<<endl<<flush;
      }
      int endBytes = xRes%4;
      unsigned int colorsUsed;
      unsigned int colorsImportant;
      unsigned short B;
      unsigned short G;
      unsigned short R;
      unsigned short color_short;
      unsigned int color_int;
      unsigned int currentX = 0;
      unsigned int currentY = 0;
      unsigned short nibble0;
      unsigned short nibble1;
      unsigned short byte0;
      unsigned short byte1;
      bool endOfFile = false;
      bool parity = true;
      vector<Color> colorTable(0);

      // read raster data

      switch (bitsPerPixel)
      {

        // 32 bit bitmap, no compression

        case 32:
        ifs.seekg(dataOffset);
        for (unsigned int y=0; y<yRes; ++y)
        {
          for (unsigned int x=0; x<xRes; ++x)
          {
            read_int(&color_int, ifs);
            R = (color_int>>16) & 0xFF;
            G = (color_int>>8) & 0xFF;
            B = color_int & 0xFF;
            pixel_data[y][x] = Color((float)R/255.0, (float)G/255.0, (float)B/255.0);
          }
        }
        break;

        // 24 bit bitmap, no compression

        case 24:
        ifs.seekg(dataOffset);
        for (unsigned int y=0; y<yRes; ++y)
        {
          for (unsigned int x=0; x<xRes; ++x)
          {
            read_byte(&B, ifs);
            read_byte(&G, ifs);
            read_byte(&R, ifs);
            pixel_data[y][x] = Color((float)R/255.0, (float)G/255.0, (float)B/255.0);
          }
          ifs.ignore(endBytes);
        }
        break;

        // 16 bit bitmap, no compression

        case 16:
        ifs.seekg(dataOffset);
        for (unsigned int y=0; y<yRes; ++y)
        {
          for (unsigned int x=0; x<xRes; x+=2)
          {
            read_int(&color_int, ifs);
            for (int i=0; i<2; ++i)
            {
              if (x+i >= xRes) break;
              int color = (color_int>>(i<<4)) & 0xFFFF;
              R = (color>>10) & 0x1F;
              G = (color>>5) & 0x1F;
              B = color & 0x1F;
              pixel_data[y][x + i] = Color((float)R/32.0, (float)G/32.0, (float)B/32.0);
            }
          }
        }
        break;

        // 8 bit bitmap

        case 8:
        read_int(&colorsUsed, ifs);
        cout<<"Number of colors used: "<<colorsUsed<<endl<<flush;
        read_int(&colorsImportant, ifs);
        cout<<"Number of important colors: "<<colorsImportant<<endl<<flush;
        if (colorsUsed==0) colorsUsed = 256;
        readColorTable(colorTable, colorsUsed, dataOffset, ifs);

        // no compression

        if (compression == 0)
        {
          for (unsigned int y=0; y<yRes; ++y)
          {
            for (unsigned int x=0; x<xRes; x+=4)
            {
              read_int(&color_int, ifs);
              for (int i=0; i<4; ++i)
              {
                if (x+i >= xRes) break;
                int color = (color_int>>(i<<3)) & 0xFF;
                pixel_data[y][x+i] = colorTable[color];
              }
            }
          }
        }
        else
        {

          // compression

          if (compression != 1)
          {
            cout<<"unsupported bitmap format"<<endl;
            return;
          }
          while (!endOfFile)
          {
            read_short(&color_short, ifs);
            byte0 = color_short & 0xFF;
            byte1 = (color_short>>8) & 0xFF;
            if (byte0 == 0) // escape
            {
              unsigned short x_offset;
              unsigned short y_offset;
              switch (byte1)
              {
                case 0: // new line
                currentX = 0;
                ++currentY;
                break;
                case 1: // end of file
                endOfFile = true;
                break;
                case 2: // translate
                read_byte(&x_offset, ifs);
                read_byte(&y_offset, ifs);
                currentX += x_offset;
                currentY -= y_offset;
                break;
                default: // absolute mode
                for (int i=0; i<byte1; ++i)
                {
                  read_byte(&color_short, ifs);
                  pixel_data[currentY][currentX] = colorTable[color_short];
                  ++currentX;
                }
                ifs.ignore(byte1%2);
              }
            }
            else // encoded mode
            {
              for (int i=0; i<byte0; ++i)
              {
                pixel_data[currentY][currentX] = colorTable[byte1];
                ++currentX;
              }
            }
          }
        }
        break;

        // 4 bit bitmap

        case 4:
        read_int(&colorsUsed, ifs);
        cout<<"Number of colors used: "<<colorsUsed<<endl<<flush;
        read_int(&colorsImportant, ifs);
        cout<<"Number of important colors: "<<colorsImportant<<endl<<flush;
        if (colorsUsed==0) colorsUsed = 16;
        readColorTable(colorTable, colorsUsed, dataOffset, ifs);

        // no compression

        if (compression == 0)
        {
          for (unsigned int y=0; y<yRes; ++y)
          {
            for (unsigned int x=0; x<xRes; x+=8)
            {
              read_int(&color_int, ifs);
              color_int = flipNibbles(color_int);
              for (int i=0; i<8; ++i)
              {
                if (x+i >= xRes) break;
                int color = (color_int>>((i<<2)) & 0xF);
                pixel_data[y][x+i] = colorTable[color];
              }
            }
          }
        }
        else
        {

          // compression

          if (compression != 2)
          {
            cout<<"unsupported bitmap format"<<endl;
            return;
          }
          while (!endOfFile)
          {
            read_short(&color_short, ifs);
            byte0 = color_short & 0xFF;
            byte1 = (color_short>>8) & 0xFF;
            if (byte0 == 0) // escape
            {
              switch (byte1)
              {
                case 0: // new line
                currentX = 0;
                ++currentY;
                break;
                case 1: // end of file
                endOfFile = true;
                break;
                case 2: // translate
                unsigned short x_offset;
                unsigned short y_offset;
                read_short(&x_offset, ifs);
                read_short(&y_offset, ifs);
                currentX += x_offset;
                currentY -= y_offset;
                break;
                default: // absolute mode
                parity = true;
                for (int i=0; i<byte1; ++i)
                {
                  if (parity)
                  {
                    read_short(&color_short, ifs);
                    pixel_data[currentY][currentX] = colorTable[(color_short) & 0xF];
                  }
                  else
                  {
                    pixel_data[currentY][currentX] = colorTable[(color_short>>4) & 0xF];
                  }
                  parity = !parity;
                  ++currentX;
                }
              }
            }
            else // encoded mode
            {
              parity = true;
              nibble0 = byte1 & 0xF;
              nibble1 = (byte1>>4) & 0xF;
              for (int i=0; i<byte0; ++i)
              {
                if (parity) pixel_data[currentY][currentX] = colorTable[nibble0];
                else pixel_data[currentY][currentX] = colorTable[nibble1];
                parity = !parity;
                ++currentX;
                if (currentX==xRes)
                {
                  currentX = 0;
                  ++currentY;
                }
              }
            }
          }
        }
        break;

        // monochrome bitmap, no compression

        case 1:
        read_int(&colorsUsed, ifs);
        cout<<"Number of colors used: "<<colorsUsed<<endl<<flush;
        read_int(&colorsImportant, ifs);
        cout<<"Number of important colors: "<<colorsImportant<<endl<<flush;
        if (colorsUsed==0) colorsUsed = 2;
        readColorTable(colorTable, colorsUsed, dataOffset, ifs);
        for (unsigned int y=0; y<yRes; ++y)
        {
          for (unsigned int x=0; x<xRes; x+=32)
          {
            read_int(&color_int, ifs);
            color_int = flipBits(color_int);
            for (int i=0; i<32; ++i)
            {
              if (x+i >= xRes) break;
              int color = (color_int>>i) & 1;
              pixel_data[y][x+i] = colorTable[color];
            }
          }
        }
        break;
        default:
        cout<<"unsupported bitmap format"<<endl;
      }
      ifs.close();
    }
    Color& getPixel(int x, int y)
    {
      return pixel_data[y][x];
    }
    void draw(float x, float y)
    {
      glBegin( GL_POINTS );
      int X = x;
      int Y = y;
      for (vector<vector<Color> >::iterator y_iter = pixel_data.begin();
           y_iter!=pixel_data.end(); ++y_iter)
      {
        for (vector<Color>::iterator x_iter = y_iter->begin();
             x_iter != y_iter->end(); ++x_iter)
        {
          glColor3f(x_iter->R, x_iter->G, x_iter->B);
          glVertex2i(X, Y);
          ++X;
        }
        X = x;
        ++Y;
      }
      glEnd();
    }
  private:
    void readColorTable(vector<Color>& colorTable, unsigned int colorsUsed, unsigned int dataOffset, ifstream& ifs)
    {
      unsigned short R, G, B;
      ifs.seekg(dataOffset - 4*colorsUsed);
      for (unsigned int i=0; i<colorsUsed; ++i)
      {
        read_byte(&B, ifs);
        read_byte(&G, ifs);
        read_byte(&R, ifs);
        ifs.ignore(1);
        colorTable.push_back(Color((float)R/255.0, (float)G/255.0, (float)B/255.0));
      }
      ifs.seekg(dataOffset);
    }

    unsigned short flipNibbles(unsigned short x)
    {
      unsigned int A = (x >> 4) & 0x0F000000;
      unsigned int B = (x << 4) & 0xF0000000;
      unsigned int C = (x >> 4) & 0x000F0000;
      unsigned int D = (x << 4) & 0x00F00000;

      return A | B | C | D;
    }


    unsigned int flipNibbles(unsigned int x)
    {
      unsigned int A = (x >> 4) & 0x0F000000;
      unsigned int B = (x << 4) & 0xF0000000;
      unsigned int C = (x >> 4) & 0x000F0000;
      unsigned int D = (x << 4) & 0x00F00000;
      unsigned int E = (x >> 4) & 0x00000F00;
      unsigned int F = (x << 4) & 0x0000F000;
      unsigned int G = (x >> 4) & 0x0000000F;
      unsigned int H = (x << 4) & 0x000000F0;

      return A | B | C | D | E | F | G | H;
    }
    unsigned int flipBits(unsigned int x)
    {
      unsigned int A = x & 0xFF;
      unsigned int B = (x>>8) & 0xFF;
      unsigned int C = (x>>16) & 0xFF;
      unsigned int D = (x>>24) & 0xFF;
      A = invert(A);
      B = invert(B);
      C = invert(C);
      D = invert(D);
      return A | (B<<8) | (C<<16) | (D<<24);
    }
    int invert(int x)
    {
      for (int i = 0; i<4; ++i)
      {
        bool temp = getBit(x,i);
        setBit(x, i, (getBit(x, 7-i)));
        setBit(x, 7-i, temp);
      }
      return x;
    }
    void setBit(int& s, const int i, const bool val)
    {
      if (val) s = s | 1<<i;
      else s = s & ~(1<<i);
    }
    bool getBit(const int& s, const int i)
    {
      return (s & 1<<i) != 0;
    }
    void read_byte(unsigned short* S, ifstream& ifs)
    {
      ifs.read((char*) S, 1);
      (*S) = (*S) & 0xFF;
    }
    void read_short(unsigned short* S, ifstream& ifs)
    {
      ifs.read((char*) S, 2);
    }
    void read_int(unsigned int* I, ifstream& ifs)
    {
      ifs.read((char*) I, 4);
    }
    void write(unsigned int i, ofstream& ofs)
    {
      ofs.write((char*)&i, 4);
    }
    void write(unsigned short i, ofstream& ofs)
    {
      ofs.write((char*)&i, 2);
    }

    // functions to write bitmap files

    // function to write extra bytes onto row in bitmap to make the number of bytes
    // a multiple of 4.

    inline void writeEndBytes(int endBytes, ofstream& ofs)
    {
      int blank = 0;
      for (int i=0; i<endBytes; ++i)
      {
        ofs.write((char*)&blank, 1);
      }
    }

    // write a pixel to the bitmap file

    inline void write(float r, float g, float b, ofstream& ofs)
    {
      unsigned short red = max(min(floor(255*r),255),0);
      unsigned short green = max(min(floor(255*g),255),0);
      unsigned short blue = max(min(floor(255*b),255),0);
      ofs.write((char*)&blue, 1);
      ofs.write((char*)&green, 1);
      ofs.write((char*)&red, 1);
    }
    unsigned int xRes;
    unsigned int yRes;
    vector<vector<Color> > pixel_data;
};

  #2  
Old 30-Nov-2005, 07:54
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,720
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: reading bmp's


Quote:
Originally Posted by Blake
I've written the following code for reading/writing/displaying bmp images. Everything but reading 4 bit encoded mode works. I have been trying to figure out why for two days. I think it's an endianness problem, but I can't figure out what to do about it.

Here's the code:

1. What compiler/operating system? Did you receive any compiler warning error messages?

2. What do you mean when you say, "Everything but reading 4 bit encoded mode works"? How many modes did you test? How many modes worked? How did you know that they worked. (In other words, how did you test them?) How many modes didn't work? How do you know it didn't work? (In other words, how did you test it?)

3. What makes you say, "I think it's an endianness problem"? Which of the routines are used in both the mode that doesn't work and the modes that work? Are any of the routines only used in the mode that doesn't work? Are any of the routines used differently for the different modes? How did the modes that worked get the endianness right? Have you identified exactly where in each mode that endianness affects the code?

4. How the heck can anyone help you if you don't give us the program you used for testing and a file that exhibits the problem? For example: the code for flipNibbles(unsigned short x) is certainly wrong, but I don't see where it is used in any of the member functions. Did you write all of the code that you posted? Did you test the member functions of your class? How?

Regards,

Dave
Last edited by davekw7x : 30-Nov-2005 at 08:33.
  #3  
Old 30-Nov-2005, 08:40
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

Re: reading bmp's


Quote:
1. What compiler/operating system? Did you receive any compiler warning error messages?

Borland C++Builder/Windows XP
No compiler errors/warnings.

Quote:
2. What do you mean when you say, "Everything but reading 4 bit encoded mode works"? How many modes did you test? How many modes worked? How did you know that they worked. (In other words, how did you test them?) How many modes didn't work? How do you know it didn't work? (In other words, how did you test it?)

I've tested every mode by making images in photoshop, saving them in the various bmp formats, and reading them. To check if they have been read correctly I use the draw function to display them on the screen.

I know that 4 bit encoded mode does not work because there is a run time error while the image is being read. I believe the problem is that the currentX variable exceeds the x resolution of the image.


Quote:
3. What makes you say, "I think it's an endianness problem"? Which of the routines are used in both the mode that doesn't work and the modes that work? Are any of the routines only used in the mode that doesn't work? Are any of the routines used differently for the different modes? How did the modes that worked get the endianness right? Have you identified exactly where in each mode that endianness affects the code?

I'm only guessing that it's an endianess problem because that's the only thing I can think of. The read_int and read_short functions are used in all modes. They are the only subroutines called when reading 4 bit encoded mode.
I dealt with endianess by reading r, g, and b in the correct order in the other modes > 4. In 4 bit mode, I need to switch the high and low order nibbles in each byte.

Quote:
4. How the heck can anyone help you if you don't give us the program you used for testing and a file that exhibits the problem?

I don't have the code on me at the moment. I'll post it later today.
  #4  
Old 30-Nov-2005, 09:12
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,720
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: reading bmp's


Quote:
Originally Posted by Blake
Borland C++Builder/Windows XP
No compiler errors/warnings.



I've tested every mode by making images in photoshop, saving them in the various bmp formats, and reading them. To check if they have been read correctly I use the draw function to display them on the screen.

I know that 4 bit encoded mode does not work
Does the 4 bit unencoded mode work? This is important to help you debug the program: test as many things as you can so that you can try to isolate the problem to one part of the code.
Quote:
Originally Posted by Blake
because there is a run time error while the image is being read. I believe the problem is that the currentX variable exceeds the x resolution of the image.
Now, this could be important! The run time error may, indeed, occur when you try to access memory beyond the legal limits of an array.

Quote:
Originally Posted by Blake
I'm only guessing that it's an endianess problem because that's the only thing I can think of.

What does endianness have to do with your observation that you believe the problem is that a variable exceeds the x resolution of the image?

Why don't you go into the code where it is reading a 4-bit bitmap and put cout<< statements to show what the current value of array index is and also show the array size (the maximum index value allowed for that array)?

This does several things:

1. It makes you look at the part of the code where your observation leads you to believe the problem may lie. Sometimes just looking closely can let you see something that was overlooked in the 590 lines of code of the class definitions.

2. It prints out values that may show you what's wrong.

3. If it bombs sometime in the middle of the loop, you confirm that that is where the problem is. (And you can see the values that may be causing the problem.)

4. If it gets through that part of the code without bombing, you know the problem may be somewhere else. (So you go to the next step in the algorithm and put some cout<< stuff there to see what the program is working on.)

Quote:
Originally Posted by Blake

I dealt with endianess by reading r, g, and b in the correct order in the other modes > 4. In 4 bit mode, I need to switch the high and low order nibbles in each byte.

In the first place, endianness usually refers to byte positions within 16-bit or 32-bit (or 64-bit) data items, not nibbles within a byte.
However...
If you didn't perform the nibble-swap properly, I could see how it would mess up the picture, but I don't see exactly how it would cause a run time error. However, it may be something to test, if your other efforts don't lead to a complete fix.

Quote:
Originally Posted by Blake
I don't have the code on me at the moment. I'll post it later today.

Before you do that, why don't you try to isolate the runtime error? Maybe some of my suggestions can help you get started. (The point is to get you to a place where you can debug your code yourself.)

A very powerful debugging methodology is: Make the program tell you what is happening.

This is faster, I claim, than posting a request for help and waiting for a helpful response. And it helps you learn about debugging. Since I spend something less than 15 percent of my time writing code and the rest of the time debugging, I think debugging skills are worth developing.

Regards,

Dave

"We can face our problem.
We can arrange such facts as we have
with order and method."

Hercule Poirot
--- in Murder on the Orient Express
  #5  
Old 30-Nov-2005, 10:44
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

Re: reading bmp's


I don't think I was clear on one thing. I know that I am getting out of bounds exceptions, because I've printed out the values of currentX and currentY. I think that may be happening because something is being read in incorrectly, possibly because of endianess.


Quote:
In the first place, endianness usually refers to byte positions within 16-bit or 32-bit (or 64-bit) data items, not nibbles within a byte.

I'm aware of that much, and I can't figure out why, then, I have to flip nibbles in the four bit enencoded case.

I agree with everything you've said about debugging. That's exactly how I normally proceed with a debugging a program, and it usually works for me, but I have been doing that for two days now with now luck. I've even tried comparing my code to code written by others, and as far as I can tell it does exactly the same thing. This is driving me crazy.

The algorithm itself my be the problem.

Thanks for the help.
  #6  
Old 30-Nov-2005, 13:13
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

Re: reading bmp's


Here's the code I use to test it:

CPP / C++ / C Code:
#include "image.h"
#include <iostream>

// Defining default values for window size, shape and location.
#define INITIAL_WIN_W 800
#define INITIAL_WIN_H 800
#define INITIAL_WIN_X 150
#define INITIAL_WIN_Y 50

int windowWidth;
int windowHeight;

Image img;

using namespace std;

void draw()
{
  glClear(GL_COLOR_BUFFER_BIT);
  img.draw(100,100);
  glFlush();
  glutSwapBuffers();
}

 int main( int argc, char * argv[] )
{
  glColor3f(1.0,1.0,1.0);

  // Mask floating point exceptions.
  _control87( MCW_EM, MCW_EM );

  // Initialize glut with command line parameters.
  glutInit( & argc, argv );

  // Choose RGB display mode for normal screen window.
  glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE);

  // Set initial window size, position, and title.
  glutInitWindowSize( INITIAL_WIN_W, INITIAL_WIN_H );
  glutInitWindowPosition( INITIAL_WIN_X, INITIAL_WIN_Y );
  windowWidth = INITIAL_WIN_W;
  windowHeight = INITIAL_WIN_H;
  glutCreateWindow( "Simple Drawing Program" );

  // Set up matrices
  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  gluOrtho2D( 0.0, ( double )INITIAL_WIN_W, 0.0, ( double )INITIAL_WIN_H ), glMatrixMode( GL_MODELVIEW );
  glLoadIdentity();
  glTranslatef( 0.375, 0.375, 0.0 );

  // Set the color for clearing the normal screen window.
  glClearColor( 1.0, 1.0, 1.0, 1.0 );

  // Set the callbacks for the normal screen window.
  glutDisplayFunc( draw );

  img = Image();
  img.read_bmp("4bit-cmp.bmp");
  glutMainLoop();
  return 0;
}

4bit-cmp.bmp is a compressed 4bit bitmap.
  #7  
Old 30-Nov-2005, 15:04
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,720
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: reading bmp's


Quote:
Originally Posted by Blake
I don't think I was clear on one thing. I know that I am getting out of bounds exceptions, because I've printed out the values of currentX and currentY. I think that may be happening because something is being read in incorrectly, possibly because of endianess.

So: how can the values go out of bounds? Now I understand now how you could think it's related to endianness. And I see how it could create a problem. (Your problem, that is.)

Well, when you read in values for the sizes that are used as limits on the loops that are giving you grief: print out the sizes.

Regards,

Dave
  #8  
Old 30-Nov-2005, 20:33
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

Re: reading bmp's


I found one problem; I was reading 2 bytes in several places where I should have been reading only 1. Unfortunatly that didn't fix it.

I'm still getting the out of bounds exception. The interesting is that when it happens, it only exceeds the bounds by ; I replaced this:

CPP / C++ / C Code:
for (int i=0; i<byte0; ++i)
{
   if (parity) pixel_data[currentY][currentX] = colorTable[nibble0];
   else pixel_data[currentY][currentX] = colorTable[nibble1];
   parity = !parity;
   ++currentX;
}

with this:

CPP / C++ / C Code:
for (int i=0; i<byte0; ++i)
{
   if (currentX >= xRes)
   {
      cout<<currentX<<" "<<byte0<<endl;
   }
   else
   {
      if (parity) pixel_data[currentY][currentX] = colorTable[nibble0];
      else pixel_data[currentY][currentX] = colorTable[nibble1];
      parity = !parity;
   }
   ++currentX;
}

Thus if currentX gets to large, it simply ignores it and keeps on going. Every single time the first "if" branch of the if-else statement is taken, it is because xCurrent == xRes.

I'm really not sure what to make of that.

This keeps it from crashing. Interestingly the image comes out ~ 99% correct when it displays.
  #9  
Old 01-Dec-2005, 07:42
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,720
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: reading bmp's


Quote:
Originally Posted by Blake
I found one problem; I was reading 2 bytes in several places where I should have been reading only 1. Unfortunatly that didn't fix it.

I'm still getting the out of bounds exception.

Thanks for the update.

Well, it is possible that there are more than one bugs that cause bad behavior. Fix each one that you find and do some more testing. (You can't prove that this correct by testing, of course, but you can uncover some bugs.)

Quote:
Originally Posted by Blake

The interesting is that when it happens, it only exceeds the bounds by ; I replaced this:

CPP / C++ / C Code:
for (int i=0; i<byte0; ++i)
{
   if (parity) pixel_data[currentY][currentX] = colorTable[nibble0];
   else pixel_data[currentY][currentX] = colorTable[nibble1];
   parity = !parity;
   ++currentX;
}

with this:

CPP / C++ / C Code:
for (int i=0; i<byte0; ++i)
{
   if (currentX >= xRes)
   {
      cout<<currentX<<" "<<byte0<<endl;
   }
   else
   {
      if (parity) pixel_data[currentY][currentX] = colorTable[nibble0];
      else pixel_data[currentY][currentX] = colorTable[nibble1];
      parity = !parity;
   }
   ++currentX;
}

OK. That's pretty good detective work to get this far.

pixel_data is a vector of vectors. It is perfectly legal to use the array index notation to access the elements, but you do know that no bounds checking is performed, right?

If your program has a bug that causes it to try to access something beyond the end of the array, your fix eliminates one of effect of the bug (for your test case: doesn't crash), but doesn't fix the bug. I suggest you try to find why the program doesn't know the size of whatever it is accessing.

Quote:
Originally Posted by Blake
I'm really not sure what to make of that.
~ 99% correct when it displays.

This is a deterministic algorithm. I assume that you aren't satisfied with ~99% correct on your test case. How do you know how many bad things can happen with other images?

Regards,

Dave
  #10  
Old 22-Dec-2005, 20:33
Blake's Avatar
Blake Blake is offline
Member
 
Join Date: Nov 2005
Posts: 172
Blake will become famous soon enough

Re: reading bmp's


I got it! Each block of encoded data has to start on an even-numbered byte. Here's the code incase anyone else has use for it.

The header file (image.h):
CPP / C++ / C Code:
#include <iostream>
#include <vector>

using namespace std;

struct Color
{
  Color (float r, float g, float b, float a);
  Color (float r, float g, float b);
  Color ();
  float R;
  float G;
  float B;
  float alpha;
};

class Image
{
  public:
    Image(unsigned short xr, unsigned short yr); //construct an xr x yr image
    Image(); // construct a 0 x 0 image
    void write_bmp(char * name); // write a 24 bit bitmap (name = file name)
    void read_bmp(char * name); // read any bitmap. Supports 32 bit, 24 bit,
                                // 16 bit, 8 bit (encoded or unencoded), 4 bit 
                                // (encoded or unencoded), and monochrome.
    Color& getPixel(int x, int y); // get color of pixel x, y (indexed from
                                   // bottom left.)
    void draw(); // draw the image at (0,0). (requires initialized GLUT window)
    void draw(float x, float y); // draw the image at (0,0). (requires 
                                 // initialized GLUT window)
    void enable_alpha(); // enable alpha channel (used when drawing image)
    void disable_alpha(); // disable alpha channel (used when drawing image)
    bool alpha() const; // true iff alpha channel is enabled
    void set_opacity(const float& op); // set master opacity for image. Used
                                       // with alpha channel.
  private:
    void read_bmp_32(ifstream& ifs);
    void read_bmp_24(ifstream& ifs);
    void read_bmp_16(ifstream& ifs);
    void read_bmp_8(ifstream& ifs, vector<Color>* colorTable);
    void read_bmp_8_cmp(ifstream& ifs, vector<Color>* colorTable);
    void read_bmp_4(ifstream& ifs, vector<Color>* colorTable);
    void read_bmp_4_cmp(ifstream& ifs, vector<Color>* colorTable);
    void read_bmp_1(ifstream& ifs, vector<Color>* colorTable);
    void readColorTable(vector<Color>& colorTable, unsigned int maxColors,
                        unsigned int dataOffset, ifstream& ifs);
    unsigned short flipNibbles(unsigned short x);
    unsigned int flipNibbles(unsigned int x);
    unsigned int flipBits(unsigned int x);
    int invert(int x);
    void setBit(int& s, const int i, const bool val);
    bool getBit(const int& s, const int i);
    void read_byte(unsigned short* S, ifstream& ifs);
    void read_short(unsigned short* S, ifstream& ifs);
    void read_int(unsigned int* I, ifstream& ifs);
    void write(unsigned int i, ofstream& ofs);
    void write(unsigned short i, ofstream& ofs);
    inline void writeEndBytes(int endBytes, ofstream& ofs);
    inline void write(float r, float g, float b, ofstream& ofs);

    unsigned int xRes;
    unsigned int yRes;
    vector<vector<Color> > pixel_data;
    bool alpha_enabled;
    float master_opacity;
};


and the cpp file (image.cpp):

CPP / C++ / C Code:
#include <fstream>
#include <iostream>
#include <vector>
#include <GL/glut.h>
#include <math>
#include "image.h"

using namespace std;

// Color class

Color::Color (float r, float g, float b, float a)
: R(r), G(g), B(b), alpha(a) {}

Color::Color (float r, float g, float b)
: R(r), G(g), B(b), alpha(1.0) {}

Color::Color ()
: R(0), G(0), B(0) {}

// Image class

// construct an xr x yr Image.
Image::Image(unsigned short xr, unsigned short yr)
: xRes(xr), yRes(yr), pixel_data(vector< vector<Color> >(0)),
  alpha_enabled(false), master_opacity(1.0)
{
  for (int i=0; i<yr; ++i)
  {
    pixel_data.push_back(vector<Color>(xr, Color()));
  }
}

// construct a 0 x 0 Image.
Image::Image()
: pixel_data(vector< vector<Color> >(0)),
  alpha_enabled(false), master_opacity(1.0)
{}

// write a 24-bit bitmap
void Image::write_bmp(char * name)
{
  ofstream ofs;
  ofs.open(name, ios::binary);
  int endBytes = xRes%4; // extra bytes at end of row
  unsigned int infoHeaderSize = 40; // size of info header
  unsigned int dataOffset = infoHeaderSize + 14; // offset to raster data in bits
  unsigned int fileSize = dataOffset + (endBytes + 3*xRes)*yRes; // size of imagine file
  unsigned int compression = 0; // no compression
  unsigned int compressedFileSize = 0; // 0 because no compression
  unsigned int colorsUsed = 0; // 24 bit bitmap does not use color table
  unsigned int colorsImportant = 0; // 24 bit bitmap does not use color table
  unsigned short bitsPerPixel = 24; // 24 bit bitmap
  unsigned int xPrintRes = 0; // x Pixels per meter
  unsigned int yPrintRes = 0; // y Pixels per meter

  // write file header

  ofs<<"BM";
  write(fileSize, ofs);
  write((unsigned int)0, ofs);
  write(dataOffset, ofs);

  // write info header

  write(infoHeaderSize, ofs);
  write(xRes, ofs);
  write(yRes, ofs);
  write((unsigned short)1, ofs);
  write(bitsPerPixel, ofs);
  write(compression, ofs);
  write(compressedFileSize, ofs);
  write(xPrintRes, ofs);
  write(yPrintRes, ofs);
  write(colorsUsed, ofs);
  write(colorsImportant, ofs);
  for (unsigned int y = 0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; ++x)
    {
      Color C = pixel_data[y][x];
      write(C.R, C.G, C.B, ofs);
    }
    writeEndBytes(endBytes, ofs);
  }
  ofs.close();
}

// Read a Windows bitmap. Supports 32 bit, 24 bit, 16 bit,
// 8 bit (encoded or enencoded), 4 bit (encoded or unencoded),
// and monochrome
void Image::read_bmp(char * name)
{
  ifstream ifs;
  ifs.open(name, ios::binary);

  // read file header

  unsigned short signature;
  read_short(&signature, ifs);
  if (signature != 19778)
  {
    cout<<"invalid file format"<<endl;
    ifs.close();
    return;
  }
  cout<<"Reading "<<name<<endl<<flush;;
  unsigned int filseSize;
  read_int(&filseSize, ifs);
  cout<<"File size: "<<filseSize<<" bytes" <<endl<<flush;

  // read info header

  ifs.ignore(4);
  unsigned int dataOffset;
  read_int(&dataOffset, ifs);
  cout<<"Data offset: "<<dataOffset<<endl<<flush;
  unsigned int infoHeaderSize;
  read_int(&infoHeaderSize, ifs);
  read_int(&xRes, ifs);
  read_int(&yRes, ifs);
  pixel_data = vector< vector<Color> >(0);
  for (unsigned int i=0; i<yRes; ++i)
  {
    pixel_data.push_back(vector<Color>(xRes, Color()));
  }
  cout<<"Dimensions: "<<xRes<<" x "<<yRes<<endl<<flush;
  ifs.ignore(2);
  unsigned short bitsPerPixel;
  read_short(&bitsPerPixel, ifs);
  cout<<"Bits per pixel: "<<bitsPerPixel<<endl<<flush;
  unsigned int compression;
  read_int(&compression, ifs);
  switch (compression)
  {
    case 0: cout<<"No compression"<<endl<<flush;
    break;
    case 1: cout<<"Compression: 8 bit RLE encoding"<<endl<<flush;
    break;
    case 2: cout<<"Compression: 4 bit RLE encoding"<<endl<<flush;
  }
  unsigned int compressedSize;
  read_int(&compressedSize, ifs);
  if (compression != 0)
  {
    cout<<"Compressed image size: "<<compressedSize<<" bytes"<<endl<<flush;
  }
  unsigned int xDim;
  read_int(&xDim, ifs);
  unsigned int yDim;
  read_int(&yDim, ifs);
  if (xDim!=0 && yDim!=0)
  {
    float xPrintD = ((float)xRes/(float)xDim) * 100.0;
    float yPrintD = ((float)yRes/(float)yDim) * 100.0;
    cout<<"Print dimensions: "<<xPrintD<<" x "<<yPrintD<<" centimeters"<<endl<<flush;
  }

  vector<Color> colorTable(0);

  // read raster data

  switch (bitsPerPixel)
  {
    case 32: // 32 bit bitmap
    ifs.seekg(dataOffset);
    read_bmp_32(ifs);
    break;

    case 24: // 24 bit bitmap
    ifs.seekg(dataOffset);
    read_bmp_24(ifs);
    break;

    case 16: // 16 bit bitmap
    ifs.seekg(dataOffset);
    read_bmp_16(ifs);
    break;

    case 8: // 8 bit bitmap
    readColorTable(colorTable, 256, dataOffset, ifs);
    if (compression == 0) // unencoded
    {
      read_bmp_8(ifs, &colorTable);
    }
    else // encoded
    {
      if (compression != 1)
      {
        cout<<"unsupported bitmap format"<<endl;
        return;
      }
      read_bmp_8_cmp(ifs, &colorTable);
    }
    break;

    case 4: // 4 bit bitmap
    readColorTable(colorTable, 16, dataOffset, ifs);
    if (compression == 0) // unencoded
    {
      read_bmp_4(ifs, &colorTable);
    }
    else
    {
      if (compression != 2) // encoded
      {
        cout<<"unsupported bitmap format"<<endl;
        return;
      }
      read_bmp_4_cmp(ifs, &colorTable);
    }
    break;

    case 1: // monochrome
    readColorTable(colorTable, 2, dataOffset, ifs);
    read_bmp_1(ifs, &colorTable);

    default:
    cout<<"unsupported bitmap format"<<endl;
  }
  ifs.close();
}

Color& Image::getPixel(int x, int y)
{
  return pixel_data[y][x];
}

void Image::draw()
{
  draw(0,0);
}

void Image::draw(float x, float y)
{
  glBegin( GL_POINTS );
  int X = x;
  int Y = y;
  if (alpha_enabled)
  {
    for (vector<vector<Color> >::iterator y_iter = pixel_data.begin();
         y_iter!=pixel_data.end(); ++y_iter)
    {
      for (vector<Color>::iterator x_iter = y_iter->begin();
           x_iter != y_iter->end(); ++x_iter)
      {
        glColor4f(x_iter->R, x_iter->G, x_iter->B, (x_iter->alpha)*master_opacity);
        glVertex2i(X, Y);
        ++X;
      }
      X = x;
      ++Y;
    }
  }
  else
  {
    for (vector<vector<Color> >::iterator y_iter = pixel_data.begin();
         y_iter!=pixel_data.end(); ++y_iter)
    {
      for (vector<Color>::iterator x_iter = y_iter->begin();
           x_iter != y_iter->end(); ++x_iter)
      {
        glColor4f(x_iter->R, x_iter->G, x_iter->B, 1.0);
        glVertex2i(X, Y);
        ++X;
      }
      X = x;
      ++Y;
    }
  }
  glEnd();
}

void Image::enable_alpha()
{
  alpha_enabled = true;
}

void Image::disable_alpha()
{
  alpha_enabled = false;
}

bool Image::alpha() const
{
  return alpha_enabled;
}

void Image::set_opacity(const float& op)
{
  master_opacity = op;
}

// private Image member functions

void Image::read_bmp_32(ifstream& ifs)
{
  unsigned int data;
  unsigned short R, G, B;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; ++x)
    {
      read_int(&data, ifs);
      R = (data>>16) & 0xFF;
      G = (data>>8) & 0xFF;
      B = data & 0xFF;
      pixel_data[y][x] = Color((float)R/255.0, (float)G/255.0, (float)B/255.0);
    }
  }
}

void Image::read_bmp_24(ifstream& ifs)
{
  unsigned short R, G, B;
  int endBytes = xRes%4;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; ++x)
    {
      read_byte(&B, ifs);
      read_byte(&G, ifs);
      read_byte(&R, ifs);
      pixel_data[y][x] = Color((float)R/255.0, (float)G/255.0, (float)B/255.0);
    }
    ifs.ignore(endBytes);
  }
}

void Image::read_bmp_16(ifstream& ifs)
{
  unsigned int data;
  unsigned short R, G, B;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; x+=2)
    {
      read_int(&data, ifs);
      for (int i=0; i<2; ++i)
      {
        if (x+i >= xRes) break;
        int color = (data>>(i<<4)) & 0xFFFF;
        R = (color>>10) & 0x1F;
        G = (color>>5) & 0x1F;
        B = color & 0x1F;
        pixel_data[y][x + i] = Color((float)R/32.0, (float)G/32.0, (float)B/32.0);
      }
    }
  }
}

void Image::read_bmp_8(ifstream& ifs, vector<Color>* colorTable)
{
  unsigned int data;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; x+=4)
    {
      read_int(&data, ifs);
      for (int i=0; i<4; ++i)
      {
        if (x+i >= xRes) break;
        int color = (data>>(i<<3)) & 0xFF;
        pixel_data[y][x+i] = (*colorTable)[color];
      }
    }
  }
}


void Image::read_bmp_8_cmp(ifstream& ifs, vector<Color>* colorTable)
{
  unsigned int currentX = 0;
  unsigned int currentY = 0;
  unsigned short byte0, byte1, data;
  bool endOfFile = false;
  while (!endOfFile)
  {
    read_short(&data, ifs);
    byte0 = data & 0xFF;
    byte1 = (data>>8) & 0xFF;
    if (byte0 == 0) // escape
    {
      unsigned short x_offset;
      unsigned short y_offset;
      switch (byte1)
      {
        case 0: // new line
        currentX = 0;
        ++currentY;
        break;
        case 1: // end of file
        endOfFile = true;
        break;
        case 2: // translate
        read_byte(&x_offset, ifs);
        read_byte(&y_offset, ifs);
        currentX += x_offset;
        currentY -= y_offset;
        break;
        default: // absolute mode
        for (int i=0; i<byte1; ++i)
        {
          read_byte(&data, ifs);
          pixel_data[currentY][currentX] = (*colorTable)[data];
          ++currentX;
        }
        ifs.ignore(byte1%2);
      }
    }
    else // encoded mode
    {
      for (int i=0; i<byte0; ++i)
      {
        pixel_data[currentY][currentX] = (*colorTable)[byte1];
        ++currentX;
      }
    }
  }
}


void Image::read_bmp_4(ifstream& ifs, vector<Color>* colorTable)
{
  unsigned int data;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; x+=8)
    {
      read_int(&data, ifs);
      data = flipNibbles(data);
      for (int i=0; i<8; ++i)
      {
        if (x+i >= xRes) break;
        int color = (data>>((i<<2)) & 0xF);
        pixel_data[y][x+i] = (*colorTable)[color];
      }
    }
  }
}

void Image::read_bmp_4_cmp(ifstream& ifs, vector<Color>* colorTable)
{
  unsigned short nibble0, nibble1, byte0, byte1, data;
  unsigned int currentX = 0;
  unsigned int currentY = 0;
  bool endOfFile = false;
  bool parity = true;
  unsigned int byte_index = 0;
  while (!endOfFile)
  {
    if (byte_index % 2 != 0)
    {
      ifs.ignore(1);
      byte_index = 0;
    }
    read_short(&data, ifs);
    byte0 = data & 0xFF;
    byte1 = (data>>8) & 0xFF;
    if (byte0 == 0) // escape
    {
      switch (byte1)
      {
        case 0: // new line
        currentX = 0;
        ++currentY;
        if (currentY >= yRes) endOfFile = true;
        break;
        case 1: // end of file
        endOfFile = true;
        break;
        case 2: // translate
        unsigned short x_offset;
        unsigned short y_offset;
        read_byte(&x_offset, ifs);
        read_byte(&y_offset, ifs);
        currentX += x_offset;
        currentY -= y_offset;
        break;
        default: // absolute mode
        parity = true;
        for (int i=0; i<byte1; ++i)
        {
          if (parity)
          {
            read_byte(&data, ifs);
            byte_index = (byte_index + 1)%2;
            pixel_data[currentY][currentX] = (*colorTable)[(data>>4) & 0xF];
          }
          else
          {
            pixel_data[currentY][currentX] = (*colorTable)[(data) & 0xF];
          }
          parity = !parity;
          ++currentX;
        }
      }
    }
    else // encoded mode
    {
      parity = true;
      nibble0 = (byte1>>4) & 0xF;
      nibble1 = byte1 & 0xF;
      for (int i=0; i<byte0; ++i)
      {
        if (currentX >= xRes)
        {
          cout<<"********** out of bounds exception ************";
        }
        else
        {
          if (parity) pixel_data[currentY][currentX] = (*colorTable)[nibble0];
          else pixel_data[currentY][currentX] = (*colorTable)[nibble1];
        }
        parity = !parity;
        ++currentX;
      }
    }
  }
}

void Image::read_bmp_1(ifstream& ifs, vector<Color>* colorTable)
{
  unsigned int data;
  for (unsigned int y=0; y<yRes; ++y)
  {
    for (unsigned int x=0; x<xRes; x+=32