/* Library for drawing sprites - Blit.c - By Blah on gp32x.de based on code by Pea (pea.co.nz)
*
* Note - Sprites must be rotated to framebuffer orientation.
* Also, I reccomend viewing this code in a good IDE, or it'll get confusing.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Changelog:
*
* June 10 2005 - Blah on gp32x.de
* First release
*
* June 11 2005 - Blah on gp32x.de
* Added transparency and headerless graphics support
*
* June 18 2005 - Blah on gp32x.de
* Added GNU GPL License
* Added basic tile support
* Added basic 8bit (256 color) graphics support
* Changed function names to make more sense (and be shorter and easier on the typist)
*
* Todo:
* Clean up commenting
* Add support for blitting 8bit graphics in 16bit mode
* Add 8bit transparency support
* Add rotation support
* Add scaling support
* Add "hardware scrolling" (changing framebuffer pointer)
* Add DMA support?
* Fix bugs?
* Anything else?
*/
#define max(a, B) ((a) > (B) ? (a) : (B))
#define min(a, B) ((a) < (B) ? (a) : (B))
void gp_blit_16( u16 *sprite, short put_x, short put_y, unsigned width, unsigned height,u16 *framebuffer){
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 ((put_x>=LCD_WIDTH) || (put_y>=LCD_HEIGHT)){ return; }
tx = put_x+width; ty = put_y+height;
if ((tx<0) || (ty<0)){ return; }
// The rest of the coordinates are calculated based on a screen rotation
// of 90 degrees clockwise.
ty = put_y;
put_y = put_x;
put_x = LCD_HEIGHT - ty - height;
tx = put_x+height; ty = put_y+width;
// Positional offsets
px = max( (short)0, put_x );
py = max( (short)0, put_y );
// Visible height
if (put_y<0){
vh = min( ty, (short)LCD_WIDTH );
}else{
if (ty>LCD_WIDTH){
vh = width - (ty-LCD_WIDTH);
}else{
vh = width;
}
}
// Visible width
if (put_x<0){
vw = min( tx, (short)LCD_HEIGHT );
}else{
if (tx>LCD_HEIGHT){
vw = height - (tx-LCD_HEIGHT);
}else{
vw = height;
}
}
}
void gp_blit_8(unsigned char *sprite, short put_x, short put_y, unsigned width, unsigned height,unsigned char *framebuffer){
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 ((put_x>=LCD_WIDTH) || (put_y>=LCD_HEIGHT)){ return; }
tx = put_x+width; ty = put_y+height;
if ((tx<0) || (ty<0)){ return; }
// The rest of the coordinates are calculated based on a screen rotation
// of 90 degrees clockwise.
ty = put_y;
put_y = put_x;
put_x = LCD_HEIGHT - ty - height;
tx = put_x+height; ty = put_y+width;
// Positional offsets
px = max( (short)0, put_x );
py = max( (short)0, put_y );
// Visible height
if (put_y<0){
vh = min( ty, (short)LCD_WIDTH );
}else{
if (ty>LCD_WIDTH){
vh = width - (ty-LCD_WIDTH);
}else{
vh = width;
}
}
// Visible width
if (put_x<0){
vw = min( tx, (short)LCD_HEIGHT );
}else{
if (tx>LCD_HEIGHT){
vw = height - (tx-LCD_HEIGHT);
}else{
vw = height;
}
}
// Data offset
doff = ( height * max( 0, -put_y ) ) + max( 0, -put_x ); // removed + 6, no sprite header here.
// Canvas data offset
coff = py*LCD_HEIGHT + px;
// Draw visible pixels only
for ( yy=0; yy<vh; yy++ ){
memcpy( &framebuffer[coff], &sprite[doff], vw);
doff += height;
coff += LCD_HEIGHT;
}
}
typedef struct tGP_alphaLUT{
unsigned char values[1024];
}tGP_alphaLUT;
static tGP_alphaLUT gp_alphaLUT[32];
void gp_createAlphaLUT( void ){
tGP_alphaLUT *level;
char a,d,s;
// Create alpha look up table. The look up table is implemented
// as a series of shorter tables in the hope that it is cached.
for (a=0; a<32; a++){ // alpha
level = &gp_alphaLUT[a];
for (s=0; s<32; s++){ // source
for (d=0; d<32; d++){ // destination
level->values[(s<<5)+d] = ((s*a)+(d*(31-a)))/31;
}
}
}
}
void gp_blitA_16(u16 *sprite,short x, short y, unsigned height, unsigned width, u16 *framebuffer, unsigned char alpha){
// If alpha is max, render fast method
if (alpha>30){
gp_blit_16(sprite,x,y,width,height,framebuffer);
return;
}
// If no alpha, simply exit (none of source is rendered)
if (alpha==0){
return;
}
unsigned short xx,yy;
unsigned short vw,vh;
unsigned short *doff, *coff;
short dstep, cstep;
unsigned short px,py;
short tx,ty;
unsigned short scolour, dcolour;
unsigned short rs, gs, bs;
unsigned char rd, gd, bd;
unsigned char ra, ga, ba;
tGP_alphaLUT *level;
// If rect is off the screen, exit
if ((x>=LCD_WIDTH) || (y>=LCD_HEIGHT)){ return; }
tx = x+width; ty = y+height;
if ((tx<0) || (ty<0)){ return; }
// The rest of the coordinates are calculated based on a screen rotation
// of 90 degrees clockwise.
ty = y;
y = x;
x = LCD_HEIGHT - ty - height;
tx = x+height; ty = y+width;
// Positional offsets
px = max( 0, x );
py = max( 0, y );
// Visible height
if (y<0){
vh = min( ty, LCD_WIDTH );
}else{
if (ty>LCD_WIDTH){
vh = width - (ty-LCD_WIDTH);
}else{
vh = width;
}
}
// Visible width
if (x<0){
vw = min( tx, LCD_HEIGHT );
}else{
if (tx>LCD_HEIGHT){
vw = height - (tx-LCD_HEIGHT);
}else{
vw = height;
}
}
// Data offset
doff = sprite + ( height * max( 0, -y ) ) + max( 0, -x ); //removed +6, no sprite header here.
dstep = height - vw;
// Canvas data offset
coff = framebuffer + (py*LCD_HEIGHT + px);
cstep = LCD_HEIGHT - vw;
// Get the row of the LUT we need
level = &gp_alphaLUT[alpha];
// Draw visible pixels only
for ( yy=0; yy<vh; yy++ ){
for (xx=0; xx<vw; xx++ ){
// Get source colour components pre-multiplied by 32 (for LUT)
scolour = *doff;
rs = (((scolour) >> 6) & 0x03E0);
gs = (((scolour) >> 1) & 0x03E0);
bs = (((scolour) << 4) & 0x03E0);
// Get existing (destination) colour components
dcolour = *coff;
rd = (((dcolour) >> 11) & 0x1F);
gd = (((dcolour) >> 6) & 0x1F);
bd = (((dcolour) >> 1) & 0x1F);
// Blend using LUT
ra = level->values[rs+rd];
ga = level->values[gs+gd];
ba = level->values[bs+bd];
// Combine colours and set
*coff = ((ra<<11)+(ga<<6)+(ba<<1));
coff++; doff++;
}
coff += cstep;
doff += dstep;
}
}
void gp_blitAT_16(u16 *sprite,short x, short y, unsigned height, unsigned width, u16 *framebuffer, u16 trans, unsigned char alpha){
unsigned short xx,yy;
unsigned short vw,vh;
unsigned short *doff, *coff;
short dstep, cstep;
unsigned short px,py;
short tx,ty;
unsigned short scolour, dcolour;
unsigned short rs, gs, bs;
unsigned char rd, gd, bd;
unsigned char ra, ga, ba;
tGP_alphaLUT *level;
// If rect is off the screen, exit
if ((x>=LCD_WIDTH) || (y>=LCD_HEIGHT)){ return; }
tx = x+width; ty = y+height;
if ((tx<0) || (ty<0)){ return; }
// The rest of the coordinates are calculated based on a screen rotation
// of 90 degrees clockwise.
ty = y;
y = x;
x = LCD_HEIGHT - ty - height;
tx = x+height; ty = y+width;
// Positional offsets
px = max( 0, x );
py = max( 0, y );
// Visible height
if (y<0){
vh = min( ty, LCD_WIDTH );
}else{
if (ty>LCD_WIDTH){
vh = width - (ty-LCD_WIDTH);
}else{
vh = width;
}
}
// Visible width
if (x<0){
vw = min( tx, LCD_HEIGHT );
}else{
if (tx>LCD_HEIGHT){
vw = height - (tx-LCD_HEIGHT);
}else{
vw = height;
}
}
// Data offset
doff = sprite + ( height * max( 0, -y ) ) + max( 0, -x ); //removed +6, no sprite header here.
dstep = height - vw;
// Canvas data offset
coff = framebuffer + (py*LCD_HEIGHT + px);
cstep = LCD_HEIGHT - vw;
// Get the row of the LUT we need
level = &gp_alphaLUT[alpha];
// Draw visible pixels only
for ( yy=0; yy<vh; yy++ ){
for (xx=0; xx<vw; xx++ ){
// Get source colour components pre-multiplied by 32 (for LUT)
scolour = *doff;
if (scolour != trans) {
rs = (((scolour) >> 6) & 0x03E0);
gs = (((scolour) >> 1) & 0x03E0);
bs = (((scolour) << 4) & 0x03E0);
// Get existing (destination) colour components
dcolour = *coff;
rd = (((dcolour) >> 11) & 0x1F);
gd = (((dcolour) >> 6) & 0x1F);
bd = (((dcolour) >> 1) & 0x1F);
// Blend using LUT
ra = level->values[rs+rd];
ga = level->values[gs+gd];
ba = level->values[bs+bd];
// Combine colours and set
*coff = ((ra<<11)+(ga<<6)+(ba<<1));
}
coff++; doff++;
}
coff += cstep;
doff += dstep;
}
}
void gp_blitT_16(u16 *sprite,short x, short y, unsigned height, unsigned width, u16 *framebuffer, u16 trans){
unsigned short xx,yy;
unsigned short vw,vh;
unsigned short *doff, *coff;
short dstep, cstep;
unsigned short px,py;
short tx,ty;
unsigned short color;
// If rect is off the screen, exit
if ((x>=LCD_WIDTH) || (y>=LCD_HEIGHT)){ return; }
tx = x+width; ty = y+height;
if ((tx<0) || (ty<0)){ return; }
// The rest of the coordinates are calculated based on a screen rotation
// of 90 degrees clockwise.
ty = y;
y = x;
x = LCD_HEIGHT - ty - height;
tx = x+height; ty = y+width;
// Positional offsets
px = max( 0, x );
py = max( 0, y );
// Visible height
if (y<0){
vh = min( ty, LCD_WIDTH );
}else{
if (ty>LCD_WIDTH){
vh = width - (ty-LCD_WIDTH);
}else{
vh = width;
}
}
// Visible width
if (x<0){
vw = min( tx, LCD_HEIGHT );
}else{
if (tx>LCD_HEIGHT){
vw = height - (tx-LCD_HEIGHT);
}else{
vw = height;
}
}
// Data offset
doff = sprite + ( height * max( 0, -y ) ) + max( 0, -x ); //removed +6, no sprite header here.
dstep = height - vw;
// Canvas data offset
coff = framebuffer + (py*LCD_HEIGHT + px);
cstep = LCD_HEIGHT - vw;
// Draw visible pixels only
for ( yy=0; yy<vh; yy++ ){
for (xx=0; xx<vw; xx++ ){
color = *doff;
if (color != trans) {
*coff = color;
}
coff++; doff++;
}
coff += cstep;
doff += dstep;
}
}
void gp_blitH_16(u16 *sprite, short put_x, short put_y, u16 *framebuffer){
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
width = sheader->size_x;
gp_blit_16(sprite+6, put_x, put_y, width, height, framebuffer);
}
void gp_blitH_8(unsigned char *sprite, short put_x, short put_y, unsigned char *framebuffer){
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
width = sheader->size_x;
gp_blit_8(sprite+12, put_x, put_y, width, height, framebuffer);
}
void gp_blitHA_16(u16 *sprite,short x, short y, u16 *framebuffer, unsigned char alpha){
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
width = sheader->size_x;
gp_blitA_16(sprite + 6, x, y, height, width, framebuffer, alpha);
}
void gp_blitHAT_16(u16 *sprite,short x, short y, u16 *framebuffer, u16 trans, unsigned char alpha){
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
width = sheader->size_x;
gp_blitAT_16(sprite + 6, x, y, (int) height, (int) width, framebuffer, trans,alpha);
}
void gp_blitHT_16(u16 *sprite,short x, short y, u16 *framebuffer, u16 trans){
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
width = sheader->size_x;
gp_blitT_16(sprite + 6, x, y, height, width, framebuffer, trans);
}
void gp_blitTile_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y, unsigned height, u16 *framebuffer) {
int offset;
offset = plot_number * x_len * height;
gp_blit_16(sprite+offset, put_x, put_y, x_len, height, framebuffer);
}
void gp_blitTile_8(unsigned char *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y, unsigned height, unsigned char *framebuffer) {
int offset;
offset = plot_number * x_len * height;
gp_blit_8(sprite+offset, put_x, put_y, x_len, height, framebuffer);
}
void gp_blitTileA_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y, unsigned height, u16 *framebuffer, unsigned char alpha) {
int offset;
offset = plot_number * x_len * height;
gp_blitA_16(sprite+offset, put_x, put_y, x_len, height, framebuffer, alpha);
}
void gp_blitTileT_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y, unsigned height, u16 *framebuffer, u16 trans) {
int offset;
offset = plot_number * x_len * height;
gp_blitT_16(sprite+offset, put_x, put_y, x_len, height, framebuffer, trans);
}
void gp_blitTileAT_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y, unsigned height, u16 *framebuffer, u16 trans, unsigned char alpha){
int offset;
offset = plot_number * x_len * height;
gp_blitAT_16(sprite+offset, put_x, put_y, x_len, height, framebuffer, trans, alpha);
}
void gp_blitTileH_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y,u16 *framebuffer) {
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
gp_blitTile_16(sprite+6, x_len, plot_number, put_x, put_y, height, framebuffer);
}
void gp_blitTileH_8(unsigned char *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y,unsigned char *framebuffer) {
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
gp_blitTile_8(sprite+12, x_len, plot_number, put_x, put_y, height, framebuffer);
}
void gp_blitTileHA_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y,u16 *framebuffer, unsigned char alpha) {
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
gp_blitTileA_16(sprite+6, x_len, plot_number, put_x, put_y, height, framebuffer, alpha);
}
void gp_blitTileHT_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y,u16 *framebuffer, u16 trans) {
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
gp_blitTileT_16(sprite+6, x_len, plot_number, put_x, put_y, height, framebuffer, trans);
}
void gp_blitTileHAT_16(u16 *sprite, unsigned x_len, unsigned plot_number, short put_x, short put_y,u16 *framebuffer, u16 trans, unsigned char alpha) {
unsigned width, height;
SHEADER *sheader;
sheader = (SHEADER*) sprite;
height = sheader->size_y;
gp_blitTileAT_16(sprite+6, x_len, plot_number, put_x, put_y, height, framebuffer, trans, alpha);
}