Dirk Bertels

The greatest malfunction of spirit
is to believe things (Louis Pasteur)

Bitmap de-compression

Introduction

Just recently we needed some embedded C code to decompress bitmap files (we use bitmaps in our Retinal camera to display icon-like images on the video screen for such applications as giving user-feedback). I include this code here not because it's anything fancy, but because I couldn't find any on the net. Much can be found on the bitmap format and windows applications that uses standard API to decompress, but nothing for embedded systems.

The bitmap format is used by image files using the .bmp extension. It was developed by Microsoft who uses bitmap images in the windows operating system for storing icons. The bitmap format uses the following structs in its header:

  1. typedef struct                       /**** BMP file header structure ****/  
  2.     {  
  3.     unsigned short bfType;           /* Magic number for file */  
  4.     unsigned int   bfSize;           /* Size of file */  
  5.     unsigned short bfReserved1;      /* Reserved */  
  6.     unsigned short bfReserved2;      /* ... */  
  7.     unsigned int   bfOffBits;        /* Offset to bitmap data */  
  8.     } BITMAPFILEHEADER;  
  9.   
  10. #  define BF_TYPE 0x4D42             /* "MB" */  
  11.   
  12. typedef struct                       /**** BMP file info structure ****/  
  13.     {  
  14.     unsigned int   biSize;           /* Size of info header */  
  15.     int            biWidth;          /* Width of image */  
  16.     int            biHeight;         /* Height of image */  
  17.     unsigned short biPlanes;         /* Number of color planes */  
  18.     unsigned short biBitCount;       /* Number of bits per pixel */  
  19.     unsigned int   biCompression;    /* Type of compression to use */  
  20.     unsigned int   biSizeImage;      /* Size of image data */  
  21.     int            biXPelsPerMeter;  /* X pixels per meter */  
  22.     int            biYPelsPerMeter;  /* Y pixels per meter */  
  23.     unsigned int   biClrUsed;        /* Number of colors used */  
  24.     unsigned int   biClrImportant;   /* Number of important colors */  
  25.     } BITMAPINFOHEADER;  
  26.   
  27. /* 
  28.  * Constants for the biCompression field... 
  29.  */  
  30.   
  31. #  define BI_RGB       0             /* No compression - straight BGR data */  
  32. #  define BI_RLE8      1             /* 8-bit run-length compression */  
  33. #  define BI_RLE4      2             /* 4-bit run-length compression */  
  34. #  define BI_BITFIELDS 3             /* RGB bitmap with RGB masks */  
  35.   
  36. typedef struct                       /**** Colormap entry structure ****/  
  37.     {  
  38.     unsigned char  rgbBlue;          /* Blue value */  
  39.     unsigned char  rgbGreen;         /* Green value */  
  40.     unsigned char  rgbRed;           /* Red value */  
  41.     unsigned char  rgbReserved;      /* Reserved */  
  42.     } RGBQUAD;  
  43.   
  44. typedef struct                       /**** Bitmap information structure ****/  
  45.     {  
  46.     BITMAPINFOHEADER bmiHeader;      /* Image header */  
  47.     RGBQUAD          bmiColors[256]; /* Image colormap */  
  48.     } BITMAPINFO;         



