BMP RLE decode -- any advice?

Given the lack of a BMP decoder in PHP’s GD module, I’ve been working on writing my own. As usual, most of the existing ones are either ridiculously slow and oversized (though the lack of being able to directly manipulate the pixel buffer is mostly to blame for that), or lack support for anything but 24 bit revision 3 files.

I’ve got 2, 4, 8 and 24 bit working now when uncompressed, and am now tackling RLE4 and RLE8… Well, mostly 8 for now as it’s the simpler of the two.

In any case, my question is does anyone see a better way of handling this, or any advice for getting this code better/faster? Need more eyes on this as what I have now works – but it doesn’t ‘feel right’.


function winRLE8Decode($data,$height,$rowSize) {
	$newData=str_repeat(chr(0),$height*$rowSize);
	$dataLength=strlen($data);
	$fromOffset=0;
	$toOffset=0;
	while ($fromOffset<$dataLength) {
		$count=ord($data[$fromOffset++]);
		if ($count==0) {
			$count=ord($data[$fromOffset++]);
			switch ($count) {
				case 0:	// end of scanline
					$toOffset+=($rowSize-($toOffset % $rowSize));
				break;
				case 1: // end of data
					return $newData;
				case 2: // offset
					$toOffset+=(
						ord($data[$fromOffset++]) +
						ord($data[$fromOffset++])*$rowSize
					);
				break;
				default: // uncompressed run
					$padding=$count%2;
					while ($count-- > 0) {
						$newData[$toOffset++]=$data[$fromOffset++];
					}
					$fromOffset+=$padding;
			}
		} else { // compressed run
			$b=$data[$fromOffset++];
			while ($count-- > 0) {
				$newData[$toOffset++]=$b;
			}
		}
	}
	return $newData;
}

I tell you though – I’m NOT looking forward to coding nybbles using PHP… could be worse though, could be dyads – thankfully they never officially made a BMP 2.x or higher 4 color format.

GAH, I really wish php had real pointers and typecasting at times like this.

So… any ideas? See any problems with the code? (I know it could use a bit more range checking…). It’s really stupid having to pre-fill the data string, but since Windows RLE includes the ability to skip over large sections of an image…

REAL fun is going to be dealing with 15/16 bit… especially since my decoder turns all images into 24 bit for formatting… the joys of figuring out if it’s 565, 555, 484… 574…

Uhg, the 4 bit version got way more complex…


<?php

class pixelBuffer4Bit {
	public 
		$data,
		$dataSize,
		$rowSize,
		$height;
	
	public function __construct($rowSize,$height) {
		$this->rowSize=$rowSize;
		$this->height=$height;
		$this->dataSize=$this->rowSize*$this->height;
		$this->data=str_repeat(chr(0),$this->dataSize);
	}
	
	public function putpixel($x,$y,$c) {
		$offset=$y*$this->rowSize+floor($x/2);
		$oldValue=ord($this->data[$offset]);
		if ($x%2) {
			$this->data[$offset]=chr(
				($oldValue & 0xF0) |
				($c & 0x0F)
			);
		} else {
			$this->data[$offset]=chr(
				($oldValue & 0x0F) |
				(($c<<4) & 0xF0)
			);
		}
	}
	
} // class pixelBuffer4Bit

function winRLE4Decode($data,$height,$rowSize) {
	$newData=new pixelBuffer4Bit($rowSize,$height);
	$dataLength=strlen($data);
	$fromOffset=0;
	$x=0;
	$y=0;
	while ($fromOffset<$dataLength) {
		$count=ord($data[$fromOffset++]);
		if ($count==0) {
			$count=ord($data[$fromOffset++]);
			switch ($count) {
				case 0:	// end of scanline
					$x=0;
					$y++;
				break;
				case 1: // end of data
					return $newData->data;
				case 2: // offset
					$x+=ord($data[$fromOffset++]);
					$y+=ord($data[$fromOffset++]);
				break;
				default: // uncompressed run
					$padding=floor($count/2)%2;
					$t=0;
					while ($t<$count) {
						if ($t%2) {
							$newData->putpixel($x++,$y,$b & 0x0F);
						} else {
							$b=ord($data[$fromOffset++]);
							$newData->putpixel($x++,$y,$b >> 4);
						}
						$t++;
					}
					$fromOffset+=$padding;
			}
		} else { // compressed run
			$b=ord($data[$fromOffset++]);
			$t=0;
			while ($t<$count) {
				if ($t%2) {
					$newData->putpixel($x++,$y,$b & 0x0F);
				} else {
					$newData->putpixel($x++,$y,$b >> 4);
				}
				$t++;
			}
		}
	}
	
	return $newData->data;
	
} // function winRLE4Decode

?>

There’s got to be an easier way…

I always thought PHP with gd could do that?
Dynamic bitmap graphics with PHP and gd

the GD module for php can read the following formats:

png, jpeg, gif, gd, gd2, xbm, xpm… and even wbmp…

But no plain jane windows BMP. (WBMP is to BMP as javascript is to java, they share the same letters but are two unrelated topics entirely).

See… wbmp, the "Wireless BMP" has nothing whatsoever to do with [url=http://en.wikipedia.org/wiki/BMP_file_format]Windows BMP

So I’m having to write a BMP importer… though I’m looking at some of the other formats, and starting to think I might be better off (and this is a laugh) using imagecreatefromstring and giving my data string a proper header for some other format… though it seems none of the supported formats actually support unencoded 24 bit bitmap data – that’s almost laughable.

Though the person I’m doing this for also asked if I can make it handle TGA and TIFF… ARGH!!!

Thanks
I always thought it was short for WindowsBMP, not actually .wbmp (really what uses that).
As for TIFF have fun…

Indeed, I was a bit shocked to find out that it WASN’T real BMP’s, and instead was the outdated 2 bit rimshot monochrome format from back in the early days of mobile computing.

… but then the same thing could be said about XPM and XBM – serious 80’s flashback at that point.

Yes, true (I only have used XPM images).