IMG_bmp.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 13 Oct 2018 15:02:56 +0300
changeset 596 4b70bfe18fb7
parent 575 36e9e2255178
child 638 e3e9d7430674
permissions -rw-r--r--
fix build against libpng-1.5.x versions.
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2018 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         default:
   375             ExpandBMP = 0;
   376             break;
   377     }
   378 
   379     /* RLE4 and RLE8 BMP compression is supported */
   380     switch (biCompression) {
   381         case BI_RGB:
   382             /* If there are no masks, use the defaults */
   383             SDL_assert(!haveRGBMasks);
   384             SDL_assert(!haveAlphaMask);
   385             /* Default values for the BMP format */
   386             switch (biBitCount) {
   387             case 15:
   388             case 16:
   389                 Rmask = 0x7C00;
   390                 Gmask = 0x03E0;
   391                 Bmask = 0x001F;
   392                 break;
   393             case 24:
   394 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   395                 Rmask = 0x000000FF;
   396                 Gmask = 0x0000FF00;
   397                 Bmask = 0x00FF0000;
   398 #else
   399                 Rmask = 0x00FF0000;
   400                 Gmask = 0x0000FF00;
   401                 Bmask = 0x000000FF;
   402 #endif
   403                 break;
   404             case 32:
   405                 /* We don't know if this has alpha channel or not */
   406                 correctAlpha = SDL_TRUE;
   407                 Amask = 0xFF000000;
   408                 Rmask = 0x00FF0000;
   409                 Gmask = 0x0000FF00;
   410                 Bmask = 0x000000FF;
   411                 break;
   412             default:
   413                 break;
   414             }
   415             break;
   416 
   417         case BI_BITFIELDS:
   418             break;  /* we handled this in the info header. */
   419 
   420         default:
   421             break;
   422     }
   423 
   424     /* Create a compatible surface, note that the colors are RGB ordered */
   425     surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   426             biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
   427     if ( surface == NULL ) {
   428         was_error = SDL_TRUE;
   429         goto done;
   430     }
   431 
   432     /* Load the palette, if any */
   433     palette = (surface->format)->palette;
   434     if ( palette ) {
   435         if ( SDL_RWseek(src, fp_offset+14+biSize, RW_SEEK_SET) < 0 ) {
   436             SDL_Error(SDL_EFSEEK);
   437             was_error = SDL_TRUE;
   438             goto done;
   439         }
   440 
   441         /*
   442         | guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   443         | for colorsUsed
   444         */
   445         /* if ( biClrUsed == 0 ) {  */
   446         biClrUsed = 1 << biBitCount;
   447         /* } */
   448         if ( biSize == 12 ) {
   449             for ( i = 0; i < (int)biClrUsed; ++i ) {
   450                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   451                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   452                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   453                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   454             }
   455         } else {
   456             for ( i = 0; i < (int)biClrUsed; ++i ) {
   457                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   458                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   459                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   460                 SDL_RWread(src, &palette->colors[i].a, 1, 1);
   461 
   462                 /* According to Microsoft documentation, the fourth element
   463                    is reserved and must be zero, so we shouldn't treat it as
   464                    alpha.
   465                 */
   466                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   467             }
   468         }
   469         palette->ncolors = biClrUsed;
   470     }
   471 
   472     /* Read the surface pixels.  Note that the bmp image is upside down */
   473     if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
   474         SDL_Error(SDL_EFSEEK);
   475         was_error = SDL_TRUE;
   476         goto done;
   477     }
   478     if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   479         was_error = (SDL_bool)readRlePixels(surface, src, biCompression == BI_RLE8);
   480         if (was_error) IMG_SetError("Error reading from BMP");
   481         goto done;
   482     }
   483     top = (Uint8 *)surface->pixels;
   484     end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   485     switch (ExpandBMP) {
   486         case 1:
   487             bmpPitch = (biWidth + 7) >> 3;
   488             pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   489             break;
   490         case 4:
   491             bmpPitch = (biWidth + 1) >> 1;
   492             pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   493             break;
   494         default:
   495             pad  = ((surface->pitch%4) ?
   496                     (4-(surface->pitch%4)) : 0);
   497             break;
   498     }
   499     if ( topDown ) {
   500         bits = top;
   501     } else {
   502         bits = end - surface->pitch;
   503     }
   504     while ( bits >= top && bits < end ) {
   505         switch (ExpandBMP) {
   506             case 1:
   507             case 4: {
   508             Uint8 pixel = 0;
   509             int   shift = (8-ExpandBMP);
   510             for ( i=0; i<surface->w; ++i ) {
   511                 if ( i%(8/ExpandBMP) == 0 ) {
   512                     if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   513                         IMG_SetError("Error reading from BMP");
   514                         was_error = SDL_TRUE;
   515                         goto done;
   516                     }
   517                 }
   518                 *(bits+i) = (pixel>>shift);
   519                 pixel <<= ExpandBMP;
   520             } }
   521             break;
   522 
   523             default:
   524             if ( SDL_RWread(src, bits, 1, surface->pitch) != surface->pitch ) {
   525                 SDL_Error(SDL_EFREAD);
   526                 was_error = SDL_TRUE;
   527                 goto done;
   528             }
   529 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   530             /* Byte-swap the pixels if needed. Note that the 24bpp
   531                case has already been taken care of above. */
   532             switch(biBitCount) {
   533                 case 15:
   534                 case 16: {
   535                     Uint16 *pix = (Uint16 *)bits;
   536                     for(i = 0; i < surface->w; i++)
   537                         pix[i] = SDL_Swap16(pix[i]);
   538                     break;
   539                 }
   540 
   541                 case 32: {
   542                     Uint32 *pix = (Uint32 *)bits;
   543                     for(i = 0; i < surface->w; i++)
   544                         pix[i] = SDL_Swap32(pix[i]);
   545                     break;
   546                 }
   547             }
   548 #endif
   549             break;
   550         }
   551         /* Skip padding bytes, ugh */
   552         if ( pad ) {
   553             Uint8 padbyte;
   554             for ( i=0; i<pad; ++i ) {
   555                 SDL_RWread(src, &padbyte, 1, 1);
   556             }
   557         }
   558         if ( topDown ) {
   559             bits += surface->pitch;
   560         } else {
   561             bits -= surface->pitch;
   562         }
   563     }
   564     if (correctAlpha) {
   565         CorrectAlphaChannel(surface);
   566     }
   567 done:
   568     if ( was_error ) {
   569         if ( src ) {
   570             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   571         }
   572         if ( surface ) {
   573             SDL_FreeSurface(surface);
   574         }
   575         surface = NULL;
   576     }
   577     if ( freesrc && src ) {
   578         SDL_RWclose(src);
   579     }
   580     return(surface);
   581 }
   582 
   583 static Uint8
   584 SDL_Read8(SDL_RWops * src)
   585 {
   586     Uint8 value;
   587 
   588     SDL_RWread(src, &value, 1, 1);
   589     return (value);
   590 }
   591 
   592 static SDL_Surface *
   593 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
   594 {
   595     SDL_bool was_error;
   596     Sint64 fp_offset;
   597     int bmpPitch;
   598     int i, pad;
   599     SDL_Surface *surface;
   600     Uint32 Rmask;
   601     Uint32 Gmask;
   602     Uint32 Bmask;
   603     Uint8 *bits;
   604     int ExpandBMP;
   605     int maxCol = 0;
   606     int icoOfs = 0;
   607     Uint32 palette[256];
   608 
   609     /* The Win32 ICO file header (14 bytes) */
   610     Uint16 bfReserved;
   611     Uint16 bfType;
   612     Uint16 bfCount;
   613 
   614     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   615     Uint32 biSize;
   616     Sint32 biWidth;
   617     Sint32 biHeight;
   618     Uint16 biPlanes;
   619     Uint16 biBitCount;
   620     Uint32 biCompression;
   621     Uint32 biSizeImage;
   622     Sint32 biXPelsPerMeter;
   623     Sint32 biYPelsPerMeter;
   624     Uint32 biClrUsed;
   625     Uint32 biClrImportant;
   626 
   627     /* Make sure we are passed a valid data source */
   628     surface = NULL;
   629     was_error = SDL_FALSE;
   630     if (src == NULL) {
   631         was_error = SDL_TRUE;
   632         goto done;
   633     }
   634 
   635     /* Read in the ICO file header */
   636     fp_offset = SDL_RWtell(src);
   637     SDL_ClearError();
   638 
   639     bfReserved = SDL_ReadLE16(src);
   640     bfType = SDL_ReadLE16(src);
   641     bfCount = SDL_ReadLE16(src);
   642     if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
   643         IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
   644         was_error = SDL_TRUE;
   645         goto done;
   646     }
   647 
   648     /* Read the Win32 Icon Directory */
   649     for (i = 0; i < bfCount; i++) {
   650         /* Icon Directory Entries */
   651         int bWidth = SDL_Read8(src);    /* Uint8, but 0 = 256 ! */
   652         int bHeight = SDL_Read8(src);   /* Uint8, but 0 = 256 ! */
   653         int bColorCount = SDL_Read8(src);       /* Uint8, but 0 = 256 ! */
   654         Uint8 bReserved = SDL_Read8(src);
   655         Uint16 wPlanes = SDL_ReadLE16(src);
   656         Uint16 wBitCount = SDL_ReadLE16(src);
   657         Uint32 dwBytesInRes = SDL_ReadLE32(src);
   658         Uint32 dwImageOffset = SDL_ReadLE32(src);
   659 
   660         if (!bWidth)
   661             bWidth = 256;
   662         if (!bHeight)
   663             bHeight = 256;
   664         if (!bColorCount)
   665             bColorCount = 256;
   666 
   667         //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
   668         if (bColorCount > maxCol) {
   669             maxCol = bColorCount;
   670             icoOfs = dwImageOffset;
   671             //printf("marked\n");
   672         }
   673     }
   674 
   675     /* Advance to the DIB Data */
   676     if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
   677         SDL_Error(SDL_EFSEEK);
   678         was_error = SDL_TRUE;
   679         goto done;
   680     }
   681 
   682     /* Read the Win32 BITMAPINFOHEADER */
   683     biSize = SDL_ReadLE32(src);
   684     if (biSize == 40) {
   685         biWidth = SDL_ReadLE32(src);
   686         biHeight = SDL_ReadLE32(src);
   687         biPlanes = SDL_ReadLE16(src);
   688         biBitCount = SDL_ReadLE16(src);
   689         biCompression = SDL_ReadLE32(src);
   690         biSizeImage = SDL_ReadLE32(src);
   691         biXPelsPerMeter = SDL_ReadLE32(src);
   692         biYPelsPerMeter = SDL_ReadLE32(src);
   693         biClrUsed = SDL_ReadLE32(src);
   694         biClrImportant = SDL_ReadLE32(src);
   695     } else {
   696         IMG_SetError("Unsupported ICO bitmap format");
   697         was_error = SDL_TRUE;
   698         goto done;
   699     }
   700 
   701     /* Check for read error */
   702     if (SDL_strcmp(SDL_GetError(), "") != 0) {
   703         was_error = SDL_TRUE;
   704         goto done;
   705     }
   706 
   707     /* We don't support any BMP compression right now */
   708     switch (biCompression) {
   709     case BI_RGB:
   710         /* Default values for the BMP format */
   711         switch (biBitCount) {
   712         case 1:
   713         case 4:
   714             ExpandBMP = biBitCount;
   715             biBitCount = 8;
   716             break;
   717         case 8:
   718             ExpandBMP = 8;
   719             break;
   720         case 32:
   721             Rmask = 0x00FF0000;
   722             Gmask = 0x0000FF00;
   723             Bmask = 0x000000FF;
   724             ExpandBMP = 0;
   725             break;
   726         default:
   727             IMG_SetError("ICO file with unsupported bit count");
   728             was_error = SDL_TRUE;
   729             goto done;
   730         }
   731         break;
   732     default:
   733         IMG_SetError("Compressed ICO files not supported");
   734         was_error = SDL_TRUE;
   735         goto done;
   736     }
   737 
   738     /* sanity check image size, so we don't overflow integers, etc. */
   739     if ((biWidth < 0) || (biWidth > 0xFFFFFF) ||
   740         (biHeight < 0) || (biHeight > 0xFFFFFF)) {
   741         IMG_SetError("Unsupported or invalid ICO dimensions");
   742         was_error = SDL_TRUE;
   743         goto done;
   744     }
   745 
   746     /* Create a RGBA surface */
   747     biHeight = biHeight >> 1;
   748     //printf("%d x %d\n", biWidth, biHeight);
   749     surface =
   750         SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
   751                              0x0000FF00, 0x000000FF, 0xFF000000);
   752     if (surface == NULL) {
   753         was_error = SDL_TRUE;
   754         goto done;
   755     }
   756 
   757     /* Load the palette, if any */
   758     //printf("bc %d bused %d\n", biBitCount, biClrUsed);
   759     if (biBitCount <= 8) {
   760         if (biClrUsed == 0) {
   761             biClrUsed = 1 << biBitCount;
   762         }
   763         if (biClrUsed > SDL_arraysize(palette)) {
   764             IMG_SetError("Unsupported or incorrect biClrUsed field");
   765             was_error = SDL_TRUE;
   766             goto done;
   767         }
   768         for (i = 0; i < (int) biClrUsed; ++i) {
   769             SDL_RWread(src, &palette[i], 4, 1);
   770         }
   771     }
   772 
   773     /* Read the surface pixels.  Note that the bmp image is upside down */
   774     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   775     switch (ExpandBMP) {
   776     case 1:
   777         bmpPitch = (biWidth + 7) >> 3;
   778         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   779         break;
   780     case 4:
   781         bmpPitch = (biWidth + 1) >> 1;
   782         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   783         break;
   784     case 8:
   785         bmpPitch = biWidth;
   786         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   787         break;
   788     default:
   789         bmpPitch = biWidth * 4;
   790         pad = 0;
   791         break;
   792     }
   793     while (bits > (Uint8 *) surface->pixels) {
   794         bits -= surface->pitch;
   795         switch (ExpandBMP) {
   796         case 1:
   797         case 4:
   798         case 8:
   799             {
   800                 Uint8 pixel = 0;
   801                 int shift = (8 - ExpandBMP);
   802                 for (i = 0; i < surface->w; ++i) {
   803                     if (i % (8 / ExpandBMP) == 0) {
   804                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   805                             IMG_SetError("Error reading from ICO");
   806                             was_error = SDL_TRUE;
   807                             goto done;
   808                         }
   809                     }
   810                     *((Uint32 *) bits + i) = (palette[pixel >> shift]);
   811                     pixel <<= ExpandBMP;
   812                 }
   813             }
   814             break;
   815 
   816         default:
   817             if (SDL_RWread(src, bits, 1, surface->pitch)
   818                 != surface->pitch) {
   819                 SDL_Error(SDL_EFREAD);
   820                 was_error = SDL_TRUE;
   821                 goto done;
   822             }
   823             break;
   824         }
   825         /* Skip padding bytes, ugh */
   826         if (pad) {
   827             Uint8 padbyte;
   828             for (i = 0; i < pad; ++i) {
   829                 SDL_RWread(src, &padbyte, 1, 1);
   830             }
   831         }
   832     }
   833     /* Read the mask pixels.  Note that the bmp image is upside down */
   834     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   835     ExpandBMP = 1;
   836     bmpPitch = (biWidth + 7) >> 3;
   837     pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   838     while (bits > (Uint8 *) surface->pixels) {
   839         Uint8 pixel = 0;
   840         int shift = (8 - ExpandBMP);
   841 
   842         bits -= surface->pitch;
   843         for (i = 0; i < surface->w; ++i) {
   844             if (i % (8 / ExpandBMP) == 0) {
   845                 if (!SDL_RWread(src, &pixel, 1, 1)) {
   846                     IMG_SetError("Error reading from ICO");
   847                     was_error = SDL_TRUE;
   848                     goto done;
   849                 }
   850             }
   851             *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
   852             pixel <<= ExpandBMP;
   853         }
   854         /* Skip padding bytes, ugh */
   855         if (pad) {
   856             Uint8 padbyte;
   857             for (i = 0; i < pad; ++i) {
   858                 SDL_RWread(src, &padbyte, 1, 1);
   859             }
   860         }
   861     }
   862   done:
   863     if (was_error) {
   864         if (src) {
   865             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   866         }
   867         if (surface) {
   868             SDL_FreeSurface(surface);
   869         }
   870         surface = NULL;
   871     }
   872     if (freesrc && src) {
   873         SDL_RWclose(src);
   874     }
   875     return (surface);
   876 }
   877 
   878 /* Load a BMP type image from an SDL datasource */
   879 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   880 {
   881     return(LoadBMP_RW(src, 0));
   882 }
   883 
   884 /* Load a ICO type image from an SDL datasource */
   885 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   886 {
   887     return(LoadICOCUR_RW(src, 1, 0));
   888 }
   889 
   890 /* Load a CUR type image from an SDL datasource */
   891 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   892 {
   893     return(LoadICOCUR_RW(src, 2, 0));
   894 }
   895 
   896 #else
   897 
   898 /* See if an image is contained in a data source */
   899 int IMG_isBMP(SDL_RWops *src)
   900 {
   901     return(0);
   902 }
   903 
   904 int IMG_isICO(SDL_RWops *src)
   905 {
   906     return(0);
   907 }
   908 
   909 int IMG_isCUR(SDL_RWops *src)
   910 {
   911     return(0);
   912 }
   913 
   914 /* Load a BMP type image from an SDL datasource */
   915 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   916 {
   917     return(NULL);
   918 }
   919 
   920 /* Load a BMP type image from an SDL datasource */
   921 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   922 {
   923     return(NULL);
   924 }
   925 
   926 /* Load a BMP type image from an SDL datasource */
   927 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   928 {
   929     return(NULL);
   930 }
   931 
   932 #endif /* LOAD_BMP */
   933 
   934 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */