Re: [AD] stretch_blit bug

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


On Thu, 2007-04-26 at 09:28 +1000, Peter Wang wrote:
> Would either of the new stretch_blit implementations be faster if they
> didn't use indirect function calls?  I didn't think C compilers were
> generally smart enough to specialise them.
> 
> Peter

Yes. The last one I posted had everything in a macro. I'm attaching
another one now that fixes a bug where stretching to smaller sizes
doesn't work. It's still a little slower than the current one in
Allegro. If anyone can see how to speed it up that would be good.
This one also does 8 bit and masked blits.
#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
}

static struct {
		int dx, dend; /* destination x, destination end */
		int size; /* pixel size */
		int xcstart; /* x counter start */
		int sw, dw; /* source and destination width */
		int sinc;
		int xcinc;
} _al_stretch;

#define DECLARE_STRETCHER(type, size, put, get) \
		ASSERT(dptr); \
		ASSERT(sptr); \
		int x; \
		int xc = _al_stretch.xcstart; \
		type* s = (type*)sptr; \
		for (x = _al_stretch.dx; x < _al_stretch.dend; x++, \
				s = (type*)((unsigned char*)s + _al_stretch.sinc*size)) { \
			put(dptr, get(s)); \
			dptr += size; \
			xc += _al_stretch.xcinc; \
			if (xc >= _al_stretch.dw) { \
      			s = (type *) ((unsigned char*)s + (size)); \
				xc -= _al_stretch.dw; \
			} \
		}

#define DECLARE_MASKED_STRETCHER(type, size, put, get, mask) \
		ASSERT(dptr); \
		ASSERT(sptr); \
		int x; \
		int xc = _al_stretch.xcstart; \
		type* s = (type*)sptr; \
		for (x = _al_stretch.dx; x < _al_stretch.dend; x++, \
				s = (type*)((unsigned char*)s + _al_stretch.sinc*size)) { \
			int color = get(s); \
			if (color != mask) \
				put(dptr, get(s)); \
			dptr += size; \
			xc += _al_stretch.xcinc; \
			if (xc >= _al_stretch.dw) { \
      			s = (type *) ((unsigned char*)s + (size)); \
				xc -= _al_stretch.dw; \
			} \
		}

#ifdef ALLEGRO_COLOR8
static void stretch_line8(uintptr_t dptr, unsigned char *sptr)
{
   DECLARE_STRETCHER(unsigned char, 1, bmp_write8, *);
}

static void masked_stretch_line8(uintptr_t dptr, unsigned char *sptr)
{
   DECLARE_MASKED_STRETCHER(unsigned char, 1, bmp_write8, *, 0);
}
#endif

#ifdef ALLEGRO_COLOR16
static void stretch_line15(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_STRETCHER(unsigned short, _al_stretch.size, bmp_write16, *);
}

static void stretch_line16(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_STRETCHER(unsigned short, _al_stretch.size, bmp_write16, *);
}

static void masked_stretch_line15(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_MASKED_STRETCHER(unsigned short, _al_stretch.size, bmp_write16, *, MASK_COLOR_15);
}

static void masked_stretch_line16(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_MASKED_STRETCHER(unsigned short, _al_stretch.size, bmp_write16, *, MASK_COLOR_16);
}
#endif

#ifdef ALLEGRO_COLOR24
static void stretch_line24(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_STRETCHER(unsigned char, _al_stretch.size, bmp_write24, READ3BYTES);
}

static void masked_stretch_line24(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_MASKED_STRETCHER(unsigned char, _al_stretch.size, bmp_write24, READ3BYTES, MASK_COLOR_24);
}
#endif

#ifdef ALLEGRO_COLOR32
static void stretch_line32(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_STRETCHER(uint32_t, _al_stretch.size, bmp_write32, *);
}

static void masked_stretch_line32(uintptr_t dptr, unsigned char* sptr)
{
	DECLARE_MASKED_STRETCHER(uint32_t, _al_stretch.size, bmp_write32, *, MASK_COLOR_32);
}
#endif

void _al_stretch_blit(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh,
	int masked)
{
	int y; /* current destination y */
	int yc; /* y counter */
	int sxofs, dxofs; /* start offsets */

	void (*stretch_line)(uintptr_t, unsigned char*);

	ASSERT(bitmap_color_depth(source) == bitmap_color_depth(destination));
	ASSERT(source != destination);

	if (masked) {
			switch (bitmap_color_depth(source)) {
					case 8:
							stretch_line = masked_stretch_line8;
							_al_stretch.size = 1;
							break;
					case 15:
							stretch_line = masked_stretch_line15;
							_al_stretch.size = 2;
							break;
					case 16:
							stretch_line = masked_stretch_line16;
							_al_stretch.size = 2;
							break;
					case 24:
							stretch_line = masked_stretch_line24;
							_al_stretch.size = 3;
							break;
					default:
							stretch_line = masked_stretch_line32;
							_al_stretch.size = 4;
							break;
			}
	}
	else {
			switch (bitmap_color_depth(source)) {
					case 8:
							stretch_line = masked_stretch_line8;
							_al_stretch.size = 1;
							break;
					case 15:
							stretch_line = stretch_line15;
							_al_stretch.size = 2;
							break;
					case 16:
							stretch_line = stretch_line16;
							_al_stretch.size = 2;
							break;
					case 24:
							stretch_line = stretch_line24;
							_al_stretch.size = 3;
							break;
					default:
							stretch_line = stretch_line32;
							_al_stretch.size = 4;
							break;
			}
	}

	yc = sh/2;
	sxofs = sx * _al_stretch.size;
	dxofs = dx * _al_stretch.size;

	_al_stretch.dx = dx;
	_al_stretch.dend = dx + dw;
	_al_stretch.sw = sw;
	_al_stretch.dw = dw;
	_al_stretch.sinc = sw / dw;
	_al_stretch.xcinc = sw % dw;
	_al_stretch.xcstart = 0;

	bmp_select(destination);

	for (y = dy; y < dy + dh; y++) {
		(*stretch_line)(bmp_write_line(destination, y) + dxofs, source->line[sy] + sxofs);
		yc += sh;
		while (yc > dh) {
			sy++;
			yc -= dh;
		}
	}
	
	bmp_unwrite_line(destination);
}

void my_stretch(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
		_al_stretch_blit(source, destination, sx, sy, sw, sh, dx, dy, dw, dh, 0);
}

void my_masked_stretch(BITMAP *source, BITMAP *destination,
    int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh)
{
		_al_stretch_blit(source, destination, sx, sy, sw, sh, dx, dy, dw, dh, 1);
}

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(32);
	set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

	buffer = create_bitmap(640, 480);
	sprite = load_bitmap("sprite.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);

	return 0;
}


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