shithub: rd

ref: 358c45c5326b463845e900fd8f16475c85a8d681
dir: rd/rle.c

View raw version
/*
 * 2.2.9.1.1.3.1.2.4 RLE Compressed Bitmap Stream (RLE_BITMAP_STREAM)
 *	http://msdn.microsoft.com/en-us/library/cc240895.aspx
 * 3.1.9 Interleaved RLE-Based Bitmap Compression
 *	http://msdn.microsoft.com/en-us/library/dd240593.aspx
*/
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"

enum
{
	Bits4=	15,
	Bits5=	31,
};

enum
{
	DWhite		= 0xFFFFFFFF,
};

enum
{
	Bg,
	Fg,
	FgS,
	Dith,
	Fill,
	Mix,
	MixS,
	Lit,
	Spec,
	Mix3,
	Mix5,
	Wpix,
	Bpix,
};

enum
{
	Std= 0,
	Ext= 1,
};

static int
decode[2][16] =
{
[Std]={
		Bg, 	Bg, 	Fg, 	Fg,	Mix,	Mix,	Fill,	Fill,
		Lit,	Lit,	-1, 	-1,	FgS,	MixS,	Dith,	-1,
	},
[Ext]={
		Bg, 	Fg, 	Mix,	Fill,	Lit,	-1, 	FgS,	MixS,
		Dith,	Mix3,	Mix5,	-1, 	-1, 	Wpix,	Bpix,	-1,
	}
};

static void*
memfill(void *a1, ulong n1, void *a2, ulong n2)
{
	char *s1, *s2, *e1, *e2;

	if((long)n1 < 0 || (long)n2 <= 0)
		abort();
	s1 = a1;
	s2 = a2;
	e1 = s1+n1;
	e2 = s2+n2;
	while(s1 < e1){
		*s1++ = *s2++;
		if(s2 >= e2)
			s2 = a2;
	}
	return a1;
}

static void*
memxor(void *a1, void *a2, ulong n)
{
	char *s1, *s2;

	if((long)n < 0)
		abort();
	s1 = a1;
	s2 = a2;
	while(n > 0){
		*s1++ ^= *s2++;
		n--;
	}
	return a1;
}

uchar*
unrle(uchar* d, int nd, uchar* s, int ns, int bpl, int pixelsize)
{
	int t, hdr, code, bits, len, wasline1, wasbg;
	uchar pen[4], dpen[8], wpen[4], *p, *ep, *wp, *we;
	uint sreg;

	p = s;
	ep = s+ns;
	wp = d;
	we = d+nd;
	wasbg = 0;
	wasline1 = 1;
	iputl(pen, DWhite);
	iputl(wpen, DWhite);

	while(p < ep){
		hdr = *p++;
		code = hdr>>4;
		if(code != Bits4){
			t = decode[Std][code];
			if(code>>2 == 3)
				bits = Bits4;
			else
				bits = Bits5;
			len = hdr&bits;
			if(t==Mix || t==MixS){
				if(len == 0)
					len = 1+*p++;
				else
					len *= 8;
			}else{
				if(len == 0)
					len = 1+bits+*p++;
			}
		}else{
			code = hdr&Bits4;
			if(code < 9){
				len = igets(p);
				p += 2;
			}else
				len = 0;
			t = decode[Ext][code];
		}
		len *= pixelsize;
		if(wp+len > we){
		   Overrun:
			werrstr("unrle: output buffer is %zd bytes short", (intptr)(wp+len-we));
			return nil;
		}

		if(t != Bg)
			wasbg = 0;
		if(wasline1 && wp-bpl >= d){
			wasline1 = 0;
			wasbg = 0;
		}

		switch(t){
		default:
			werrstr("unrle: bad decode");
			return nil;
		case Lit:
			memmove(wp, p, len);
			p += len;
			break;
		case Bg:
			if(wasbg){
				memmove(wp, pen, pixelsize);
				if(wp-bpl >= d)
					memxor(wp, wp-bpl, pixelsize);
				wp += pixelsize;
				len -= pixelsize;
			}
			if(wp-bpl >= d){
				while(len>bpl){
					memmove(wp, wp-bpl, bpl);
					wp += bpl;
					len -= bpl;
				}
				memmove(wp, wp-bpl, len);
			}else
				memset(wp, len, 0);
			wasbg = 1;
			break;
		case FgS:
			memmove(pen, p, pixelsize);
			p += pixelsize;
			/* fall through */
		case Fg:
			memfill(wp, len, pen, pixelsize);
			if(wp-bpl >= d)
				memxor(wp, wp-bpl, len);
			break;
		case Mix3:
			sreg = 3;
			bits = 8;
			len = 8*pixelsize;
			goto case_MixAll;
		case Mix5:
			sreg = 5;
			bits = 8;
			len = 8*pixelsize;
			goto case_MixAll;
		case MixS:
			memmove(pen, p, pixelsize);
			p += pixelsize;
			/* fall through */
		case Mix:
			sreg = 0;
			bits = 0;
			/* fall through */
		case_MixAll:
			if(wp+len > we)
				goto Overrun;
			while(len > 0){
				if(bits == 0){
					sreg = *p++;
					bits = 8;
				}
				if(sreg&1){
					memmove(wp, pen, pixelsize);
					if(wp-bpl >= d)
						memxor(wp, wp-bpl, pixelsize);
				}else{
					if(wp-bpl >= d)
						memmove(wp, wp-bpl, pixelsize);
					else
						memset(wp, pixelsize, 0);
				}
				wp += pixelsize;
				len -= pixelsize;
				sreg >>= 1;
				bits--;
			}
			break;
		case Fill:
			memmove(dpen, p, pixelsize);
			p += pixelsize;
			memfill(wp, len, dpen, pixelsize);
			break;
		case Dith:
			len *= 2;
			if(wp+len > we)
				goto Overrun;
			memmove(dpen, p, pixelsize);
			memmove(dpen+pixelsize, p+pixelsize, pixelsize);
			p += 2*pixelsize;
			memfill(wp, len, dpen, 2*pixelsize);
			break;
		case Wpix:
			len = pixelsize;
			if(wp+len > we)
				goto Overrun;
			memmove(wp, wpen, pixelsize);
			break;
		case Bpix:
			len = pixelsize;
			if(wp+len > we)
				goto Overrun;
			memset(wp, pixelsize, 0);
			break;
		}
		wp += len;
	}
	return wp;
}