bmp.cpp

Go to the documentation of this file.
00001 /* $Id: bmp.cpp 22601 2011-06-18 19:43:51Z rubidium $ */
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 i;
00134   uint x = 0;
00135   uint y = info->height - 1;
00136   byte n, c, b;
00137   byte *pixel = &data->bitmap[y * info->width];
00138   while (y != 0 || x < info->width) {
00139     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00140     n = ReadByte(buffer);
00141     c = ReadByte(buffer);
00142     if (n == 0) {
00143       switch (c) {
00144       case 0: // end of line
00145         x = 0;
00146         pixel = &data->bitmap[--y * info->width];
00147         break;
00148       case 1: // end of bitmap
00149         x = info->width;
00150         y = 0;
00151         pixel = NULL;
00152         break;
00153       case 2: // delta
00154         x += ReadByte(buffer);
00155         i = ReadByte(buffer);
00156         if (x >= info->width || (y == 0 && i > 0)) return false;
00157         y -= i;
00158         pixel = &data->bitmap[y * info->width + x];
00159         break;
00160       default: // uncompressed
00161         i = 0;
00162         while (i++ < c) {
00163           if (EndOfBuffer(buffer) || x >= info->width) return false;
00164           b = ReadByte(buffer);
00165           *pixel++ = GB(b, 4, 4);
00166           x++;
00167           if (x < info->width && i++ < c) {
00168             *pixel++ = GB(b, 0, 4);
00169             x++;
00170           }
00171         }
00172         /* Padding for 16 bit align */
00173         SkipBytes(buffer, ((c + 1) / 2) % 2);
00174         break;
00175       }
00176     } else {
00177       i = 0;
00178       while (i++ < n) {
00179         if (EndOfBuffer(buffer) || x >= info->width) return false;
00180         *pixel++ = GB(c, 4, 4);
00181         x++;
00182         if (x < info->width && i++ < n) {
00183           *pixel++ = GB(c, 0, 4);
00184           x++;
00185         }
00186       }
00187     }
00188   }
00189   return true;
00190 }
00191 
00195 static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00196 {
00197   uint i;
00198   uint y;
00199   byte pad = GB(4 - info->width, 0, 2);
00200   byte *pixel;
00201   for (y = info->height; y > 0; y--) {
00202     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00203     pixel = &data->bitmap[(y - 1) * info->width];
00204     for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
00205     /* Padding for 32 bit align */
00206     SkipBytes(buffer, pad);
00207   }
00208   return true;
00209 }
00210 
00214 static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00215 {
00216   uint i;
00217   uint x = 0;
00218   uint y = info->height - 1;
00219   byte n, c;
00220   byte *pixel = &data->bitmap[y * info->width];
00221   while (y != 0 || x < info->width) {
00222     if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00223     n = ReadByte(buffer);
00224     c = ReadByte(buffer);
00225     if (n == 0) {
00226       switch (c) {
00227       case 0: // end of line
00228         x = 0;
00229         pixel = &data->bitmap[--y * info->width];
00230         break;
00231       case 1: // end of bitmap
00232         x = info->width;
00233         y = 0;
00234         pixel = NULL;
00235         break;
00236       case 2: // delta
00237         x += ReadByte(buffer);
00238         i = ReadByte(buffer);
00239         if (x >= info->width || (y == 0 && i > 0)) return false;
00240         y -= i;
00241         pixel = &data->bitmap[y * info->width + x];
00242         break;
00243       default: // uncompressed
00244         if ((x += c) > info->width) return false;
00245         for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer);
00246         /* Padding for 16 bit align */
00247         SkipBytes(buffer, c % 2);
00248         break;
00249       }
00250     } else {
00251       for (i = 0; i < n; i++) {
00252         if (x >= info->width) return false;
00253         *pixel++ = c;
00254         x++;
00255       }
00256     }
00257   }
00258   return true;
00259 }
00260 
00264 static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00265 {
00266   uint x, y;
00267   byte pad = GB(4 - info->width * 3, 0, 2);
00268   byte *pixel_row;
00269   for (y = info->height; y > 0; y--) {
00270     pixel_row = &data->bitmap[(y - 1) * info->width * 3];
00271     for (x = 0; x < info->width; x++) {
00272       if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
00273       *(pixel_row + 2) = ReadByte(buffer); // green
00274       *(pixel_row + 1) = ReadByte(buffer); // blue
00275       *pixel_row       = ReadByte(buffer); // red
00276       pixel_row += 3;
00277     }
00278     /* Padding for 32 bit align */
00279     SkipBytes(buffer, pad);
00280   }
00281   return true;
00282 }
00283 
00284 /*
00285  * Reads bitmap headers, and palette (if any)
00286  */
00287 bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00288 {
00289   uint32 header_size;
00290   assert(info != NULL);
00291   MemSetT(info, 0);
00292 
00293   /* Reading BMP header */
00294   if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
00295   SkipBytes(buffer, 8); // skip file size and reserved
00296   info->offset = ReadDword(buffer);
00297 
00298   /* Reading info header */
00299   header_size = ReadDword(buffer);
00300   if (header_size < 12) return false; // info header should be at least 12 bytes long
00301 
00302   info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
00303 
00304   if (info->os2_bmp) {
00305     info->width = ReadWord(buffer);
00306     info->height = ReadWord(buffer);
00307     header_size -= 8;
00308   } else {
00309     info->width = ReadDword(buffer);
00310     info->height = ReadDword(buffer);
00311     header_size -= 12;
00312   }
00313 
00314   if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
00315 
00316   info->bpp = ReadWord(buffer);
00317   if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
00318     /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
00319     return false;
00320   }
00321 
00322   /* Reads compression method if available in info header*/
00323   if ((header_size -= 4) >= 4) {
00324     info->compression = ReadDword(buffer);
00325     header_size -= 4;
00326   }
00327 
00328   /* Only 4-bit and 8-bit rle compression is supported */
00329   if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
00330 
00331   if (info->bpp <= 8) {
00332     uint i;
00333 
00334     /* Reads number of colours if available in info header */
00335     if (header_size >= 16) {
00336       SkipBytes(buffer, 12);                  // skip image size and resolution
00337       info->palette_size = ReadDword(buffer); // number of colours in palette
00338       SkipBytes(buffer, header_size - 16);    // skip the end of info header
00339     }
00340     if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
00341 
00342     data->palette = CallocT<Colour>(info->palette_size);
00343 
00344     for (i = 0; i < info->palette_size; i++) {
00345       data->palette[i].b = ReadByte(buffer);
00346       data->palette[i].g = ReadByte(buffer);
00347       data->palette[i].r = ReadByte(buffer);
00348       if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
00349     }
00350   }
00351 
00352   return buffer->real_pos <= info->offset;
00353 }
00354 
00355 /*
00356  * Reads the bitmap
00357  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
00358  */
00359 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
00360 {
00361   assert(info != NULL && data != NULL);
00362 
00363   data->bitmap = CallocT<byte>(info->width * info->height * ((info->bpp == 24) ? 3 : 1));
00364 
00365   /* Load image */
00366   SetStreamOffset(buffer, info->offset);
00367   switch (info->compression) {
00368   case 0: // no compression
00369     switch (info->bpp) {
00370     case 1:  return BmpRead1(buffer, info, data);
00371     case 4:  return BmpRead4(buffer, info, data);
00372     case 8:  return BmpRead8(buffer, info, data);
00373     case 24: return BmpRead24(buffer, info, data);
00374     default: NOT_REACHED();
00375     }
00376   case 1:  return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
00377   case 2:  return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
00378   default: NOT_REACHED();
00379   }
00380 }
00381 
00382 void BmpDestroyData(BmpData *data)
00383 {
00384   assert(data != NULL);
00385   free(data->palette);
00386   free(data->bitmap);
00387 }