bmp.cpp

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