GP32 Windows Bitmap Loader


Feeblez

Member
Joined
Mar 10, 2003
Messages
172
Yesterday i wrote a Windows Bitmap loader and today i thought i would share it with "the community"...

images can be compressed with either RLE4, RLE8, or totally uncompressed.

it will load 4bit/8bit bitmaps in 8bit colour mode,
or 4bit/8bit/24bit bitmaps in 16bit colour mode!

e.g. usage:
Code:
	sGpImage gpi;
	gpi.flags = GPIMG_8BIT|GPIMG_PAL;
	GpLoadBMP( "gp:\gpmm\bitmaps\test.bmp", &gpi );
	GP_HPALETTE h_pal;
	h_pal = GpPaletteCreateEx( gpi.colours, gpi.pPalette );
	gm_free( gpi.pPalette );
	gpi.pPalette = NULL;
	GpPaletteSelect( h_pal );
	GpPaletteRealize();
	GpPaletteDelete( h_pal );
	h_pal = NULL;
	GpBitBlt( NULL, &gp32.LCD[0], 0, 0, 320, 240, (unsigned char*)gpi.pImage, 0, 0, 320, 240 );
	GpBitBlt( NULL, &gp32.LCD[1], 0, 0, 320, 240, (unsigned char*)gpi.pImage, 0, 0, 320, 240 );
	gm_free( gpi.pImage );

flags:
GPIMG_8BIT - load the bitmap as 8bit*
GPIMG_16BIT - load the bitmap as 16bit
GPIMG_PAL - load the bitmap's palette*

*does not work for 24bit bitmaps with no palette information!

flag combinations:
GPIMG_PAL - only load the palette!
GPIMG_8BIT - only load the bitmap!
GPIMG_8BIT|GPIMG_PAL - load the bitmap and the palette!

Code:
/*******************************************

gp_image.h - GamePark Image Lib - by Feeblez

*******************************************/

#ifndef	_GP_IMAGE_H_
#define	_GP_IMAGE_H_

#include "gpdef.h"
#include "gpgraphic.h" /* GP_LOGPALENTRY */

#ifdef __cplusplus
	extern "C" {
#endif

enum eGpImage
{
/* flags */
	GPIMG_8BIT	= 1<<0,
	GPIMG_16BIT	= 1<<1,
	GPIMG_PAL  = 1<<2
};

struct sGpImage
{
	dword flags;

	unsigned char* pImage;
	ulong w;
	ulong h;

	GP_LOGPALENTRY* pPalette;
	ulong colours; /* <- Mr.Bean's English! */
};

#ifdef __cplusplus
	}
#endif

#endif	//_GP_IMAGE_H_

Code:
/*****************************************

gp_bmp.h - GamePark Image Lib - by Feeblez

*****************************************/

#ifndef	_GP_BMP_H_
#define	_GP_BMP_H_

#ifdef __cplusplus
	extern "C" {
#endif

#include "gp_image.h"

int GpLoadBMP( const char* strFilename, struct sGpImage* gpi );

#ifdef __cplusplus
	}
#endif

#endif	//_GP_BMP_H_

Code:
/*****************************************

gp_bmp.c - GamePark Image Lib - by Feeblez

*****************************************/

#include "gp_bmp.h"

#include "gpstdio.h"	/* F_HANDLE */
#include "gpmem.h" /* gp_mem */

#define BMP_HDR_DATA_OFFSET	10 /* dword	bfOffBits */
#define BMP_HDR_WIDTH    18 /* long  biWidth */
#define BMP_HDR_HEIGHT  	22 /* long  biHeight */
#define BMP_HDR_BPP    	28 /* word  biBitCount */
#define BMP_HDR_COMPRESSION	30 /* dword	biCompression */
#define BMP_HDR_DATA_SIZE  34 /* dword	biSizeImage */
#define BMP_HDR_COLOURS  	46 /* dword	biClrUsed */
#define BMP_HDR_SIZE    54

