Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

OpenBMP.cpp

Go to the documentation of this file.
00001 // format information gleaned from
00002 //   http://www.daubnet.com/formats/BMP.html
00003 // and
00004 //   http://www.edm2.com/0107/os2bmp.html
00005 //
00006 // If you have Visual Studio.NET:
00007 //   ms-help://MS.VSCC/MS.MSDNVS/gdi/bitmaps_7c36.htm
00008 
00009 #include <string.h>
00010 #include "corona.h"
00011 #include "SimpleImage.h"
00012 #include "Utility.h"
00013 
00014 
00015 namespace corona {
00016 
00017   struct Header {
00018     bool os2;
00019 
00020     int file_size;
00021     int data_offset;
00022     int width;
00023     int height;
00024     int bpp;
00025     int compression;
00026 
00027     int pitch;  // number of bytes in each scanline
00028     int image_size;
00029 
00030     auto_array<BGR> palette;
00031     int palette_size;
00032 
00033     // for bitfield specification...
00034     //                               /*- 16-bit only -*/
00035     u32 bf_red_mask,   bf_red_shift,   bf_red_rshift;
00036     u32 bf_green_mask, bf_green_shift, bf_green_rshift;
00037     u32 bf_blue_mask,  bf_blue_shift,  bf_blue_rshift;
00038   };
00039 
00040 
00041   bool   ReadHeader(File* file, Header& h);
00042   bool   ReadInfoHeader(File* file, Header& h);
00043   bool   ReadPalette(File* file, Header& h);
00044   Image* DecodeBitmap(File* file, const Header& h);
00045 
00046   
00047   Image* OpenBMP(File* file) {
00048     Header h;
00049     if (ReadHeader(file, h) &&
00050         ReadInfoHeader(file, h) &&
00051         ReadPalette(file, h)) {
00052 
00053       return DecodeBitmap(file, h);
00054 
00055     } else {
00056 
00057       return 0;
00058 
00059     }
00060   }
00061 
00062 
00063   bool ReadHeader(File* file, Header& h) {
00064     byte header[14];
00065     if (file->read(header, 14) != 14) {
00066       return false;
00067     }
00068 
00069     // check signature
00070     if (header[0] != 'B' || header[1] != 'M') {
00071       return false;
00072     }
00073 
00074     h.file_size   = read32_le(header + 2);
00075     h.data_offset = read32_le(header + 10);
00076     return true;
00077   }
00078 
00079 
00080   bool ReadInfoHeader(File* file, Header& h) {
00081 
00082     const int HEADER_READ_SIZE = 24;
00083 
00084     // read the only part of the header we need
00085     byte header[HEADER_READ_SIZE];
00086     if (file->read(header, HEADER_READ_SIZE) != HEADER_READ_SIZE) {
00087       return false;
00088     }
00089 
00090     int size = read32_le(header + 0);
00091     int width;
00092     int height;
00093     int planes;
00094     int bpp;
00095     int compression;
00096     int image_size;
00097 
00098     if (size < 40) {  // assume OS/2 bitmap
00099       if (size < 12) {
00100         return false;
00101       }
00102 
00103       h.os2 = true;
00104       width  = read16_le(header + 4);
00105       height = read16_le(header + 6);
00106       planes = read16_le(header + 8);
00107       bpp    = read16_le(header + 10);
00108       compression = 0;
00109       image_size = 0;
00110       
00111     } else {
00112 
00113       h.os2 = false;
00114       width       = read32_le(header + 4);
00115       height      = read32_le(header + 8);
00116       planes      = read16_le(header + 12);
00117       bpp         = read16_le(header + 14);
00118       compression = read32_le(header + 16);
00119       image_size  = read32_le(header + 20);
00120 
00121     }
00122     
00123     // sanity check the info header
00124     if (planes != 1) {
00125       return false;
00126     }
00127 
00128     // adjust image_size
00129     // (if compression == 0 or 3, manually calculate image size)
00130     int line_size = 0;
00131     if (compression == 0 || compression == 3) {
00132       line_size = (width * bpp + 7) / 8;
00133       line_size = (line_size + 3) / 4 * 4;  // 32-bit-aligned
00134       image_size = line_size * height;
00135     }
00136 
00137     h.width       = width;
00138     h.height      = height;
00139     h.bpp         = bpp;
00140     h.compression = compression;
00141     h.pitch       = line_size;
00142     h.image_size  = image_size;
00143 
00144     // jump forward (backward in the OS/2 case :) to the palette data
00145     file->seek(size - HEADER_READ_SIZE, File::CURRENT);
00146 
00147     return true;
00148   }
00149 
00150   // count the number of consecutive zeroes on the right side of a
00151   // binary number
00152   // 0x00F0 will return 4
00153   int count_right_zeroes(u32 n) {
00154     int total = 0;
00155     u32 c = 1;
00156     while ((total < 32) && ((n & c) == 0)) {
00157       c <<= 1;
00158       ++total;
00159     }
00160     return total;
00161   }
00162 
00163   // count the number of ones in a binary number
00164   // 0x00F1 will return 5
00165   int count_ones(u32 n) {
00166     int total = 0;
00167     u32 c = 1;
00168     for (int i = 0; i < 32; ++i) {
00169       if (n & c) {
00170         ++total;
00171       }
00172       c <<= 1;
00173     }
00174     return total;
00175   }
00176 
00177   bool ReadPalette(File* file, Header& h) {
00178 
00179     // initialize bit masks/shifts...  just in case
00180     h.bf_red_mask   = h.bf_red_shift   = h.bf_red_rshift   = 0;
00181     h.bf_green_mask = h.bf_green_shift = h.bf_green_rshift = 0;
00182     h.bf_blue_mask  = h.bf_blue_shift  = h.bf_blue_rshift  = 0;
00183 
00184     // if we're not a palettized image, don't even read a palette
00185     if (h.bpp > 8) {
00186       h.palette_size = 0;
00187 
00188       // do we have bitfields?
00189       if (h.compression == 3) {
00190 
00191         auto_array<byte> bitfields(new byte[12]);
00192         if (file->read(bitfields, 12) != 12) {
00193           return false;
00194         }
00195 
00196         h.bf_red_mask   = read32_le((byte*)bitfields);
00197         h.bf_green_mask = read32_le((byte*)bitfields + 4);
00198         h.bf_blue_mask  = read32_le((byte*)bitfields + 8);
00199 
00200         // calculate shifts
00201         h.bf_red_shift    = count_right_zeroes(h.bf_red_mask);
00202         h.bf_green_shift  = count_right_zeroes(h.bf_green_mask);
00203         h.bf_blue_shift   = count_right_zeroes(h.bf_blue_mask);
00204         h.bf_red_rshift   = 8 - count_ones(h.bf_red_mask);
00205         h.bf_green_rshift = 8 - count_ones(h.bf_green_mask);
00206         h.bf_blue_rshift  = 8 - count_ones(h.bf_blue_mask);
00207 
00208       // otherwise, set default bitfield entries
00209       } else {
00210 
00211         if (h.bpp == 16) {
00212 
00213           h.bf_red_mask     = 0x7C00;
00214           h.bf_red_shift    = 10;
00215           h.bf_red_rshift   = 3;
00216 
00217           h.bf_green_mask   = 0x03E0;
00218           h.bf_green_shift  = 5;
00219           h.bf_green_rshift = 3;
00220 
00221           h.bf_blue_mask    = 0x001F;
00222           h.bf_blue_shift   = 0;
00223           h.bf_blue_rshift  = 3;
00224 
00225         } else if (h.bpp == 32) {
00226 
00227           // these don't need any rshift
00228           h.bf_red_mask   = 0x00FF0000; h.bf_red_shift   = 16;
00229           h.bf_green_mask = 0x0000FF00; h.bf_green_shift = 8;
00230           h.bf_blue_mask  = 0x000000FF; h.bf_blue_shift  = 0;
00231 
00232         }
00233       }
00234 
00235       return true;
00236     }
00237 
00238     if (h.bpp <= 8) {
00239       h.palette_size = 1 << h.bpp;
00240     } else {
00241       h.palette_size = 0;
00242       return true;
00243     }
00244     h.palette = new BGR[h.palette_size];
00245 
00246     // read the BMP color table
00247     const int buffer_size = h.palette_size * (h.os2 ? 3 : 4);
00248     auto_array<byte> buffer(new byte[buffer_size]);
00249     if (file->read(buffer, buffer_size) != buffer_size) {
00250       return false;
00251     }
00252 
00253     byte* in = buffer;
00254     BGR* out = h.palette;
00255     for (int i = 0; i < h.palette_size; ++i) {
00256       out->blue  = *in++;
00257       out->green = *in++;
00258       out->red   = *in++;
00259       if (!h.os2) {
00260         ++in;  // skip alpha
00261       }
00262       ++out;
00263     }
00264 
00265     return true;
00266   }
00267 
00268 
00269   bool advance(int& x, int& y, const Header& h) {
00270     if (++x >= h.width) {
00271       x = 0;
00272       if (++y >= h.height) {
00273         return false;
00274       }
00275     }
00276     return true;
00277   }
00278 
00279   Image* ReadBitmap1(const byte* raster_data, const Header& h) {
00280     auto_array<byte> pixels(new byte[h.width * h.height]);
00281     
00282     auto_array<BGR> palette(new BGR[256]);
00283     memset(palette, 0, 256 * sizeof(BGR));
00284     memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
00285 
00286     for (int i = 0; i < h.height; ++i) {
00287       const byte* in = raster_data + i * h.pitch;
00288       byte* out = pixels + (h.height - i - 1) * h.width;
00289 
00290       int mask = 128;
00291       for (int j = 0; j < h.width; ++j) {
00292         *out++ = (*in & mask) > 0;
00293 
00294         mask >>= 1;
00295         if (mask == 0) {
00296           ++in;
00297           mask = 128;
00298         }
00299       }
00300     }
00301 
00302     return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
00303                            (byte*)palette.release(), 256, PF_B8G8R8);
00304   }
00305 
00306   Image* ReadBitmap4(const byte* raster_data, const Header& h) {
00307     auto_array<byte> pixels(new byte[h.width * h.height]);
00308     
00309     auto_array<BGR> palette(new BGR[256]);
00310     memset(palette, 0, 256 * sizeof(BGR));
00311     memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
00312 
00313     for (int i = 0; i < h.height; ++i) {
00314       const byte* in = raster_data + i * h.pitch;
00315       byte* out = pixels + (h.height - i - 1) * h.width;
00316 
00317       for (int j = 0; j < h.width / 2; ++j) {
00318         *out++ = (*in >> 4);
00319         *out++ = (*in & 0x0F);
00320 
00321         ++in;
00322       }
00323 
00324       if (h.width % 2) {
00325         *out++ = (*in >> 4);
00326       }
00327     }
00328 
00329     return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
00330                            (byte*)palette.release(), 256, PF_B8G8R8);
00331   }
00332 
00333   Image* ReadBitmapRLE4(const byte* raster_data, const Header& h) {
00334     auto_array<byte> pixels(new byte[h.width * h.height]);
00335     
00336     auto_array<BGR> palette(new BGR[256]);
00337     memset(palette, 0, 256 * sizeof(BGR));
00338     memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
00339 
00340     // by default, we have an empty bitmap
00341     memset(pixels, 0, h.width * h.height);
00342 
00343     // we read the image from the bottom down, and then flip it when
00344     // we're done
00345     int x = 0;
00346     int y = 0;
00347 
00348     const byte* in = raster_data;
00349     while (in - raster_data < h.image_size - 1) {
00350       byte n = *in++;
00351       byte c = *in++;
00352 
00353       if (n == 0) {  // escape code
00354 
00355         if (c == 0) {         // end of line
00356           x = 0;
00357 
00358           //++y;  // XXXaegis uhhh...  uhhh...  :)  it works this way...
00359 
00360           if (y >= h.height) {
00361             // did we go too far?
00362             break;
00363           }
00364         } else if (c == 1) {  // end of bitmap
00365           break;
00366         } else if (c == 2) {  // delta
00367 
00368           // do we have enough space?
00369           if (in - raster_data >= h.image_size - 1) {
00370             break;
00371           }
00372 
00373           // I have no idea how I'm supposed to do this...
00374           // Let's take a guess!
00375           int dx = *in++;
00376           int dy = *in++;
00377           x = (x + dx) % h.width;
00378           y += dy + (x + dx) / h.width;
00379           if (y >= h.height) {
00380             // if we went too far, stop now
00381             break;
00382           }
00383 
00384         } else {              // read uncompressed
00385 
00386           // the input raster data is padded on DWORD boundaries
00387           // c == num_pixels
00388           int num_bytes = (c + 3) / 4 * 2;
00389           
00390           // make sure we have enough space
00391           if (in - raster_data > h.image_size - num_bytes) {
00392             break;
00393           }
00394 
00395           // nasty decoding loop...
00396           int i = 0;
00397           int j = 0;
00398           while (true) {
00399             byte l = (in[j] & 0xF0) >> 4;
00400             byte r = (in[j] & 0x0F);
00401             ++j;
00402 
00403             pixels[y * h.width + x] = l;
00404             if (!advance(x, y, h) || ++i >= c) {
00405               break;
00406             }
00407 
00408             pixels[y * h.width + x] = r;
00409             if (!advance(x, y, h) || ++i >= c) {
00410               break;
00411             }
00412           }
00413           // make SURE we move forward the right number of bytes
00414           in += num_bytes;
00415         }
00416 
00417       } else {
00418 
00419         // a less nasty decoding loop...
00420         byte lc = (c & 0xF0) >> 4;
00421         byte rc = c & 0x0F;
00422 
00423         int i = 0;
00424         while (true) {
00425           pixels[y * h.width + x] = lc;
00426           if (!advance(x, y, h) || ++i >= n) {
00427             break;
00428           }
00429 
00430           pixels[y * h.width + x] = rc;
00431           if (!advance(x, y, h) || ++i >= n) {
00432             break;
00433           }
00434         }
00435 
00436       } // end if
00437     } // end while
00438 
00439     // flippy flippy!
00440     int pitch = h.width;
00441     auto_array<byte> row(new byte[pitch]);
00442     for (int i = 0; i < h.height / 2; ++i) {
00443       int j = h.height - i - 1;
00444       memcpy((byte*)row,         pixels + i * pitch, pitch);
00445       memcpy(pixels + i * pitch, pixels + j * pitch, pitch);
00446       memcpy(pixels + j * pitch, (byte*)row,         pitch);
00447     }
00448 
00449     return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
00450                            (byte*)palette.release(), 256, PF_B8G8R8);
00451   }
00452 
00453   Image* ReadBitmap8(const byte* raster_data, const Header& h) {
00454     auto_array<byte> pixels(new byte[h.width * h.height]);
00455     
00456     auto_array<BGR> palette(new BGR[256]);
00457     memset(palette, 0, 256 * sizeof(BGR));
00458     memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
00459 
00460     for (int i = 0; i < h.height; ++i) {
00461       const byte* in = raster_data + i * h.pitch;
00462       byte* out = pixels + (h.height - i - 1) * h.width;
00463 
00464       for (int j = 0; j < h.width; ++j) {
00465         *out++ = *in++;
00466       }
00467     }
00468 
00469     return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
00470                            (byte*)palette.release(), 256, PF_B8G8R8);
00471   }
00472 
00473   Image* ReadBitmapRLE8(const byte* raster_data, const Header& h) {
00474     auto_array<byte> pixels(new byte[h.width * h.height]);
00475     
00476     auto_array<BGR> palette(new BGR[256]);
00477     memset(palette, 0, 256 * sizeof(BGR));
00478     memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
00479 
00480     // by default, we have an empty bitmap
00481     memset(pixels, 0, h.width * h.height);
00482 
00483     // we read the image from the bottom down, and then flip it when
00484     // we're done
00485     int x = 0;
00486     int y = 0;
00487 
00488     const byte* in = raster_data;
00489     while (in - raster_data < h.image_size - 1) {
00490       byte n = *in++;
00491       byte c = *in++;
00492 
00493       if (n == 0) {  // escape code
00494 
00495         if (c == 0) {         // end of line
00496           x = 0;
00497 
00498           //++y;  // XXXaegis uhhh...  uhhh...  :)  it works this way...
00499 
00500           if (y >= h.height) {
00501             // did we go too far?
00502             break;
00503           }
00504         } else if (c == 1) {  // end of bitmap
00505           break;
00506         } else if (c == 2) {  // delta
00507 
00508           // do we have enough space?
00509           if (in - raster_data >= h.image_size - 1) {
00510             break;
00511           }
00512 
00513           // I have no idea how I'm supposed to do this...
00514           // Let's take a guess!
00515           int dx = *in++;
00516           int dy = *in++;
00517           x = (x + dx) % h.width;
00518           y += dy + (x + dx) / h.width;
00519           if (y >= h.height) {
00520             // if we went too far, stop now
00521             break;
00522           }
00523 
00524         } else {              // read uncompressed
00525 
00526           // c == num_pixels
00527           int num_bytes = (c + 1) / 2 * 2;
00528           
00529           // make sure we have enough space
00530           if (in - raster_data > h.image_size - num_bytes) {
00531             break;
00532           }
00533 
00534           // decoding loop...
00535           int i = 0;
00536           int j = 0;
00537           while (true) {
00538             pixels[y * h.width + x] = in[j++];
00539             if (!advance(x, y, h) || ++i >= c) {
00540               break;
00541             }
00542           }
00543           // make SURE we move forward the right number of bytes
00544           in += num_bytes;
00545         }
00546 
00547       } else {
00548 
00549         int i = 0;
00550         while (true) {
00551           pixels[y * h.width + x] = c;
00552           if (!advance(x, y, h) || ++i >= n) {
00553             break;
00554           }
00555         }
00556 
00557       } // end if
00558     } // end while
00559 
00560     // flippy flippy!
00561     int pitch = h.width;
00562     auto_array<byte> row(new byte[pitch]);
00563     for (int i = 0; i < h.height / 2; ++i) {
00564       int j = h.height - i - 1;
00565       memcpy((byte*)row,         pixels + i * pitch, pitch);
00566       memcpy(pixels + i * pitch, pixels + j * pitch, pitch);
00567       memcpy(pixels + j * pitch, (byte*)row,         pitch);
00568     }
00569 
00570     return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
00571                            (byte*)palette.release(), 256, PF_B8G8R8);
00572   }
00573   
00574   Image* ReadBitmap16(const byte* raster_data, const Header& h) {
00575     auto_array<RGB> pixels(new RGB[h.width * h.height]);
00576 
00577     for (int i = 0; i < h.height; ++i) {
00578       const byte* in = raster_data + i * h.pitch;
00579       RGB* out = pixels + (h.height - i - 1) * h.width;
00580 
00581       for (int j = 0; j < h.width; ++j) {
00582         int clr = read16_le(in);
00583         in += 2;
00584 
00585 #define C16(C) \
00586   (byte)( ((clr & h.bf_##C##_mask) >> h.bf_##C##_shift) << h.bf_##C##_rshift);
00587 
00588         out->red   = C16(red);
00589         out->green = C16(green);
00590         out->blue  = C16(blue);
00591         ++out;
00592 
00593 #undef C16
00594       }
00595     }
00596 
00597     return new SimpleImage(h.width, h.height, PF_R8G8B8,
00598                            (byte*)pixels.release());
00599   }
00600 
00601   Image* ReadBitmap24(const byte* raster_data, const Header& h) {
00602     auto_array<BGR> pixels(new BGR[h.width * h.height]);
00603 
00604     for (int i = 0; i < h.height; ++i) {
00605       const byte* in = raster_data + i * h.pitch;
00606       BGR* out = pixels + (h.height - i - 1) * h.width;
00607 
00608       for (int j = 0; j < h.width; ++j) {
00609         out->blue  = *in++;
00610         out->green = *in++;
00611         out->red   = *in++;
00612         ++out;
00613       }
00614     }
00615 
00616     return new SimpleImage(h.width, h.height, PF_B8G8R8,
00617                            (byte*)pixels.release());
00618   }
00619 
00620   Image* ReadBitmap32(const byte* raster_data, const Header& h) {
00621     auto_array<RGB> pixels(new RGB[h.width * h.height]);
00622 
00623     for (int i = 0; i < h.height; ++i) {
00624       const byte* in = raster_data + i * h.pitch;
00625       RGB* out = pixels + (h.height - i - 1) * h.width;
00626 
00627       for (int j = 0; j < h.width; ++j) {
00628         u32 pixel = read32_le(in);
00629         in += 4;
00630         out->red   = (byte)((pixel & h.bf_red_mask)   >> h.bf_red_shift);
00631         out->green = (byte)((pixel & h.bf_green_mask) >> h.bf_green_shift);
00632         out->blue  = (byte)((pixel & h.bf_blue_mask)  >> h.bf_blue_shift);
00633         ++out;
00634       }
00635     }
00636 
00637     return new SimpleImage(h.width, h.height, PF_R8G8B8,
00638                            (byte*)pixels.release());
00639   }
00640 
00641   Image* DecodeBitmap(File* file, const Header& h) {
00642 
00643     if (!file->seek(h.data_offset, File::BEGIN)) {
00644       return 0;
00645     }
00646 
00647     // the raster data stored in the file
00648     auto_array<byte> raster_data(new byte[h.image_size]);
00649     if (file->read(raster_data, h.image_size) != h.image_size) {
00650       return 0;
00651     }
00652 
00653     // the output pixel buffer (parameter to new SimpleImage)
00654     auto_array<byte> pixels(new byte[h.width * h.height * 3]);
00655 
00656     typedef Image* (*Decoder)(const byte* raster_data, const Header& h);
00657 
00658     Decoder decoder = 0;
00659 
00660     if      (h.bpp == 1  &&  h.compression == 0)  { decoder = ReadBitmap1;    }
00661     else if (h.bpp == 4  &&  h.compression == 0)  { decoder = ReadBitmap4;    }
00662     else if (h.bpp == 4  &&  h.compression == 2)  { decoder = ReadBitmapRLE4; }
00663     else if (h.bpp == 8  &&  h.compression == 0)  { decoder = ReadBitmap8;    }
00664     else if (h.bpp == 8  &&  h.compression == 1)  { decoder = ReadBitmapRLE8; }
00665     else if (h.bpp == 16 && (h.compression == 0 ||
00666                              h.compression == 3)) { decoder = ReadBitmap16;   }
00667     else if (h.bpp == 24 &&  h.compression == 0)  { decoder = ReadBitmap24;   }
00668     else if (h.bpp == 32 && (h.compression == 0 ||
00669                              h.compression == 3)) { decoder = ReadBitmap32;   }
00670 
00671     if (decoder) {
00672       return decoder(raster_data.get(), h);
00673     } else {
00674       return 0;
00675     }
00676   }
00677 
00678 }

Generated on Thu Oct 2 12:59:31 2003 for corona by doxygen1.3-rc1