1 /*
2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #if (!defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)) || !defined(BMP_USES_IMAGEIO)
23
24 /* This is a BMP image file loading framework
25 *
26 * ICO/CUR file support is here as well since it uses similar internal
27 * representation
28 *
29 * A good test suite of BMP images is available at:
30 * http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
31 */
32
33 #include "SDL_image.h"
34
35 #ifdef LOAD_BMP
36
37 /* See if an image is contained in a data source */
IMG_isBMP(SDL_RWops * src)38 int IMG_isBMP(SDL_RWops *src)
39 {
40 Sint64 start;
41 int is_BMP;
42 char magic[2];
43
44 if ( !src )
45 return 0;
46 start = SDL_RWtell(src);
47 is_BMP = 0;
48 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
49 if ( SDL_strncmp(magic, "BM", 2) == 0 ) {
50 is_BMP = 1;
51 }
52 }
53 SDL_RWseek(src, start, RW_SEEK_SET);
54 return(is_BMP);
55 }
56
IMG_isICOCUR(SDL_RWops * src,int type)57 static int IMG_isICOCUR(SDL_RWops *src, int type)
58 {
59 Sint64 start;
60 int is_ICOCUR;
61
62 /* The Win32 ICO file header (14 bytes) */
63 Uint16 bfReserved;
64 Uint16 bfType;
65 Uint16 bfCount;
66
67 if ( !src )
68 return 0;
69 start = SDL_RWtell(src);
70 is_ICOCUR = 0;
71 bfReserved = SDL_ReadLE16(src);
72 bfType = SDL_ReadLE16(src);
73 bfCount = SDL_ReadLE16(src);
74 if ((bfReserved == 0) && (bfType == type) && (bfCount != 0))
75 is_ICOCUR = 1;
76 SDL_RWseek(src, start, RW_SEEK_SET);
77
78 return (is_ICOCUR);
79 }
80
IMG_isICO(SDL_RWops * src)81 int IMG_isICO(SDL_RWops *src)
82 {
83 return IMG_isICOCUR(src, 1);
84 }
85
IMG_isCUR(SDL_RWops * src)86 int IMG_isCUR(SDL_RWops *src)
87 {
88 return IMG_isICOCUR(src, 2);
89 }
90
91 #include "SDL_error.h"
92 #include "SDL_video.h"
93 #include "SDL_endian.h"
94
95 /* Compression encodings for BMP files */
96 #ifndef BI_RGB
97 #define BI_RGB 0
98 #define BI_RLE8 1
99 #define BI_RLE4 2
100 #define BI_BITFIELDS 3
101 #endif
102
readRlePixels(SDL_Surface * surface,SDL_RWops * src,int isRle8)103 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
104 {
105 /*
106 | Sets the surface pixels from src. A bmp image is upside down.
107 */
108 int pitch = surface->pitch;
109 int height = surface->h;
110 Uint8 *start = (Uint8 *)surface->pixels;
111 Uint8 *end = start + (height*pitch);
112 Uint8 *bits = end-pitch, *spot;
113 int ofs = 0;
114 Uint8 ch;
115 Uint8 needsPad;
116
117 #define COPY_PIXEL(x) spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
118
119 for (;;) {
120 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
121 /*
122 | encoded mode starts with a run length, and then a byte
123 | with two colour indexes to alternate between for the run
124 */
125 if ( ch ) {
126 Uint8 pixel;
127 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
128 if ( isRle8 ) { /* 256-color bitmap, compressed */
129 do {
130 COPY_PIXEL(pixel);
131 } while (--ch);
132 } else { /* 16-color bitmap, compressed */
133 Uint8 pixel0 = pixel >> 4;
134 Uint8 pixel1 = pixel & 0x0F;
135 for (;;) {
136 COPY_PIXEL(pixel0); /* even count, high nibble */
137 if (!--ch) break;
138 COPY_PIXEL(pixel1); /* odd count, low nibble */
139 if (!--ch) break;
140 }
141 }
142 } else {
143 /*
144 | A leading zero is an escape; it may signal the end of the bitmap,
145 | a cursor move, or some absolute data.
146 | zero tag may be absolute mode or an escape
147 */
148 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
149 switch (ch) {
150 case 0: /* end of line */
151 ofs = 0;
152 bits -= pitch; /* go to previous */
153 break;
154 case 1: /* end of bitmap */
155 return 0; /* success! */
156 case 2: /* delta */
157 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
158 ofs += ch;
159 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
160 bits -= (ch * pitch);
161 break;
162 default: /* no compression */
163 if (isRle8) {
164 needsPad = ( ch & 1 );
165 do {
166 Uint8 pixel;
167 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
168 COPY_PIXEL(pixel);
169 } while (--ch);
170 } else {
171 needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
172 for (;;) {
173 Uint8 pixel;
174 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
175 COPY_PIXEL(pixel >> 4);
176 if (!--ch) break;
177 COPY_PIXEL(pixel & 0x0F);
178 if (!--ch) break;
179 }
180 }
181 /* pad at even boundary */
182 if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
183 break;
184 }
185 }
186 }
187 }
188
CorrectAlphaChannel(SDL_Surface * surface)189 static void CorrectAlphaChannel(SDL_Surface *surface)
190 {
191 /* Check to see if there is any alpha channel data */
192 SDL_bool hasAlpha = SDL_FALSE;
193 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
194 int alphaChannelOffset = 0;
195 #else
196 int alphaChannelOffset = 3;
197 #endif
198 Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
199 Uint8 *end = alpha + surface->h * surface->pitch;
200
201 while (alpha < end) {
202 if (*alpha != 0) {
203 hasAlpha = SDL_TRUE;
204 break;
205 }
206 alpha += 4;
207 }
208
209 if (!hasAlpha) {
210 alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
211 while (alpha < end) {
212 *alpha = SDL_ALPHA_OPAQUE;
213 alpha += 4;
214 }
215 }
216 }
217
LoadBMP_RW(SDL_RWops * src,int freesrc)218 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
219 {
220 SDL_bool was_error;
221 Sint64 fp_offset;
222 int bmpPitch;
223 int i, pad;
224 SDL_Surface *surface;
225 Uint32 Rmask = 0;
226 Uint32 Gmask = 0;
227 Uint32 Bmask = 0;
228 Uint32 Amask = 0;
229 SDL_Palette *palette;
230 Uint8 *bits;
231 Uint8 *top, *end;
232 SDL_bool topDown;
233 int ExpandBMP;
234 SDL_bool haveRGBMasks = SDL_FALSE;
235 SDL_bool haveAlphaMask = SDL_FALSE;
236 SDL_bool correctAlpha = SDL_FALSE;
237
238 /* The Win32 BMP file header (14 bytes) */
239 char magic[2];
240 Uint32 bfSize;
241 Uint16 bfReserved1;
242 Uint16 bfReserved2;
243 Uint32 bfOffBits;
244
245 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
246 Uint32 biSize;
247 Sint32 biWidth;
248 Sint32 biHeight = 0;
249 Uint16 biPlanes;
250 Uint16 biBitCount;
251 Uint32 biCompression;
252 Uint32 biSizeImage;
253 Sint32 biXPelsPerMeter;
254 Sint32 biYPelsPerMeter;
255 Uint32 biClrUsed;
256 Uint32 biClrImportant;
257
258 /* Make sure we are passed a valid data source */
259 surface = NULL;
260 was_error = SDL_FALSE;
261 if ( src == NULL ) {
262 was_error = SDL_TRUE;
263 goto done;
264 }
265
266 /* Read in the BMP file header */
267 fp_offset = SDL_RWtell(src);
268 SDL_ClearError();
269 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
270 SDL_Error(SDL_EFREAD);
271 was_error = SDL_TRUE;
272 goto done;
273 }
274 if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
275 IMG_SetError("File is not a Windows BMP file");
276 was_error = SDL_TRUE;
277 goto done;
278 }
279 bfSize = SDL_ReadLE32(src);
280 bfReserved1 = SDL_ReadLE16(src);
281 bfReserved2 = SDL_ReadLE16(src);
282 bfOffBits = SDL_ReadLE32(src);
283
284 /* Read the Win32 BITMAPINFOHEADER */
285 biSize = SDL_ReadLE32(src);
286 if ( biSize == 12 ) { /* really old BITMAPCOREHEADER */
287 biWidth = (Uint32)SDL_ReadLE16(src);
288 biHeight = (Uint32)SDL_ReadLE16(src);
289 biPlanes = SDL_ReadLE16(src);
290 biBitCount = SDL_ReadLE16(src);
291 biCompression = BI_RGB;
292 biSizeImage = 0;
293 biXPelsPerMeter = 0;
294 biYPelsPerMeter = 0;
295 biClrUsed = 0;
296 biClrImportant = 0;
297 } else if (biSize >= 40) { /* some version of BITMAPINFOHEADER */
298 Uint32 headerSize;
299 biWidth = SDL_ReadLE32(src);
300 biHeight = SDL_ReadLE32(src);
301 biPlanes = SDL_ReadLE16(src);
302 biBitCount = SDL_ReadLE16(src);
303 biCompression = SDL_ReadLE32(src);
304 biSizeImage = SDL_ReadLE32(src);
305 biXPelsPerMeter = SDL_ReadLE32(src);
306 biYPelsPerMeter = SDL_ReadLE32(src);
307 biClrUsed = SDL_ReadLE32(src);
308 biClrImportant = SDL_ReadLE32(src);
309
310 /* 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now. */
311 if (biSize != 64) {
312 /* This is complicated. If compression is BI_BITFIELDS, then
313 we have 3 DWORDS that specify the RGB masks. This is either
314 stored here in an BITMAPV2INFOHEADER (which only differs in
315 that it adds these RGB masks) and biSize >= 52, or we've got
316 these masks stored in the exact same place, but strictly
317 speaking, this is the bmiColors field in BITMAPINFO immediately
318 following the legacy v1 info header, just past biSize. */
319 if (biCompression == BI_BITFIELDS) {
320 haveRGBMasks = SDL_TRUE;
321 Rmask = SDL_ReadLE32(src);
322 Gmask = SDL_ReadLE32(src);
323 Bmask = SDL_ReadLE32(src);
324
325 /* ...v3 adds an alpha mask. */
326 if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
327 haveAlphaMask = SDL_TRUE;
328 Amask = SDL_ReadLE32(src);
329 }
330 } else {
331 /* the mask fields are ignored for v2+ headers if not BI_BITFIELD. */
332 if (biSize >= 52) { /* BITMAPV2INFOHEADER; adds RGB masks */
333 /*Rmask = */ SDL_ReadLE32(src);
334 /*Gmask = */ SDL_ReadLE32(src);
335 /*Bmask = */ SDL_ReadLE32(src);
336 }
337 if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
338 /*Amask = */ SDL_ReadLE32(src);
339 }
340 }
341
342 /* Insert other fields here; Wikipedia and MSDN say we're up to
343 v5 of this header, but we ignore those for now (they add gamma,
344 color spaces, etc). Ignoring the weird OS/2 2.x format, we
345 currently parse up to v3 correctly (hopefully!). */
346 }
347
348 /* skip any header bytes we didn't handle... */
349 headerSize = (Uint32) (SDL_RWtell(src) - (fp_offset + 14));
350 if (biSize > headerSize) {
351 SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR);
352 }
353 }
354 if (biHeight < 0) {
355 topDown = SDL_TRUE;
356 biHeight = -biHeight;
357 } else {
358 topDown = SDL_FALSE;
359 }
360
361 /* Check for read error */
362 if (SDL_strcmp(SDL_GetError(), "") != 0) {
363 was_error = SDL_TRUE;
364 goto done;
365 }
366
367 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
368 switch (biBitCount) {
369 case 1:
370 case 4:
371 ExpandBMP = biBitCount;
372 biBitCount = 8;
373 break;
374 case 2:
375 case 3:
376 case 5:
377 case 6:
378 case 7:
379 SDL_SetError("%d-bpp BMP images are not supported", biBitCount);
380 was_error = SDL_TRUE;
381 goto done;
382 default:
383 ExpandBMP = 0;
384 break;
385 }
386
387 /* RLE4 and RLE8 BMP compression is supported */
388 switch (biCompression) {
389 case BI_RGB:
390 /* If there are no masks, use the defaults */
391 SDL_assert(!haveRGBMasks);
392 SDL_assert(!haveAlphaMask);
393 /* Default values for the BMP format */
394 switch (biBitCount) {
395 case 15:
396 case 16:
397 Rmask = 0x7C00;
398 Gmask = 0x03E0;
399 Bmask = 0x001F;
400 break;
401 case 24:
402 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
403 Rmask = 0x000000FF;
404 Gmask = 0x0000FF00;
405 Bmask = 0x00FF0000;
406 #else
407 Rmask = 0x00FF0000;
408 Gmask = 0x0000FF00;
409 Bmask = 0x000000FF;
410 #endif
411 break;
412 case 32:
413 /* We don't know if this has alpha channel or not */
414 correctAlpha = SDL_TRUE;
415 Amask = 0xFF000000;
416 Rmask = 0x00FF0000;
417 Gmask = 0x0000FF00;
418 Bmask = 0x000000FF;
419 break;
420 default:
421 break;
422 }
423 break;
424
425 case BI_BITFIELDS:
426 break; /* we handled this in the info header. */
427
428 default:
429 break;
430 }
431
432 /* Create a compatible surface, note that the colors are RGB ordered */
433 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
434 biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
435 if ( surface == NULL ) {
436 was_error = SDL_TRUE;
437 goto done;
438 }
439
440 /* Load the palette, if any */
441 palette = (surface->format)->palette;
442 if ( palette ) {
443 if ( SDL_RWseek(src, fp_offset+14+biSize, RW_SEEK_SET) < 0 ) {
444 SDL_Error(SDL_EFSEEK);
445 was_error = SDL_TRUE;
446 goto done;
447 }
448
449 /*
450 | guich: always use 1<<bpp b/c some bitmaps can bring wrong information
451 | for colorsUsed
452 */
453 /* if ( biClrUsed == 0 ) { */
454 biClrUsed = 1 << biBitCount;
455 /* } */
456 if ( biSize == 12 ) {
457 for ( i = 0; i < (int)biClrUsed; ++i ) {
458 SDL_RWread(src, &palette->colors[i].b, 1, 1);
459 SDL_RWread(src, &palette->colors[i].g, 1, 1);
460 SDL_RWread(src, &palette->colors[i].r, 1, 1);
461 palette->colors[i].a = SDL_ALPHA_OPAQUE;
462 }
463 } else {
464 for ( i = 0; i < (int)biClrUsed; ++i ) {
465 SDL_RWread(src, &palette->colors[i].b, 1, 1);
466 SDL_RWread(src, &palette->colors[i].g, 1, 1);
467 SDL_RWread(src, &palette->colors[i].r, 1, 1);
468 SDL_RWread(src, &palette->colors[i].a, 1, 1);
469
470 /* According to Microsoft documentation, the fourth element
471 is reserved and must be zero, so we shouldn't treat it as
472 alpha.
473 */
474 palette->colors[i].a = SDL_ALPHA_OPAQUE;
475 }
476 }
477 palette->ncolors = biClrUsed;
478 }
479
480 /* Read the surface pixels. Note that the bmp image is upside down */
481 if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
482 SDL_Error(SDL_EFSEEK);
483 was_error = SDL_TRUE;
484 goto done;
485 }
486 if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
487 was_error = (SDL_bool)readRlePixels(surface, src, biCompression == BI_RLE8);
488 if (was_error) IMG_SetError("Error reading from BMP");
489 goto done;
490 }
491 top = (Uint8 *)surface->pixels;
492 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
493 switch (ExpandBMP) {
494 case 1:
495 bmpPitch = (biWidth + 7) >> 3;
496 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
497 break;
498 case 4:
499 bmpPitch = (biWidth + 1) >> 1;
500 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
501 break;
502 default:
503 pad = ((surface->pitch%4) ?
504 (4-(surface->pitch%4)) : 0);
505 break;
506 }
507 if ( topDown ) {
508 bits = top;
509 } else {
510 bits = end - surface->pitch;
511 }
512 while ( bits >= top && bits < end ) {
513 switch (ExpandBMP) {
514 case 1:
515 case 4: {
516 Uint8 pixel = 0;
517 int shift = (8-ExpandBMP);
518 for ( i=0; i<surface->w; ++i ) {
519 if ( i%(8/ExpandBMP) == 0 ) {
520 if ( !SDL_RWread(src, &pixel, 1, 1) ) {
521 IMG_SetError("Error reading from BMP");
522 was_error = SDL_TRUE;
523 goto done;
524 }
525 }
526 bits[i] = (pixel >> shift);
527 if (bits[i] >= biClrUsed) {
528 IMG_SetError("A BMP image contains a pixel with a color out of the palette");
529 was_error = SDL_TRUE;
530 goto done;
531 }
532 pixel <<= ExpandBMP;
533 }
534 }
535 break;
536
537 default:
538 if ( SDL_RWread(src, bits, 1, surface->pitch) != surface->pitch ) {
539 SDL_Error(SDL_EFREAD);
540 was_error = SDL_TRUE;
541 goto done;
542 }
543 if (biBitCount == 8 && palette && biClrUsed < (1 << biBitCount)) {
544 for (i = 0; i < surface->w; ++i) {
545 if (bits[i] >= biClrUsed) {
546 SDL_SetError("A BMP image contains a pixel with a color out of the palette");
547 was_error = SDL_TRUE;
548 goto done;
549 }
550 }
551 }
552 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
553 /* Byte-swap the pixels if needed. Note that the 24bpp
554 case has already been taken care of above. */
555 switch(biBitCount) {
556 case 15:
557 case 16: {
558 Uint16 *pix = (Uint16 *)bits;
559 for(i = 0; i < surface->w; i++)
560 pix[i] = SDL_Swap16(pix[i]);
561 break;
562 }
563
564 case 32: {
565 Uint32 *pix = (Uint32 *)bits;
566 for(i = 0; i < surface->w; i++)
567 pix[i] = SDL_Swap32(pix[i]);
568 break;
569 }
570 }
571 #endif
572 break;
573 }
574 /* Skip padding bytes, ugh */
575 if ( pad ) {
576 Uint8 padbyte;
577 for ( i=0; i<pad; ++i ) {
578 SDL_RWread(src, &padbyte, 1, 1);
579 }
580 }
581 if ( topDown ) {
582 bits += surface->pitch;
583 } else {
584 bits -= surface->pitch;
585 }
586 }
587 if (correctAlpha) {
588 CorrectAlphaChannel(surface);
589 }
590 done:
591 if ( was_error ) {
592 if ( src ) {
593 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
594 }
595 if ( surface ) {
596 SDL_FreeSurface(surface);
597 }
598 surface = NULL;
599 }
600 if ( freesrc && src ) {
601 SDL_RWclose(src);
602 }
603 return(surface);
604 }
605
606 static Uint8
SDL_Read8(SDL_RWops * src)607 SDL_Read8(SDL_RWops * src)
608 {
609 Uint8 value;
610
611 SDL_RWread(src, &value, 1, 1);
612 return (value);
613 }
614
615 static SDL_Surface *
LoadICOCUR_RW(SDL_RWops * src,int type,int freesrc)616 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
617 {
618 SDL_bool was_error;
619 Sint64 fp_offset;
620 int bmpPitch;
621 int i, pad;
622 SDL_Surface *surface;
623 Uint32 Rmask;
624 Uint32 Gmask;
625 Uint32 Bmask;
626 Uint8 *bits;
627 int ExpandBMP;
628 int maxCol = 0;
629 int icoOfs = 0;
630 Uint32 palette[256];
631
632 /* The Win32 ICO file header (14 bytes) */
633 Uint16 bfReserved;
634 Uint16 bfType;
635 Uint16 bfCount;
636
637 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
638 Uint32 biSize;
639 Sint32 biWidth;
640 Sint32 biHeight;
641 Uint16 biPlanes;
642 Uint16 biBitCount;
643 Uint32 biCompression;
644 Uint32 biSizeImage;
645 Sint32 biXPelsPerMeter;
646 Sint32 biYPelsPerMeter;
647 Uint32 biClrUsed;
648 Uint32 biClrImportant;
649
650 /* Make sure we are passed a valid data source */
651 surface = NULL;
652 was_error = SDL_FALSE;
653 if (src == NULL) {
654 was_error = SDL_TRUE;
655 goto done;
656 }
657
658 /* Read in the ICO file header */
659 fp_offset = SDL_RWtell(src);
660 SDL_ClearError();
661
662 bfReserved = SDL_ReadLE16(src);
663 bfType = SDL_ReadLE16(src);
664 bfCount = SDL_ReadLE16(src);
665 if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
666 IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
667 was_error = SDL_TRUE;
668 goto done;
669 }
670
671 /* Read the Win32 Icon Directory */
672 for (i = 0; i < bfCount; i++) {
673 /* Icon Directory Entries */
674 int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
675 int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
676 int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
677 Uint8 bReserved = SDL_Read8(src);
678 Uint16 wPlanes = SDL_ReadLE16(src);
679 Uint16 wBitCount = SDL_ReadLE16(src);
680 Uint32 dwBytesInRes = SDL_ReadLE32(src);
681 Uint32 dwImageOffset = SDL_ReadLE32(src);
682
683 if (!bWidth)
684 bWidth = 256;
685 if (!bHeight)
686 bHeight = 256;
687 if (!bColorCount)
688 bColorCount = 256;
689
690 //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
691 if (bColorCount > maxCol) {
692 maxCol = bColorCount;
693 icoOfs = dwImageOffset;
694 //printf("marked\n");
695 }
696 }
697
698 /* Advance to the DIB Data */
699 if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
700 SDL_Error(SDL_EFSEEK);
701 was_error = SDL_TRUE;
702 goto done;
703 }
704
705 /* Read the Win32 BITMAPINFOHEADER */
706 biSize = SDL_ReadLE32(src);
707 if (biSize == 40) {
708 biWidth = SDL_ReadLE32(src);
709 biHeight = SDL_ReadLE32(src);
710 biPlanes = SDL_ReadLE16(src);
711 biBitCount = SDL_ReadLE16(src);
712 biCompression = SDL_ReadLE32(src);
713 biSizeImage = SDL_ReadLE32(src);
714 biXPelsPerMeter = SDL_ReadLE32(src);
715 biYPelsPerMeter = SDL_ReadLE32(src);
716 biClrUsed = SDL_ReadLE32(src);
717 biClrImportant = SDL_ReadLE32(src);
718 } else {
719 IMG_SetError("Unsupported ICO bitmap format");
720 was_error = SDL_TRUE;
721 goto done;
722 }
723
724 /* Check for read error */
725 if (SDL_strcmp(SDL_GetError(), "") != 0) {
726 was_error = SDL_TRUE;
727 goto done;
728 }
729
730 /* We don't support any BMP compression right now */
731 switch (biCompression) {
732 case BI_RGB:
733 /* Default values for the BMP format */
734 switch (biBitCount) {
735 case 1:
736 case 4:
737 ExpandBMP = biBitCount;
738 biBitCount = 8;
739 break;
740 case 8:
741 ExpandBMP = 8;
742 break;
743 case 32:
744 Rmask = 0x00FF0000;
745 Gmask = 0x0000FF00;
746 Bmask = 0x000000FF;
747 ExpandBMP = 0;
748 break;
749 default:
750 IMG_SetError("ICO file with unsupported bit count");
751 was_error = SDL_TRUE;
752 goto done;
753 }
754 break;
755 default:
756 IMG_SetError("Compressed ICO files not supported");
757 was_error = SDL_TRUE;
758 goto done;
759 }
760
761 /* sanity check image size, so we don't overflow integers, etc. */
762 if ((biWidth < 0) || (biWidth > 0xFFFFFF) ||
763 (biHeight < 0) || (biHeight > 0xFFFFFF)) {
764 IMG_SetError("Unsupported or invalid ICO dimensions");
765 was_error = SDL_TRUE;
766 goto done;
767 }
768
769 /* Create a RGBA surface */
770 biHeight = biHeight >> 1;
771 //printf("%d x %d\n", biWidth, biHeight);
772 surface =
773 SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
774 0x0000FF00, 0x000000FF, 0xFF000000);
775 if (surface == NULL) {
776 was_error = SDL_TRUE;
777 goto done;
778 }
779
780 /* Load the palette, if any */
781 //printf("bc %d bused %d\n", biBitCount, biClrUsed);
782 if (biBitCount <= 8) {
783 if (biClrUsed == 0) {
784 biClrUsed = 1 << biBitCount;
785 }
786 if (biClrUsed > SDL_arraysize(palette)) {
787 IMG_SetError("Unsupported or incorrect biClrUsed field");
788 was_error = SDL_TRUE;
789 goto done;
790 }
791 for (i = 0; i < (int) biClrUsed; ++i) {
792 SDL_RWread(src, &palette[i], 4, 1);
793 }
794 }
795
796 /* Read the surface pixels. Note that the bmp image is upside down */
797 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
798 switch (ExpandBMP) {
799 case 1:
800 bmpPitch = (biWidth + 7) >> 3;
801 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
802 break;
803 case 4:
804 bmpPitch = (biWidth + 1) >> 1;
805 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
806 break;
807 case 8:
808 bmpPitch = biWidth;
809 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
810 break;
811 default:
812 bmpPitch = biWidth * 4;
813 pad = 0;
814 break;
815 }
816 while (bits > (Uint8 *) surface->pixels) {
817 bits -= surface->pitch;
818 switch (ExpandBMP) {
819 case 1:
820 case 4:
821 case 8:
822 {
823 Uint8 pixel = 0;
824 int shift = (8 - ExpandBMP);
825 for (i = 0; i < surface->w; ++i) {
826 if (i % (8 / ExpandBMP) == 0) {
827 if (!SDL_RWread(src, &pixel, 1, 1)) {
828 IMG_SetError("Error reading from ICO");
829 was_error = SDL_TRUE;
830 goto done;
831 }
832 }
833 *((Uint32 *) bits + i) = (palette[pixel >> shift]);
834 pixel <<= ExpandBMP;
835 }
836 }
837 break;
838
839 default:
840 if (SDL_RWread(src, bits, 1, surface->pitch)
841 != surface->pitch) {
842 SDL_Error(SDL_EFREAD);
843 was_error = SDL_TRUE;
844 goto done;
845 }
846 break;
847 }
848 /* Skip padding bytes, ugh */
849 if (pad) {
850 Uint8 padbyte;
851 for (i = 0; i < pad; ++i) {
852 SDL_RWread(src, &padbyte, 1, 1);
853 }
854 }
855 }
856 /* Read the mask pixels. Note that the bmp image is upside down */
857 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
858 ExpandBMP = 1;
859 bmpPitch = (biWidth + 7) >> 3;
860 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
861 while (bits > (Uint8 *) surface->pixels) {
862 Uint8 pixel = 0;
863 int shift = (8 - ExpandBMP);
864
865 bits -= surface->pitch;
866 for (i = 0; i < surface->w; ++i) {
867 if (i % (8 / ExpandBMP) == 0) {
868 if (!SDL_RWread(src, &pixel, 1, 1)) {
869 IMG_SetError("Error reading from ICO");
870 was_error = SDL_TRUE;
871 goto done;
872 }
873 }
874 *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
875 pixel <<= ExpandBMP;
876 }
877 /* Skip padding bytes, ugh */
878 if (pad) {
879 Uint8 padbyte;
880 for (i = 0; i < pad; ++i) {
881 SDL_RWread(src, &padbyte, 1, 1);
882 }
883 }
884 }
885 done:
886 if (was_error) {
887 if (src) {
888 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
889 }
890 if (surface) {
891 SDL_FreeSurface(surface);
892 }
893 surface = NULL;
894 }
895 if (freesrc && src) {
896 SDL_RWclose(src);
897 }
898 return (surface);
899 }
900
901 /* Load a BMP type image from an SDL datasource */
IMG_LoadBMP_RW(SDL_RWops * src)902 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
903 {
904 return(LoadBMP_RW(src, 0));
905 }
906
907 /* Load a ICO type image from an SDL datasource */
IMG_LoadICO_RW(SDL_RWops * src)908 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
909 {
910 return(LoadICOCUR_RW(src, 1, 0));
911 }
912
913 /* Load a CUR type image from an SDL datasource */
IMG_LoadCUR_RW(SDL_RWops * src)914 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
915 {
916 return(LoadICOCUR_RW(src, 2, 0));
917 }
918
919 #else
920
921 /* See if an image is contained in a data source */
IMG_isBMP(SDL_RWops * src)922 int IMG_isBMP(SDL_RWops *src)
923 {
924 return(0);
925 }
926
IMG_isICO(SDL_RWops * src)927 int IMG_isICO(SDL_RWops *src)
928 {
929 return(0);
930 }
931
IMG_isCUR(SDL_RWops * src)932 int IMG_isCUR(SDL_RWops *src)
933 {
934 return(0);
935 }
936
937 /* Load a BMP type image from an SDL datasource */
IMG_LoadBMP_RW(SDL_RWops * src)938 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
939 {
940 return(NULL);
941 }
942
943 /* Load a BMP type image from an SDL datasource */
IMG_LoadCUR_RW(SDL_RWops * src)944 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
945 {
946 return(NULL);
947 }
948
949 /* Load a BMP type image from an SDL datasource */
IMG_LoadICO_RW(SDL_RWops * src)950 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
951 {
952 return(NULL);
953 }
954
955 #endif /* LOAD_BMP */
956
957 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
958