bmp.cpp

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

Generated on Mon Aug 30 19:36:53 2010 for OpenTTD by  doxygen 1.6.1