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