OpenTTD: src/bmp.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

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 }