OpenTTD Source: src/bmp.cpp Source File

OpenTTD Source 14.1

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_t ReadWord(BmpBuffer *buffer)
52 {
53 uint16_t var = ReadByte(buffer);
54 return var | (ReadByte(buffer) << 8);
55 }
56
57 static inline uint32_t ReadDword(BmpBuffer *buffer)
58 {
59 uint32_t 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_t 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
371 uint maximum_palette_size = 1U << info->bpp;
372 if (info->palette_size == 0) info->palette_size = maximum_palette_size;
373
374 /* More palette colours than palette indices is not supported. */
375 if (info->palette_size > maximum_palette_size) return false;
376
377 data->palette = CallocT<Colour>(info->palette_size);
378
379 for (i = 0; i < info->palette_size; i++) {
380 data->palette[i].b = ReadByte(buffer);
381 data->palette[i].g = ReadByte(buffer);
382 data->palette[i].r = ReadByte(buffer);
383 if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
384 }
385 }
386
387 return buffer->real_pos <= info->offset;
388 }
389
390 /*
391 * Reads the bitmap
392 * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
393 */
394 bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
395 {
396 assert(info != nullptr && data != nullptr);
397
398 data->bitmap = CallocT<byte>(static_cast<size_t>(info->width) * info->height * ((info->bpp == 24) ? 3 : 1));
399
400 /* Load image */
401 SetStreamOffset(buffer, info->offset);
402 switch (info->compression) {
403 case 0: // no compression
404 switch (info->bpp) {
405 case 1: return BmpRead1(buffer, info, data);
406 case 4: return BmpRead4(buffer, info, data);
407 case 8: return BmpRead8(buffer, info, data);
408 case 24: return BmpRead24(buffer, info, data);
409 default: NOT_REACHED();
410 }
411 case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
412 case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
413 default: NOT_REACHED();
414 }
415 }
416
417 void BmpDestroyData(BmpData *data)
418 {
419 assert(data != nullptr);
420 free(data->palette);
421 free(data->bitmap);
422 }
BmpInfo::height
uint32_t height
bitmap height
Definition: bmp.h:18
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
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
GB
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.
Definition: bitmath_func.hpp:32
BmpInfo::offset
uint32_t offset
offset of bitmap data from .bmp file beginning
Definition: bmp.h:16
BmpInfo::bpp
uint16_t bpp
bits per pixel
Definition: bmp.h:20
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
free
void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:382
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
BmpInfo::palette_size
uint32_t palette_size
number of colours in palette
Definition: bmp.h:22
stdafx.h
BmpBuffer
Definition: bmp.h:32
BmpInfo::os2_bmp
bool os2_bmp
true if OS/2 1.x or windows 2.x bitmap
Definition: bmp.h:19
alloc_func.hpp
MemSetT
void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:49
BmpData
Definition: bmp.h:25
BmpInfo::width
uint32_t width
bitmap width
Definition: bmp.h:17
BmpInfo::compression
uint32_t compression
compression method (0 = none, 1 = 8-bit RLE, 2 = 4-bit RLE)
Definition: bmp.h:21