And here is the code that displays both bitmap and compressed bitmap files. The type of compression used is 8 bit Run Length Encoding (RLE), in which essentially, recurring pixels are stored as a single pixel with a count value. The compression algorithm can be found here. Other information on bitmaps can be found on the same site.

  1. // displays bitmap image top left corner at Xoff, Yoff  
  2. int BmpDisplay(const char *Image, int Xoff, int Yoff)  
  3. {  
  4.   unsigned char * ImageData;         // pointer to array keeping the binary bitmap data  
  5.   unsigned int Width;                // width of image  
  6.   unsigned int DisplayWidth;         // width of display  
  7.   unsigned int Height;               // height of image  
  8.   unsigned int Compression;          // stores value of compression                       
  9.   int x,y;                           // coordinates  
  10.     
  11.   BITMAPFILEHEADER * BMFH = (BITMAPFILEHEADER *) Image;  
  12.   BITMAPINFOHEADER * BMIH = (BITMAPINFOHEADER *) (Image + sizeof(BITMAPFILEHEADER));  
  13.   if(8 != SwapBytesShort(BMIH->biBitCount))  
  14.   {  
  15.     OutStr("biBitCount is %d, should be 8 !!!",SwapBytesShort(BMIH->biBitCount));  
  16.     return ERR_BITCOUNT_NOT_8;  
  17.   }  
  18.     
  19.   // calculate offset to 1st byte of image  
  20.   ImageData = (unsigned char *)(Image + SwapBytesInt(BMFH->bfOffBits));    
  21.   DisplayWidth = Width  = SwapBytesInt(BMIH->biWidth);  
  22.   Compression = SwapBytesInt(BMIH->biCompression);   
  23.   if (0 != Width & 0x03)  
  24.   {  
  25.     Width &= ~0x03;  
  26.     Width+=4;  
  27.   }  
  28.   Height = SwapBytesInt(BMIH->biHeight);  
  29.   
  30.   // No compression  
  31.   if(0 == Compression)                               
  32.   {  
  33.     for(y=Height-1;y>=0;y--)  
  34.     {  
  35.        for(x=0;x<Width;x++)  
  36.        {  
  37.          if(x<DisplayWidth)  
  38.            plot_pixel(x+Xoff,y+Yoff,*ImageData);  
  39.          ImageData++;  
  40.        }  
  41.     }  
  42.   }  
  43.   
  44.   // Bitmap is 8-bit RLE compressed  
  45.   else if (1 == Compression)                      
  46.   {  
  47.     unsigned char firstByte, secondByte;  
  48.     unsigned int currX, currY, i, state;  
  49.   
  50.     currX = Xoff;  
  51.     currY = Yoff;  
  52.     state = 0;  
  53.   
  54.     while(state == 0)  
  55.     {  
  56.       firstByte = *ImageData;  
  57.       ImageData++;  
  58.       if(0 != firstByte)  
  59.       {  
  60.         secondByte =*ImageData;  
  61.         for(i=0; i < firstByte; i++)  
  62.         {  
  63.           plot_pixel(currX, currY, secondByte);  
  64.           currX++;  
  65.         }  
  66.         ImageData++;  
  67.       }  
  68.       else   // value is zero  
  69.       {  
  70.         firstByte = *ImageData;            // store next byte  
  71.         ImageData++;  
  72.         switch (firstByte)  
  73.         {  
  74.           case 0:  
  75.             currX = Xoff;  
  76.             currY++;                       // move cursor to beginning of next line  
  77.             break;  
  78.           case 1:  
  79.             state = ERR_NONE;  
  80.             return ERR_NONE;               // end of bitmap, finish plotting  
  81.           case 2:  
  82.             currX += (int)(*ImageData);    // read byte and add value to x value  
  83.             ImageData++;  
  84.             currY += (int)(*ImageData);    // read byte and add value to y value  
  85.             ImageData++;  
  86.             break;  
  87.           // for any other value, print the next firstByte bytes  
  88.           default:                           
  89.             for(i = 0; i < firstByte; i++)  
  90.             {  
  91.               secondByte = *ImageData;  
  92.               plot_pixel(currX, currY, secondByte);  
  93.               ImageData++;  
  94.               currX++;  
  95.             }  
  96.             if(0 != firstByte & 0x01)      // if the run doesn't end on a word boundary,   
  97.               ImageData++;                 // advance the pointer  
  98.           } // END switch  
  99.         } // END else  
  100.       } // END while  
  101.     } // END else(if 1 == compression)  
  102.   
  103.   else  
  104.   {  
  105.      return ERR_COMPRESSION_NOT_SUPPORTED;  
  106.   }  
  107.   
  108.   return ERR_NONE;  
  109. }