OpenTTD Source  20241121-master-g67a0fccfad
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"
12 #include "bmp.h"
13 #include "core/bitmath_func.hpp"
14 
15 #include "safeguards.h"
16 
21 static inline bool BmpRead1(RandomAccessFile &file, BmpInfo &info, BmpData &data)
22 {
23  uint8_t pad = GB(4 - info.width / 8, 0, 2);
24  for (uint y = info.height; y > 0; y--) {
25  uint x = 0;
26  uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast<size_t>(info.width)];
27  while (x < info.width) {
28  if (file.AtEndOfFile()) return false; // the file is shorter than expected
29  uint8_t b = file.ReadByte();
30  for (uint i = 8; i > 0; i--) {
31  if (x < info.width) *pixel_row++ = GB(b, i - 1, 1);
32  x++;
33  }
34  }
35  /* Padding for 32 bit align */
36  file.SkipBytes(pad);
37  }
38  return true;
39 }
40 
45 static inline bool BmpRead4(RandomAccessFile &file, BmpInfo &info, BmpData &data)
46 {
47  uint8_t pad = GB(4 - info.width / 2, 0, 2);
48  for (uint y = info.height; y > 0; y--) {
49  uint x = 0;
50  uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast<size_t>(info.width)];
51  while (x < info.width) {
52  if (file.AtEndOfFile()) return false; // the file is shorter than expected
53  uint8_t b = file.ReadByte();
54  *pixel_row++ = GB(b, 4, 4);
55  x++;
56  if (x < info.width) {
57  *pixel_row++ = GB(b, 0, 4);
58  x++;
59  }
60  }
61  /* Padding for 32 bit align */
62  file.SkipBytes(pad);
63  }
64  return true;
65 }
66 
71 static inline bool BmpRead4Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data)
72 {
73  uint x = 0;
74  uint y = info.height - 1;
75  uint8_t *pixel = &data.bitmap[y * static_cast<size_t>(info.width)];
76  while (y != 0 || x < info.width) {
77  if (file.AtEndOfFile()) return false; // the file is shorter than expected
78 
79  uint8_t n = file.ReadByte();
80  uint8_t c = file.ReadByte();
81  if (n == 0) {
82  switch (c) {
83  case 0: // end of line
84  x = 0;
85  if (y == 0) return false;
86  pixel = &data.bitmap[--y * static_cast<size_t>(info.width)];
87  break;
88 
89  case 1: // end of bitmap
90  return true;
91 
92  case 2: { // delta
93  if (file.AtEndOfFile()) return false;
94  uint8_t dx = file.ReadByte();
95  uint8_t dy = file.ReadByte();
96 
97  /* Check for over- and underflow. */
98  if (x + dx >= info.width || x + dx < x || dy > y) return false;
99 
100  x += dx;
101  y -= dy;
102  pixel = &data.bitmap[y * info.width + x];
103  break;
104  }
105 
106  default: { // uncompressed
107  uint i = 0;
108  while (i++ < c) {
109  if (file.AtEndOfFile() || x >= info.width) return false;
110  uint8_t b = file.ReadByte();
111  *pixel++ = GB(b, 4, 4);
112  x++;
113  if (i++ < c) {
114  if (x >= info.width) return false;
115  *pixel++ = GB(b, 0, 4);
116  x++;
117  }
118  }
119  /* Padding for 16 bit align */
120  file.SkipBytes(((c + 1) / 2) % 2);
121  break;
122  }
123  }
124  } else {
125  /* Apparently it is common to encounter BMPs where the count of
126  * pixels to be written is higher than the remaining line width.
127  * Ignore the superfluous pixels instead of reporting an error. */
128  uint i = 0;
129  while (x < info.width && i++ < n) {
130  *pixel++ = GB(c, 4, 4);
131  x++;
132  if (x < info.width && i++ < n) {
133  *pixel++ = GB(c, 0, 4);
134  x++;
135  }
136  }
137  }
138  }
139  return true;
140 }
141 
145 static inline bool BmpRead8(RandomAccessFile &file, BmpInfo &info, BmpData &data)
146 {
147  uint8_t pad = GB(4 - info.width, 0, 2);
148  for (uint y = info.height; y > 0; y--) {
149  if (file.AtEndOfFile()) return false; // the file is shorter than expected
150  uint8_t *pixel = &data.bitmap[(y - 1) * static_cast<size_t>(info.width)];
151  for (uint i = 0; i < info.width; i++) *pixel++ = file.ReadByte();
152  /* Padding for 32 bit align */
153  file.SkipBytes(pad);
154  }
155  return true;
156 }
157 
161 static inline bool BmpRead8Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data)
162 {
163  uint x = 0;
164  uint y = info.height - 1;
165  uint8_t *pixel = &data.bitmap[y * static_cast<size_t>(info.width)];
166  while (y != 0 || x < info.width) {
167  if (file.AtEndOfFile()) return false; // the file is shorter than expected
168 
169  uint8_t n = file.ReadByte();
170  uint8_t c = file.ReadByte();
171  if (n == 0) {
172  switch (c) {
173  case 0: // end of line
174  x = 0;
175  if (y == 0) return false;
176  pixel = &data.bitmap[--y * static_cast<size_t>(info.width)];
177  break;
178 
179  case 1: // end of bitmap
180  return true;
181 
182  case 2: { // delta
183  if (file.AtEndOfFile()) return false;
184  uint8_t dx = file.ReadByte();
185  uint8_t dy = file.ReadByte();
186 
187  /* Check for over- and underflow. */
188  if (x + dx >= info.width || x + dx < x || dy > y) return false;
189 
190  x += dx;
191  y -= dy;
192  pixel = &data.bitmap[y * static_cast<size_t>(info.width) + x];
193  break;
194  }
195 
196  default: { // uncompressed
197  for (uint i = 0; i < c; i++) {
198  if (file.AtEndOfFile() || x >= info.width) return false;
199  *pixel++ = file.ReadByte();
200  x++;
201  }
202  /* Padding for 16 bit align */
203  file.SkipBytes(c % 2);
204  break;
205  }
206  }
207  } else {
208  /* Apparently it is common to encounter BMPs where the count of
209  * pixels to be written is higher than the remaining line width.
210  * Ignore the superfluous pixels instead of reporting an error. */
211  for (uint i = 0; x < info.width && i < n; i++) {
212  *pixel++ = c;
213  x++;
214  }
215  }
216  }
217  return true;
218 }
219 
223 static inline bool BmpRead24(RandomAccessFile &file, BmpInfo &info, BmpData &data)
224 {
225  uint8_t pad = GB(4 - info.width * 3, 0, 2);
226  for (uint y = info.height; y > 0; --y) {
227  uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast<size_t>(info.width) * 3];
228  for (uint x = 0; x < info.width; ++x) {
229  if (file.AtEndOfFile()) return false; // the file is shorter than expected
230  *(pixel_row + 2) = file.ReadByte(); // green
231  *(pixel_row + 1) = file.ReadByte(); // blue
232  *pixel_row = file.ReadByte(); // red
233  pixel_row += 3;
234  }
235  /* Padding for 32 bit align */
236  file.SkipBytes(pad);
237  }
238  return true;
239 }
240 
241 /*
242  * Reads bitmap headers, and palette (if any)
243  */
244 bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data)
245 {
246  info = {};
247 
248  /* Reading BMP header */
249  if (file.ReadWord() != 0x4D42) return false; // signature should be 'BM'
250  file.SkipBytes(8); // skip file size and reserved
251  info.offset = file.ReadDword() + file.GetStartPos();
252 
253  /* Reading info header */
254  uint32_t header_size = file.ReadDword();
255  if (header_size < 12) return false; // info header should be at least 12 bytes long
256 
257  info.os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
258 
259  if (info.os2_bmp) {
260  info.width = file.ReadWord();
261  info.height = file.ReadWord();
262  header_size -= 8;
263  } else {
264  info.width = file.ReadDword();
265  info.height = file.ReadDword();
266  header_size -= 12;
267  }
268 
269  if (file.ReadWord() != 1) return false; // BMP can have only 1 plane
270 
271  info.bpp = file.ReadWord();
272  if (info.bpp != 1 && info.bpp != 4 && info.bpp != 8 && info.bpp != 24) {
273  /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
274  return false;
275  }
276 
277  /* Reads compression method if available in info header*/
278  if ((header_size -= 4) >= 4) {
279  info.compression = file.ReadDword();
280  header_size -= 4;
281  }
282 
283  /* Only 4-bit and 8-bit rle compression is supported */
284  if (info.compression > 2 || (info.compression > 0 && !(info.bpp == 4 || info.bpp == 8))) return false;
285 
286  if (info.bpp <= 8) {
287  /* Reads number of colours if available in info header */
288  if (header_size >= 16) {
289  file.SkipBytes(12); // skip image size and resolution
290  info.palette_size = file.ReadDword(); // number of colours in palette
291  file.SkipBytes(header_size - 16); // skip the end of info header
292  }
293 
294  uint maximum_palette_size = 1U << info.bpp;
295  if (info.palette_size == 0) info.palette_size = maximum_palette_size;
296 
297  /* More palette colours than palette indices is not supported. */
298  if (info.palette_size > maximum_palette_size) return false;
299 
300  data.palette.resize(info.palette_size);
301 
302  for (auto &colour : data.palette) {
303  colour.b = file.ReadByte();
304  colour.g = file.ReadByte();
305  colour.r = file.ReadByte();
306  if (!info.os2_bmp) file.SkipBytes(1); // unused
307  }
308  }
309 
310  return file.GetPos() <= info.offset;
311 }
312 
313 /*
314  * Reads the bitmap
315  * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
316  */
317 bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data)
318 {
319  data.bitmap.resize(static_cast<size_t>(info.width) * info.height * ((info.bpp == 24) ? 3 : 1));
320 
321  /* Load image */
322  file.SeekTo(info.offset, SEEK_SET);
323  switch (info.compression) {
324  case 0: // no compression
325  switch (info.bpp) {
326  case 1: return BmpRead1(file, info, data);
327  case 4: return BmpRead4(file, info, data);
328  case 8: return BmpRead8(file, info, data);
329  case 24: return BmpRead24(file, info, data);
330  default: NOT_REACHED();
331  }
332  break;
333 
334  case 1: return BmpRead8Rle(file, info, data); // 8-bit RLE compression
335  case 2: return BmpRead4Rle(file, info, data); // 4-bit RLE compression
336  default: NOT_REACHED();
337  }
338 }
Functions related to bit mathematics.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static bool BmpRead1(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 1 bpp uncompressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:21
static bool BmpRead8Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 8-bit RLE compressed bpp bitmap.
Definition: bmp.cpp:161
static bool BmpRead24(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 24 bpp uncompressed bitmap.
Definition: bmp.cpp:223
static bool BmpRead8(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 8 bpp bitmap.
Definition: bmp.cpp:145
static bool BmpRead4(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 4 bpp uncompressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:45
static bool BmpRead4Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads a 4-bit RLE compressed bitmap The bitmap is converted to a 8 bpp bitmap.
Definition: bmp.cpp:71
Read and write support for bmps.
A file from which bytes, words and double words are read in (potentially) a random order.
size_t GetPos() const
Get position in the file.
bool AtEndOfFile() const
Test if we have reached the end of the file.
void SeekTo(size_t pos, int mode)
Seek in the current file.
uint8_t ReadByte()
Read a byte from the file.
uint32_t ReadDword()
Read a double word (32 bits) from the file (in low endian format).
void SkipBytes(size_t n)
Skip n bytes ahead in the file.
uint16_t ReadWord()
Read a word (16 bits) from the file (in low endian format).
Class related to random access to files.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Definition: bmp.h:26
Definition: bmp.h:16
uint32_t height
bitmap height
Definition: bmp.h:19
uint32_t width
bitmap width
Definition: bmp.h:18
uint32_t compression
compression method (0 = none, 1 = 8-bit RLE, 2 = 4-bit RLE)
Definition: bmp.h:22
uint32_t palette_size
number of colours in palette
Definition: bmp.h:23
uint16_t bpp
bits per pixel
Definition: bmp.h:21
size_t offset
offset of bitmap data from .bmp file beginning
Definition: bmp.h:17
bool os2_bmp
true if OS/2 1.x or windows 2.x bitmap
Definition: bmp.h:20