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/>.
8 /** @file bmp.cpp Read and write support for bmps. */
12 #include "core/bitmath_func.hpp"
13 #include "core/alloc_func.hpp"
14 #include "core/mem_func.hpp"
16 #include "safeguards.h"
18 void BmpInitializeBuffer(BmpBuffer
*buffer
, FILE *file
)
23 buffer
->real_pos
= ftell(file
);
26 static inline void AdvanceBuffer(BmpBuffer
*buffer
)
28 if (buffer
->read
< 0) return;
30 buffer
->read
= (int)fread(buffer
->data
, 1, BMP_BUFFER_SIZE
, buffer
->file
);
34 static inline bool EndOfBuffer(BmpBuffer
*buffer
)
36 if (buffer
->read
< 0) return false;
38 if (buffer
->pos
== buffer
->read
|| buffer
->pos
< 0) AdvanceBuffer(buffer
);
39 return buffer
->pos
== buffer
->read
;
42 static inline byte
ReadByte(BmpBuffer
*buffer
)
44 if (buffer
->read
< 0) return 0;
46 if (buffer
->pos
== buffer
->read
|| buffer
->pos
< 0) AdvanceBuffer(buffer
);
48 return buffer
->data
[buffer
->pos
++];
51 static inline uint16_t ReadWord(BmpBuffer
*buffer
)
53 uint16_t var
= ReadByte(buffer
);
54 return var
| (ReadByte(buffer
) << 8);
57 static inline uint32_t ReadDword(BmpBuffer
*buffer
)
59 uint32_t var
= ReadWord(buffer
);
60 return var
| (ReadWord(buffer
) << 16);
63 static inline void SkipBytes(BmpBuffer
*buffer
, int bytes
)
66 for (i
= 0; i
< bytes
; i
++) ReadByte(buffer
);
69 static inline void SetStreamOffset(BmpBuffer
*buffer
, int offset
)
71 if (fseek(buffer
->file
, offset
, SEEK_SET
) < 0) {
75 buffer
->real_pos
= offset
;
76 AdvanceBuffer(buffer
);
80 * Reads a 1 bpp uncompressed bitmap
81 * The bitmap is converted to a 8 bpp bitmap
83 static inline bool BmpRead1(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
86 byte pad
= GB(4 - info
->width
/ 8, 0, 2);
89 for (y
= info
->height
; y
> 0; y
--) {
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
95 for (i
= 8; i
> 0; i
--) {
96 if (x
< info
->width
) *pixel_row
++ = GB(b
, i
- 1, 1);
100 /* Padding for 32 bit align */
101 SkipBytes(buffer
, pad
);
107 * Reads a 4 bpp uncompressed bitmap
108 * The bitmap is converted to a 8 bpp bitmap
110 static inline bool BmpRead4(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
113 byte pad
= GB(4 - info
->width
/ 2, 0, 2);
116 for (y
= info
->height
; y
> 0; y
--) {
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);
124 if (x
< info
->width
) {
125 *pixel_row
++ = GB(b
, 0, 4);
129 /* Padding for 32 bit align */
130 SkipBytes(buffer
, pad
);
136 * Reads a 4-bit RLE compressed bitmap
137 * The bitmap is converted to a 8 bpp bitmap
139 static inline bool BmpRead4Rle(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
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
147 byte n
= ReadByte(buffer
);
148 byte c
= ReadByte(buffer
);
151 case 0: // end of line
153 if (y
== 0) return false;
154 pixel
= &data
->bitmap
[--y
* info
->width
];
157 case 1: // end of bitmap
161 if (EndOfBuffer(buffer
)) return false;
162 byte dx
= ReadByte(buffer
);
163 byte dy
= ReadByte(buffer
);
165 /* Check for over- and underflow. */
166 if (x
+ dx
>= info
->width
|| x
+ dx
< x
|| dy
> y
) return false;
170 pixel
= &data
->bitmap
[y
* info
->width
+ x
];
174 default: { // uncompressed
177 if (EndOfBuffer(buffer
) || x
>= info
->width
) return false;
178 byte b
= ReadByte(buffer
);
179 *pixel
++ = GB(b
, 4, 4);
182 if (x
>= info
->width
) return false;
183 *pixel
++ = GB(b
, 0, 4);
187 /* Padding for 16 bit align */
188 SkipBytes(buffer
, ((c
+ 1) / 2) % 2);
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. */
197 while (x
< info
->width
&& i
++ < n
) {
198 *pixel
++ = GB(c
, 4, 4);
200 if (x
< info
->width
&& i
++ < n
) {
201 *pixel
++ = GB(c
, 0, 4);
211 * Reads a 8 bpp bitmap
213 static inline bool BmpRead8(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
217 byte pad
= GB(4 - info
->width
, 0, 2);
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
);
230 * Reads a 8-bit RLE compressed bpp bitmap
232 static inline bool BmpRead8Rle(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
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
240 byte n
= ReadByte(buffer
);
241 byte c
= ReadByte(buffer
);
244 case 0: // end of line
246 if (y
== 0) return false;
247 pixel
= &data
->bitmap
[--y
* info
->width
];
250 case 1: // end of bitmap
254 if (EndOfBuffer(buffer
)) return false;
255 byte dx
= ReadByte(buffer
);
256 byte dy
= ReadByte(buffer
);
258 /* Check for over- and underflow. */
259 if (x
+ dx
>= info
->width
|| x
+ dx
< x
|| dy
> y
) return false;
263 pixel
= &data
->bitmap
[y
* info
->width
+ x
];
267 default: { // uncompressed
268 for (uint i
= 0; i
< c
; i
++) {
269 if (EndOfBuffer(buffer
) || x
>= info
->width
) return false;
270 *pixel
++ = ReadByte(buffer
);
273 /* Padding for 16 bit align */
274 SkipBytes(buffer
, c
% 2);
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
++) {
292 * Reads a 24 bpp uncompressed bitmap
294 static inline bool BmpRead24(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
297 byte pad
= GB(4 - info
->width
* 3, 0, 2);
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
308 /* Padding for 32 bit align */
309 SkipBytes(buffer
, pad
);
315 * Reads bitmap headers, and palette (if any)
317 bool BmpReadHeader(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
319 uint32_t header_size
;
320 assert(info
!= nullptr);
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
);
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
332 info
->os2_bmp
= (header_size
== 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
335 info
->width
= ReadWord(buffer
);
336 info
->height
= ReadWord(buffer
);
339 info
->width
= ReadDword(buffer
);
340 info
->height
= ReadDword(buffer
);
344 if (ReadWord(buffer
) != 1) return false; // BMP can have only 1 plane
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 */
352 /* Reads compression method if available in info header*/
353 if ((header_size
-= 4) >= 4) {
354 info
->compression
= ReadDword(buffer
);
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;
361 if (info
->bpp
<= 8) {
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
371 uint maximum_palette_size
= 1U << info
->bpp
;
372 if (info
->palette_size
== 0) info
->palette_size
= maximum_palette_size
;
374 /* More palette colours than palette indices is not supported. */
375 if (info
->palette_size
> maximum_palette_size
) return false;
377 data
->palette
= CallocT
<Colour
>(info
->palette_size
);
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
387 return buffer
->real_pos
<= info
->offset
;
392 * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
394 bool BmpReadBitmap(BmpBuffer
*buffer
, BmpInfo
*info
, BmpData
*data
)
396 assert(info
!= nullptr && data
!= nullptr);
398 data
->bitmap
= CallocT
<byte
>(static_cast<size_t>(info
->width
) * info
->height
* ((info
->bpp
== 24) ? 3 : 1));
401 SetStreamOffset(buffer
, info
->offset
);
402 switch (info
->compression
) {
403 case 0: // no compression
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();
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();
417 void BmpDestroyData(BmpData
*data
)
419 assert(data
!= nullptr);