2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2009 Sam Lantinga
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
25 /* This is a BMP image file loading framework */
26 /* ICO/CUR file support is here as well since it uses similar internal
32 #include "SDL_image.h"
36 /* See if an image is contained in a data source */
37 int IMG_isBMP(SDL_RWops *src)
45 start = SDL_RWtell(src);
47 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
48 if ( strncmp(magic, "BM", 2) == 0 ) {
52 SDL_RWseek(src, start, RW_SEEK_SET);
56 static int IMG_isICOCUR(SDL_RWops *src, int type)
61 /* The Win32 ICO file header (14 bytes) */
68 start = SDL_RWtell(src);
70 bfReserved = SDL_ReadLE16(src);
71 bfType = SDL_ReadLE16(src);
72 bfCount = SDL_ReadLE16(src);
73 if ((bfReserved == 0) && (bfType == type) && (bfCount != 0))
75 SDL_RWseek(src, start, RW_SEEK_SET);
80 int IMG_isICO(SDL_RWops *src)
82 return IMG_isICOCUR(src, 1);
85 int IMG_isCUR(SDL_RWops *src)
87 return IMG_isICOCUR(src, 2);
90 #include "SDL_error.h"
91 #include "SDL_video.h"
92 #include "SDL_endian.h"
94 /* Compression encodings for BMP files */
99 #define BI_BITFIELDS 3
102 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
105 | Sets the surface pixels from src. A bmp image is upside down.
107 int pitch = surface->pitch;
108 int height = surface->h;
109 Uint8 *start = (Uint8 *)surface->pixels;
110 Uint8 *end = start + (height*pitch);
111 Uint8 *bits = end-pitch, *spot;
116 #define COPY_PIXEL(x) spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
119 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
121 | encoded mode starts with a run length, and then a byte
122 | with two colour indexes to alternate between for the run
126 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
127 if ( isRle8 ) { /* 256-color bitmap, compressed */
131 } else { /* 16-color bitmap, compressed */
132 Uint8 pixel0 = pixel >> 4;
133 Uint8 pixel1 = pixel & 0x0F;
135 COPY_PIXEL(pixel0); /* even count, high nibble */
137 COPY_PIXEL(pixel1); /* odd count, low nibble */
143 | A leading zero is an escape; it may signal the end of the bitmap,
144 | a cursor move, or some absolute data.
145 | zero tag may be absolute mode or an escape
147 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
149 case 0: /* end of line */
151 bits -= pitch; /* go to previous */
153 case 1: /* end of bitmap */
154 return 0; /* success! */
156 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
158 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
159 bits -= (ch * pitch);
161 default: /* no compression */
163 needsPad = ( ch & 1 );
166 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
170 needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
173 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
174 COPY_PIXEL(pixel >> 4);
176 COPY_PIXEL(pixel & 0x0F);
180 /* pad at even boundary */
181 if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
188 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
194 SDL_Surface *surface;
199 SDL_Palette *palette;
205 /* The Win32 BMP file header (14 bytes) */
212 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
218 Uint32 biCompression;
220 Sint32 biXPelsPerMeter;
221 Sint32 biYPelsPerMeter;
223 Uint32 biClrImportant;
225 /* Make sure we are passed a valid data source */
227 was_error = SDL_FALSE;
229 was_error = SDL_TRUE;
233 /* Read in the BMP file header */
234 fp_offset = SDL_RWtell(src);
236 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
237 SDL_Error(SDL_EFREAD);
238 was_error = SDL_TRUE;
241 if ( strncmp(magic, "BM", 2) != 0 ) {
242 IMG_SetError("File is not a Windows BMP file");
243 was_error = SDL_TRUE;
246 bfSize = SDL_ReadLE32(src);
247 bfReserved1 = SDL_ReadLE16(src);
248 bfReserved2 = SDL_ReadLE16(src);
249 bfOffBits = SDL_ReadLE32(src);
251 /* Read the Win32 BITMAPINFOHEADER */
252 biSize = SDL_ReadLE32(src);
253 if ( biSize == 12 ) {
254 biWidth = (Uint32)SDL_ReadLE16(src);
255 biHeight = (Uint32)SDL_ReadLE16(src);
256 biPlanes = SDL_ReadLE16(src);
257 biBitCount = SDL_ReadLE16(src);
258 biCompression = BI_RGB;
265 biWidth = SDL_ReadLE32(src);
266 biHeight = SDL_ReadLE32(src);
267 biPlanes = SDL_ReadLE16(src);
268 biBitCount = SDL_ReadLE16(src);
269 biCompression = SDL_ReadLE32(src);
270 biSizeImage = SDL_ReadLE32(src);
271 biXPelsPerMeter = SDL_ReadLE32(src);
272 biYPelsPerMeter = SDL_ReadLE32(src);
273 biClrUsed = SDL_ReadLE32(src);
274 biClrImportant = SDL_ReadLE32(src);
278 biHeight = -biHeight;
283 /* Check for read error */
284 if ( strcmp(SDL_GetError(), "") != 0 ) {
285 was_error = SDL_TRUE;
289 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
290 switch (biBitCount) {
293 ExpandBMP = biBitCount;
301 /* RLE4 and RLE8 BMP compression is supported */
302 Rmask = Gmask = Bmask = Amask = 0;
303 switch (biCompression) {
305 /* If there are no masks, use the defaults */
306 if ( bfOffBits == (14+biSize) ) {
307 /* Default values for the BMP format */
308 switch (biBitCount) {
316 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
337 /* Fall through -- read the RGB masks */
340 switch (biBitCount) {
343 Rmask = SDL_ReadLE32(src);
344 Gmask = SDL_ReadLE32(src);
345 Bmask = SDL_ReadLE32(src);
348 Rmask = SDL_ReadLE32(src);
349 Gmask = SDL_ReadLE32(src);
350 Bmask = SDL_ReadLE32(src);
351 Amask = SDL_ReadLE32(src);
359 /* Create a compatible surface, note that the colors are RGB ordered */
360 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
361 biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
362 if ( surface == NULL ) {
363 was_error = SDL_TRUE;
367 /* Load the palette, if any */
368 palette = (surface->format)->palette;
370 if ( SDL_RWseek(src, fp_offset+14+biSize, RW_SEEK_SET) < 0 ) {
371 SDL_Error(SDL_EFSEEK);
372 was_error = SDL_TRUE;
377 | guich: always use 1<<bpp b/c some bitmaps can bring wrong information
380 /* if ( biClrUsed == 0 ) { */
381 biClrUsed = 1 << biBitCount;
383 if ( biSize == 12 ) {
384 for ( i = 0; i < (int)biClrUsed; ++i ) {
385 SDL_RWread(src, &palette->colors[i].b, 1, 1);
386 SDL_RWread(src, &palette->colors[i].g, 1, 1);
387 SDL_RWread(src, &palette->colors[i].r, 1, 1);
388 palette->colors[i].unused = 0;
391 for ( i = 0; i < (int)biClrUsed; ++i ) {
392 SDL_RWread(src, &palette->colors[i].b, 1, 1);
393 SDL_RWread(src, &palette->colors[i].g, 1, 1);
394 SDL_RWread(src, &palette->colors[i].r, 1, 1);
395 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
398 palette->ncolors = biClrUsed;
401 /* Read the surface pixels. Note that the bmp image is upside down */
402 if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
403 SDL_Error(SDL_EFSEEK);
404 was_error = SDL_TRUE;
407 if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
408 was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
409 if (was_error) IMG_SetError("Error reading from BMP");
412 top = (Uint8 *)surface->pixels;
413 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
416 bmpPitch = (biWidth + 7) >> 3;
417 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
420 bmpPitch = (biWidth + 1) >> 1;
421 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
424 pad = ((surface->pitch%4) ?
425 (4-(surface->pitch%4)) : 0);
431 bits = end - surface->pitch;
433 while ( bits >= top && bits < end ) {
438 int shift = (8-ExpandBMP);
439 for ( i=0; i<surface->w; ++i ) {
440 if ( i%(8/ExpandBMP) == 0 ) {
441 if ( !SDL_RWread(src, &pixel, 1, 1) ) {
443 "Error reading from BMP");
444 was_error = SDL_TRUE;
448 *(bits+i) = (pixel>>shift);
454 if ( SDL_RWread(src, bits, 1, surface->pitch)
455 != surface->pitch ) {
456 SDL_Error(SDL_EFREAD);
457 was_error = SDL_TRUE;
460 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
461 /* Byte-swap the pixels if needed. Note that the 24bpp
462 case has already been taken care of above. */
466 Uint16 *pix = (Uint16 *)bits;
467 for(i = 0; i < surface->w; i++)
468 pix[i] = SDL_Swap16(pix[i]);
473 Uint32 *pix = (Uint32 *)bits;
474 for(i = 0; i < surface->w; i++)
475 pix[i] = SDL_Swap32(pix[i]);
482 /* Skip padding bytes, ugh */
485 for ( i=0; i<pad; ++i ) {
486 SDL_RWread(src, &padbyte, 1, 1);
490 bits += surface->pitch;
492 bits -= surface->pitch;
498 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
501 SDL_FreeSurface(surface);
505 if ( freesrc && src ) {
512 SDL_Read8(SDL_RWops * src)
516 SDL_RWread(src, &value, 1, 1);
521 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
527 SDL_Surface *surface;
537 /* The Win32 ICO file header (14 bytes) */
542 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
548 Uint32 biCompression;
550 Sint32 biXPelsPerMeter;
551 Sint32 biYPelsPerMeter;
553 Uint32 biClrImportant;
555 /* Make sure we are passed a valid data source */
557 was_error = SDL_FALSE;
559 was_error = SDL_TRUE;
563 /* Read in the ICO file header */
564 fp_offset = SDL_RWtell(src);
567 bfReserved = SDL_ReadLE16(src);
568 bfType = SDL_ReadLE16(src);
569 bfCount = SDL_ReadLE16(src);
570 if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
571 IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
572 was_error = SDL_TRUE;
576 /* Read the Win32 Icon Directory */
577 for (i = 0; i < bfCount; i++) {
578 /* Icon Directory Entries */
579 int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
580 int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
581 int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
582 Uint8 bReserved = SDL_Read8(src);
583 Uint16 wPlanes = SDL_ReadLE16(src);
584 Uint16 wBitCount = SDL_ReadLE16(src);
585 Uint32 dwBytesInRes = SDL_ReadLE32(src);
586 Uint32 dwImageOffset = SDL_ReadLE32(src);
595 //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
596 if (bColorCount > maxCol) {
597 maxCol = bColorCount;
598 icoOfs = dwImageOffset;
599 //printf("marked\n");
603 /* Advance to the DIB Data */
604 if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
605 SDL_Error(SDL_EFSEEK);
606 was_error = SDL_TRUE;
610 /* Read the Win32 BITMAPINFOHEADER */
611 biSize = SDL_ReadLE32(src);
613 biWidth = SDL_ReadLE32(src);
614 biHeight = SDL_ReadLE32(src);
615 biPlanes = SDL_ReadLE16(src);
616 biBitCount = SDL_ReadLE16(src);
617 biCompression = SDL_ReadLE32(src);
618 biSizeImage = SDL_ReadLE32(src);
619 biXPelsPerMeter = SDL_ReadLE32(src);
620 biYPelsPerMeter = SDL_ReadLE32(src);
621 biClrUsed = SDL_ReadLE32(src);
622 biClrImportant = SDL_ReadLE32(src);
624 IMG_SetError("Unsupported ICO bitmap format");
625 was_error = SDL_TRUE;
629 /* Check for read error */
630 if (SDL_strcmp(SDL_GetError(), "") != 0) {
631 was_error = SDL_TRUE;
635 /* We don't support any BMP compression right now */
636 switch (biCompression) {
638 /* Default values for the BMP format */
639 switch (biBitCount) {
642 ExpandBMP = biBitCount;
655 IMG_SetError("ICO file with unsupported bit count");
656 was_error = SDL_TRUE;
661 IMG_SetError("Compressed ICO files not supported");
662 was_error = SDL_TRUE;
666 /* Create a RGBA surface */
667 biHeight = biHeight >> 1;
668 //printf("%d x %d\n", biWidth, biHeight);
670 SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
671 0x0000FF00, 0x000000FF, 0xFF000000);
672 if (surface == NULL) {
673 was_error = SDL_TRUE;
677 /* Load the palette, if any */
678 //printf("bc %d bused %d\n", biBitCount, biClrUsed);
679 if (biBitCount <= 8) {
680 if (biClrUsed == 0) {
681 biClrUsed = 1 << biBitCount;
683 for (i = 0; i < (int) biClrUsed; ++i) {
684 SDL_RWread(src, &palette[i], 4, 1);
688 /* Read the surface pixels. Note that the bmp image is upside down */
689 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
692 bmpPitch = (biWidth + 7) >> 3;
693 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
696 bmpPitch = (biWidth + 1) >> 1;
697 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
701 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
704 bmpPitch = biWidth * 4;
708 while (bits > (Uint8 *) surface->pixels) {
709 bits -= surface->pitch;
716 int shift = (8 - ExpandBMP);
717 for (i = 0; i < surface->w; ++i) {
718 if (i % (8 / ExpandBMP) == 0) {
719 if (!SDL_RWread(src, &pixel, 1, 1)) {
720 IMG_SetError("Error reading from ICO");
721 was_error = SDL_TRUE;
725 *((Uint32 *) bits + i) = (palette[pixel >> shift]);
732 if (SDL_RWread(src, bits, 1, surface->pitch)
734 SDL_Error(SDL_EFREAD);
735 was_error = SDL_TRUE;
740 /* Skip padding bytes, ugh */
743 for (i = 0; i < pad; ++i) {
744 SDL_RWread(src, &padbyte, 1, 1);
748 /* Read the mask pixels. Note that the bmp image is upside down */
749 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
751 bmpPitch = (biWidth + 7) >> 3;
752 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
753 while (bits > (Uint8 *) surface->pixels) {
755 int shift = (8 - ExpandBMP);
757 bits -= surface->pitch;
758 for (i = 0; i < surface->w; ++i) {
759 if (i % (8 / ExpandBMP) == 0) {
760 if (!SDL_RWread(src, &pixel, 1, 1)) {
761 IMG_SetError("Error reading from ICO");
762 was_error = SDL_TRUE;
766 *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
769 /* Skip padding bytes, ugh */
772 for (i = 0; i < pad; ++i) {
773 SDL_RWread(src, &padbyte, 1, 1);
780 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
783 SDL_FreeSurface(surface);
787 if (freesrc && src) {
793 /* Load a BMP type image from an SDL datasource */
794 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
796 return(LoadBMP_RW(src, 0));
799 /* Load a ICO type image from an SDL datasource */
800 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
802 return(LoadICOCUR_RW(src, 1, 0));
805 /* Load a CUR type image from an SDL datasource */
806 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
808 return(LoadICOCUR_RW(src, 2, 0));
813 /* See if an image is contained in a data source */
814 int IMG_isBMP(SDL_RWops *src)
819 int IMG_isICO(SDL_RWops *src)
824 int IMG_isCUR(SDL_RWops *src)
829 /* Load a BMP type image from an SDL datasource */
830 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
835 /* Load a BMP type image from an SDL datasource */
836 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
841 /* Load a BMP type image from an SDL datasource */
842 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
847 #endif /* LOAD_BMP */
849 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */