# 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 }