bmp.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "bmp.h"
00014 #include "core/bitmath_func.hpp"
00015 #include "core/alloc_func.hpp"
00016 #include "core/mem_func.hpp"
00017 
00018 void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file)
00019 {
00020   buffer->pos      = -1;
00021   buffer->file     = file;
00022   buffer->read     = 0;
00023   buffer->real_pos = ftell(file);
00024 }
00025 
00026 static inline void AdvanceBuffer(BmpBuffer *buffer)
00027 {
00028   buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
00029   buffer->pos  = 0;
00030 }
00031 
00032 static inline bool EndOfBuffer(BmpBuffer *buffer)
00033 {
00034   if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
00035   return buffer->pos == buffer->read;
00036 }
00037 
00038 static inline byte ReadByte(BmpBuffer *buffer)
00039 {
00040   if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
00041   buffer->real_pos++;
00042   return buffer->data[buffer->pos++];
00043 }
00044 
00045 static inline uint16 ReadWord(BmpBuffer *buffer)
00046 {
00047   uint16 var = ReadByte(buffer);
00048   return var | (ReadByte(buffer) << 8);
00049 }
00050 
00051 static inline uint32 ReadDword(BmpBuffer *buffer)
00052 {
00053   uint32 var = ReadWord(buffer);
00054   return var | (ReadWord(buffer) << 16);
00055 }
00056 
00057 static inline void SkipBytes(BmpBuffer *buffer, int bytes)
00058 {
00059   int i;
00060   for (i = 0; i < bytes; i++) ReadByte(buffer);
00061 }
00062 
00063 static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
00064 {
00065   fseek(buffer->file, offset, SEEK_SET);
00066   buffer->pos = -1;
00067   buffer->real_pos = offset;
00068   AdvanceBuffer(buffer);
00069 }
00070 
00075 static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00076 {
00077   uint x, y, i;
00078   byte pad = GB(4 - info->width / 8, 0, 2);
00079   byte *pixel_row;
00080   byte b;
00081   for (y = info->height; y > 0; y--) {
00082     x = 0;
00083     pixel_row = &data->bitmap[(y - 1) * info->width];
00084     while (x < info->width) {
00085       if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00086       b = ReadByte(buffer);
00087       for (i = 8; i > 0; i--) {
00088         if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
00089         x++;
00090       }
00091     }
00092     /* Padding for 32 bit align */
00093     SkipBytes(buffer, pad);
00094   }
00095   return true;
00096 }
00097 
00102 static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00103 {
00104   uint x, y;
00105   byte pad = GB(4 - info->width / 2, 0, 2);
00106   byte *pixel_row;
00107   byte b;
00108   for (y = info->height; y > 0; y--) {
00109     x = 0;
00110     pixel_row = &data->bitmap[(y - 1) * info->width];
00111     while (x < info->width) {
00112       if (EndOfBuffer(buffer)) return false;  // the file is shorter than expected
00113       b = ReadByte(buffer);
00114       *pixel_row++ = GB(b, 4, 4);
00115       x++;
00116       if (x < info->width) {
00117         *pixel_row++ = GB(b, 0, 4);
00118         x++;
00119       }
00120     }
00121     /* Padding for 32 bit align */
00122     SkipBytes(buffer, pad);
00123   }
00124   return true;
00125 }
00126 
00131 static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00132 {
00133   uint x = 0;
00134   uint y = info->height - 1;
00135   byte *pixel = &data->bitmap[y * info->width];
00136   while (y != 0 || x < info->width) {
00137     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00138 
00139     byte n = ReadByte(buffer);
00140     byte c = ReadByte(buffer);
00141     if (n == 0) {
00142       switch (c) {
00143         case 0: // end of line
00144           x = 0;
00145           if (y == 0) return false;
00146           pixel = &data->bitmap[--y * info->width];
00147           break;
00148 
00149         case 1: // end of bitmap
00150           return true;
00151 
00152         case 2: { // delta
00153           if (EndOfBuffer(buffer)) return false;
00154           byte dx = ReadByte(buffer);
00155           byte dy = ReadByte(buffer);
00156 
00157           /* Check for over- and underflow. */
00158           if (x + dx >= info->width || x + dx < x || dy > y) return false;
00159 
00160           x += dx;
00161           y -= dy;
00162           pixel = &data->bitmap[y * info->width + x];
00163           break;
00164         }
00165 
00166         default: { // uncompressed
00167           uint i = 0;
00168           while (i++ < c) {
00169             if (EndOfBuffer(buffer) || x >= info->width) return false;
00170             byte b = ReadByte(buffer);
00171             *pixel++ = GB(b, 4, 4);
00172             x++;
00173             if (i++ < c) {
00174               if (x >= info->width) return false;
00175               *pixel++ = GB(b, 0, 4);
00176               x++;
00177             }
00178           }
00179           /* Padding for 16 bit align */
00180           SkipBytes(buffer, ((c + 1) / 2) % 2);
00181           break;
00182         }
00183       }
00184     } else {
00185       /* Apparently it is common to encounter BMPs where the count of
00186        * pixels to be written is higher than the remaining line width.
00187        * Ignore the superfluous pixels instead of reporting an error. */
00188       uint i = 0;
00189       while (x < info->width && i++ < n) {
00190         *pixel++ = GB(c, 4, 4);
00191         x++;
00192         if (x < info->width && i++ < n) {
00193           *pixel++ = GB(c, 0, 4);
00194           x++;
00195         }
00196       }
00197     }
00198   }
00199   return true;
00200 }
00201 
00205 static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00206 {
00207   uint i;
00208   uint y;
00209   byte pad = GB(4 - info->width, 0, 2);
00210   byte *pixel;
00211   for (y = info->height; y > 0; y--) {
00212     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00213     pixel = &data->bitmap[(y - 1) * info->width];
00214     for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
00215     /* Padding for 32 bit align */
00216     SkipBytes(buffer, pad);
00217   }
00218   return true;
00219 }
00220 
00224 static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00225 {
00226   uint x = 0;
00227   uint y = info->height - 1;
00228   byte *pixel = &data->bitmap[y * info->width];
00229   while (y != 0 || x < info->width) {
00230     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00231 
00232     byte n = ReadByte(buffer);
00233     byte c = ReadByte(buffer);
00234     if (n == 0) {
00235       switch (c) {
00236         case 0: // end of line
00237           x = 0;
00238           if (y == 0) return false;
00239           pixel = &data->bitmap[--y * info->width];
00240           break;
00241 
00242         case 1: // end of bitmap
00243           return true;
00244 
00245         case 2: { // delta
00246           if (EndOfBuffer(buffer)) return false;
00247           byte dx = ReadByte(buffer);
00248           byte dy = ReadByte(buffer);
00249 
00250           /* Check for over- and underflow. */
00251           if (x + dx >= info->width || x + dx < x || dy > y) return false;
00252 
00253           x += dx;
00254           y -= dy;
00255           pixel = &data->bitmap[y * info->width + x];
00256           break;
00257         }
00258 
00259         default: { // uncompressed
00260           for (uint i = 0; i < c; i++) {
00261             if (EndOfBuffer(buffer) || x >= info->width) return false;
00262             *pixel++ = ReadByte(buffer);
00263             x++;
00264           }
00265           /* Padding for 16 bit align */
00266           SkipBytes(buffer, c % 2);
00267           break;
00268         }
00269       }
00270     } else {
00271       /* Apparently it is common to encounter BMPs where the count of
00272        * pixels to be written is higher than the remaining line width.
00273        * Ignore the superfluous pixels instead of reporting an error. */
00274       for (uint i = 0; x < info->width && i < n; i++) {
00275         *pixel++ = c;
00276         x++;
00277       }
00278     }
00279   }
00280   return true;
00281 }
00282 
00286 static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00287 {
00288   uint x, y;
00289   byte pad = GB(4 - info->width * 3, 0, 2);
00290   byte *pixel_row;
00291   for (y = info->height; y > 0; y--) {
00292     pixel_row = &data->bitmap[(y - 1) * info->width * 3];
00293     for (x = 0; x < info->width; x++) {
00294       if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00295       *(pixel_row + 2) = ReadByte(buffer); // green
00296       *(pixel_row + 1) = ReadByte(buffer); // blue
00297       *pixel_row       = ReadByte(buffer); // red
00298       pixel_row += 3;
00299     }
00300     /* Padding for 32 bit align */
00301     SkipBytes(buffer, pad);
00302   }
00303   return true;
00304 }
00305 
00306 /*
00307  * Reads bitmap headers, and palette (if any)
00308  */
00309 bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00310 {
00311   uint32 header_size;
00312   assert(info != NULL);
00313   MemSetT(info, 0);
00314 
00315   /* Reading BMP header */
00316   if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
00317   SkipBytes(buffer, 8); // skip file size and reserved
00318   info->offset = ReadDword(buffer);
00319 
00320   /* Reading info header */
00321   header_size = ReadDword(buffer);
00322   if (header_size < 12) return false; // info header should be at least 12 bytes long
00323 
00324   info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
00325 
00326   if (info->os2_bmp) {
00327     info->width = ReadWord(buffer);
00328     info->height = ReadWord(buffer);
00329     header_size -= 8;
00330   } else {
00331     info->width = ReadDword(buffer);
00332     info->height = ReadDword(buffer);
00333     header_size -= 12;
00334   }
00335 
00336   if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
00337 
00338   info->bpp = ReadWord(buffer);
00339   if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
00340     /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
00341     return false;
00342   }
00343 
00344   /* Reads compression method if available in info header*/
00345   if ((header_size -= 4) >= 4) {
00346     info->compression = ReadDword(buffer);
00347     header_size -= 4;
00348   }
00349 
00350   /* Only 4-bit and 8-bit rle compression is supported */
00351   if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
00352 
00353   if (info->bpp <= 8) {
00354     uint i;
00355 
00356     /* Reads number of colours if available in info header */
00357     if (header_size >= 16) {
00358       SkipBytes(buffer, 12);                  // skip image size and resolution
00359       info->palette_size = ReadDword(buffer); // number of colours in palette
00360       SkipBytes(buffer, header_size - 16);    // skip the end of info header
00361     }
00362     if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
00363 
00364     data->palette = CallocT<Colour>(info->palette_size);
00365 
00366     for (i = 0; i < info->palette_size; i++) {
00367       data->palette[i].b = ReadByte(buffer);
00368       data->palette[i].g = ReadByte(buffer);
00369       data->palette[i].r = ReadByte(buffer);
00370       if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
00371     }
00372   }
00373 
00374   return buffer->real_pos <= info->offset;
00375 }
00376 
00377 /*
00378  * Reads the bitmap
00379  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
00380  */
00381 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00382 {
00383   assert(info != NULL && data != NULL);
00384 
00385   data->bitmap = CallocT<byte>(info->width * info->height * ((info->bpp == 24) ? 3 : 1));
00386 
00387   /* Load image */
00388   SetStreamOffset(buffer, info->offset);
00389   switch (info->compression) {
00390   case 0: // no compression
00391     switch (info->bpp) {
00392     case 1:  return BmpRead1(buffer, info, data);
00393     case 4:  return BmpRead4(buffer, info, data);
00394     case 8:  return BmpRead8(buffer, info, data);
00395     case 24: return BmpRead24(buffer, info, data);
00396     default: NOT_REACHED();
00397     }
00398   case 1:  return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
00399   case 2:  return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
00400   default: NOT_REACHED();
00401   }
00402 }
00403 
00404 void BmpDestroyData(BmpData *data)
00405 {
00406   assert(data != NULL);
00407   free(data->palette);
00408   free(data->bitmap);
00409 }