GP32 Change Gpbitblt By Gm_memcpy


JyCet

Member
Joined
Feb 23, 2004
Messages
469
Age
118
Location
France
Website
Visit site
Hi,

Everybody know gm_memcpy is faster than GpBitBlt, it's very easy to use it with a full screen:
GpBitBlt(NULL,&gpDraw[nflip],0,0,320,240,(unsigned char*)bg,0,0,320,240);
gm_memcpy(gpDraw[nflip].ptbuffer,bg,320*240);


But, does someone have already made a little function to replace completely GpBitBlt by gm_memcpy
Example i want to replace :
GpBitBlt(NULL,&gpDraw[nflip],posx,posy,32,32,(unsigned char*)spr_TILES,x,0,256,32);

I think i need to use gm_memcpy column by column, but how calculate the buffer origine ?
 
Due to the fact that the screen memory is strange, you can use this formula, to get the address of a pixel:
P(x,y) = start_of_screen_memory + (240 * x * ( 240 - y )) * (bpp / 8)

Warning
: Using gm_memcpy, cause sound to crash a lot :(

Then, you will have to copy column by column, and for sprite as far as i test it's faster to use GpBitBlt than gm_memcpy ;)
 
Hi,

My graphics routines do exactly that. They use Mr.Mirkos routines, and my own 'canvas' structure rather than a simple framebuffer pointer, but its not hard to change.

The calculate an offset into thje framebuffer, and an offset into the sprite data, and then copy a column at once using memcopy. If you do it right, use the optimised ASM memcopy from my site, which is WAY faster than standard memcpy (snippet: unknown author - Fast assembler memcpy routine).

A guide to understanding this function:

destination->data :the framebuffer pointer.
destination->width: 320
destination->height: 240
source->data: the sprite data (16bpp, prerotated)
source->width: width of sprite (BEFORE rotation)
source->height: height of sprite (BEFORE rotation)
coff: offset into framebuffer (canvas offset)
doff: offset into sprite (data offset)
vw: length of each 'column' in pixels (in 16bpp, each pixel is two bytes, hence the *2 in the memcpy routine)
frame: Just ignore this (pretend it is 0 - it is used for multiple frame sprite animation)

Code:
void gp_canvasPaste( tGP_canvas *destination, tGP_canvas *source, short x, short y, unsigned char frame ){
	unsigned short xx,yy;
	unsigned short vw,vh;
	unsigned long doff;
	unsigned long coff;
	unsigned short px,py;
	short tx,ty;
     
	// If rect is off the screen, exit
	if ((x>=destination->width) || (y>=destination->height)){ return; }
	tx = x+source->width; ty = y+source->height;
	if ((tx<0) || (ty<0)){ return; }
	if (frame>source->frames){ return; }
	
	// Determine frame to render
	if (frame<1){ frame=1; }
	frame-=1;
	
	// The rest of the coordinates are calculated based on a screen rotation
	// of 90 degrees clockwise.
	ty = y;
	y = x;
	x = destination->height - ty - source->height;
	tx = x+source->height; ty = y+source->width;
	
  // Positional offsets
  px = max( 0, x );
  py = max( 0, y );
  // Visible height
	if (y<0){ 
  vh = min( ty, destination->width );
	}else{
  if (ty>destination->width){
  	vh = source->width - (ty-destination->width);
  }else{
  	vh = source->width;
  }
  }
  // Visible width
	if (x<0){
  vw = min( tx, destination->height );
	}else{
  if (tx>destination->height){
  	vw = source->height - (tx-destination->height);
  }else{
  	vw = source->height;
  }
  }
  // Data offset (including frame offset)
  doff = ( source->height * max( 0, -y ) ) + max( 0, -x ) + (frame*source->width*source->height);
  // Canvas data offset
  coff = py*destination->height + px;

  // Draw visible pixels only
	for ( yy=0; yy<vh; yy++ ){
  memcpy( (unsigned short*)&destination->data[coff], (unsigned short*)&source->data[doff], vw*2 );
  doff += source->height;
  coff += destination->height;
	}
	
	// Set flag to indicate that canvas has been drawn to
	destination->redraw=1;
}

EDIT: This routine handles all cases:
1) Sprite fits entirely in the screen
2) Sprite overlaps one or more borders (e.g. is only half on screen)
3) Sprite is larger than the screen, and two or more sides of the sprite are 'cropped'. e.g. a 640x480 sprite moving around, and you can only see part of it.
 
