OpenTTD Source 20241224-master-gf74b0cf984
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
21static 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
45static 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
71static 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
145static 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
161static 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
223static 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 */
244bool 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 */
317bool 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.
debug_inline static constexpr 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