PATCH for BMP RLE 4 and RLE 8 compressed images

No idea if this is the right way to submit this path… If not, let me know!-------------------------------------------
— SDL_bmp_old.c Tue Mar 16 11:07:27 2004
+++ SDL_bmp.c Fri Jul 30 10:31:46 2004
@@ -22,7 +22,7 @@

#ifdef SAVE_RCSID
static char rcsid =

  • “@(#) $Id: SDL_bmp.c,v 1.1.1.1 2004/03/16 11:07:27 petrus Exp $”;
  • “@(#) $Id: SDL_bmp.c,v 1.2 2004/07/30 10:31:46 petrus Exp $”;
    #endif

#ifndef DISABLE_FILE
@@ -53,6 +53,7 @@
#define BI_BITFIELDS 3
#endif

+static int readRlePixels(SDL_Surface * surface, SDL_RWops *src, int isRle8);

SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
{
@@ -158,7 +159,7 @@
break;
}

  • /* We don’t support any BMP compression right now */
  • /* RLE4 and RLE8 BMP compression is supported /
    Rmask = Gmask = Bmask = 0;
    switch (biCompression) {
    case BI_RGB:
    @@ -191,7 +192,7 @@
    }
    /
    Fall through – read the RGB masks */
  •   case BI_BITFIELDS:
    
  •   default:
      	switch (biBitCount) {
      		case 15:
      		case 16:
    

@@ -204,10 +205,6 @@
break;
}
break;

  •   default:
    
  •   	SDL_SetError("Compressed BMP files not supported");
    
  •   	was_error = 1;
    
  •   	goto done;
    

    }

    /* Create a compatible surface, note that the colors are RGB ordered /
    @@ -221,9 +218,13 @@
    /
    Load the palette, if any */
    palette = (surface->format)->palette;
    if ( palette ) {

  •   if ( biClrUsed == 0 ) {
    
  •   	biClrUsed = 1 << biBitCount;
    
  •   }
    
  •   /*
    
  •   | guich: always use 1<<bpp b/c some bitmaps can bring wrong 
    

information

  •   | for colorsUsed
    
  •   */
    
  •   /* if ( biClrUsed == 0 ) {  */
    
  •   biClrUsed = 1 << biBitCount;
    
  •   /* } */
      if ( biSize == 12 ) {
      	for ( i = 0; i < (int)biClrUsed; ++i ) {
      		SDL_RWread(src, &palette->colors[i].b, 1, 1);
    

@@ -248,6 +249,13 @@
was_error = 1;
goto done;
}

  • if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
  •   SDL_LockSurface(surface);   /* petrus: not sure if I have to 
    

this. Sam? */

  •   was_error = readRlePixels(surface, src, biCompression == 
    

BI_RLE8);

  •   SDL_UnlockSurface(surface); /* see comment above. */
    
  •   if (was_error) SDL_SetError("Error reading from BMP");
    
  •   goto done;
    
  • }
    bits = (Uint8 )surface->pixels+(surface->hsurface->pitch);
    switch (ExpandBMP) {
    case 1:
    @@ -332,6 +340,86 @@
    SDL_RWclose(src);
    }
    return(surface);
    +}

+static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
+{

  • /*
  • | Sets the surface pixels from src. A bmp image is upside down.
  • */
  • int pitch = surface->pitch;
  • int height = surface->h;
  • Uint8 * bits = (Uint8 *)surface->pixels + ((height-1) * pitch);
  • int ofs = 0;
  • Uint8 ch;
  • Uint8 needsPad;
  • for (;:wink: {
  •   if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    
  •   /*
    
  •   | encoded mode starts with a run length, and then a byte
    
  •   | with two colour indexes to alternate between for the run
    
  •   */
    
  •   if ( ch ) {
    
  •   	Uint8 pixel;
    
  •   	if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    
  •   	if ( isRle8 ) {                 /* 256-color bitmap, 
    

compressed */

  •   		do {
    
  •   			bits[ofs++] = pixel;
    
  •   		} while (--ch);
    
  •   	}else {                         /* 16-color bitmap, 
    

compressed */

  •   		Uint8 pixel0 = pixel >> 4;
    
  •   		Uint8 pixel1 = pixel & 0x0F;
    
  •   		for (;;) {
    
  •   			bits[ofs++] = pixel0;     /* even 
    

count, high nibble */

  •   			if (!--ch) break;
    
  •   			bits[ofs++] = pixel1;     /* odd count, 
    

low nibble */

  •   			if (!--ch) break;
    
  •   		}
    
  •   	}
    
  •   } else {
    
  •   	/*
    
  •   	| A leading zero is an escape; it may signal the end of 
    

the bitmap,

  •   	| a cursor move, or some absolute data.
    
  •   	| zero tag may be absolute mode or an escape
    
  •   	*/
    
  •   	if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    
  •   	switch (ch) {
    
  •   	case 0:                         /* end of line */
    
  •   		ofs = 0;
    
  •   		bits -= pitch;               /* go to previous 
    

*/

  •   		break;
    
  •   	case 1:                         /* end of bitmap */
    
  •   		return 0;                    /* success! */
    
  •   	case 2:                         /* delta */
    
  •   		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    
  •   		ofs += ch;
    
  •   		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    
  •   		bits -= (ch * pitch);
    
  •   		break;
    
  •   	default:                        /* no compression */
    
  •   		if (isRle8) {
    
  •   			needsPad = ( ch & 1 );
    
  •   			do {
    
  •   				if ( !SDL_RWread(src, bits + 
    

ofs++, 1, 1) ) return 1;

  •   			} while (--ch);
    
  •   		} else {
    
  •   			needsPad = ( ((ch+1)>>1) & 1 ); /* 
    

(ch+1)>>1: bytes size */

  •   			for (;;) {
    
  •   				Uint8 pixel;
    
  •   				if ( !SDL_RWread(src, &pixel, 
    

1, 1) ) return 1;

  •   				bits[ofs++] = pixel >> 4;
    
  •   				if (!--ch) break;
    
  •   				bits[ofs++] = pixel & 0x0F;
    
  •   				if (!--ch) break;
    
  •   			}
    
  •   		}
    
  •   		/* pad at even boundary */
    
  •   		if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) 
    

return 1;

  •   		break;
    
  •   	}
    
  •   }
    
  • }
    }

int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)

