1 /* aNetHack 0.0.1 bmptiles.c $ANH-Date: 1457207054 2016/03/05 19:44:14 $ $ANH-Branch: chasonr $:$ANH-Revision: 1.0 $ */
2 /* Copyright (c) Ray Chason, 2016. */
3 /* aNetHack may be freely redistributed. See license for details. */
9 /* First BMP file header */
16 /* Color model information for larger BMP headers */
23 struct CIE_XYZTriple
{
24 struct CIE_XYZ ciexyzRed
;
25 struct CIE_XYZ ciexyzGreen
;
26 struct CIE_XYZ ciexyzBlue
;
29 /* Second BMP file header */
30 /* This one can vary in size; contents can vary according to the size */
31 struct BitmapInfoHeader
{
32 uint32 Size
; /* 12 40 52 56 108 124 64 */
33 int32 Width
; /* 12 40 52 56 108 124 64 */
34 int32 Height
; /* 12 40 52 56 108 124 64 */
35 uint16 NumPlanes
; /* 12 40 52 56 108 124 64 */
36 uint16 BitsPerPixel
; /* 12 40 52 56 108 124 64 */
37 uint32 Compression
; /* 40 52 56 108 124 64 */
38 uint32 ImageDataSize
; /* 40 52 56 108 124 64 */
39 int32 XResolution
; /* 40 52 56 108 124 64 */
40 int32 YResolution
; /* 40 52 56 108 124 64 */
41 uint32 ColorsUsed
; /* 40 52 56 108 124 64 */
42 uint32 ColorsImportant
; /* 40 52 56 108 124 64 */
43 uint32 RedMask
; /* 52 56 108 124 */
44 uint32 GreenMask
; /* 52 56 108 124 */
45 uint32 BlueMask
; /* 52 56 108 124 */
46 uint32 AlphaMask
; /* 56 108 124 */
47 uint32 CSType
; /* 108 124 */
48 struct CIE_XYZTriple Endpoints
; /* 108 124 */
49 uint32 GammaRed
; /* 108 124 */
50 uint32 GammaGreen
; /* 108 124 */
51 uint32 GammaBlue
; /* 108 124 */
52 uint32 Intent
; /* 124 */
53 uint32 ProfileData
; /* 124 */
54 uint32 ProfileSize
; /* 124 */
60 #define BI_BITFIELDS 3
64 static uint16
FDECL(read_u16
, (const unsigned char buf
[2]));
65 static uint32
FDECL(read_u32
, (const unsigned char buf
[4]));
66 static int32
FDECL(read_s32
, (const unsigned char buf
[4]));
67 static struct Pixel
FDECL(build_pixel
, (const struct BitmapInfoHeader
*, uint32
));
68 static unsigned char FDECL(pixel_element
, (uint32
, uint32
));
69 static boolean
FDECL(read_header
, (FILE *, struct BitmapHeader
*));
70 static boolean
FDECL(read_info_header
, (FILE *, struct BitmapInfoHeader
*));
71 static boolean
FDECL(check_info_header
, (const struct BitmapInfoHeader
*));
72 static unsigned FDECL(get_palette_size
, (const struct BitmapInfoHeader
*));
73 static boolean
FDECL(read_palette
, (FILE *, struct Pixel
*, unsigned));
75 /* Read a .BMP file into the image structure */
76 /* Return TRUE if successful, FALSE on any error */
78 read_bmp_tiles(filename
, image
)
80 struct TileSetImage
*image
;
82 struct BitmapHeader header1
;
83 struct BitmapInfoHeader header2
;
84 unsigned palette_size
;
85 size_t num_pixels
, size
;
86 unsigned x
, y
, y_start
, y_end
, y_inc
;
87 unsigned bytes_per_row
;
89 FILE *fp
= NULL
; /* custodial */
90 unsigned char *row_bytes
= NULL
; /* custodial */
94 image
->pixels
= NULL
; /* custodial, returned */
95 image
->indexes
= NULL
; /* custodial, returned */
96 image
->image_desc
= NULL
; /* custodial, returned */
97 image
->tile_width
= 0;
98 image
->tile_height
= 0;
100 fp
= fopen(filename
, "rb");
101 if (fp
== NULL
) goto error
;
103 /* Read the headers */
104 if (!read_header(fp
, &header1
)) goto error
;
105 if (memcmp(header1
.magic
, "BM", 2) != 0) goto error
;
107 if (!read_info_header(fp
, &header2
)) goto error
;
108 if (!check_info_header(&header2
)) goto error
;
111 if (header2
.Compression
== BI_PNG
) {
112 /* Image data is an embedded PNG bit stream */
113 boolean ok
= do_read_png_tiles(fp
, image
));
119 /* header2.Height < 0 means the Y coordinate is reversed; the origin is
120 * top left rather than bottom left */
121 image
->width
= header2
.Width
;
122 image
->height
= labs(header2
.Height
);
124 /* Allocate pixel area; watch out for overflow */
125 num_pixels
= (size_t) image
->width
* (size_t) image
->height
;
126 if (num_pixels
/ image
->width
!= image
->height
) goto error
; /* overflow */
127 size
= num_pixels
* sizeof(image
->pixels
[0]);
128 if (size
/ sizeof(image
->pixels
[0]) != num_pixels
) goto error
; /* overflow */
129 image
->pixels
= (struct Pixel
*) alloc(size
);
130 if (header2
.BitsPerPixel
<= 8) {
131 image
->indexes
= (unsigned char *) alloc(num_pixels
);
134 /* Read the palette */
135 palette_size
= get_palette_size(&header2
);
136 if (!read_palette(fp
, image
->palette
, palette_size
)) goto error
;
138 /* Read the pixels */
139 fseek(fp
, header1
.img_offset
, SEEK_SET
);
140 if (header2
.Height
< 0) {
142 y_end
= image
->height
;
145 y_start
= image
->height
- 1;
146 y_end
= (unsigned) -1;
149 if (header2
.Compression
== BI_RLE4
|| header2
.Compression
== BI_RLE8
) {
152 memset(p
, 0, num_pixels
);
154 y
= image
->height
- 1;
158 if (b1
== EOF
) goto error
;
160 if (b2
== EOF
) goto error
;
165 * 0 2 next two bytes are x and y offset
166 * 0 >2 b2 is a count of bytes
167 * >0 any repeat b2, b1 times
174 } else if (b2
== 1) {
177 } else if (b2
== 2) {
178 /* next two bytes are x and y offset */
180 if (b1
== EOF
) break;
182 if (b2
== EOF
) break;
188 if (y
< image
->height
) {
189 p
= image
->indexes
+ y
* image
->width
;
190 for (i
= 0; i
< b2
; ++i
) {
192 if (b1
== EOF
) break;
193 if (header2
.BitsPerPixel
== 8) {
194 if (x
< image
->width
) {
199 if (x
< image
->width
) {
203 if (x
< image
->width
) {
211 if (b1
== EOF
) break;
216 /* repeat b2, b1 times */
218 if (y
< image
->height
) {
219 p
= image
->indexes
+ y
* image
->width
;
220 for (i
= 0; i
< b1
; ++i
) {
221 if (header2
.BitsPerPixel
== 8) {
222 if (x
< image
->width
) {
227 if (x
< image
->width
) {
231 if (x
< image
->width
) {
241 bytes_per_row
= (image
->width
* header2
.BitsPerPixel
+ 31) / 32 * 4;
242 row_bytes
= (unsigned char *) alloc(bytes_per_row
);
243 if (header2
.Compression
== BI_RGB
) {
244 switch (header2
.BitsPerPixel
) {
246 header2
.RedMask
= 0x001F;
247 header2
.GreenMask
= 0x07E0;
248 header2
.BlueMask
= 0xF800;
249 header2
.AlphaMask
= 0x0000;
253 header2
.RedMask
= 0x000000FF;
254 header2
.GreenMask
= 0x0000FF00;
255 header2
.BlueMask
= 0x00FF0000;
256 header2
.AlphaMask
= 0xFF000000;
260 for (y
= y_start
; y
!= y_end
; y
+= y_inc
) {
261 struct Pixel
*row
= image
->pixels
+ y
* image
->width
;
262 unsigned char *ind
= image
->indexes
+ y
* image
->width
;
263 size
= fread(row_bytes
, 1, bytes_per_row
, fp
);
264 if (size
< bytes_per_row
) goto error
;
265 switch (header2
.BitsPerPixel
) {
267 for (x
= 0; x
< image
->width
; ++x
) {
268 unsigned byte
= x
/ 8;
269 unsigned shift
= x
% 8;
270 unsigned color
= (row_bytes
[byte
] >> shift
) & 1;
275 for (x
= 0; x
< image
->width
; ++x
) {
276 unsigned byte
= x
/ 2;
277 unsigned shift
= (x
% 2) * 4;
278 unsigned color
= (row_bytes
[byte
] >> shift
) & 1;
283 for (x
= 0; x
< image
->width
; ++x
) {
284 ind
[x
] = row_bytes
[x
];
288 for (x
= 0; x
< image
->width
; ++x
) {
289 uint16 color
= read_u16(row_bytes
+ x
* 2);
290 row
[x
] = build_pixel(&header2
, color
);
294 for (x
= 0; x
< image
->width
; ++x
) {
295 row
[x
].r
= row_bytes
[x
* 3 + 2];
296 row
[x
].g
= row_bytes
[x
* 3 + 1];
297 row
[x
].b
= row_bytes
[x
* 3 + 0];
302 for (x
= 0; x
< image
->width
; ++x
) {
303 uint32 color
= read_u32(row_bytes
+ x
* 2);
304 row
[x
] = build_pixel(&header2
, color
);
313 if (image
->indexes
!= NULL
) {
315 for (i
= 0; i
< num_pixels
; ++i
) {
316 image
->pixels
[i
] = image
->palette
[image
->indexes
[i
]];
327 image
->pixels
= NULL
;
328 free(image
->indexes
);
329 image
->indexes
= NULL
;
330 free(image
->image_desc
);
331 image
->image_desc
= NULL
;
335 /* Read and decode the first header */
337 read_header(fp
, header
)
339 struct BitmapHeader
*header
;
341 unsigned char buf
[14];
344 size
= fread(buf
, 1, sizeof(buf
), fp
);
345 if (size
< sizeof(buf
)) return FALSE
;
347 memcpy(header
->magic
, buf
+ 0, 2);
348 header
->bmp_size
= read_u32(buf
+ 2);
349 /* 6 and 8 are 16 bit integers giving the hotspot of a cursor */
350 header
->img_offset
= read_u32(buf
+ 10);
354 /* Read and decode the second header */
356 read_info_header(fp
, header
)
358 struct BitmapInfoHeader
*header
;
360 unsigned char buf
[124]; /* maximum size */
362 boolean have_color_mask
;
364 memset(header
, 0, sizeof(*header
));
366 /* Get the header size */
367 size
= fread(buf
, 1, 4, fp
);
368 if (size
< 4) return FALSE
;
369 header
->Size
= read_u32(buf
+ 0);
370 if (header
->Size
> sizeof(buf
)) return FALSE
;
372 /* Get the rest of the header */
373 size
= fread(buf
+ 4, 1, header
->Size
- 4, fp
);
374 if (size
< header
->Size
- 4) return FALSE
;
376 have_color_mask
= FALSE
;
377 switch (header
->Size
) {
378 case 124: /* BITMAPV5INFOHEADER */
379 /* 120 is reserved */
380 header
->ProfileSize
= read_u32(buf
+ 116);
381 header
->ProfileData
= read_u32(buf
+ 112);
382 header
->Intent
= read_u32(buf
+ 108);
385 case 108: /* BITMAPV4INFOHEADER */
386 header
->GammaBlue
= read_u32(buf
+ 104);
387 header
->GammaGreen
= read_u32(buf
+ 100);
388 header
->GammaRed
= read_u32(buf
+ 96);
389 header
->Endpoints
.ciexyzBlue
.ciexyzZ
= read_u32(buf
+ 92);
390 header
->Endpoints
.ciexyzBlue
.ciexyzY
= read_u32(buf
+ 88);
391 header
->Endpoints
.ciexyzBlue
.ciexyzX
= read_u32(buf
+ 84);
392 header
->Endpoints
.ciexyzGreen
.ciexyzZ
= read_u32(buf
+ 80);
393 header
->Endpoints
.ciexyzGreen
.ciexyzY
= read_u32(buf
+ 76);
394 header
->Endpoints
.ciexyzGreen
.ciexyzX
= read_u32(buf
+ 72);
395 header
->Endpoints
.ciexyzRed
.ciexyzZ
= read_u32(buf
+ 68);
396 header
->Endpoints
.ciexyzRed
.ciexyzY
= read_u32(buf
+ 64);
397 header
->Endpoints
.ciexyzRed
.ciexyzX
= read_u32(buf
+ 60);
398 header
->CSType
= read_u32(buf
+ 56);
401 case 56: /* BITMAPV3INFOHEADER */
402 header
->AlphaMask
= read_u32(buf
+ 52);
405 case 52: /* BITMAPV2INFOHEADER */
406 header
->BlueMask
= read_u32(buf
+ 48);
407 header
->GreenMask
= read_u32(buf
+ 44);
408 header
->RedMask
= read_u32(buf
+ 40);
409 have_color_mask
= TRUE
;
412 case 40: /* BITMAPINFOHEADER */
413 case 64: /* OS22XBITMAPHEADER */
414 /* The last 24 bytes in OS22XBITMAPHEADER are incompatible with the
415 * later Microsoft versions of the header */
416 header
->ColorsImportant
= read_u32(buf
+ 36);
417 header
->ColorsUsed
= read_u32(buf
+ 32);
418 header
->YResolution
= read_s32(buf
+ 28);
419 header
->XResolution
= read_s32(buf
+ 24);
420 header
->ImageDataSize
= read_u32(buf
+ 20);
421 header
->Compression
= read_u32(buf
+ 16);
422 header
->BitsPerPixel
= read_u16(buf
+ 14);
423 header
->NumPlanes
= read_u16(buf
+ 12);
424 header
->Height
= read_s32(buf
+ 8);
425 header
->Width
= read_s32(buf
+ 4);
428 case 12: /* BITMAPCOREHEADER */
429 header
->BitsPerPixel
= read_u16(buf
+ 10);
430 header
->NumPlanes
= read_u16(buf
+ 8);
431 header
->Height
= read_u16(buf
+ 6);
432 header
->Width
= read_u16(buf
+ 4);
439 /* For BI_BITFIELDS, the next three 32 bit words are the color masks */
440 if (header
->Compression
== BI_BITFIELDS
&& !have_color_mask
) {
441 size
= fread(buf
, 1, 12, fp
);
442 if (size
< 12) return FALSE
;
443 header
->RedMask
= read_u32(buf
+ 0);
444 header
->GreenMask
= read_u32(buf
+ 4);
445 header
->BlueMask
= read_u32(buf
+ 8);
451 /* Check the second header for consistency and unsupported features */
453 check_info_header(header
)
454 const struct BitmapInfoHeader
*header
;
456 if (header
->NumPlanes
!= 1) return FALSE
;
457 switch (header
->BitsPerPixel
) {
460 if (header
->Compression
!= BI_PNG
) return FALSE
;
461 /* JPEG not supported */
467 if (header
->Compression
!= BI_RGB
) return FALSE
;
471 if (header
->Compression
!= BI_RGB
472 && header
->Compression
!= BI_RLE4
) return FALSE
;
476 if (header
->Compression
!= BI_RGB
477 && header
->Compression
!= BI_RLE8
) return FALSE
;
482 if (header
->Compression
!= BI_RGB
483 && header
->Compression
!= BI_BITFIELDS
) return FALSE
;
484 /* Any of the color masks could conceivably be zero; the bitmap, though
485 * limited, would still be meaningful */
486 if (header
->Compression
== BI_BITFIELDS
487 && header
->RedMask
== 0
488 && header
->GreenMask
== 0
489 && header
->BlueMask
== 0) return FALSE
;
496 if (header
->Height
< 0 && header
->Compression
!= BI_RGB
497 && header
->Compression
!= BI_BITFIELDS
) return FALSE
;
502 /* Return the number of palette entries to read from the file */
504 get_palette_size(header
)
505 const struct BitmapInfoHeader
*header
;
507 switch (header
->BitsPerPixel
) {
512 return header
->ColorsUsed
? header
->ColorsUsed
: 16;
515 return header
->ColorsUsed
? header
->ColorsUsed
: 256;
523 * Read the palette from the file
524 * palette_size is the number of entries to read, but no more than 256 will
525 * be written into the palette array
526 * Return TRUE if successful, FALSE on any error
529 read_palette(fp
, palette
, palette_size
)
531 struct Pixel
*palette
;
532 unsigned palette_size
;
535 unsigned char buf
[4];
538 read_size
= (palette_size
< 256) ? palette_size
: 256;
539 for (i
= 0; i
< read_size
; ++i
) {
540 size_t size
= fread(buf
, 1, sizeof(buf
), fp
);
541 if (size
< sizeof(buf
)) return FALSE
;
542 palette
[i
].b
= buf
[0];
543 palette
[i
].g
= buf
[1];
544 palette
[i
].r
= buf
[2];
547 for (; i
< 256; ++i
) {
553 fseek(fp
, 4 * (palette_size
- read_size
), SEEK_CUR
);
557 /* Decode an unsigned 16 bit quantity */
560 const unsigned char buf
[2];
562 return ((uint16
)buf
[0] << 0)
563 | ((uint16
)buf
[1] << 8);
566 /* Decode an unsigned 32 bit quantity */
569 const unsigned char buf
[4];
571 return ((uint32
)buf
[0] << 0)
572 | ((uint32
)buf
[1] << 8)
573 | ((uint32
)buf
[2] << 16)
574 | ((uint32
)buf
[3] << 24);
577 /* Decode a signed 32 bit quantity */
580 const unsigned char buf
[4];
582 return (int32
)((read_u32(buf
) ^ 0x80000000) - 0x80000000);
585 /* Build a pixel structure, given the mask words in the second header and
586 * a packed 16 or 32 bit pixel */
588 build_pixel(header
, color
)
589 const struct BitmapInfoHeader
*header
;
594 pixel
.r
= pixel_element(header
->RedMask
, color
);
595 pixel
.g
= pixel_element(header
->GreenMask
, color
);
596 pixel
.b
= pixel_element(header
->BlueMask
, color
);
597 pixel
.a
= header
->AlphaMask
? pixel_element(header
->AlphaMask
, color
) : 255;
601 /* Extract one element (red, green, blue or alpha) from a pixel */
603 pixel_element(mask
, color
)
609 if (mask
== 0) return 0;
610 bits
= 0xFFFF; /* 0xFF, 0xF, 0x3, 0x1 */
611 shift
= 16; /* 8, 4, 2, 1 */
613 if ((mask
& bits
) == 0) {
621 return color
* 255 / mask
;