# bmp.cpp

Go to the documentation of this file.

    00001 /* $Id: bmp.cpp 11691 2007-12-25 09:48:53Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "bmp.h"
    00007 #include "core/bitmath_func.hpp"
    00008 #include "core/alloc_func.hpp"
    00009
    00010 void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file)
    00011 {
    00012   buffer->pos      = -1;
    00013   buffer->file     = file;
    00014   buffer->read     = 0;
    00015   buffer->real_pos = ftell(file);
    00016 }
    00017
    00018 static inline void AdvanceBuffer(BmpBuffer *buffer)
    00019 {
    00020   buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
    00021   buffer->pos  = 0;
    00022 }
    00023
    00024 static inline bool EndOfBuffer(BmpBuffer *buffer)
    00025 {
    00026   if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
    00027   return buffer->pos == buffer->read;
    00028 }
    00029
    00030 static inline byte ReadByte(BmpBuffer *buffer)
    00031 {
    00032   if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
    00033   buffer->real_pos++;
    00034   return buffer->data[buffer->pos++];
    00035 }
    00036
    00037 static inline uint16 ReadWord(BmpBuffer *buffer)
    00038 {
    00039   uint16 var = ReadByte(buffer);
    00040   return var | (ReadByte(buffer) << 8);
    00041 }
    00042
    00043 static inline uint32 ReadDword(BmpBuffer *buffer)
    00044 {
    00045   uint32 var = ReadWord(buffer);
    00046   return var | (ReadWord(buffer) << 16);
    00047 }
    00048
    00049 static inline void SkipBytes(BmpBuffer *buffer, int bytes)
    00050 {
    00051   int i;
    00052   for (i = 0; i < bytes; i++) ReadByte(buffer);
    00053 }
    00054
    00055 static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
    00056 {
    00057   fseek(buffer->file, offset, SEEK_SET);
    00058   buffer->pos = -1;
    00059   buffer->real_pos = offset;
    00060   AdvanceBuffer(buffer);
    00061 }
    00062
    00067 static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00068 {
    00069   uint x, y, i;
    00070   byte pad = GB(4 - info->width / 8, 0, 2);
    00071   byte *pixel_row;
    00072   byte b;
    00073   for (y = info->height; y > 0; y--) {
    00074     x = 0;
    00075     pixel_row = &data->bitmap[(y - 1) * info->width];
    00076     while (x < info->width) {
    00077       if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
    00078       b = ReadByte(buffer);
    00079       for (i = 8; i > 0; i--) {
    00080         if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
    00081         x++;
    00082       }
    00083     }
    00084     /* Padding for 32 bit align */
    00085     SkipBytes(buffer, pad);
    00086   }
    00087   return true;
    00088 }
    00089
    00094 static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00095 {
    00096   uint x, y;
    00097   byte pad = GB(4 - info->width / 2, 0, 2);
    00098   byte *pixel_row;
    00099   byte b;
    00100   for (y = info->height; y > 0; y--) {
    00101     x = 0;
    00102     pixel_row = &data->bitmap[(y - 1) * info->width];
    00103     while (x < info->width) {
    00104       if (EndOfBuffer(buffer)) return false;  // the file is shorter than expected
    00105       b = ReadByte(buffer);
    00106       *pixel_row++ = GB(b, 4, 4);
    00107       x++;
    00108       if (x < info->width) {
    00109         *pixel_row++ = GB(b, 0, 4);
    00110         x++;
    00111       }
    00112     }
    00113     /* Padding for 32 bit align */
    00114     SkipBytes(buffer, pad);
    00115   }
    00116   return true;
    00117 }
    00118
    00123 static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00124 {
    00125   uint i;
    00126   uint x = 0;
    00127   uint y = info->height - 1;
    00128   byte n, c, b;
    00129   byte *pixel = &data->bitmap[y * info->width];
    00130   while (y != 0 || x < info->width) {
    00131     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
    00132     n = ReadByte(buffer);
    00133     c = ReadByte(buffer);
    00134     if (n == 0) {
    00135       switch (c) {
    00136       case 0: // end of line
    00137         x = 0;
    00138         pixel = &data->bitmap[--y * info->width];
    00139         break;
    00140       case 1: // end of bitmap
    00141         x = info->width;
    00142         y = 0;
    00143         pixel = NULL;
    00144         break;
    00145       case 2: // delta
    00146         x += ReadByte(buffer);
    00147         i = ReadByte(buffer);
    00148         if (x >= info->width || (y == 0 && i > 0)) return false;
    00149         y -= i;
    00150         pixel = &data->bitmap[y * info->width + x];
    00151         break;
    00152       default: // uncompressed
    00153         i = 0;
    00154         while (i++ < c) {
    00155           if (EndOfBuffer(buffer) || x >= info->width) return false;
    00156           b = ReadByte(buffer);
    00157           *pixel++ = GB(b, 4, 4);
    00158           x++;
    00159           if (x < info->width && i++ < c) {
    00160             *pixel++ = GB(b, 0, 4);
    00161             x++;
    00162           }
    00163         }
    00164         /* Padding for 16 bit align */
    00165         SkipBytes(buffer, ((c + 1) / 2) % 2);
    00166         break;
    00167       }
    00168     } else {
    00169       i = 0;
    00170       while (i++ < n) {
    00171         if (EndOfBuffer(buffer) || x >= info->width) return false;
    00172         *pixel++ = GB(c, 4, 4);
    00173         x++;
    00174         if (x < info->width && i++ < n) {
    00175           *pixel++ = GB(c, 0, 4);
    00176           x++;
    00177         }
    00178       }
    00179     }
    00180   }
    00181   return true;
    00182 }
    00183
    00187 static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00188 {
    00189   uint i;
    00190   uint y;
    00191   byte pad = GB(4 - info->width, 0, 2);
    00192   byte *pixel;
    00193   for (y = info->height; y > 0; y--) {
    00194     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
    00195     pixel = &data->bitmap[(y - 1) * info->width];
    00196     for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
    00197     /* Padding for 32 bit align */
    00198     SkipBytes(buffer, pad);
    00199   }
    00200   return true;
    00201 }
    00202
    00206 static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00207 {
    00208   uint i;
    00209   uint x = 0;
    00210   uint y = info->height - 1;
    00211   byte n, c;
    00212   byte *pixel = &data->bitmap[y * info->width];
    00213   while (y != 0 || x < info->width) {
    00214     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
    00215     n = ReadByte(buffer);
    00216     c = ReadByte(buffer);
    00217     if (n == 0) {
    00218       switch (c) {
    00219       case 0: // end of line
    00220         x = 0;
    00221         pixel = &data->bitmap[--y * info->width];
    00222         break;
    00223       case 1: // end of bitmap
    00224         x = info->width;
    00225         y = 0;
    00226         pixel = NULL;
    00227         break;
    00228       case 2: // delta
    00229         x += ReadByte(buffer);
    00230         i = ReadByte(buffer);
    00231         if (x >= info->width || (y == 0 && i > 0)) return false;
    00232         y -= i;
    00233         pixel = &data->bitmap[y * info->width + x];
    00234         break;
    00235       default: // uncompressed
    00236         if ((x += c) > info->width) return false;
    00237         for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer);
    00238         /* Padding for 16 bit align */
    00239         SkipBytes(buffer, c % 2);
    00240         break;
    00241       }
    00242     } else {
    00243       for (i = 0; i < n; i++) {
    00244         if (x >= info->width) return false;
    00245         *pixel++ = c;
    00246         x++;
    00247       }
    00248     }
    00249   }
    00250   return true;
    00251 }
    00252
    00256 static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00257 {
    00258   uint x, y;
    00259   byte pad = GB(4 - info->width * 3, 0, 2);
    00260   byte *pixel_row;
    00261   for (y = info->height; y > 0; y--) {
    00262     pixel_row = &data->bitmap[(y - 1) * info->width * 3];
    00263     for (x = 0; x < info->width; x++) {
    00264       if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
    00265       *(pixel_row + 2) = ReadByte(buffer); // green
    00266       *(pixel_row + 1) = ReadByte(buffer); // blue
    00267       *pixel_row       = ReadByte(buffer); // red
    00268       pixel_row += 3;
    00269     }
    00270     /* Padding for 32 bit align */
    00271     SkipBytes(buffer, pad);
    00272   }
    00273   return true;
    00274 }
    00275
    00276 /*
    00277  * Reads bitmap headers, and palette (if any)
    00278  */
    00279 bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00280 {
    00281   uint32 header_size;
    00282   assert(info != NULL);
    00283
    00284   /* Reading BMP header */
    00285   if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
    00286   SkipBytes(buffer, 8); // skip file size and reserved
    00287   info->offset = ReadDword(buffer);
    00288
    00289   /* Reading info header */
    00290   header_size = ReadDword(buffer);
    00291   if (header_size < 12) return false; // info header should be at least 12 bytes long
    00292
    00293   info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
    00294
    00295   if (info->os2_bmp) {
    00296     info->width = ReadWord(buffer);
    00297     info->height = ReadWord(buffer);
    00298     header_size -= 8;
    00299   } else {
    00300     info->width = ReadDword(buffer);
    00301     info->height = ReadDword(buffer);
    00302     header_size -= 12;
    00303   }
    00304
    00305   if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
    00306
    00307   info->bpp = ReadWord(buffer);
    00308   if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
    00309     /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
    00310     return false;
    00311   }
    00312
    00313   /* Reads compression method if available in info header*/
    00314   if ((header_size -= 4) >= 4) {
    00315     info->compression = ReadDword(buffer);
    00316     header_size -= 4;
    00317   }
    00318
    00319   /* Only 4-bit and 8-bit rle compression is supported */
    00320   if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
    00321
    00322   if (info->bpp <= 8) {
    00323     uint i;
    00324
    00325     /* Reads number of colors if available in info header */
    00326     if (header_size >= 16) {
    00327       SkipBytes(buffer, 12);                  // skip image size and resolution
    00328       info->palette_size = ReadDword(buffer); // number of colors in palette
    00329       SkipBytes(buffer, header_size - 16);    // skip the end of info header
    00330     }
    00331     if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
    00332
    00333     data->palette = CallocT<Colour>(info->palette_size);
    00334     if (data->palette == NULL) return false;
    00335
    00336     for (i = 0; i < info->palette_size; i++) {
    00337       data->palette[i].b = ReadByte(buffer);
    00338       data->palette[i].g = ReadByte(buffer);
    00339       data->palette[i].r = ReadByte(buffer);
    00340       if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
    00341     }
    00342   }
    00343
    00344   return buffer->real_pos <= info->offset;
    00345 }
    00346
    00347 /*
    00348  * Reads the bitmap
    00349  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
    00350  */
    00351 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
    00352 {
    00353   assert(info != NULL && data != NULL);
    00354
    00355   data->bitmap = CallocT<byte>(info->width * info->height * ((info->bpp == 24) ? 3 : 1));
    00356   if (data->bitmap == NULL) return false;
    00357
    00358   /* Load image */
    00359   SetStreamOffset(buffer, info->offset);
    00360   switch (info->compression) {
    00361   case 0: // no compression
    00362     switch (info->bpp) {
    00363     case 1:  return BmpRead1(buffer, info, data);
    00364     case 4:  return BmpRead4(buffer, info, data);
    00365     case 8:  return BmpRead8(buffer, info, data);
    00366     case 24: return BmpRead24(buffer, info, data);
    00367     default: NOT_REACHED(); return false;
    00368     }
    00369   case 1:  return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
    00370   case 2:  return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
    00371   default: NOT_REACHED(); return false;
    00372   }
    00373 }
    00374
    00375 void BmpDestroyData(BmpData *data)
    00376 {
    00377   assert(data != NULL);
    00378   free(data->palette);
    00379   free(data->bitmap);
    00380 }