Pierre G.Richard wrote:

No idea if this is the right way to submit this path… If not, let me know!

[…]

/* Load the palette, if any */
palette = (surface->format)->palette;
if ( palette ) {

  •  if ( biClrUsed == 0 ) {
    
  •  	biClrUsed = 1 << biBitCount;
    
  •  }
    
  •  /*
    
  •  | guich: always use 1<<bpp b/c some bitmaps can bring wrong 
    

information

  •  | for colorsUsed
    

Hmm, what’s this about ?

  •  */
    
  •  /* if ( biClrUsed == 0 ) {  */
    
  •  biClrUsed = 1 << biBitCount;
    
  •  /* } */
    if ( biSize == 12 ) {
    	for ( i = 0; i < (int)biClrUsed; ++i ) {
    		SDL_RWread(src, &palette->colors[i].b, 1, 1);
    

@@ -248,6 +249,13 @@
was_error = 1;
goto done;
}

  • if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
  •  SDL_LockSurface(surface);   /* petrus: not sure if I have to 
    

this. Sam? */

I’m not Sam, but it’s not needed since “surface” is a SDL_SWSURFACE and
doesn’t use either alpha or colorkeying.

Stephane

  •  if ( biClrUsed == 0 ) {
    
  •  	biClrUsed = 1 << biBitCount;
    
  •  }
    
  •  /*
    
  •  | guich: always use 1<<bpp b/c some bitmaps can bring wrong
    

information

  •  | for colorsUsed
    
  •  */
    
  •  /* if ( biClrUsed == 0 ) {  */
    
  •  biClrUsed = 1 << biBitCount;
    
  •  /* } */
    

Hmm, what’s this about ?

I ask Guich (he will answer in this thread.) He has run lot of bitmaps
through this
code and may have found wierd ones. People not setting biClrUsed to 0?

At looking at the code, it won’t crash. If the EOF is reached while filling
the palette, it is harmless – although not too clean! Then SDL_RWSeek is
called before to start processing the data. It sets the read pointer at the
right position.

Therefore:

  • if biClrUsed was OK, there will be a few wrong, but irrelevant and
    theoretically useless, values in the palette;
  • if biClrUsed was wrong, then the palette will be large enough to avoid
    segfaults, and possibly recover from the format error.

Hmmmm… One can see it as tolerance and robustness versus correctness.
Guich will tell.

  •  SDL_LockSurface(surface);   /* petrus: not sure if I have to this.
    

Sam? */

I’m not Sam, but it’s not needed since “surface” is a SDL_SWSURFACE and
doesn’t use either alpha or colorkeying.

Yes: I’m sometimes a bit paranoid. Quite sure Sam will fix it!

Thanks, Stephane, for having review it.
Pierre.

No idea if this is the right way to submit this path… If not, let me know!

Yep, this is fine. Instead of adding it to the core SDL library, I added RLE
support to SDL_image.

Thanks!
-Sam Lantinga, Software Engineer, Blizzard Entertainment