2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #if (!defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)) || !defined(BMP_USES_IMAGEIO)
24 /* This is a BMP image file loading framework
26 * ICO/CUR file support is here as well since it uses similar internal
29 * A good test suite of BMP images is available at:
30 * http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
36 #include "SDL_image.h"
40 /* See if an image is contained in a data source */
41 int IMG_isBMP(SDL_RWops *src)
49 start = SDL_RWtell(src);
51 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
52 if ( SDL_strncmp(magic, "BM", 2) == 0 ) {
56 SDL_RWseek(src, start, RW_SEEK_SET);
60 static int IMG_isICOCUR(SDL_RWops *src, int type)
65 /* The Win32 ICO file header (14 bytes) */
72 start = SDL_RWtell(src);
74 bfReserved = SDL_ReadLE16(src);
75 bfType = SDL_ReadLE16(src);
76 bfCount = SDL_ReadLE16(src);
77 if ((bfReserved == 0) && (bfType == type) && (bfCount != 0))
79 SDL_RWseek(src, start, RW_SEEK_SET);
84 int IMG_isICO(SDL_RWops *src)
86 return IMG_isICOCUR(src, 1);
89 int IMG_isCUR(SDL_RWops *src)
91 return IMG_isICOCUR(src, 2);
94 #include "SDL_error.h"
95 #include "SDL_video.h"
96 #include "SDL_endian.h"
98 /* Compression encodings for BMP files */
103 #define BI_BITFIELDS 3
106 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
109 | Sets the surface pixels from src. A bmp image is upside down.
111 int pitch = surface->pitch;
112 int height = surface->h;
113 Uint8 *start = (Uint8 *)surface->pixels;
114 Uint8 *end = start + (height*pitch);
115 Uint8 *bits = end-pitch, *spot;
120 #define COPY_PIXEL(x) spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
123 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
125 | encoded mode starts with a run length, and then a byte
126 | with two colour indexes to alternate between for the run
130 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
131 if ( isRle8 ) { /* 256-color bitmap, compressed */
135 } else { /* 16-color bitmap, compressed */
136 Uint8 pixel0 = pixel >> 4;
137 Uint8 pixel1 = pixel & 0x0F;
139 COPY_PIXEL(pixel0); /* even count, high nibble */
141 COPY_PIXEL(pixel1); /* odd count, low nibble */
147 | A leading zero is an escape; it may signal the end of the bitmap,
148 | a cursor move, or some absolute data.
149 | zero tag may be absolute mode or an escape
151 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
153 case 0: /* end of line */
155 bits -= pitch; /* go to previous */
157 case 1: /* end of bitmap */
158 return 0; /* success! */
160 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
162 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
163 bits -= (ch * pitch);
165 default: /* no compression */
167 needsPad = ( ch & 1 );
170 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
174 needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
177 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
178 COPY_PIXEL(pixel >> 4);
180 COPY_PIXEL(pixel & 0x0F);
184 /* pad at even boundary */
185 if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
192 static void CorrectAlphaChannel(SDL_Surface *surface)
194 /* Check to see if there is any alpha channel data */
195 SDL_bool hasAlpha = SDL_FALSE;
196 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
197 int alphaChannelOffset = 0;
199 int alphaChannelOffset = 3;
201 Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
202 Uint8 *end = alpha + surface->h * surface->pitch;
204 while (alpha < end) {
213 alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
214 while (alpha < end) {
215 *alpha = SDL_ALPHA_OPAQUE;
221 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
227 SDL_Surface *surface;
232 SDL_Palette *palette;
237 SDL_bool correctAlpha = SDL_FALSE;
239 /* The Win32 BMP file header (14 bytes) */
246 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
252 Uint32 biCompression;
254 Sint32 biXPelsPerMeter;
255 Sint32 biYPelsPerMeter;
257 Uint32 biClrImportant;
259 /* Make sure we are passed a valid data source */
261 was_error = SDL_FALSE;
263 was_error = SDL_TRUE;
267 /* Read in the BMP file header */
268 fp_offset = SDL_RWtell(src);
270 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
271 SDL_Error(SDL_EFREAD);
272 was_error = SDL_TRUE;
275 if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
276 IMG_SetError("File is not a Windows BMP file");
277 was_error = SDL_TRUE;
280 bfSize = SDL_ReadLE32(src);
281 bfReserved1 = SDL_ReadLE16(src);
282 bfReserved2 = SDL_ReadLE16(src);
283 bfOffBits = SDL_ReadLE32(src);
285 /* Read the Win32 BITMAPINFOHEADER */
286 biSize = SDL_ReadLE32(src);
287 if ( biSize == 12 ) {
288 biWidth = (Uint32)SDL_ReadLE16(src);
289 biHeight = (Uint32)SDL_ReadLE16(src);
290 biPlanes = SDL_ReadLE16(src);
291 biBitCount = SDL_ReadLE16(src);
292 biCompression = BI_RGB;
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);
312 biHeight = -biHeight;
317 /* Check for read error */
318 if ( strcmp(SDL_GetError(), "") != 0 ) {
319 was_error = SDL_TRUE;
323 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
324 switch (biBitCount) {
327 ExpandBMP = biBitCount;
335 /* RLE4 and RLE8 BMP compression is supported */
336 Rmask = Gmask = Bmask = Amask = 0;
337 switch (biCompression) {
339 /* If there are no masks, use the defaults */
340 if ( bfOffBits == (14+biSize) ) {
341 /* Default values for the BMP format */
342 switch (biBitCount) {
350 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
361 /* We don't know if this has alpha channel or not */
362 correctAlpha = SDL_TRUE;
373 /* Fall through -- read the RGB masks */
376 switch (biBitCount) {
379 Rmask = SDL_ReadLE32(src);
380 Gmask = SDL_ReadLE32(src);
381 Bmask = SDL_ReadLE32(src);
384 Rmask = SDL_ReadLE32(src);
385 Gmask = SDL_ReadLE32(src);
386 Bmask = SDL_ReadLE32(src);
387 Amask = SDL_ReadLE32(src);
395 /* Create a compatible surface, note that the colors are RGB ordered */
396 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
397 biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
398 if ( surface == NULL ) {
399 was_error = SDL_TRUE;
403 /* Load the palette, if any */
404 palette = (surface->format)->palette;
406 if ( SDL_RWseek(src, fp_offset+14+biSize, RW_SEEK_SET) < 0 ) {
407 SDL_Error(SDL_EFSEEK);
408 was_error = SDL_TRUE;
413 | guich: always use 1<<bpp b/c some bitmaps can bring wrong information
416 /* if ( biClrUsed == 0 ) { */
417 biClrUsed = 1 << biBitCount;
419 if ( biSize == 12 ) {
420 for ( i = 0; i < (int)biClrUsed; ++i ) {
421 SDL_RWread(src, &palette->colors[i].b, 1, 1);
422 SDL_RWread(src, &palette->colors[i].g, 1, 1);
423 SDL_RWread(src, &palette->colors[i].r, 1, 1);
424 palette->colors[i].a = SDL_ALPHA_OPAQUE;
427 for ( i = 0; i < (int)biClrUsed; ++i ) {
428 SDL_RWread(src, &palette->colors[i].b, 1, 1);
429 SDL_RWread(src, &palette->colors[i].g, 1, 1);
430 SDL_RWread(src, &palette->colors[i].r, 1, 1);
431 SDL_RWread(src, &palette->colors[i].a, 1, 1);
433 /* According to Microsoft documentation, the fourth element
434 is reserved and must be zero, so we shouldn't treat it as
437 palette->colors[i].a = SDL_ALPHA_OPAQUE;
440 palette->ncolors = biClrUsed;
443 /* Read the surface pixels. Note that the bmp image is upside down */
444 if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
445 SDL_Error(SDL_EFSEEK);
446 was_error = SDL_TRUE;
449 if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
450 was_error = (SDL_bool)readRlePixels(surface, src, biCompression == BI_RLE8);
451 if (was_error) IMG_SetError("Error reading from BMP");
454 top = (Uint8 *)surface->pixels;
455 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
458 bmpPitch = (biWidth + 7) >> 3;
459 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
462 bmpPitch = (biWidth + 1) >> 1;
463 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
466 pad = ((surface->pitch%4) ?
467 (4-(surface->pitch%4)) : 0);
473 bits = end - surface->pitch;
475 while ( bits >= top && bits < end ) {
480 int shift = (8-ExpandBMP);
481 for ( i=0; i<surface->w; ++i ) {
482 if ( i%(8/ExpandBMP) == 0 ) {
483 if ( !SDL_RWread(src, &pixel, 1, 1) ) {
484 IMG_SetError("Error reading from BMP");
485 was_error = SDL_TRUE;
489 *(bits+i) = (pixel>>shift);
495 if ( SDL_RWread(src, bits, 1, surface->pitch) != surface->pitch ) {
496 SDL_Error(SDL_EFREAD);
497 was_error = SDL_TRUE;
500 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
501 /* Byte-swap the pixels if needed. Note that the 24bpp
502 case has already been taken care of above. */
506 Uint16 *pix = (Uint16 *)bits;
507 for(i = 0; i < surface->w; i++)
508 pix[i] = SDL_Swap16(pix[i]);
513 Uint32 *pix = (Uint32 *)bits;
514 for(i = 0; i < surface->w; i++)
515 pix[i] = SDL_Swap32(pix[i]);
522 /* Skip padding bytes, ugh */
525 for ( i=0; i<pad; ++i ) {
526 SDL_RWread(src, &padbyte, 1, 1);
530 bits += surface->pitch;
532 bits -= surface->pitch;
536 CorrectAlphaChannel(surface);
541 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
544 SDL_FreeSurface(surface);
548 if ( freesrc && src ) {
555 SDL_Read8(SDL_RWops * src)
559 SDL_RWread(src, &value, 1, 1);
564 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
570 SDL_Surface *surface;
580 /* The Win32 ICO file header (14 bytes) */
585 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
591 Uint32 biCompression;
593 Sint32 biXPelsPerMeter;
594 Sint32 biYPelsPerMeter;
596 Uint32 biClrImportant;
598 /* Make sure we are passed a valid data source */
600 was_error = SDL_FALSE;
602 was_error = SDL_TRUE;
606 /* Read in the ICO file header */
607 fp_offset = SDL_RWtell(src);
610 bfReserved = SDL_ReadLE16(src);
611 bfType = SDL_ReadLE16(src);
612 bfCount = SDL_ReadLE16(src);
613 if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
614 IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
615 was_error = SDL_TRUE;
619 /* Read the Win32 Icon Directory */
620 for (i = 0; i < bfCount; i++) {
621 /* Icon Directory Entries */
622 int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
623 int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
624 int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
625 Uint8 bReserved = SDL_Read8(src);
626 Uint16 wPlanes = SDL_ReadLE16(src);
627 Uint16 wBitCount = SDL_ReadLE16(src);
628 Uint32 dwBytesInRes = SDL_ReadLE32(src);
629 Uint32 dwImageOffset = SDL_ReadLE32(src);
638 //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
639 if (bColorCount > maxCol) {
640 maxCol = bColorCount;
641 icoOfs = dwImageOffset;
642 //printf("marked\n");
646 /* Advance to the DIB Data */
647 if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
648 SDL_Error(SDL_EFSEEK);
649 was_error = SDL_TRUE;
653 /* Read the Win32 BITMAPINFOHEADER */
654 biSize = SDL_ReadLE32(src);
656 biWidth = SDL_ReadLE32(src);
657 biHeight = SDL_ReadLE32(src);
658 biPlanes = SDL_ReadLE16(src);
659 biBitCount = SDL_ReadLE16(src);
660 biCompression = SDL_ReadLE32(src);
661 biSizeImage = SDL_ReadLE32(src);
662 biXPelsPerMeter = SDL_ReadLE32(src);
663 biYPelsPerMeter = SDL_ReadLE32(src);
664 biClrUsed = SDL_ReadLE32(src);
665 biClrImportant = SDL_ReadLE32(src);
667 IMG_SetError("Unsupported ICO bitmap format");
668 was_error = SDL_TRUE;
672 /* Check for read error */
673 if (SDL_strcmp(SDL_GetError(), "") != 0) {
674 was_error = SDL_TRUE;
678 /* We don't support any BMP compression right now */
679 switch (biCompression) {
681 /* Default values for the BMP format */
682 switch (biBitCount) {
685 ExpandBMP = biBitCount;
698 IMG_SetError("ICO file with unsupported bit count");
699 was_error = SDL_TRUE;
704 IMG_SetError("Compressed ICO files not supported");
705 was_error = SDL_TRUE;
709 /* Create a RGBA surface */
710 biHeight = biHeight >> 1;
711 //printf("%d x %d\n", biWidth, biHeight);
713 SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
714 0x0000FF00, 0x000000FF, 0xFF000000);
715 if (surface == NULL) {
716 was_error = SDL_TRUE;
720 /* Load the palette, if any */
721 //printf("bc %d bused %d\n", biBitCount, biClrUsed);
722 if (biBitCount <= 8) {
723 if (biClrUsed == 0) {
724 biClrUsed = 1 << biBitCount;
726 for (i = 0; i < (int) biClrUsed; ++i) {
727 SDL_RWread(src, &palette[i], 4, 1);
731 /* Read the surface pixels. Note that the bmp image is upside down */
732 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
735 bmpPitch = (biWidth + 7) >> 3;
736 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
739 bmpPitch = (biWidth + 1) >> 1;
740 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
744 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
747 bmpPitch = biWidth * 4;
751 while (bits > (Uint8 *) surface->pixels) {
752 bits -= surface->pitch;
759 int shift = (8 - ExpandBMP);
760 for (i = 0; i < surface->w; ++i) {
761 if (i % (8 / ExpandBMP) == 0) {
762 if (!SDL_RWread(src, &pixel, 1, 1)) {
763 IMG_SetError("Error reading from ICO");
764 was_error = SDL_TRUE;
768 *((Uint32 *) bits + i) = (palette[pixel >> shift]);
775 if (SDL_RWread(src, bits, 1, surface->pitch)
777 SDL_Error(SDL_EFREAD);
778 was_error = SDL_TRUE;
783 /* Skip padding bytes, ugh */
786 for (i = 0; i < pad; ++i) {
787 SDL_RWread(src, &padbyte, 1, 1);
791 /* Read the mask pixels. Note that the bmp image is upside down */
792 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
794 bmpPitch = (biWidth + 7) >> 3;
795 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
796 while (bits > (Uint8 *) surface->pixels) {
798 int shift = (8 - ExpandBMP);
800 bits -= surface->pitch;
801 for (i = 0; i < surface->w; ++i) {
802 if (i % (8 / ExpandBMP) == 0) {
803 if (!SDL_RWread(src, &pixel, 1, 1)) {
804 IMG_SetError("Error reading from ICO");
805 was_error = SDL_TRUE;
809 *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
812 /* Skip padding bytes, ugh */
815 for (i = 0; i < pad; ++i) {
816 SDL_RWread(src, &padbyte, 1, 1);
823 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
826 SDL_FreeSurface(surface);
830 if (freesrc && src) {
836 /* Load a BMP type image from an SDL datasource */
837 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
839 return(LoadBMP_RW(src, 0));
842 /* Load a ICO type image from an SDL datasource */
843 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
845 return(LoadICOCUR_RW(src, 1, 0));
848 /* Load a CUR type image from an SDL datasource */
849 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
851 return(LoadICOCUR_RW(src, 2, 0));
856 /* See if an image is contained in a data source */
857 int IMG_isBMP(SDL_RWops *src)
862 int IMG_isICO(SDL_RWops *src)
867 int IMG_isCUR(SDL_RWops *src)
872 /* Load a BMP type image from an SDL datasource */
873 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
878 /* Load a BMP type image from an SDL datasource */
879 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
884 /* Load a BMP type image from an SDL datasource */
885 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
890 #endif /* LOAD_BMP */
892 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */