bmp.cpp

Go to the documentation of this file.
00001 /* $Id: bmp.cpp 15568 2009-02-24 20:59:17Z smatz $ */
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 colours 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 colours 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 
00335     for (i = 0; i < info->palette_size; i++) {
00336       data->palette[i].b = ReadByte(buffer);
00337       data->palette[i].g = ReadByte(buffer);
00338       data->palette[i].r = ReadByte(buffer);
00339       if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
00340     }
00341   }
00342 
00343   return buffer->real_pos <= info->offset;
00344 }
00345 
00346 /*
00347  * Reads the bitmap
00348  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
00349  */
00350 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00351 {
00352   assert(info != NULL && data != NULL);
00353 
00354   data->bitmap = CallocT<byte>(info->width * info->height * ((info->bpp == 24) ? 3 : 1));
00355 
00356   /* Load image */
00357   SetStreamOffset(buffer, info->offset);
00358   switch (info->compression) {
00359   case 0: // no compression
00360     switch (info->bpp) {
00361     case 1:  return BmpRead1(buffer, info, data);
00362     case 4:  return BmpRead4(buffer, info, data);
00363     case 8:  return BmpRead8(buffer, info, data);
00364     case 24: return BmpRead24(buffer, info, data);
00365     default: NOT_REACHED(); return false;
00366     }
00367   case 1:  return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
00368   case 2:  return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
00369   default: NOT_REACHED(); return false;
00370   }
00371 }
00372 
00373 void BmpDestroyData(BmpData *data)
00374 {
00375   assert(data != NULL);
00376   free(data->palette);
00377   free(data->bitmap);
00378 }

Generated on Wed Jun 3 19:05:09 2009 for OpenTTD by  doxygen 1.5.6