slouken@libsdl.org
http://www.multimania.com/mavati
http://www.multimania.com/mavati
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL_endian.h"
#include "SDL_image.h"
#ifdef LOAD_LBM
#define MAXCOLORS 256
typedef struct
{
Uint16 w, h;
Sint16 x, y;
Uint8 planes;
Uint8 mask;
Uint8 tcomp;
Uint8 pad1;
Uint16 tcolor;
Uint8 xAspect,
yAspect;
Sint16 Lpage;
Sint16 Hpage;
} BMHD;
int IMG_isLBM( SDL_RWops *src )
{
int is_LBM;
Uint8 magic[4+4+4];
is_LBM = 0;
if ( SDL_RWread( src, magic, 4+4+4, 1 ) )
{
if ( !memcmp( magic, "FORM", 4 ) &&
( !memcmp( magic + 8, "PBM ", 4 ) ||
!memcmp( magic + 8, "ILBM", 4 ) ) )
{
is_LBM = 1;
}
}
return( is_LBM );
}
SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
{
SDL_Surface *Image;
Uint8 id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
Uint32 size, bytesloaded, nbcolors;
Uint32 i, j, bytesperline, nbplanes, plane, h;
Uint32 remainingbytes;
Uint32 width;
BMHD bmhd;
char *error;
Uint8 flagHAM,flagEHB;
Image = NULL;
error = NULL;
MiniBuf = NULL;
if ( !src ) {
return NULL;
}
if ( !SDL_RWread( src, id, 4, 1 ) )
{
error="error reading IFF chunk";
goto done;
}
if ( !SDL_RWread( src, &size, 4, 1 ) )
{
error="error reading IFF chunk size";
goto done;
}
if ( memcmp( id, "FORM", 4 ) != 0 )
{
error="not a IFF file";
goto done;
}
if ( !SDL_RWread( src, id, 4, 1 ) )
{
error="error reading IFF chunk";
goto done;
}
pbm = 0;
if ( !memcmp( id, "PBM ", 4 ) ) pbm = 1;
else if ( memcmp( id, "ILBM", 4 ) )
{
error="not a IFF picture";
goto done;
}
nbcolors = 0;
memset( &bmhd, 0, sizeof( BMHD ) );
flagHAM = 0;
flagEHB = 0;
while ( memcmp( id, "BODY", 4 ) != 0 )
{
if ( !SDL_RWread( src, id, 4, 1 ) )
{
error="error reading IFF chunk";
goto done;
}
if ( !SDL_RWread( src, &size, 4, 1 ) )
{
error="error reading IFF chunk size";
goto done;
}
bytesloaded = 0;
size = SDL_SwapBE32( size );
if ( !memcmp( id, "BMHD", 4 ) )
{
if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
{
error="error reading BMHD chunk";
goto done;
}
bytesloaded = sizeof( BMHD );
bmhd.w = SDL_SwapBE16( bmhd.w );
bmhd.h = SDL_SwapBE16( bmhd.h );
bmhd.x = SDL_SwapBE16( bmhd.x );
bmhd.y = SDL_SwapBE16( bmhd.y );
bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
bmhd.Lpage = SDL_SwapBE16( bmhd.Lpage );
bmhd.Hpage = SDL_SwapBE16( bmhd.Hpage );
}
if ( !memcmp( id, "CMAP", 4 ) )
{
if ( !SDL_RWread( src, &colormap, size, 1 ) )
{
error="error reading CMAP chunk";
goto done;
}
bytesloaded = size;
nbcolors = size / 3;
}
if ( !memcmp( id, "CAMG", 4 ) )
{
Uint32 viewmodes;
if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
{
error="error reading CAMG chunk";
goto done;
}
bytesloaded = size;
viewmodes = SDL_SwapBE32( viewmodes );
if ( viewmodes & 0x0800 )
flagHAM = 1;
if ( viewmodes & 0x0080 )
flagEHB = 1;
}
if ( memcmp( id, "BODY", 4 ) )
{
if ( size & 1 ) ++size;
size -= bytesloaded;
if ( size ) SDL_RWseek( src, size, SEEK_CUR );
}
}
width = ( bmhd.w + 15 ) & 0xFFFFFFF0;
bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
nbplanes = bmhd.planes;
if ( pbm )
{
bytesperline *= 8;
nbplanes = 1;
}
if ( bmhd.mask & 1 ) ++nbplanes;
if ( ( MiniBuf = (void *)malloc( bytesperline * nbplanes ) ) == NULL )
{
error="no enough memory for temporary buffer";
goto done;
}
if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
goto done;
if ( bmhd.mask & 2 )
SDL_SetColorKey( Image, SDL_SRCCOLORKEY, bmhd.tcolor );
if ( nbcolors>0 && flagHAM==0 )
{
int nbrcolorsfinal = 1 << nbplanes;
ptr = &colormap[0];
for ( i=0; i<nbcolors; i++ )
{
Image->format->palette->colors[i].r = *ptr++;
Image->format->palette->colors[i].g = *ptr++;
Image->format->palette->colors[i].b = *ptr++;
}
if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
{
nbcolors = 64;
ptr = &colormap[0];
for ( i=32; i<64; i++ )
{
Image->format->palette->colors[i].r = (*ptr++)/2;
Image->format->palette->colors[i].g = (*ptr++)/2;
Image->format->palette->colors[i].b = (*ptr++)/2;
}
}
if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
nbrcolorsfinal = (1<<bmhd.planes);
}
for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
{
Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
}
Image->format->palette->ncolors = nbrcolorsfinal;
}
for ( h=0; h < bmhd.h; h++ )
{
for ( plane=0; plane < nbplanes; plane++ )
{
ptr = MiniBuf + ( plane * bytesperline );
remainingbytes = bytesperline;
if ( bmhd.tcomp == 1 )
{
do
{
if ( !SDL_RWread( src, &count, 1, 1 ) )
{
error="error reading BODY chunk";
goto done;
}
if ( count & 0x80 )
{
count ^= 0xFF;
count += 2;
if ( !SDL_RWread( src, &color, 1, 1 ) )
{
error="error reading BODY chunk";
goto done;
}
memset( ptr, color, count );
}
else
{
++count;
if ( !SDL_RWread( src, ptr, count, 1 ) )
{
error="error reading BODY chunk";
goto done;
}
}
ptr += count;
remainingbytes -= count;
} while ( remainingbytes > 0 );
}
else
{
if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
{
error="error reading BODY chunk";
goto done;
}
}
}
ptr = Image->pixels;
if ( nbplanes==24 || flagHAM==1 )
ptr += h * width * 3;
else
ptr += h * width;
if ( pbm )
{
memcpy( ptr, MiniBuf, width );
}
else
{
if ( nbplanes!=24 && flagHAM==0 )
{
size = ( width + 7 ) / 8;
for ( i=0; i < size; i++ )
{
memset( ptr, 0, 8 );
for ( plane=0; plane < nbplanes; plane++ )
{
color = *( MiniBuf + i + ( plane * bytesperline ) );
msk = 0x80;
for ( j=0; j<8; j++ )
{
if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
else ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
msk >>= 1;
}
}
ptr += 8;
}
}
else
{
Uint32 finalcolor = 0;
size = ( width + 7 ) / 8;
for ( i=0; i<width; i=i+8 )
{
Uint8 maskBit = 0x80;
for ( j=0; j<8; j++ )
{
Uint32 pixelcolor = 0;
Uint32 maskColor = 1;
Uint8 dataBody;
for ( plane=0; plane < nbplanes; plane++ )
{
dataBody = MiniBuf[ plane*size+i/8 ];
if ( dataBody&maskBit )
pixelcolor = pixelcolor | maskColor;
maskColor = maskColor<<1;
}
if ( flagHAM )
{
switch( pixelcolor>>(nbplanes-2) )
{
case 0:
finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
break;
case 1:
finalcolor = finalcolor&0x00FFFF;
finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
break;
case 2:
finalcolor = finalcolor&0xFFFF00;
finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
break;
case 3:
finalcolor = finalcolor&0xFF00FF;
finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
break;
}
}
else
{
finalcolor = pixelcolor;
}
if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
{
*ptr++ = finalcolor>>16;
*ptr++ = finalcolor>>8;
*ptr++ = finalcolor;
}
else
{
*ptr++ = finalcolor;
*ptr++ = finalcolor>>8;
*ptr++ = finalcolor>>16;
}
maskBit = maskBit>>1;
}
}
}
}
}
done:
if ( MiniBuf ) free( MiniBuf );
if ( error )
{
IMG_SetError( error );
SDL_FreeSurface( Image );
Image = NULL;
}
return( Image );
}
#else
int IMG_isLBM(SDL_RWops *src)
{
return(0);
}
SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
{
return(NULL);
}
#endif