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