Re: [AD] stretch_blit bug

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Attached is an improved version of Elias' stretching routines, with a
benchmark program. It's not as fast as stretch_blit, but it's more
accurate and can blit between bitmaps of different color depths.

On Sun, 2007-04-22 at 09:30 -0600, Trent Gamblin wrote:
> I've attached the version of this code that I'm using that does high and
> truecolor modes. I didn't implement 8 bit but it should be easy. I've
> separated it into four functions to handle all the cases of whether the
> bitmaps are memory or video. It was significantly slower as one
> function.

#include <allegro.h>
#include <stdio.h>

#ifdef __linux__
#include <sys/time.h>
#else
#include <allegro.h>
#include <winalleg.h>
#endif

long currentTimeMillis()
{
#ifdef __linux__
	struct timeval tv;
	gettimeofday(&tv, 0);
	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
#else
	return timeGetTime();
#endif
}

unsigned char* memory_get_address(BITMAP* bmp, int y)
{
	return bmp->line[y];
}

unsigned char* video_get_read_address(BITMAP* bmp, int y)
{
	return (unsigned char*)bmp_read_line(bmp, y);
}

unsigned char* video_get_write_address(BITMAP* bmp, int y)
{
	return (unsigned char*)bmp_write_line(bmp, y);
}

inline int memory_get15_or_16(unsigned char* addr)
{
	return *((short *)addr);
}

inline int memory_get24(unsigned char* addr)
{
	return *addr | (*(addr+1) << 8) | (*(addr+2) << 16);
}

inline int memory_get32(unsigned char* addr)
{
	return *((int *)addr);
}

inline void memory_put15_or_16(unsigned char* addr, int color)
{
	*((short *)addr) = color;
}

inline void memory_put24(unsigned char* addr, int color)
{
	*addr = color & 0xff;
	*(addr+1) = (color >> 8) & 0xff;
	*(addr+2) = (color >> 16) & 0xff;
}

inline void memory_put32(unsigned char* addr, int color)
{
	*((int *)addr) = color;
}

inline int video_get15_or_16(unsigned char* addr)
{
	return bmp_read16((long)addr);
}

inline int video_get24(unsigned char* addr)
{
	return bmp_read24((long)addr);
}

inline int video_get32(unsigned char* addr)
{
	return bmp_read32((long)addr);
}

inline void video_put15_or_16(unsigned char* addr, int color)
{
	bmp_write16((long)addr, color);
}

inline void video_put24(unsigned char* addr, int color)
{
	bmp_write24((long)addr, color);
}

inline void video_put32(unsigned char* addr, int color)
{
	bmp_write32((long)addr, color);
}

void my_stretch_vv(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
	int x, y; // current destination pixel
	int xc, yc; // counters
	int xx; // current source x
	int ss, ds; // pixel sizes

	unsigned char* sa, *da; // source and destination addresses

	unsigned char* (*get_source_address)(BITMAP* bmp, int y);
	unsigned char* (*get_destination_address)(BITMAP*, int y);
	void (*put)(unsigned char* addr, int color);
	int (*get)(unsigned char* addr);

	get_source_address = video_get_read_address;
	get_destination_address = video_get_write_address;

	switch (bitmap_color_depth(source)) {
	case 15:
	case 16:
		get = video_get15_or_16;
		ss = 2;
		break;
	case 24:
		get = video_get24;
		ss = 3;
		break;
	case 32:
		get = video_get32;
		ss = 4;
		break;
    	}

	switch (bitmap_color_depth(destination)) {
	case 15:
	case 16:
		put = video_put15_or_16;
		ds = 2;
		break;
	case 24:
		put = video_put24;
		ds = 3;
		break;
	case 32:
		put = video_put32;
		ds = 4;
		break;
	}

	yc = sh/2;

	for (y = dy; y < dy + dh; y++) {
		int n;
		bmp_select(source);
		sa = get_source_address(source, sy) + (sx * ss);
		bmp_select(destination);
		da = get_destination_address(destination, y) + (dx * ds);
		xc = sw/2;	
		for (x = dx; x < dx + dw; x++) {
			int color;
			bmp_select(source);
			color = get(sa);
			bmp_select(destination);
			put(da, color);
			da += ds;
			xc += sw;
			while (xc > dw) {
				sa += ss;
				xc -= dw;
			}
		}
		yc += sh;
		while (yc > dh) {
			sy++;
			yc -= dh;
		}
		bmp_unwrite_line(source);
		bmp_unwrite_line(destination);
	}
}

