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:
 

Pekele

Still Fresh
Joined
Mar 28, 2003
Messages
45
thank you very much !
this is going to help many of us !

thanx again :)
 

Rico

Certified Guru
Joined
Apr 4, 2003
Messages
5,649
Age
36
Location
England, UK
Website
Visit site
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
 

Feeblez

Member
Joined
Mar 10, 2003
Messages
172
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.
 

Pirotic

Certified Guru
Joined
Feb 16, 2004
Messages
593
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
 
Top