OpenTTD
bmp.cpp
Go to the documentation of this file.
1 /* $Id: bmp.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "stdafx.h"
13 #include "bmp.h"
14 #include "core/bitmath_func.hpp"
15 #include "core/alloc_func.hpp"
16 #include "core/mem_func.hpp"
17 
18 #include "safeguards.h"
19 
20 void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file)
21 {
22  buffer->pos = -1;
23  buffer->file = file;
24  buffer->read = 0;
25  buffer->real_pos = ftell(file);
26 }
27 
28 static inline void AdvanceBuffer(BmpBuffer *buffer)
29 {
30  if (buffer->read < 0) return;
31 
32  buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
33  buffer->pos = 0;
34 }
35 
36 static inline bool EndOfBuffer(BmpBuffer *buffer)
37 {
38  if (buffer->read < 0) return false;
39 
40  if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
41  return buffer->pos == buffer->read;
42 }
43 
44 static inline byte ReadByte(BmpBuffer *buffer)
45 {
46  if (buffer->read < 0) return 0;
47 
48  if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
49  buffer->real_pos++;
50  return buffer->data[buffer->pos++];
51 }
52 
53 static inline uint16 ReadWord(BmpBuffer *buffer)
54 {
55  uint16 var = ReadByte(buffer);
56  return var | (ReadByte(buffer) << 8);
57 }
58 
59 static inline uint32 ReadDword(BmpBuffer *buffer)
60 {
61  uint32 var = ReadWord(buffer);
62  return var | (ReadWord(buffer) << 16);
63 }
64 
65 static inline void SkipBytes(BmpBuffer *buffer, int bytes)
66 {
67  int i;
68  for (i = 0; i < bytes; i++) ReadByte(buffer);
69 }
70 
71 static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
72 {
73  if (fseek(buffer->file, offset, SEEK_SET) < 0) {
74  buffer->read = -1;
75  }
76  buffer->pos = -1;
77  buffer->real_pos = offset;
78  AdvanceBuffer(buffer);
79 }
80 
85 static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
86 {
87  uint x, y, i;
88  byte pad = GB(4 - info->width / 8, 0, 2);
89  byte *pixel_row;
90  byte b;
91  for (y = info->height; y > 0; y--) {
92  x = 0;
93  pixel_row = &data->bitmap[(y - 1) * info->width];
94  while (x < info->width) {
95  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
96  b = ReadByte(buffer);
97  for (i = 8; i > 0; i--) {
98  if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
99  x++;
100  }
101  }
102  /* Padding for 32 bit align */
103  SkipBytes(buffer, pad);
104  }
105  return true;
106 }
107 
112 static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
113 {
114  uint x, y;
115  byte pad = GB(4 - info->width / 2, 0, 2);
116  byte *pixel_row;
117  byte b;
118  for (y = info->height; y > 0; y--) {
119  x = 0;
120  pixel_row = &data->bitmap[(y - 1) * info->width];
121  while (x < info->width) {
122  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
123  b = ReadByte(buffer);
124  *pixel_row++ = GB(b, 4, 4);
125  x++;
126  if (x < info->width) {
127  *pixel_row++ = GB(b, 0, 4);
128  x++;
129  }
130  }
131  /* Padding for 32 bit align */
132  SkipBytes(buffer, pad);
133  }
134  return true;
135 }
136 
141 static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
142 {
143  uint x = 0;
144  uint y = info->height - 1;
145  byte *pixel = &data->bitmap[y * info->width];
146  while (y != 0 || x < info->width) {
147  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
148 
149  byte n = ReadByte(buffer);
150  byte c = ReadByte(buffer);
151  if (n == 0) {
152  switch (c) {
153  case 0: // end of line
154  x = 0;
155  if (y == 0) return false;
156  pixel = &data->bitmap[--y * info->width];
157  break;
158 
159  case 1: // end of bitmap
160  return true;
161 
162  case 2: { // delta
163  if (EndOfBuffer(buffer)) return false;
164  byte dx = ReadByte(buffer);
165  byte dy = ReadByte(buffer);
166 
167  /* Check for over- and underflow. */
168  if (x + dx >= info->width || x + dx < x || dy > y) return false;
169 
170  x += dx;
171  y -= dy;
172  pixel = &data->bitmap[y * info->width + x];
173  break;
174  }
175 
176  default: { // uncompressed
177  uint i = 0;
178  while (i++ < c) {
179  if (EndOfBuffer(buffer) || x >= info->width) return false;
180  byte b = ReadByte(buffer);
181  *pixel++ = GB(b, 4, 4);
182  x++;
183  if (i++ < c) {
184  if (x >= info->width) return false;
185  *pixel++ = GB(b, 0, 4);
186  x++;
187  }
188  }
189  /* Padding for 16 bit align */
190  SkipBytes(buffer, ((c + 1) / 2) % 2);
191  break;
192  }
193  }
194  } else {
195  /* Apparently it is common to encounter BMPs where the count of
196  * pixels to be written is higher than the remaining line width.
197  * Ignore the superfluous pixels instead of reporting an error. */
198  uint i = 0;
199  while (x < info->width && i++ < n) {
200  *pixel++ = GB(c, 4, 4);
201  x++;
202  if (x < info->width && i++ < n) {
203  *pixel++ = GB(c, 0, 4);
204  x++;
205  }
206  }
207  }
208  }
209  return true;
210 }
211 
215 static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
216 {
217  uint i;
218  uint y;
219  byte pad = GB(4 - info->width, 0, 2);
220  byte *pixel;
221  for (y = info->height; y > 0; y--) {
222  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
223  pixel = &data->bitmap[(y - 1) * info->width];
224  for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
225  /* Padding for 32 bit align */
226  SkipBytes(buffer, pad);
227  }
228  return true;
229 }
230 
234 static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
235 {
236  uint x = 0;
237  uint y = info->height - 1;
238  byte *pixel = &data->bitmap[y * info->width];
239  while (y != 0 || x < info->width) {
240  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
241 
242  byte n = ReadByte(buffer);
243  byte c = ReadByte(buffer);
244  if (n == 0) {
245  switch (c) {
246  case 0: // end of line
247  x = 0;
248  if (y == 0) return false;
249  pixel = &data->bitmap[--y * info->width];
250  break;
251 
252  case 1: // end of bitmap
253  return true;
254 
255  case 2: { // delta
256  if (EndOfBuffer(buffer)) return false;
257  byte dx = ReadByte(buffer);
258  byte dy = ReadByte(buffer);
259 
260  /* Check for over- and underflow. */
261  if (x + dx >= info->width || x + dx < x || dy > y) return false;
262 
263  x += dx;
264  y -= dy;
265  pixel = &data->bitmap[y * info->width + x];
266  break;
267  }
268 
269  default: { // uncompressed
270  for (uint i = 0; i < c; i++) {
271  if (EndOfBuffer(buffer) || x >= info->width) return false;
272  *pixel++ = ReadByte(buffer);
273  x++;
274  }
275  /* Padding for 16 bit align */
276  SkipBytes(buffer, c % 2);
277  break;
278  }
279  }
280  } else {
281  /* Apparently it is common to encounter BMPs where the count of
282  * pixels to be written is higher than the remaining line width.
283  * Ignore the superfluous pixels instead of reporting an error. */
284  for (uint i = 0; x < info->width && i < n; i++) {
285  *pixel++ = c;
286  x++;
287  }
288  }
289  }
290  return true;
291 }
292 
296 static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
297 {
298  uint x, y;
299  byte pad = GB(4 - info->width * 3, 0, 2);
300  byte *pixel_row;
301  for (y = info->height; y > 0; y--) {
302  pixel_row = &data->bitmap[(y - 1) * info->width * 3];
303  for (x = 0; x < info->width; x++) {
304  if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
305  *(pixel_row + 2) = ReadByte(buffer); // green
306  *(pixel_row + 1) = ReadByte(buffer); // blue
307  *pixel_row = ReadByte(buffer); // red
308  pixel_row += 3;
309  }
310  /* Padding for 32 bit align */
311  SkipBytes(buffer, pad);
312  }
313  return true;
314 }
315 
316 /*
317  * Reads bitmap headers, and palette (if any)
318  */
319 bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
320 {
321  uint32 header_size;
322  assert(info != NULL);
323  MemSetT(info, 0);
324 
325  /* Reading BMP header */
326  if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
327  SkipBytes(buffer, 8); // skip file size and reserved
328  info->offset = ReadDword(buffer);
329 
330  /* Reading info header */
331  header_size = ReadDword(buffer);
332  if (header_size < 12) return false; // info header should be at least 12 bytes long
333 
334  info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
335 
336  if (info->os2_bmp) {
337  info->width = ReadWord(buffer);
338  info->height = ReadWord(buffer);
339  header_size -= 8;
340  } else {
341  info->width = ReadDword(buffer);
342  info->height = ReadDword(buffer);
343  header_size -= 12;
344  }
345 
346  if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
347 
348  info->bpp = ReadWord(buffer);
349  if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
350  /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
351  return false;
352  }
353 
354  /* Reads compression method if available in info header*/
355  if ((header_size -= 4) >= 4) {
356  info->compression = ReadDword(buffer);
357  header_size -= 4;
358  }
359 
360  /* Only 4-bit and 8-bit rle compression is supported */
361  if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
362 
363  if (info->bpp <= 8) {
364  uint i;
365 
366  /* Reads number of colours if available in info header */
367  if (header_size >= 16) {
368  SkipBytes(buffer, 12); // skip image size and resolution
369  info->palette_size = ReadDword(buffer); // number of colours in palette
370  SkipBytes(buffer, header_size - 16); // skip the end of info header
371  }
372  if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
373 
374  data->palette = CallocT<Colour>(info->palette_size);
375 
376  for (i = 0; i < info->palette_size; i++) {
377  data->palette[i].b = ReadByte(buffer);
378  data->palette[i].g = ReadByte(buffer);
379  data->palette[i].r = ReadByte(buffer);
380  if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
381  }
382  }
383 
384  return buffer->real_pos <= info->offset;
385 }
386 
387 /*
388  * Reads the bitmap
389  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
390  */
391 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
392 {
393  assert(info != NULL && data != NULL);
394 
395  data->bitmap = CallocT<byte>(info->width * info->height * ((info->bpp == 24) ? 3 : 1));
396 
397  /* Load image */
398  SetStreamOffset(buffer, info->offset);
399  switch (info->compression) {
400  case 0: // no compression
401  switch (info->bpp) {
402  case 1: return BmpRead1(buffer, info, data);
403  case 4: return BmpRead4(buffer, info, data);
404  case 8: return BmpRead8(buffer, info, data);
405  case 24: return BmpRead24(buffer, info, data);
406  default: NOT_REACHED();
407  }
408  case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
409  case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
410  default: NOT_REACHED();
411  }
412 }
413 
414 void BmpDestroyData(BmpData *data)
415 {
416  assert(data != NULL);
417  free(data->palette);
418  free(data->bitmap);
419 }
static bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 8-bit RLE compressed bpp bitmap.
Definition: bmp.cpp:234
uint32 compression
compression method (0 = none, 1 = 8-bit RLE, 2 = 4-bit RLE)
Definition: bmp.h:23
bool os2_bmp
true if OS/2 1.x or windows 2.x bitmap
Definition: bmp.h:21
static bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 24 bpp uncompressed bitmap.
Definition: bmp.cpp:296
Definition: bmp.h:27
Definition: bmp.h:34
Definition: bmp.h:17
Functions related to bit mathematics.
uint32 palette_size
number of colours in palette
Definition: bmp.h:24
Functions related to the allocation of memory.
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
Read and write support for bmps.
uint32 height
bitmap height
Definition: bmp.h:20
uint32 width
bitmap width
Definition: bmp.h:19
uint16 bpp
bits per pixel
Definition: bmp.h:22
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
static bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 8 bpp bitmap.
Definition: bmp.cpp:215
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:114
static bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 1 bpp uncompressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:85
uint32 offset
offset of bitmap data from .bmp file begining
Definition: bmp.h:18
Functions related to memory operations.
static bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 4-bit RLE compressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:141
static bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
Reads a 4 bpp uncompressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:112
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:51