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