IMG_lbm.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 /* This is a ILBM image file loading framework
    23    Load IFF pictures, PBM & ILBM packing methods, with or without stencil
    24    Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001.
    25    24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati)
    26    in December 2002.
    27    EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain
    28    (http://www.multimania.com/mavati) in December 2003.
    29    Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004.
    30    Buffer overflow fix in RLE decompression by David Raulo in January 2008.
    31 */
    32 
    33 #include "SDL_endian.h"
    34 #include "SDL_image.h"
    35 
    36 #ifdef LOAD_LBM
    37 
    38 
    39 #define MAXCOLORS 256
    40 
    41 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
    42 
    43 typedef struct
    44 {
    45     Uint16 w, h;        /* width & height of the bitmap in pixels */
    46     Sint16 x, y;        /* screen coordinates of the bitmap */
    47     Uint8 planes;       /* number of planes of the bitmap */
    48     Uint8 mask;         /* mask type ( 0 => no mask ) */
    49     Uint8 tcomp;        /* compression type */
    50     Uint8 pad1;         /* dummy value, for padding */
    51     Uint16 tcolor;      /* transparent color */
    52     Uint8 xAspect,      /* pixel aspect ratio */
    53           yAspect;
    54     Sint16  Lpage;      /* width of the screen in pixels */
    55     Sint16  Hpage;      /* height of the screen in pixels */
    56 } BMHD;
    57 
    58 int IMG_isLBM( SDL_RWops *src )
    59 {
    60     Sint64 start;
    61     int   is_LBM;
    62     Uint8 magic[4+4+4];
    63 
    64     if ( !src )
    65         return 0;
    66     start = SDL_RWtell(src);
    67     is_LBM = 0;
    68     if ( SDL_RWread( src, magic, sizeof(magic), 1 ) )
    69     {
    70         if ( !SDL_memcmp( magic, "FORM", 4 ) &&
    71             ( !SDL_memcmp( magic + 8, "PBM ", 4 ) ||
    72               !SDL_memcmp( magic + 8, "ILBM", 4 ) ) )
    73         {
    74             is_LBM = 1;
    75         }
    76     }
    77     SDL_RWseek(src, start, RW_SEEK_SET);
    78     return( is_LBM );
    79 }
    80 
    81 SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
    82 {
    83     Sint64 start;
    84     SDL_Surface *Image;
    85     Uint8       id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
    86     Uint32      size, bytesloaded, nbcolors;
    87     Uint32      i, j, bytesperline, nbplanes, stencil, plane, h;
    88     Uint32      remainingbytes;
    89     Uint32      width;
    90     BMHD          bmhd;
    91     char        *error;
    92     Uint8       flagHAM,flagEHB;
    93 
    94     Image   = NULL;
    95     error   = NULL;
    96     MiniBuf = NULL;
    97 
    98     if ( !src ) {
    99         /* The error message has been set in SDL_RWFromFile */
   100         return NULL;
   101     }
   102     start = SDL_RWtell(src);
   103 
   104     if ( !SDL_RWread( src, id, 4, 1 ) )
   105     {
   106         error="error reading IFF chunk";
   107         goto done;
   108     }
   109 
   110     /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
   111     if ( !SDL_RWread( src, &size, 4, 1 ) )
   112     {
   113         error="error reading IFF chunk size";
   114         goto done;
   115     }
   116 
   117     /* As size is not used here, no need to swap it */
   118 
   119     if ( SDL_memcmp( id, "FORM", 4 ) != 0 )
   120     {
   121         error="not a IFF file";
   122         goto done;
   123     }
   124 
   125     if ( !SDL_RWread( src, id, 4, 1 ) )
   126     {
   127         error="error reading IFF chunk";
   128         goto done;
   129     }
   130 
   131     pbm = 0;
   132 
   133     /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
   134     if ( !SDL_memcmp( id, "PBM ", 4 ) ) pbm = 1;
   135     else if ( SDL_memcmp( id, "ILBM", 4 ) )
   136     {
   137         error="not a IFF picture";
   138         goto done;
   139     }
   140 
   141     nbcolors = 0;
   142 
   143     SDL_memset( &bmhd, 0, sizeof( BMHD ) );
   144     flagHAM = 0;
   145     flagEHB = 0;
   146 
   147     while ( SDL_memcmp( id, "BODY", 4 ) != 0 )
   148     {
   149         if ( !SDL_RWread( src, id, 4, 1 ) )
   150         {
   151             error="error reading IFF chunk";
   152             goto done;
   153         }
   154 
   155         if ( !SDL_RWread( src, &size, 4, 1 ) )
   156         {
   157             error="error reading IFF chunk size";
   158             goto done;
   159         }
   160 
   161         bytesloaded = 0;
   162 
   163         size = SDL_SwapBE32( size );
   164 
   165         if ( !SDL_memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
   166         {
   167             if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
   168             {
   169                 error="error reading BMHD chunk";
   170                 goto done;
   171             }
   172 
   173             bytesloaded = sizeof( BMHD );
   174 
   175             bmhd.w      = SDL_SwapBE16( bmhd.w );
   176             bmhd.h      = SDL_SwapBE16( bmhd.h );
   177             bmhd.x      = SDL_SwapBE16( bmhd.x );
   178             bmhd.y      = SDL_SwapBE16( bmhd.y );
   179             bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
   180             bmhd.Lpage  = SDL_SwapBE16( bmhd.Lpage );
   181             bmhd.Hpage  = SDL_SwapBE16( bmhd.Hpage );
   182         }
   183 
   184         if ( !SDL_memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
   185         {
   186             if (size > sizeof (colormap)) {
   187                 error="colormap size is too large";
   188                 goto done;
   189             }
   190 
   191             if ( !SDL_RWread( src, &colormap, size, 1 ) )
   192             {
   193                 error="error reading CMAP chunk";
   194                 goto done;
   195             }
   196 
   197             bytesloaded = size;
   198             nbcolors = size / 3;
   199         }
   200 
   201         if ( !SDL_memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode  */
   202         {
   203             Uint32 viewmodes;
   204             if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
   205             {
   206                 error="error reading CAMG chunk";
   207                 goto done;
   208             }
   209 
   210             bytesloaded = size;
   211             viewmodes = SDL_SwapBE32( viewmodes );
   212             if ( viewmodes & 0x0800 )
   213                 flagHAM = 1;
   214             if ( viewmodes & 0x0080 )
   215                 flagEHB = 1;
   216         }
   217 
   218         if ( SDL_memcmp( id, "BODY", 4 ) )
   219         {
   220             if ( size & 1 ) ++size;     /* padding ! */
   221             size -= bytesloaded;
   222             /* skip the remaining bytes of this chunk */
   223             if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR );
   224         }
   225     }
   226 
   227     /* compute some usefull values, based on the bitmap header */
   228 
   229     width = ( bmhd.w + 15 ) & 0xFFFFFFF0;  /* Width in pixels modulo 16 */
   230 
   231     bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
   232 
   233     nbplanes = bmhd.planes;
   234 
   235     if ( pbm )                         /* File format : 'Packed Bitmap' */
   236     {
   237         bytesperline *= 8;
   238         nbplanes = 1;
   239     }
   240 
   241     if ((nbplanes != 1) && (nbplanes != 4) && (nbplanes != 8) && (nbplanes != 24))
   242     {
   243         error="unsupported number of color planes";
   244         goto done;
   245     }
   246 
   247     stencil = (bmhd.mask & 1);   /* There is a mask ( 'stencil' ) */
   248 
   249     /* Allocate memory for a temporary buffer ( used for
   250            decompression/deinterleaving ) */
   251 
   252     MiniBuf = (Uint8 *)SDL_malloc( bytesperline * (nbplanes + stencil) );
   253     if ( MiniBuf == NULL )
   254     {
   255         error="not enough memory for temporary buffer";
   256         goto done;
   257     }
   258 
   259     if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (nbplanes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
   260        goto done;
   261 
   262     if ( bmhd.mask & 2 )               /* There is a transparent color */
   263         SDL_SetColorKey( Image, SDL_TRUE, bmhd.tcolor );
   264 
   265     /* Update palette informations */
   266 
   267     /* There is no palette in 24 bits ILBM file */
   268     if ( nbcolors>0 && flagHAM==0 )
   269     {
   270         /* FIXME: Should this include the stencil? See comment below */
   271         int nbrcolorsfinal = 1 << (nbplanes + stencil);
   272         ptr = &colormap[0];
   273 
   274         for ( i=0; i<nbcolors; i++ )
   275         {
   276             Image->format->palette->colors[i].r = *ptr++;
   277             Image->format->palette->colors[i].g = *ptr++;
   278             Image->format->palette->colors[i].b = *ptr++;
   279         }
   280 
   281         /* Amiga EHB mode (Extra-Half-Bright) */
   282         /* 6 bitplanes mode with a 32 colors palette */
   283         /* The 32 last colors are the same but divided by 2 */
   284         /* Some Amiga pictures save 64 colors with 32 last wrong colors, */
   285         /* they shouldn't !, and here we overwrite these 32 bad colors. */
   286         if ( (nbcolors==32 || flagEHB ) && (1<<nbplanes)==64 )
   287         {
   288             nbcolors = 64;
   289             ptr = &colormap[0];
   290             for ( i=32; i<64; i++ )
   291             {
   292                 Image->format->palette->colors[i].r = (*ptr++)/2;
   293                 Image->format->palette->colors[i].g = (*ptr++)/2;
   294                 Image->format->palette->colors[i].b = (*ptr++)/2;
   295             }
   296         }
   297 
   298         /* If nbcolors < 2^nbplanes, repeat the colormap */
   299         /* This happens when pictures have a stencil mask */
   300         if ( nbrcolorsfinal > (1<<nbplanes) ) {
   301             nbrcolorsfinal = (1<<nbplanes);
   302         }
   303         for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
   304         {
   305             Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
   306             Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
   307             Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
   308         }
   309         if ( !pbm )
   310             Image->format->palette->ncolors = nbrcolorsfinal;
   311     }
   312 
   313     /* Get the bitmap */
   314 
   315     for ( h=0; h < bmhd.h; h++ )
   316     {
   317         /* uncompress the datas of each planes */
   318 
   319         for ( plane=0; plane < (nbplanes+stencil); plane++ )
   320         {
   321             ptr = MiniBuf + ( plane * bytesperline );
   322 
   323             remainingbytes = bytesperline;
   324 
   325             if ( bmhd.tcomp == 1 )      /* Datas are compressed */
   326             {
   327                 do
   328                 {
   329                     if ( !SDL_RWread( src, &count, 1, 1 ) )
   330                     {
   331                         error="error reading BODY chunk";
   332                         goto done;
   333                     }
   334 
   335                     if ( count & 0x80 )
   336                     {
   337                         count ^= 0xFF;
   338                         count += 2; /* now it */
   339 
   340                         if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
   341                         {
   342                             error="error reading BODY chunk";
   343                             goto done;
   344                         }
   345                         SDL_memset( ptr, color, count );
   346                     }
   347                     else
   348                     {
   349                         ++count;
   350 
   351                         if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
   352                         {
   353                            error="error reading BODY chunk";
   354                             goto done;
   355                         }
   356                     }
   357 
   358                     ptr += count;
   359                     remainingbytes -= count;
   360 
   361                 } while ( remainingbytes > 0 );
   362             }
   363             else
   364             {
   365                 if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
   366                 {
   367                     error="error reading BODY chunk";
   368                     goto done;
   369                 }
   370             }
   371         }
   372 
   373         /* One line has been read, store it ! */
   374 
   375         ptr = (Uint8 *)Image->pixels;
   376         if ( nbplanes==24 || flagHAM==1 )
   377             ptr += h * width * 3;
   378         else
   379             ptr += h * width;
   380 
   381         if ( pbm )                 /* File format : 'Packed Bitmap' */
   382         {
   383            SDL_memcpy( ptr, MiniBuf, width );
   384         }
   385         else        /* We have to un-interlace the bits ! */
   386         {
   387             if ( nbplanes!=24 && flagHAM==0 )
   388             {
   389                 size = ( width + 7 ) / 8;
   390 
   391                 for ( i=0; i < size; i++ )
   392                 {
   393                     SDL_memset( ptr, 0, 8 );
   394 
   395                     for ( plane=0; plane < (nbplanes + stencil); plane++ )
   396                     {
   397                         color = *( MiniBuf + i + ( plane * bytesperline ) );
   398                         msk = 0x80;
   399 
   400                         for ( j=0; j<8; j++ )
   401                         {
   402                             if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
   403                             else                        ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
   404 
   405                             msk >>= 1;
   406                         }
   407                     }
   408                     ptr += 8;
   409                 }
   410             }
   411             else
   412             {
   413                 Uint32 finalcolor = 0;
   414                 size = ( width + 7 ) / 8;
   415                 /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
   416                 /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
   417                 for ( i=0; i<width; i=i+8 )
   418                 {
   419                     Uint8 maskBit = 0x80;
   420                     for ( j=0; j<8; j++ )
   421                     {
   422                         Uint32 pixelcolor = 0;
   423                         Uint32 maskColor = 1;
   424                         Uint8 dataBody;
   425                         for ( plane=0; plane < nbplanes; plane++ )
   426                         {
   427                             dataBody = MiniBuf[ plane*size+i/8 ];
   428                             if ( dataBody&maskBit )
   429                                 pixelcolor = pixelcolor | maskColor;
   430                             maskColor = maskColor<<1;
   431                         }
   432                         /* HAM : 12 bits RGB image (4 bits per color component) */
   433                         /* HAM8 : 18 bits RGB image (6 bits per color component) */
   434                         if ( flagHAM )
   435                         {
   436                             switch( pixelcolor>>(nbplanes-2) )
   437                             {
   438                                 case 0: /* take direct color from palette */
   439                                     finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
   440                                     break;
   441                                 case 1: /* modify only blue component */
   442                                     finalcolor = finalcolor&0x00FFFF;
   443                                     finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
   444                                     break;
   445                                 case 2: /* modify only red component */
   446                                     finalcolor = finalcolor&0xFFFF00;
   447                                     finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
   448                                     break;
   449                                 case 3: /* modify only green component */
   450                                     finalcolor = finalcolor&0xFF00FF;
   451                                     finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
   452                                     break;
   453                             }
   454                         }
   455                         else
   456                         {
   457                             finalcolor = pixelcolor;
   458                         }
   459 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   460                             *ptr++ = (Uint8)(finalcolor>>16);
   461                             *ptr++ = (Uint8)(finalcolor>>8);
   462                             *ptr++ = (Uint8)(finalcolor);
   463 #else
   464                             *ptr++ = (Uint8)(finalcolor);
   465                             *ptr++ = (Uint8)(finalcolor>>8);
   466                             *ptr++ = (Uint8)(finalcolor>>16);
   467 #endif
   468                         maskBit = maskBit>>1;
   469                     }
   470                 }
   471             }
   472         }
   473     }
   474 
   475 done:
   476 
   477     if ( MiniBuf ) SDL_free( MiniBuf );
   478 
   479     if ( error )
   480     {
   481         SDL_RWseek(src, start, RW_SEEK_SET);
   482         if ( Image ) {
   483             SDL_FreeSurface( Image );
   484             Image = NULL;
   485         }
   486         IMG_SetError( "%s", error );
   487     }
   488 
   489     return( Image );
   490 }
   491 
   492 #else /* LOAD_LBM */
   493 
   494 /* See if an image is contained in a data source */
   495 int IMG_isLBM(SDL_RWops *src)
   496 {
   497     return(0);
   498 }
   499 
   500 /* Load an IFF type image from an SDL datasource */
   501 SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
   502 {
   503     return(NULL);
   504 }
   505 
   506 #endif /* LOAD_LBM */