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 /* This is a BMP image file loading framework */
24 /* ICO/CUR file support is here as well since it uses similar internal
30 #include "SDL_image.h"
34 /* See if an image is contained in a data source */
35 int IMG_isBMP(SDL_RWops *src)
43 start = SDL_RWtell(src);
45 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
46 if ( strncmp(magic, "BM", 2) == 0 ) {
50 SDL_RWseek(src, start, SEEK_SET);
54 static int IMG_isICOCUR(SDL_RWops *src, int type)
59 /* The Win32 ICO file header (14 bytes) */
66 start = SDL_RWtell(src);
68 bfReserved = SDL_ReadLE16(src);
69 bfType = SDL_ReadLE16(src);
70 bfCount = SDL_ReadLE16(src);
71 if ((bfReserved == 0) && (bfType == type) && (bfCount != 0))
73 SDL_RWseek(src, start, SEEK_SET);
78 int IMG_isICO(SDL_RWops *src)
80 return IMG_isICOCUR(src, 1);
83 int IMG_isCUR(SDL_RWops *src)
85 return IMG_isICOCUR(src, 2);
88 #include "SDL_error.h"
89 #include "SDL_video.h"
90 #include "SDL_endian.h"
92 /* Compression encodings for BMP files */
97 #define BI_BITFIELDS 3
100 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
103 | Sets the surface pixels from src. A bmp image is upside down.
105 int pitch = surface->pitch;
106 int height = surface->h;
107 Uint8 *start = (Uint8 *)surface->pixels;
108 Uint8 *end = start + (height*pitch);
109 Uint8 *bits = end-pitch, *spot;
114 #define COPY_PIXEL(x) spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
117 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
119 | encoded mode starts with a run length, and then a byte
120 | with two colour indexes to alternate between for the run
124 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
125 if ( isRle8 ) { /* 256-color bitmap, compressed */
129 } else { /* 16-color bitmap, compressed */
130 Uint8 pixel0 = pixel >> 4;
131 Uint8 pixel1 = pixel & 0x0F;
133 COPY_PIXEL(pixel0); /* even count, high nibble */
135 COPY_PIXEL(pixel1); /* odd count, low nibble */
141 | A leading zero is an escape; it may signal the end of the bitmap,
142 | a cursor move, or some absolute data.
143 | zero tag may be absolute mode or an escape
145 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
147 case 0: /* end of line */
149 bits -= pitch; /* go to previous */
151 case 1: /* end of bitmap */
152 return 0; /* success! */
154 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
156 if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
157 bits -= (ch * pitch);
159 default: /* no compression */
161 needsPad = ( ch & 1 );
164 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
168 needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
171 if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
172 COPY_PIXEL(pixel >> 4);
174 COPY_PIXEL(pixel & 0x0F);
178 /* pad at even boundary */
179 if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
186 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
192 SDL_Surface *surface;
197 SDL_Palette *palette;
203 /* The Win32 BMP file header (14 bytes) */
210 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
216 Uint32 biCompression;
218 Sint32 biXPelsPerMeter;
219 Sint32 biYPelsPerMeter;
221 Uint32 biClrImportant;
223 /* Make sure we are passed a valid data source */
225 was_error = SDL_FALSE;
227 was_error = SDL_TRUE;
231 /* Read in the BMP file header */
232 fp_offset = SDL_RWtell(src);
234 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
235 SDL_Error(SDL_EFREAD);
236 was_error = SDL_TRUE;
239 if ( strncmp(magic, "BM", 2) != 0 ) {
240 SDL_SetError("File is not a Windows BMP file");
241 was_error = SDL_TRUE;
244 bfSize = SDL_ReadLE32(src);
245 bfReserved1 = SDL_ReadLE16(src);
246 bfReserved2 = SDL_ReadLE16(src);
247 bfOffBits = SDL_ReadLE32(src);
249 /* Read the Win32 BITMAPINFOHEADER */
250 biSize = SDL_ReadLE32(src);
251 if ( biSize == 12 ) {
252 biWidth = (Uint32)SDL_ReadLE16(src);
253 biHeight = (Uint32)SDL_ReadLE16(src);
254 biPlanes = SDL_ReadLE16(src);
255 biBitCount = SDL_ReadLE16(src);
256 biCompression = BI_RGB;
263 biWidth = SDL_ReadLE32(src);
264 biHeight = SDL_ReadLE32(src);
265 biPlanes = SDL_ReadLE16(src);
266 biBitCount = SDL_ReadLE16(src);
267 biCompression = SDL_ReadLE32(src);
268 biSizeImage = SDL_ReadLE32(src);
269 biXPelsPerMeter = SDL_ReadLE32(src);
270 biYPelsPerMeter = SDL_ReadLE32(src);
271 biClrUsed = SDL_ReadLE32(src);
272 biClrImportant = SDL_ReadLE32(src);
276 biHeight = -biHeight;
281 /* Check for read error */
282 if ( strcmp(SDL_GetError(), "") != 0 ) {
283 was_error = SDL_TRUE;
287 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
288 switch (biBitCount) {
291 ExpandBMP = biBitCount;
299 /* RLE4 and RLE8 BMP compression is supported */
300 Rmask = Gmask = Bmask = Amask = 0;
301 switch (biCompression) {
303 /* If there are no masks, use the defaults */
304 if ( bfOffBits == (14+biSize) ) {
305 /* Default values for the BMP format */
306 switch (biBitCount) {
314 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
335 /* Fall through -- read the RGB masks */
338 switch (biBitCount) {
342 Rmask = SDL_ReadLE32(src);
343 Gmask = SDL_ReadLE32(src);
344 Bmask = SDL_ReadLE32(src);
345 Amask = SDL_ReadLE32(src);
353 /* Create a compatible surface, note that the colors are RGB ordered */
354 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
355 biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
356 if ( surface == NULL ) {
357 was_error = SDL_TRUE;
361 /* Load the palette, if any */
362 palette = (surface->format)->palette;
364 if ( SDL_RWseek(src, fp_offset+14+biSize, SEEK_SET) < 0 ) {
365 SDL_Error(SDL_EFSEEK);
366 was_error = SDL_TRUE;
371 | guich: always use 1<<bpp b/c some bitmaps can bring wrong information
374 /* if ( biClrUsed == 0 ) { */
375 biClrUsed = 1 << biBitCount;
377 if ( biSize == 12 ) {
378 for ( i = 0; i < (int)biClrUsed; ++i ) {
379 SDL_RWread(src, &palette->colors[i].b, 1, 1);
380 SDL_RWread(src, &palette->colors[i].g, 1, 1);
381 SDL_RWread(src, &palette->colors[i].r, 1, 1);
382 palette->colors[i].unused = 0;
385 for ( i = 0; i < (int)biClrUsed; ++i ) {
386 SDL_RWread(src, &palette->colors[i].b, 1, 1);
387 SDL_RWread(src, &palette->colors[i].g, 1, 1);
388 SDL_RWread(src, &palette->colors[i].r, 1, 1);
389 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
392 palette->ncolors = biClrUsed;
395 /* Read the surface pixels. Note that the bmp image is upside down */
396 if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
397 SDL_Error(SDL_EFSEEK);
398 was_error = SDL_TRUE;
401 if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
402 was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
403 if (was_error) SDL_SetError("Error reading from BMP");
406 top = (Uint8 *)surface->pixels;
407 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
410 bmpPitch = (biWidth + 7) >> 3;
411 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
414 bmpPitch = (biWidth + 1) >> 1;
415 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
418 pad = ((surface->pitch%4) ?
419 (4-(surface->pitch%4)) : 0);
425 bits = end - surface->pitch;
427 while ( bits >= top && bits < end ) {
432 int shift = (8-ExpandBMP);
433 for ( i=0; i<surface->w; ++i ) {
434 if ( i%(8/ExpandBMP) == 0 ) {
435 if ( !SDL_RWread(src, &pixel, 1, 1) ) {
437 "Error reading from BMP");
438 was_error = SDL_TRUE;
442 *(bits+i) = (pixel>>shift);
448 if ( SDL_RWread(src, bits, 1, surface->pitch)
449 != surface->pitch ) {
450 SDL_Error(SDL_EFREAD);
451 was_error = SDL_TRUE;
454 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
455 /* Byte-swap the pixels if needed. Note that the 24bpp
456 case has already been taken care of above. */
460 Uint16 *pix = (Uint16 *)bits;
461 for(i = 0; i < surface->w; i++)
462 pix[i] = SDL_Swap16(pix[i]);
467 Uint32 *pix = (Uint32 *)bits;
468 for(i = 0; i < surface->w; i++)
469 pix[i] = SDL_Swap32(pix[i]);
476 /* Skip padding bytes, ugh */
479 for ( i=0; i<pad; ++i ) {
480 SDL_RWread(src, &padbyte, 1, 1);
484 bits += surface->pitch;
486 bits -= surface->pitch;
492 SDL_RWseek(src, fp_offset, SEEK_SET);
495 SDL_FreeSurface(surface);
499 if ( freesrc && src ) {
506 SDL_Read8(SDL_RWops * src)
510 SDL_RWread(src, &value, 1, 1);
515 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
521 SDL_Surface *surface;
531 /* The Win32 ICO file header (14 bytes) */
536 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
542 Uint32 biCompression;
544 Sint32 biXPelsPerMeter;
545 Sint32 biYPelsPerMeter;
547 Uint32 biClrImportant;
549 /* Make sure we are passed a valid data source */
551 was_error = SDL_FALSE;
553 was_error = SDL_TRUE;
557 /* Read in the ICO file header */
558 fp_offset = SDL_RWtell(src);
561 bfReserved = SDL_ReadLE16(src);
562 bfType = SDL_ReadLE16(src);
563 bfCount = SDL_ReadLE16(src);
564 if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
565 SDL_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
566 was_error = SDL_TRUE;
570 /* Read the Win32 Icon Directory */
571 for (i = 0; i < bfCount; i++) {
572 /* Icon Directory Entries */
573 int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
574 int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
575 int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */
576 Uint8 bReserved = SDL_Read8(src);
577 Uint16 wPlanes = SDL_ReadLE16(src);
578 Uint16 wBitCount = SDL_ReadLE16(src);
579 Uint32 dwBytesInRes = SDL_ReadLE32(src);
580 Uint32 dwImageOffset = SDL_ReadLE32(src);
589 //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
590 if (bColorCount > maxCol) {
591 maxCol = bColorCount;
592 icoOfs = dwImageOffset;
593 //printf("marked\n");
597 /* Advance to the DIB Data */
598 if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
599 SDL_Error(SDL_EFSEEK);
600 was_error = SDL_TRUE;
604 /* Read the Win32 BITMAPINFOHEADER */
605 biSize = SDL_ReadLE32(src);
607 biWidth = SDL_ReadLE32(src);
608 biHeight = SDL_ReadLE32(src);
609 biPlanes = SDL_ReadLE16(src);
610 biBitCount = SDL_ReadLE16(src);
611 biCompression = SDL_ReadLE32(src);
612 biSizeImage = SDL_ReadLE32(src);
613 biXPelsPerMeter = SDL_ReadLE32(src);
614 biYPelsPerMeter = SDL_ReadLE32(src);
615 biClrUsed = SDL_ReadLE32(src);
616 biClrImportant = SDL_ReadLE32(src);
618 SDL_SetError("Unsupported ICO bitmap format");
619 was_error = SDL_TRUE;
623 /* Check for read error */
624 if (SDL_strcmp(SDL_GetError(), "") != 0) {
625 was_error = SDL_TRUE;
629 /* We don't support any BMP compression right now */
630 switch (biCompression) {
632 /* Default values for the BMP format */
633 switch (biBitCount) {
636 ExpandBMP = biBitCount;
649 SDL_SetError("ICO file with unsupported bit count");
650 was_error = SDL_TRUE;
655 SDL_SetError("Compressed ICO files not supported");
656 was_error = SDL_TRUE;
660 /* Create a RGBA surface */
661 biHeight = biHeight >> 1;
662 //printf("%d x %d\n", biWidth, biHeight);
664 SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
665 0x0000FF00, 0x000000FF, 0xFF000000);
666 if (surface == NULL) {
667 was_error = SDL_TRUE;
671 /* Load the palette, if any */
672 //printf("bc %d bused %d\n", biBitCount, biClrUsed);
673 if (biBitCount <= 8) {
674 if (biClrUsed == 0) {
675 biClrUsed = 1 << biBitCount;
677 for (i = 0; i < (int) biClrUsed; ++i) {
678 SDL_RWread(src, &palette[i], 4, 1);
682 /* Read the surface pixels. Note that the bmp image is upside down */
683 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
686 bmpPitch = (biWidth + 7) >> 3;
687 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
690 bmpPitch = (biWidth + 1) >> 1;
691 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
695 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
698 bmpPitch = biWidth * 4;
702 while (bits > (Uint8 *) surface->pixels) {
703 bits -= surface->pitch;
710 int shift = (8 - ExpandBMP);
711 for (i = 0; i < surface->w; ++i) {
712 if (i % (8 / ExpandBMP) == 0) {
713 if (!SDL_RWread(src, &pixel, 1, 1)) {
714 SDL_SetError("Error reading from ICO");
715 was_error = SDL_TRUE;
719 *((Uint32 *) bits + i) = (palette[pixel >> shift]);
726 if (SDL_RWread(src, bits, 1, surface->pitch)
728 SDL_Error(SDL_EFREAD);
729 was_error = SDL_TRUE;
734 /* Skip padding bytes, ugh */
737 for (i = 0; i < pad; ++i) {
738 SDL_RWread(src, &padbyte, 1, 1);
742 /* Read the mask pixels. Note that the bmp image is upside down */
743 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
745 bmpPitch = (biWidth + 7) >> 3;
746 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
747 while (bits > (Uint8 *) surface->pixels) {
749 int shift = (8 - ExpandBMP);
751 bits -= surface->pitch;
752 for (i = 0; i < surface->w; ++i) {
753 if (i % (8 / ExpandBMP) == 0) {
754 if (!SDL_RWread(src, &pixel, 1, 1)) {
755 SDL_SetError("Error reading from ICO");
756 was_error = SDL_TRUE;
760 *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
763 /* Skip padding bytes, ugh */
766 for (i = 0; i < pad; ++i) {
767 SDL_RWread(src, &padbyte, 1, 1);
774 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
777 SDL_FreeSurface(surface);
781 if (freesrc && src) {
787 /* Load a BMP type image from an SDL datasource */
788 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
790 return(LoadBMP_RW(src, 0));
793 /* Load a ICO type image from an SDL datasource */
794 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
796 return(LoadICOCUR_RW(src, 1, 0));
799 /* Load a CUR type image from an SDL datasource */
800 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
802 return(LoadICOCUR_RW(src, 2, 0));
807 /* See if an image is contained in a data source */
808 int IMG_isBMP(SDL_RWops *src)
813 int IMG_isICO(SDL_RWops *src)
818 int IMG_isCUR(SDL_RWops *src)
823 /* Load a BMP type image from an SDL datasource */
824 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
829 /* Load a BMP type image from an SDL datasource */
830 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
835 /* Load a BMP type image from an SDL datasource */
836 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
841 #endif /* LOAD_BMP */