void my_stretch_vm(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
	int x, y; // current destination pixel
	int xc, yc; // counters
	int xx; // current source x
	int ss, ds; // pixel sizes

	unsigned char* sa, *da; // source and destination addresses

	unsigned char* (*get_source_address)(BITMAP* bmp, int y);
	unsigned char* (*get_destination_address)(BITMAP*, int y);
	void (*put)(unsigned char* addr, int color);
	int (*get)(unsigned char* addr);

	get_source_address = video_get_read_address;
	get_destination_address = memory_get_address;

	switch (bitmap_color_depth(source)) {
	case 15:
	case 16:
		get = video_get15_or_16;
		ss = 2;
		break;
	case 24:
		get = video_get24;
		ss = 3;
		break;
	case 32:
		get = video_get32;
		ss = 4;
		break;
    	}

	switch (bitmap_color_depth(destination)) {
	case 15:
	case 16:
		put = memory_put15_or_16;
		ds = 2;
		break;
	case 24:
		put = memory_put24;
		ds = 3;
		break;
	case 32:
		put = memory_put32;
		ds = 4;
		break;
	}

	yc = sh/2;

	bmp_select(source);

	for (y = dy; y < dy + dh; y++) {
		int n;
		sa = get_source_address(source, sy) + (sx * ss);
		da = get_destination_address(destination, y) + (dx * ds);
		xc = sw/2;	
		for (x = dx; x < dx + dw; x++) {
			put(da, get(sa));
			da += ds;
			xc += sw;
			while (xc > dw) {
				sa += ss;
				xc -= dw;
			}
		}
		yc += sh/2;
		while (yc > dh) {
			sy++;
			yc -= dh;
		}
	}

	bmp_unwrite_line(source);
}

void my_stretch_mv(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
	int x, y; // current destination pixel
	int xc, yc; // counters
	int xx; // current source x
	int ss, ds; // pixel sizes

	unsigned char* sa, *da; // source and destination addresses

	unsigned char* (*get_source_address)(BITMAP* bmp, int y);
	unsigned char* (*get_destination_address)(BITMAP*, int y);
	void (*put)(unsigned char* addr, int color);
	int (*get)(unsigned char* addr);

	get_source_address = memory_get_address;
	get_destination_address = video_get_write_address;

	switch (bitmap_color_depth(source)) {
	case 15:
	case 16:
		get = memory_get15_or_16;
		ss = 2;
		break;
	case 24:
		get = memory_get24;
		ss = 3;
		break;
	case 32:
		get = memory_get32;
		ss = 4;
		break;
    	}

	switch (bitmap_color_depth(destination)) {
	case 15:
	case 16:
		put = video_put15_or_16;
		ds = 2;
		break;
	case 24:
		put = video_put24;
		ds = 3;
		break;
	case 32:
		put = video_put32;
		ds = 4;
		break;
	}

	yc = sh/2;

	bmp_select(destination);

	for (y = dy; y < dy + dh; y++) {
		sa = get_source_address(source, sy) + (sx * ss);
		da = get_destination_address(destination, y) + (dx * ds);
		xc = sw/2;	
		for (x = dx; x < dx + dw; x++) {
			put(da, get(sa));
			da += ds;
			xc += sw;
			while (xc > dw) {
				sa += ss;
				xc -= dw;
			}
		}
		yc += sh;
		while (yc > dh) {
			sy++;
			yc -= dh;
		}
	}
	bmp_unwrite_line(destination);
}

