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. */
11 #include "random_access_file_type.h"
13 #include "core/bitmath_func.hpp"
15 #include "safeguards.h"
18 * Reads a 1 bpp uncompressed bitmap
19 * The bitmap is converted to a 8 bpp bitmap
21 static inline bool BmpRead1(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
23 uint8_t pad
= GB(4 - info
.width
/ 8, 0, 2);
24 for (uint y
= info
.height
; y
> 0; y
--) {
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);
35 /* Padding for 32 bit align */
42 * Reads a 4 bpp uncompressed bitmap
43 * The bitmap is converted to a 8 bpp bitmap
45 static inline bool BmpRead4(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
47 uint8_t pad
= GB(4 - info
.width
/ 2, 0, 2);
48 for (uint y
= info
.height
; y
> 0; y
--) {
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);
57 *pixel_row
++ = GB(b
, 0, 4);
61 /* Padding for 32 bit align */
68 * Reads a 4-bit RLE compressed bitmap
69 * The bitmap is converted to a 8 bpp bitmap
71 static inline bool BmpRead4Rle(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
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
79 uint8_t n
= file
.ReadByte();
80 uint8_t c
= file
.ReadByte();
83 case 0: // end of line
85 if (y
== 0) return false;
86 pixel
= &data
.bitmap
[--y
* static_cast<size_t>(info
.width
)];
89 case 1: // end of bitmap
93 if (file
.AtEndOfFile()) return false;
94 uint8_t dx
= file
.ReadByte();
95 uint8_t dy
= file
.ReadByte();
97 /* Check for over- and underflow. */
98 if (x
+ dx
>= info
.width
|| x
+ dx
< x
|| dy
> y
) return false;
102 pixel
= &data
.bitmap
[y
* info
.width
+ x
];
106 default: { // uncompressed
109 if (file
.AtEndOfFile() || x
>= info
.width
) return false;
110 uint8_t b
= file
.ReadByte();
111 *pixel
++ = GB(b
, 4, 4);
114 if (x
>= info
.width
) return false;
115 *pixel
++ = GB(b
, 0, 4);
119 /* Padding for 16 bit align */
120 file
.SkipBytes(((c
+ 1) / 2) % 2);
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. */
129 while (x
< info
.width
&& i
++ < n
) {
130 *pixel
++ = GB(c
, 4, 4);
132 if (x
< info
.width
&& i
++ < n
) {
133 *pixel
++ = GB(c
, 0, 4);
143 * Reads a 8 bpp bitmap
145 static inline bool BmpRead8(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
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 */
159 * Reads a 8-bit RLE compressed bpp bitmap
161 static inline bool BmpRead8Rle(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
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
169 uint8_t n
= file
.ReadByte();
170 uint8_t c
= file
.ReadByte();
173 case 0: // end of line
175 if (y
== 0) return false;
176 pixel
= &data
.bitmap
[--y
* static_cast<size_t>(info
.width
)];
179 case 1: // end of bitmap
183 if (file
.AtEndOfFile()) return false;
184 uint8_t dx
= file
.ReadByte();
185 uint8_t dy
= file
.ReadByte();
187 /* Check for over- and underflow. */
188 if (x
+ dx
>= info
.width
|| x
+ dx
< x
|| dy
> y
) return false;
192 pixel
= &data
.bitmap
[y
* static_cast<size_t>(info
.width
) + x
];
196 default: { // uncompressed
197 for (uint i
= 0; i
< c
; i
++) {
198 if (file
.AtEndOfFile() || x
>= info
.width
) return false;
199 *pixel
++ = file
.ReadByte();
202 /* Padding for 16 bit align */
203 file
.SkipBytes(c
% 2);
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
++) {
221 * Reads a 24 bpp uncompressed bitmap
223 static inline bool BmpRead24(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
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
235 /* Padding for 32 bit align */
242 * Reads bitmap headers, and palette (if any)
244 bool BmpReadHeader(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
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();
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
257 info
.os2_bmp
= (header_size
== 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
260 info
.width
= file
.ReadWord();
261 info
.height
= file
.ReadWord();
264 info
.width
= file
.ReadDword();
265 info
.height
= file
.ReadDword();
269 if (file
.ReadWord() != 1) return false; // BMP can have only 1 plane
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 */
277 /* Reads compression method if available in info header*/
278 if ((header_size
-= 4) >= 4) {
279 info
.compression
= file
.ReadDword();
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;
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
294 uint maximum_palette_size
= 1U << info
.bpp
;
295 if (info
.palette_size
== 0) info
.palette_size
= maximum_palette_size
;
297 /* More palette colours than palette indices is not supported. */
298 if (info
.palette_size
> maximum_palette_size
) return false;
300 data
.palette
.resize(info
.palette_size
);
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
310 return file
.GetPos() <= info
.offset
;
315 * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
317 bool BmpReadBitmap(RandomAccessFile
&file
, BmpInfo
&info
, BmpData
&data
)
319 data
.bitmap
.resize(static_cast<size_t>(info
.width
) * info
.height
* ((info
.bpp
== 24) ? 3 : 1));
322 file
.SeekTo(info
.offset
, SEEK_SET
);
323 switch (info
.compression
) {
324 case 0: // no compression
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();
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();