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