IMG_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 11 Jun 2019 00:19:38 -0700
changeset 659 92ff8280a823
parent 639 03bd33e8cb49
permissions -rw-r--r--
Added patch note for security fixes
     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 */
    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 
    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 
    81 int IMG_isICO(SDL_RWops *src)
    82 {
    83     return IMG_isICOCUR(src, 1);
    84 }
    85 
    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 
   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 
   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 
   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
   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 *
   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 */
   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 */
   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 */
   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 */
   922 int IMG_isBMP(SDL_RWops *src)
   923 {
   924     return(0);
   925 }
   926 
   927 int IMG_isICO(SDL_RWops *src)
   928 {
   929     return(0);
   930 }
   931 
   932 int IMG_isCUR(SDL_RWops *src)
   933 {
   934     return(0);
   935 }
   936 
   937 /* Load a BMP type image from an SDL datasource */
   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 */
   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 */
   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) */