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