void my_stretch_mm(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
	int x, y; // current destination pixel
	int xc, yc; // counters
	int xx; // current source x
	int ss, ds; // pixel sizes

	unsigned char* sa, *da; // source and destination addresses

	unsigned char* (*get_source_address)(BITMAP* bmp, int y);
	unsigned char* (*get_destination_address)(BITMAP*, int y);
	void (*put)(unsigned char* addr, int color);
	int (*get)(unsigned char* addr);

	get_source_address = get_destination_address = memory_get_address;

	switch (bitmap_color_depth(source)) {
	case 15:
	case 16:
		get = memory_get15_or_16;
		ss = 2;
		break;
	case 24:
		get = memory_get24;
		ss = 3;
		break;
	case 32:
		get = memory_get32;
		ss = 4;
		break;
    	}

	switch (bitmap_color_depth(destination)) {
	case 15:
	case 16:
		put = memory_put15_or_16;
		ds = 2;
		break;
	case 24:
		put = memory_put24;
		ds = 3;
		break;
	case 32:
		put = memory_put32;
		ds = 4;
		break;
	}

	yc = sh/2;

	for (y = dy; y < dy + dh; y++) {
		int n;
		sa = get_source_address(source, sy) + (sx * ss);
		da = get_destination_address(destination, y) + (dx * ds);
		xc = sw/2;	
		for (x = dx; x < dx + dw; x++) {
			put(da, get(sa));
			da += ds;
			xc += sw;
			while (xc > dw) {
				sa += ss;
				xc -= dw;
			}
		}
		yc += sh;
		while (yc > dh) {
			sy++;
			yc -= dh;
		}
	}
}

void my_stretch(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
	if (is_video_bitmap(source) && is_video_bitmap(destination)) {
		my_stretch_vv(source, destination, sx, sy, sw, sh, dx, dy, dw, dh);
	}
	else if (is_video_bitmap(source)) {
		my_stretch_vm(source, destination, sx, sy, sw, sh, dx, dy, dw, dh);
	}
	else if (is_video_bitmap(destination)) {
		my_stretch_mv(source, destination, sx, sy, sw, sh, dx, dy, dw, dh);
	}
	else {
		my_stretch_mm(source, destination, sx, sy, sw, sh, dx, dy, dw, dh);
	}
}

int main()
{
	const int count = 2500;
	const int w = 200;
	const int h = 200;
	BITMAP* buffer, *sprite;
	long start, end;
	int i;

	allegro_init();
	install_keyboard();
	set_color_depth(24);
	set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

	buffer = create_bitmap(640, 480);
	sprite = load_bitmap("test.pcx", 0);

	if (!buffer || !sprite) return 1;

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		stretch_blit(sprite, buffer, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();
	
	printf("mm stretch_blit took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		my_stretch(sprite, buffer, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();

	printf("mm my_stretch took %d millis\n", end - start);
	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		stretch_blit(sprite, screen, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();
	
	printf("mv stretch_blit took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		my_stretch(sprite, screen, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();

	printf("mv my_stretch took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		stretch_blit(screen, buffer, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();
	
	printf("vm stretch_blit took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		my_stretch(screen, buffer, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();

	printf("vm my_stretch took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		stretch_blit(screen, screen, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();
	
	printf("vv stretch_blit took %d millis\n", end - start);

	start = currentTimeMillis();

	for (i = 0; i < count; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		x = MAX(0, MIN(640-w, x));
		y = MAX(0, MIN(480-h, y));
		my_stretch(screen, screen, 0, 0, sprite->w, sprite->h,
				x, y, w, h);
	}

	end = currentTimeMillis();

	printf("vv my_stretch took %d millis\n", end - start);
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/