enum eGpBmp
{
/* bit depth */
	BMP_MONOCHROME	= 1,
	BMP_4_BIT    = 4,
	BMP_8_BIT    = 8,
	BMP_24_BIT    = 24,

/* compression */
	BMP_RGB = 0,
	BMP_RLE8,
	BMP_RLE4,
	BMP_BITFIELDS
};

int GpLoadBMP( const char* strFilename, struct sGpImage* gpi )
{
	F_HANDLE hFile;
	ERR_CODE err;
	ulong read_count = 0;
	long old_offset;
	char* pData;
/* relevant header info */
	ulong bfOffBits;
	int biHeight;
	word biBitCount;
	ulong biCompression;
	ulong biSizeImage;
	ulong i;
	int next_line;
	int x, y;

	gpi->w = gpi->h = gpi->colours = 0;
	gpi->pImage = NULL;
	gpi->pPalette = NULL;

	if( !strFilename || !*strFilename ) /* bad filename! */
  return 0;
	err = GpFileOpen( strFilename, OPEN_R, &hFile );
	if( err != SM_OK ) /* error opening file */
  return 0;

	pData = (char*)gm_malloc( BMP_HDR_SIZE );
  err = GpFileRead( hFile, pData, BMP_HDR_SIZE, &read_count );
	if( err != SM_OK ) /* error reading file! */
  goto _fail;

	if( gpi->flags & (GPIMG_8BIT|GPIMG_16BIT) )
	{
  gm_memcpy( &bfOffBits, pData + BMP_HDR_DATA_OFFSET, sizeof(dword) );
  gm_memcpy( &biCompression, pData + BMP_HDR_COMPRESSION, sizeof(dword) );
  gm_memcpy( &biSizeImage, pData + BMP_HDR_DATA_SIZE, sizeof(dword) );
  gm_memcpy( &gpi->w, pData + BMP_HDR_WIDTH, sizeof(dword) );
  gm_memcpy( &biHeight, pData + BMP_HDR_HEIGHT, sizeof(dword) );
  gpi->h = biHeight & 0x8fffffff;
	}
	gm_memcpy( &biBitCount, pData + BMP_HDR_BPP, sizeof(word) );
	gm_memcpy( &gpi->colours, pData + BMP_HDR_COLOURS, sizeof(dword) );
	gm_free( pData );
	switch( biBitCount )
	{
  case BMP_4_BIT:
  	if( !gpi->colours )
    gpi->colours = 16;
  	break;
  case BMP_8_BIT:
  	if( !gpi->colours )
    gpi->colours = 256;
  	break;
  case BMP_24_BIT:
  	if( (gpi->flags & GPIMG_8BIT) || !(gpi->flags & GPIMG_16BIT) )
    goto _fail;
  	break;
  default: /* unsupported bpp! */
/*  	case BMP_MONOCHROME: */
  	goto _fail;
	};

	if( (biBitCount <= BMP_8_BIT) && ((gpi->flags & GPIMG_PAL) || (gpi->flags & GPIMG_16BIT)) )
	{
  gpi->pPalette = (GP_LOGPALENTRY*)gm_malloc( sizeof(GP_LOGPALENTRY) * gpi->colours );
  err = GpFileRead( hFile, gpi->pPalette, sizeof(GP_LOGPALENTRY) * gpi->colours, &read_count );
  if( err != SM_OK ) /* error reading file */
  	goto _fail;
/* BGR -> RGB */
  for( i = 0; i < gpi->colours; i++ )
  	gpi->pPalette[i].peRed ^= gpi->pPalette[i].peBlue ^= gpi->pPalette[i].peRed ^= gpi->pPalette[i].peBlue;
  if( !(gpi->flags & (GPIMG_8BIT|GPIMG_16BIT)) )
  	goto _done; /* all done! */
	}

	if( gpi->flags & GPIMG_8BIT )
	{
  unsigned char* sp;
  unsigned char* dp;
  int count;
  int align;

  if( biBitCount == BMP_24_BIT )  /* unsupported bpp for 8bit! */
  	goto _fail;

  err = GpFileSeek( hFile, FROM_BEGIN, bfOffBits, &old_offset );
  if( err != SM_OK ) /* error seeking file */
  	goto _fail;
  if( !biSizeImage )
  {
  	if( biCompression != BMP_RGB ) 
    goto _fail;
  	biSizeImage = gpi->w * gpi->h;
  }
  pData = (unsigned char*)gm_malloc( biSizeImage );
  err = GpFileRead( hFile, pData, biSizeImage, &read_count );
  if( err != SM_OK ) /* error reading file */
  	goto _fail;
  sp = pData;
  gpi->pImage = (unsigned char*)gm_malloc( gpi->w * gpi->h );
  dp = gpi->pImage;
  if( biHeight < 0 )
  {
  	dp += gpi->h - 1;
  	next_line = -1 - (gpi->w * gpi->h);
  }
  else /* bottom-up! oooer */
  	next_line = 1 - (gpi->w * gpi->h);
  x = gpi->w;
  y = gpi->h;
  if( biBitCount == BMP_8_BIT )
  {
  	if( biCompression == BMP_RLE8 )
  	{
    unsigned char colour;
    do
    {
    	switch( *sp )
    	{
      case 0:
      	sp++;
      	switch( *sp )
      	{
/* end of line */
        case 0:
        	sp++;
        	while( x-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	dp += next_line;
        	x = gpi->w;
        	y--;
        	break;
/*  end of bitmap */
        case 1:
        	y = 0;
        	break;
/*  delta offset 00 02 X Y */
        case 2:
        	sp++;
        	count = *sp++;
        	x -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	count = *sp++;
        	y -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += next_line;
        	}
        	break;
/* run of pixels */
        default:
        	count = *sp++;
        	align = count & 1;
        	x -= count;
        	while( count-- )
        	{
          *dp = *sp++;
          dp += gpi->h;
        	}
        	if( align )
          sp++;
        	break;
      	}
      	break;
/* repeat pixel */
      default:
      	count = *sp++;
      	colour = *sp++;
      	x -= count;
      	while( count-- )
      	{
        *dp = colour;
        dp += gpi->h;
      	}
      	break;
    	}
    }
    while(y);
  	}
  	else
  	{
    for( y = 0; y < gpi->h; y++ )
    {
    	for( x = 0; x < gpi->w; x++ )
    	{
      *dp = *sp++;
      dp += gpi->h;
    	}
    	dp += next_line;
    }
  	}
  }
  else
  if( biBitCount == BMP_4_BIT )
  {
  	int nibble;
  	unsigned char nibble_shift[2] = { 4, 0 };
  	unsigned char nibble_colour[2];
  	if( biCompression == BMP_RLE4 )
  	{
    do
    {
    	switch( *sp )
    	{
      case 0:
      	sp++;
      	switch( *sp )
      	{
/* end of line */
        case 0:
        	sp++;
        	while( x-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	dp += next_line;
        	x = gpi->w;
        	y--;
        	break;
/* end of bitmap */
        case 1:
        	y = 0;
        	break;
/* delta offset 00 02 X Y */
        case 2:
        	sp++;
        	count = *sp++;
        	x -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	count = *sp++;
        	y -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += next_line;
        	}
        	break;
/* run of pixels */
        default:
        	count = *sp++;
        	align = count % 4;
        	if( align )
        	{
          if( align == 1 )
          	align = 2;
          else
          	align = 1;
        	}
        	x -= count;
        	nibble = 0;
        	while( count-- )
        	{
          *dp = (*sp >> nibble_shift[nibble]) & 0xf;
          dp += gpi->h;
          nibble ^= 1;
          if( !nibble )
          	sp++;
        	}
        	if( align )
          sp += align;
        	break;
      	}
      	break;
/* repeat pixel */
      default:
      	count = *sp++;
      	nibble_colour[0] = *sp >> 4;
      	nibble_colour[1] = *sp++ & 0xf;
      	x -= count;
      	nibble = 0;
      	while( count-- )
      	{
        *dp = nibble_colour[nibble];
        dp += gpi->h;
        nibble ^= 1;
      	}
      	break;
    	}
    }
    while(y);
  	}
  	else
  	{
    for( y = 0; y < gpi->h; y++ )
    {
    	nibble = 0;
    	for( x = 0; x < gpi->w; x++ )
    	{
      *dp = (*sp >> nibble_shift[nibble]) & 0xf;
      dp += gpi->h;
      nibble ^= 1;
      if( !nibble )
      	sp++;
    	}
    	dp += next_line;
    }
  	}
  }
	}
	else
	if( gpi->flags & GPIMG_16BIT )
	{
  unsigned char* sp;
  word* dp;
  int count;
  int align;
  word* pPalette = NULL;

  err = GpFileSeek( hFile, FROM_BEGIN, bfOffBits, &old_offset );
  if( err != SM_OK ) /* error seeking file */
  	goto _fail;
  if( !biSizeImage )
  {
  	if( biCompression != BMP_RGB ) 
    goto _fail;
  	biSizeImage = gpi->w * gpi->h * 2;
  }
  pData = (unsigned char*)gm_malloc( biSizeImage );
  err = GpFileRead( hFile, pData, biSizeImage, &read_count );
  if( err != SM_OK ) /* error reading file */
  	goto _fail;
  sp = pData;
  gpi->pImage = (unsigned char*)gm_malloc( gpi->w * gpi->h * 2 );
  dp = (word*)gpi->pImage;
  if( biHeight < 0 )
  {
  	dp += gpi->h - 1;
  	next_line = -1 - (gpi->w * gpi->h);
  }
  else /* bottom-up! oooer */
  	next_line = 1 - (gpi->w * gpi->h);

  if( gpi->pPalette )
  {
  	pPalette = (word*)gm_malloc( gpi->colours * 2 );
  	for( i = 0; i < gpi->colours; i++ )
  	{
    pPalette[i] =
    	((gpi->pPalette[i].peRed >> 3) << 11) |
    	((gpi->pPalette[i].peGreen >> 3) << 6) |
    	((gpi->pPalette[i].peBlue >> 3) << 1);
  	}
  	if( !(gpi->flags & GPIMG_PAL) )
  	{
    gm_free( gpi->pPalette );
    gpi->pPalette = NULL;
  	}
  }
  
  x = gpi->w;
  y = gpi->h;
  if( biBitCount == BMP_24_BIT )
  {
  	for( y = 0; y < gpi->h; y++ )
  	{
    for( x = 0; x < gpi->w; x++ )
    {
    	*dp =
      ((sp[2] >> 3) << 11) |
      ((sp[1] >> 3) << 6) |
      ((sp[0] >> 3) << 1);
    	sp += 3;
    	dp += gpi->h;
    }
    dp += next_line;
  	}
  }
  else
  if( biBitCount == BMP_8_BIT )
  {
  	if( biCompression == BMP_RLE8 )
  	{
    word colour;
    do
    {
    	switch( *sp )
    	{
      case 0:
      	sp++;
      	switch( *sp )
      	{
/* end of line */
        case 0:
        	sp++;
        	while( x-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	dp += next_line;
        	x = gpi->w;
        	y--;
        	break;
/* end of bitmap */
        case 1:
        	y = 0;
        	break;
/* delta offset 00 02 X Y */
        case 2:
        	sp++;
        	count = *sp++;
        	x -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	count = *sp++;
        	y -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += next_line;
        	}
        	break;
/* run of pixels */
        default:
        	count = *sp++;
        	align = count & 1;
        	x -= count;
        	while( count-- )
        	{
          *dp = pPalette[*sp++];
          dp += gpi->h;
        	}
        	if( align )
          sp++;
        	break;
      	}
      	break;
/* repeat pixel */
      default:
      	count = *sp++;
      	colour = pPalette[*sp++];
      	x -= count;
      	while( count-- )
      	{
        *dp = colour;
        dp += gpi->h;
      	}
      	break;
    	}
    }
    while(y);
  	}
  	else
  	{
    for( y = 0; y < gpi->h; y++ )
    {
    	for( x = 0; x < gpi->w; x++ )
    	{
      *dp = pPalette[*sp++];
      dp += gpi->h;
    	}
    	dp += next_line;
    }
  	}
  }
  else
  if( biBitCount == BMP_4_BIT )
  {
  	int nibble;
  	word nibble_shift[2] = { 4, 0 };
  	word nibble_colour[2];
  	if( biCompression == BMP_RLE4 )
  	{
    do
    {
    	switch( *sp )
    	{
      case 0:
      	sp++;
      	switch( *sp )
      	{
/* end of line */
        case 0:
        	sp++;
        	while( x-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	dp += next_line;
        	x = gpi->w;
        	y--;
        	break;
/* end of bitmap */
        case 1:
        	y = 0;
        	break;
/* delta offset 00 02 X Y */
        case 2:
        	sp++;
        	count = *sp++;
        	x -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += gpi->h;
        	}
        	count = *sp++;
        	y -= count;
        	while( count-- )
        	{
          /* *dp = 0; */
          dp += next_line;
        	}
        	break;
/* run of pixels */
        default:
        	count = *sp++;
        	align = count % 4;
        	if( align )
        	{
          if( align == 1 )
          	align = 2;
          else
          	align = 1;
        	}
        	x -= count;
        	nibble = 0;
        	while( count-- )
        	{
          *dp = pPalette[(*sp >> nibble_shift[nibble]) & 0xf];
          dp += gpi->h;
          nibble ^= 1;
          if( !nibble )
          	sp++;
        	}
        	if( align )
          sp += align;
        	break;
      	}
      	break;
/* repeat pixel */
      default:
      	count = *sp++;
      	nibble_colour[0] = pPalette[*sp >> 4];
      	nibble_colour[1] = pPalette[*sp++ & 0xf];
      	x -= count;
      	nibble = 0;
      	while( count-- )
      	{
        *dp = nibble_colour[nibble];
        dp += gpi->h;
        nibble ^= 1;
      	}
      	break;
    	}
    }
    while(y);
  	}
  	else
  	{
    for( y = 0; y < gpi->h; y++ )
    {
    	nibble = 0;
    	for( x = 0; x < gpi->w; x++ )
    	{
      *dp = pPalette[(*sp >> nibble_shift[nibble]) & 0xf];
      dp += gpi->h;
      nibble ^= 1;
      if( !nibble )
      	sp++;
    	}
    	dp += next_line;
    }
  	}
  }
  if( pPalette )
  {
  	gm_free( pPalette );
  	pPalette = NULL;
  }
	}
	gm_free( pData );

_done:
	err = GpFileClose( hFile );
	if( err != SM_OK ) /* error closing file */
  return 0;
	return 1;
_fail:
	GpFileClose( hFile );
	if( pData )
  gm_free( pData );
	if( gpi->pPalette )
	{
  gm_free( gpi->pPalette );
  gpi->pPalette = NULL;
	}
	if( gpi->pImage )
	{
  gm_free( gpi->pImage );
  gpi->pImage = NULL;
	}
	return 0;
}

ok, so the code is not 6 lines of optimized ARM! :p

if you find this code useful then let me know, also it would be nice to get a credit if you use it. :)

one last thing, i only tested this with 320x240 bitmaps, so it may screw up if your bitmap is 111x111, i dunno! :rolleyes:
 
thank you very much !
this is going to help many of us !

thanx again :)
 
I'm sure that will come in very useful for people, especially as bitmaps are now easy to edit (no conversion) and not included in the fxe (filesize). Specifically GPArt could load/save to bitmaps...

- Rico
 
i'll maybe do a save later, i want to do it properly so it includes compression.
i was thinking PCX may be a better option as it has 16bit RLE.
 
thanks for the code, sadly my compiler doesn't seem to like it - keeps moaning about data type definitions when it trys to create the gpi here:-

sGpImage gpi;

i think i used it all correctly, wrote all the 3 files, dumped them in the gpinclude folder of my dev kit and called #include "gp32_bmp.c" at the start of my app. :p
 
Back
Top