It needs to be if you want to cover all those bases:

1) Sprite fits entirely in the screen
2) Sprite overlaps one or more borders (e.g. is only half on screen)
3) Sprite is larger than the screen, and two or more sides of the sprite are 'cropped'. e.g. a 640x480 sprite moving around, and you can only see part of it.

But fear not, it is extremely fast.
 
What happened to your site? o_O It's not there.

Well anyway, I'm interested on how you would use the multi-frame sprites.
Also, how should I handle sprites + bg? The way I was doing it is that I had 3 8bpp buffers: one buffer was the BG, and I was just using a memory copy to "clear" one of the double buffers for sprite drawing with the bg buffer, instead of just blanking it out. Is there a better way to do this?

Also, I don't know if some games just redraw the whole screen every frame... (bg and sprites).
 
Drag posted on Apr 26 2005 at 09:56 PM said:
Also, I don't know if some games just redraw the whole screen every frame... (bg and sprites).
That all depends on how much needs redrawing.

Some games use a static background and just redraw it where sprites have been then draw sprites over that.
Some start again from scratch by copying the bg into the frame buffer and drawing sprites over.
And then there are the tile based games which create the background from smaller blocks.
 
Last edited by a moderator:
I'm probably just going to have to redraw the entire screen, bg and sprites, each frame, due to the way my game is. But hey, if 3D engines work without problems (though, I don't know how the FPS is), I should be able to do this without much hassle. :) It'd be nice if that fast asm framebuffer clearer wasn't for 16bpp mode... I'll just modify it. :p
 
re: Frames

My routines handle multiple 'frames' by placing them in one large line horizontally. This works very well because once the routines that load the graphics rotate them by 90 degrees to match the framebuffer, each 'frame' within the graphic is in its own seperate block of memory so the drawing routines don't lose any speed. All frames are the same size, but can have different widths from heights (e.g. 32x32 is ok, but so is 100x80 etc).

EDIT: Here is an example I made:
explosion_sprite1.jpg


For example, I have an image with 4 frames of 32x32 pixels each. The entire image is 128x32 pixels (w*h). I then load it like this:

Code:
int res;
tGP_canvas *mySprite;

mySprite = gp_canvasLoadFromGif( 'dev0:\\GPMM\\mySprite.gif', 1, 4, &res );

This then loads mySprite.gif, rotates it 90 (thats what the '1' is for - '0' if it is pre-rotated already), and tells it there are 4 frames.

Now whenever I draw the sprite, I can specify which frame to draw like this:

Code:
gp_canvasPaste( lcd->canvas, mySprite, x,y, 3 ); // draw frame 3

I also have another struct called a tGP_animation which works on a scripting type language like this:
Code:
frame 1,5; // Show frame 1 for 5 frames
frame 2,3; // Show frame 2 for 3 frames
walk 3-10,3; // Show frames 3 through 10 for 3 frames each
loop 5
  frame 11,3;
  frame 12,4;
end loop;
stop;

And you use it just by calling 'gp_animStep( animation )' every game loop and then using it to specify the frame of the canvas to draw. Each sprite has its own animation object.

Code:
gp_animStep( animation );
gp_canvasPaste( lcd->canvas, mySprite, x,y, animation->currentFrame );

re: sprites + bg

Like rich says - it depends on your application. In GPDesktop (WIP) the mouse pointer is a canvas object with multiple frames - one for each mouse pointer type such as normal, hourglass, scroll etc. The very first frame is actually a 'swap' frame. Before I draw the mouse, I copy the background directly under the pointer to the 'swap' frame, and then draw the mouse. When the mouse moves, I paste this swap-frame back, and then do it again.

What I would do for loads of action is to hold the background in one canvas, and just redraw the whole scene each time.

re: gp32.co.nz

My web hosts screwed it. Its back now though :)
 
Back
Top