diff --git a/src/video/sprite.cpp b/src/video/sprite.cpp new file mode 100644 index 000000000..2c7f9d135 --- /dev/null +++ b/src/video/sprite.cpp @@ -0,0 +1,1812 @@ +// ___________ _________ _____ __ +// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ +// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | +// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| +// \/ \/ \/ \/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// FreeCraft - A free fantasy real time strategy game engine +// +/**@name sprite.c - The general sprite functions. */ +/* +** (c) Copyright 2000 by Lutz Sammer +** +** $Id$ +*/ + +//@{ + +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> + +#include "freecraft.h" +#include "video.h" + +/*---------------------------------------------------------------------------- +-- Declarations +----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- +-- Externals +----------------------------------------------------------------------------*/ + +extern int ClipX1; /// current clipping top left +extern int ClipY1; /// current clipping top left +extern int ClipX2; /// current clipping bottom right +extern int ClipY2; /// current clipping bottom right + +#ifdef DEBUG +extern unsigned AllocatedGraphicMemory; +extern unsigned CompressedGraphicMemory; +#endif + +/*---------------------------------------------------------------------------- +-- Variables +----------------------------------------------------------------------------*/ + +local GraphicType GraphicSprite8Type; /// sprite type 8bit palette +local GraphicType GraphicSprite16Type; /// sprite type 16bit palette + +/*---------------------------------------------------------------------------- +-- Local functions +----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- +-- RLE Sprites +----------------------------------------------------------------------------*/ + +// +// The current implementation uses RLE encoded sprites. +// If you know something better, write it. +// +// The encoding translates the sprite data to a stream of segments +// of the form: +// +// <skip> <run> <data> +// +// where <skip> is the number of transparent pixels to skip, +// <run> is the number of opaque pixels to blit, +// and <data> are the pixels themselves. +// +// <skip> and <run> are unsigned 8 bit integers. +// If more than 255 transparent pixels are needed 255 0 <n> <run> is +// used. <run> is always stored, even 0 at the end of line. +// This makes the pixel data aligned at all times. +// Segments never wrap around from one scan line to the next. +// + +/** +** Draw a RLE encoded graphic object unclipped into framebuffer. +** +** NOTE: This macro looks nice, but is absolutly no debugable. +** +** @param bpp Bit depth of target framebuffer +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +#define RLE_BLIT(bpp,sprite,frame,x,y) \ + do { \ + const unsigned char* sp; \ + unsigned w; \ + VMemType##bpp* dp; \ + const VMemType##bpp* lp; \ + const VMemType##bpp* ep; \ + const VMemType##bpp* pixels; \ + const VMemType##bpp* pp; \ + unsigned da; \ + \ + pixels=(VMemType##bpp*)sprite->Pixels; \ + sp=((unsigned char**)sprite->Frames)[frame]; \ + w=sprite->Width; \ + da=VideoWidth-w; \ + dp=VideoMemory##bpp+x+y*VideoWidth; \ + ep=dp+VideoWidth*sprite->Height; \ + \ + do { \ + lp=dp+w; \ + do { /* 1 line */ \ + dp+=*sp++; /* transparent # */ \ + pp=dp-1+*sp++; /* opaque # */ \ + while( dp<pp ) { /* unrolled */ \ + *dp++=pixels[*sp++]; \ + *dp++=pixels[*sp++]; \ + } \ + if( dp<=pp ) { \ + *dp++=pixels[*sp++]; \ + } \ + } while( dp<lp ); \ + dp+=da; \ + } while( dp<ep ); /* all lines */ \ + } while( 0 ) + +/** +** Draw 8bit graphic object unclipped into 8 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to8(const Graphic* sprite,unsigned frame,int x,int y) +{ + RLE_BLIT(8,sprite,frame,x,y); +} + +/** +** Draw 8bit graphic object unclipped into 16 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to16(const Graphic* sprite,unsigned frame,int x,int y) +{ + RLE_BLIT(16,sprite,frame,x,y); +} + +/** +** Draw 8bit graphic object unclipped into 24 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to24(const Graphic* sprite,unsigned frame,int x,int y) +{ + RLE_BLIT(24,sprite,frame,x,y); +} + +/** +** Draw 8bit graphic object unclipped into 32 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to32(const Graphic* sprite,unsigned frame,int x,int y) +{ + RLE_BLIT(32,sprite,frame,x,y); +} + +/** +** Draw 8bit graphic object unclipped and flipped in X direction +** into 8 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to8X(const Graphic* sprite,unsigned frame,int x,int y) +{ + const unsigned char* sp; + unsigned w; + VMemType8* dp; + const VMemType8* lp; + const VMemType8* ep; + const VMemType8* pp; + const VMemType8* pixels; + unsigned da; + + pixels=(VMemType8*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + w=sprite->Width; + dp=VideoMemory8+x+y*VideoWidth+w; + da=VideoWidth+w; + ep=dp+VideoWidth*sprite->Height; + + do { + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { // unrolled + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } while( dp<ep ); // all lines +} + +/** +** Draw 8bit graphic object unclipped and flipped in X direction +** into 16 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to16X(const Graphic* sprite,unsigned frame,int x,int y) +{ + const unsigned char* sp; + unsigned w; + VMemType16* dp; + const VMemType16* lp; + const VMemType16* ep; + const VMemType16* pp; + const VMemType16* pixels; + unsigned da; + + pixels=(VMemType16*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + w=sprite->Width; + dp=VideoMemory16+x+y*VideoWidth+w; + da=VideoWidth+w; + ep=dp+VideoWidth*sprite->Height; + + do { + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { // unrolled + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } while( dp<ep ); // all lines +} + +/** +** Draw 8bit graphic object unclipped and flipped in X direction +** into 24 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to24X(const Graphic* sprite,unsigned frame,int x,int y) +{ + const unsigned char* sp; + unsigned w; + VMemType24* dp; + const VMemType24* lp; + const VMemType24* ep; + const VMemType24* pp; + const VMemType24* pixels; + unsigned da; + + pixels=(VMemType24*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + w=sprite->Width; + dp=VideoMemory24+x+y*VideoWidth+w; + da=VideoWidth+w; + ep=dp+VideoWidth*sprite->Height; + + do { + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { // unrolled + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } while( dp<ep ); // all lines +} + +/** +** Draw 8bit graphic object unclipped and flipped in X direction +** into 32 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to32X(const Graphic* sprite,unsigned frame,int x,int y) +{ + const unsigned char* sp; + unsigned w; + VMemType32* dp; + const VMemType32* lp; + const VMemType32* ep; + const VMemType32* pp; + const VMemType32* pixels; + unsigned da; + + pixels=(VMemType32*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + w=sprite->Width; + dp=VideoMemory32+x+y*VideoWidth+w; + da=VideoWidth+w; + ep=dp+VideoWidth*sprite->Height; + + do { + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { // unrolled + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } while( dp<ep ); // all lines +} + +/** +** Draw 8bit graphic object clipped into 8 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to8Clip(const Graphic* sprite,unsigned frame,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType8* dp; + const VMemType8* lp; + const VMemType8* ep; + VMemType8* pp; + const VMemType8* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType8*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines, if needed. + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth-sw; + dp=VideoMemory8+x+y*VideoWidth; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + do { + lp=dp+sw; + do { // 1 line + dp+=*sp++; // transparent + pp=dp-1+*sp++; // opaque + while( dp<pp ) { // unroll + *dp++=pixels[*sp++]; + *dp++=pixels[*sp++]; + } + if( dp<=pp ) { + *dp++=pixels[*sp++]; + } + } while( dp<lp ); + dp+=da; + } while( dp<ep ); // all lines + + } else { // Clip horizontal + + da+=ox; + do { + lp=dp+w; + // + // Clip left + // + pp=dp-ox; + for( ;; ) { + pp+=*sp++; // transparent + if( pp>=dp ) { + dp=pp; + goto middle_trans; + } + pp+=*sp; // opaque + if( pp>=dp ) { + sp+=*sp-(pp-dp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp+=*sp++; // transparent +middle_trans: + if( dp>=lp ) { + lp+=sw-w-ox; + goto right_trans; + } + pp=dp+*sp++; // opaque +middle_pixel: + if( pp<lp ) { + while( dp<pp ) { + *dp++=pixels[*sp++]; + } + continue; + } + while( dp<lp ) { + *dp++=pixels[*sp++]; + } + sp+=pp-dp; + dp=pp; + break; + } + + // + // Clip right + // + lp+=sw-w-ox; + while( dp<lp ) { + dp+=*sp++; // transparent +right_trans: + dp+=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + } while( dp<ep ); // all lines + + } +} + +/** +** Draw 8bit graphic object clipped into 16 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to16Clip(const Graphic* sprite,unsigned frame,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType16* dp; + const VMemType16* lp; + const VMemType16* ep; + VMemType16* pp; + const VMemType16* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType16*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines, if needed. + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth-sw; + dp=VideoMemory16+x+y*VideoWidth; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + do { + lp=dp+sw; + do { // 1 line + dp+=*sp++; // transparent + pp=dp-1+*sp++; // opaque + while( dp<pp ) { // unroll + *dp++=pixels[*sp++]; + *dp++=pixels[*sp++]; + } + if( dp<=pp ) { + *dp++=pixels[*sp++]; + } + } while( dp<lp ); + dp+=da; + } while( dp<ep ); // all lines + + } else { // Clip horizontal + + da+=ox; + do { + lp=dp+w; + // + // Clip left + // + pp=dp-ox; + for( ;; ) { + pp+=*sp++; // transparent + if( pp>=dp ) { + dp=pp; + goto middle_trans; + } + pp+=*sp; // opaque + if( pp>=dp ) { + sp+=*sp-(pp-dp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp+=*sp++; // transparent +middle_trans: + if( dp>=lp ) { + lp+=sw-w-ox; + goto right_trans; + } + pp=dp+*sp++; // opaque +middle_pixel: + if( pp<lp ) { + while( dp<pp ) { + *dp++=pixels[*sp++]; + } + continue; + } + while( dp<lp ) { + *dp++=pixels[*sp++]; + } + sp+=pp-dp; + dp=pp; + break; + } + + // + // Clip right + // + lp+=sw-w-ox; + while( dp<lp ) { + dp+=*sp++; // transparent +right_trans: + dp+=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + } while( dp<ep ); // all lines + + } +} + +/** +** Draw 8bit graphic object clipped into 24 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to24Clip(const Graphic* sprite,unsigned frame,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType24* dp; + const VMemType24* lp; + const VMemType24* ep; + VMemType24* pp; + const VMemType24* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType24*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines, if needed. + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth-sw; + dp=VideoMemory24+x+y*VideoWidth; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + do { + lp=dp+sw; + do { // 1 line + dp+=*sp++; // transparent + pp=dp-1+*sp++; // opaque + while( dp<pp ) { // unroll + *dp++=pixels[*sp++]; + *dp++=pixels[*sp++]; + } + if( dp<=pp ) { + *dp++=pixels[*sp++]; + } + } while( dp<lp ); + dp+=da; + } while( dp<ep ); // all lines + + } else { // Clip horizontal + + da+=ox; + do { + lp=dp+w; + // + // Clip left + // + pp=dp-ox; + for( ;; ) { + pp+=*sp++; // transparent + if( pp>=dp ) { + dp=pp; + goto middle_trans; + } + pp+=*sp; // opaque + if( pp>=dp ) { + sp+=*sp-(pp-dp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp+=*sp++; // transparent +middle_trans: + if( dp>=lp ) { + lp+=sw-w-ox; + goto right_trans; + } + pp=dp+*sp++; // opaque +middle_pixel: + if( pp<lp ) { + while( dp<pp ) { + *dp++=pixels[*sp++]; + } + continue; + } + while( dp<lp ) { + *dp++=pixels[*sp++]; + } + sp+=pp-dp; + dp=pp; + break; + } + + // + // Clip right + // + lp+=sw-w-ox; + while( dp<lp ) { + dp+=*sp++; // transparent +right_trans: + dp+=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + } while( dp<ep ); // all lines + + } +} + +/** +** Draw 8bit graphic object clipped into 32 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to32Clip(const Graphic* sprite,unsigned frame,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType32* dp; + const VMemType32* lp; + const VMemType32* ep; + VMemType32* pp; + const VMemType32* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType32*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines, if needed. + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth-sw; + dp=VideoMemory32+x+y*VideoWidth; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + do { + lp=dp+sw; + do { // 1 line + dp+=*sp++; // transparent + pp=dp-1+*sp++; // opaque + while( dp<pp ) { // unroll + *dp++=pixels[*sp++]; + *dp++=pixels[*sp++]; + } + if( dp<=pp ) { + *dp++=pixels[*sp++]; + } + } while( dp<lp ); + dp+=da; + } while( dp<ep ); // all lines + + } else { // Clip horizontal + + da+=ox; + do { + lp=dp+w; + // + // Clip left + // + pp=dp-ox; + for( ;; ) { + pp+=*sp++; // transparent + if( pp>=dp ) { + dp=pp; + goto middle_trans; + } + pp+=*sp; // opaque + if( pp>=dp ) { + sp+=*sp-(pp-dp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp+=*sp++; // transparent +middle_trans: + if( dp>=lp ) { + lp+=sw-w-ox; + goto right_trans; + } + pp=dp+*sp++; // opaque +middle_pixel: + if( pp<lp ) { + while( dp<pp ) { + *dp++=pixels[*sp++]; + } + continue; + } + while( dp<lp ) { + *dp++=pixels[*sp++]; + } + sp+=pp-dp; + dp=pp; + break; + } + + // + // Clip right + // + lp+=sw-w-ox; + while( dp<lp ) { + dp+=*sp++; // transparent +right_trans: + dp+=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + } while( dp<ep ); // all lines + + } +} + +/** +** Draw 8bit graphic object clipped and flipped in X direction +** into 8 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to8ClipX(const Graphic* sprite,unsigned frame,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType8* dp; + const VMemType8* lp; + const VMemType8* ep; + VMemType8* pp; + const VMemType8* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType8*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth+sw; + dp=VideoMemory8+x+y*VideoWidth+w; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + while( dp<ep ) { // all lines + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } + + } else { // Clip horizontal + + da-=sw-w-ox; + while( dp<ep ) { // all lines + lp=dp-w; + // + // Clip right side + // + pp=dp+sw-w-ox; + for( ;; ) { + pp-=*sp++; // transparent + if( pp<=dp ) { + dp=pp; + goto middle_trans; + } + pp-=*sp; // opaque + if( pp<=dp ) { + sp+=*sp-(dp-pp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp-=*sp++; // transparent +middle_trans: + if( dp<=lp ) { + lp-=ox; + goto right_trans; + } + pp=dp-*sp++; // opaque +middle_pixel: + if( pp>lp ) { + while( dp>pp ) { + *dp--=pixels[*sp++]; + } + continue; + } + while( dp>lp ) { + *dp--=pixels[*sp++]; + } + sp+=dp-pp; + dp=pp; + break; + } + + // + // Clip left side + // + lp-=ox; + while( dp>lp ) { + dp-=*sp++; // transparent +right_trans: + dp-=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + + } + } +} + +/** +** Draw 8bit graphic object clipped and flipped in X direction +** into 16 bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to16ClipX(const Graphic* sprite,unsigned frame + ,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType16* dp; + const VMemType16* lp; + const VMemType16* ep; + VMemType16* pp; + const VMemType16* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType16*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth+sw; + dp=VideoMemory16+x+y*VideoWidth+w; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + while( dp<ep ) { // all lines + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } + + } else { // Clip horizontal + + da-=sw-w-ox; + while( dp<ep ) { // all lines + lp=dp-w; + // + // Clip right side + // + pp=dp+sw-w-ox; + for( ;; ) { + pp-=*sp++; // transparent + if( pp<=dp ) { + dp=pp; + goto middle_trans; + } + pp-=*sp; // opaque + if( pp<=dp ) { + sp+=*sp-(dp-pp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp-=*sp++; // transparent +middle_trans: + if( dp<=lp ) { + lp-=ox; + goto right_trans; + } + pp=dp-*sp++; // opaque +middle_pixel: + if( pp>lp ) { + while( dp>pp ) { + *dp--=pixels[*sp++]; + } + continue; + } + while( dp>lp ) { + *dp--=pixels[*sp++]; + } + sp+=dp-pp; + dp=pp; + break; + } + + // + // Clip left side + // + lp-=ox; + while( dp>lp ) { + dp-=*sp++; // transparent +right_trans: + dp-=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + + } + } +} + +/** +** Draw 8bit graphic object clipped and flipped in X direction +** into 24bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to24ClipX(const Graphic* sprite,unsigned frame + ,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType24* dp; + const VMemType24* lp; + const VMemType24* ep; + VMemType24* pp; + const VMemType24* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType24*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth+sw; + dp=VideoMemory24+x+y*VideoWidth+w; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + while( dp<ep ) { // all lines + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } + + } else { // Clip horizontal + + da-=sw-w-ox; + while( dp<ep ) { // all lines + lp=dp-w; + // + // Clip right side + // + pp=dp+sw-w-ox; + for( ;; ) { + pp-=*sp++; // transparent + if( pp<=dp ) { + dp=pp; + goto middle_trans; + } + pp-=*sp; // opaque + if( pp<=dp ) { + sp+=*sp-(dp-pp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp-=*sp++; // transparent +middle_trans: + if( dp<=lp ) { + lp-=ox; + goto right_trans; + } + pp=dp-*sp++; // opaque +middle_pixel: + if( pp>lp ) { + while( dp>pp ) { + *dp--=pixels[*sp++]; + } + continue; + } + while( dp>lp ) { + *dp--=pixels[*sp++]; + } + sp+=dp-pp; + dp=pp; + break; + } + + // + // Clip left side + // + lp-=ox; + while( dp>lp ) { + dp-=*sp++; // transparent +right_trans: + dp-=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + + } + } +} + +/** +** Draw 8bit graphic object clipped and flipped in X direction +** into 32bit framebuffer. +** +** @param sprite pointer to object +** @param frame number of frame (object index) +** @param x x coordinate on the screen +** @param y y coordinate on the screen +*/ +global void VideoDraw8to32ClipX(const Graphic* sprite,unsigned frame + ,int x,int y) +{ + int ox; + int oy; + int w; + int h; + const unsigned char* sp; + unsigned sw; + VMemType32* dp; + const VMemType32* lp; + const VMemType32* ep; + VMemType32* pp; + const VMemType32* pixels; + unsigned da; + + ox=oy=0; + sw=w=sprite->Width; + h=sprite->Height; + + if( x<ClipX1 ) { // reduce to visible range + ox=ClipX1-x; + w-=ox; + x=ClipX1; + } + if( x+w>ClipX2 ) { + w=ClipX2-x; + } + + if( y<ClipY1 ) { + oy=ClipY1-y; + h-=oy; + y=ClipY1; + } + if( y+h>ClipY2 ) { + h=ClipY2-y; + } + + if( w<=0 || h<=0 ) { // nothing to draw + return; + } + + // + // Draw the clipped sprite + // + pixels=(VMemType32*)sprite->Pixels; + sp=((unsigned char**)sprite->Frames)[frame]; + + // + // Skip top lines + // + while( oy-- ) { + da=0; + do { + da+=*sp++; // transparent + da+=*sp; // opaque + sp+=*sp+1; + } while( da<sw ); + } + + da=VideoWidth+sw; + dp=VideoMemory32+x+y*VideoWidth+w; + ep=dp+VideoWidth*h; + + if( w==sw ) { // Unclipped horizontal + + while( dp<ep ) { // all lines + lp=dp-w; + do { // 1 line + dp-=*sp++; // transparent + pp=dp+1-*sp++; // opaque + while( dp>pp ) { + *dp--=pixels[*sp++]; + *dp--=pixels[*sp++]; + } + if( dp>=pp ) { + *dp--=pixels[*sp++]; + } + } while( dp>lp ); + dp+=da; + } + + } else { // Clip horizontal + + da-=sw-w-ox; + while( dp<ep ) { // all lines + lp=dp-w; + // + // Clip right side + // + pp=dp+sw-w-ox; + for( ;; ) { + pp-=*sp++; // transparent + if( pp<=dp ) { + dp=pp; + goto middle_trans; + } + pp-=*sp; // opaque + if( pp<=dp ) { + sp+=*sp-(dp-pp)+1; + goto middle_pixel; + } + sp+=*sp+1; + } + + // + // Draw middle + // + for( ;; ) { + dp-=*sp++; // transparent +middle_trans: + if( dp<=lp ) { + lp-=ox; + goto right_trans; + } + pp=dp-*sp++; // opaque +middle_pixel: + if( pp>lp ) { + while( dp>pp ) { + *dp--=pixels[*sp++]; + } + continue; + } + while( dp>lp ) { + *dp--=pixels[*sp++]; + } + sp+=dp-pp; + dp=pp; + break; + } + + // + // Clip left side + // + lp-=ox; + while( dp>lp ) { + dp-=*sp++; // transparent +right_trans: + dp-=*sp; // opaque + sp+=*sp+1; + } + dp+=da; + + } + } +} + +/** +** Free graphic object. +*/ +local void FreeSprite8(Graphic* graphic) +{ + IfDebug( AllocatedGraphicMemory-=graphic->Size ); + IfDebug( AllocatedGraphicMemory-=sizeof(Graphic) ); + free(graphic->Frames); + free(graphic); +} + +// FIXME: need 16 bit palette version +// FIXME: need alpha blending version +// FIXME: need zooming version + +/*---------------------------------------------------------------------------- +-- Global functions +----------------------------------------------------------------------------*/ + +/** +** Load sprite from file. +** +** Compress the file as RLE (run-length-encoded) sprite. +** +** @param name File name of sprite to load. +** @param width Width of a single frame. +** @param height Height of a single frame. +** +** @return A graphic object for the loaded sprite. +** +** @see LoadGraphic +*/ +global Graphic* LoadSprite(const char* name,unsigned width,unsigned height) +{ + Graphic* sprite; + Graphic* graphic; + unsigned char* data; + const unsigned char* sp; + unsigned char* dp; + unsigned char* cp; + int depth; + int fl; + int n; + int counter; + int i; + int h; + int w; + + graphic=LoadGraphic(name); + if( !width ) { // FIXME: this is hack for cursors! + width=graphic->Width; + } + if( !height ) { + height=graphic->Height; + } + depth=8; + + // Check if width and height fits. + DebugCheck( ((graphic->Width/width)*width!=graphic->Width) + || ((graphic->Height/height)*height!=graphic->Height) ); + + n=(graphic->Width/width)*(graphic->Height/height); + DebugLevel3(__FUNCTION__": %dx%d in %dx%d = %d frames.\n" + ,width,height,graphic->Width,graphic->Height,n); + + // + // Allocate structure + // + sprite=malloc(sizeof(Graphic)); + IfDebug( AllocatedGraphicMemory+=sizeof(Graphic) ); + + if( !sprite ) { + fprintf(stderr,"Out of memory\n"); + exit(-1); + } + if( depth==8 ) { + sprite->Type=&GraphicSprite8Type; + } else if( depth==16 ) { + sprite->Type=&GraphicSprite16Type; + } else { + fprintf(stderr,"Unsported image depth\n"); + exit(-1); + } + sprite->Width=width; + sprite->Height=height; + + sprite->NumFrames=n; + + sprite->Palette=graphic->Palette; + sprite->Pixels=graphic->Pixels; // WARNING: if not shared freed below! + + sprite->Size=0; + sprite->Frames=NULL; + + + // Worst case is alternating opaque and transparent pixels + data=malloc(n*sizeof(unsigned char*) + +(graphic->Width/2+1)*3*graphic->Height); + // Pixel area + dp=(unsigned char *)data+n*sizeof(unsigned char*); + + // + // Compress all frames of the sprite. + // + fl=graphic->Width/width; + for( i=0; i<n; ++i ) { // each frame + ((unsigned char**)data)[i]=dp; + for( h=0; h<height; ++h ) { // each line + sp=graphic->Frames+(i%fl)*width+((i/fl)*height+h)*graphic->Width; + + for( counter=w=0; w<width; ++w ) { + if( *sp==255 ) { // transparent + ++sp; + if( ++counter==256 ) { + *dp++=255; + *dp++=0; + counter=1; + } + continue; + } + *dp++=counter; + + cp=dp++; + counter=0; + for( ; w<width; ++w ) { // opaque + *dp++=*sp++; + if( ++counter==255 ) { + *cp=255; + *dp++=0; + cp=dp++; + counter=0; + } + // ARI: FIXME: wrong position + if( w+1!=width && *sp==255 ) { // transparent + break; + } + } + *cp=counter; + counter=0; + } + if( counter ) { + *dp++=counter; + *dp++=0; // 1 byte more, 1 check less! + } + } + } + + DebugLevel3("\t%d => %d RLE compressed\n" + ,graphic->Width*graphic->Height,dp-data); + + // + // Update to real length + // + sprite->Frames=data; + i=n*sizeof(unsigned char*)+dp-data; + sprite->Size=i; + data=realloc(data,i); + if( sprite->Frames!=data ) { // shrink only - happens rarely + dp=sprite->Frames; + for( h=0; h<n; ++h ) { // convert address + ((unsigned char*)dp)[h]+=data-dp; + } + sprite->Frames=data; + } + + IfDebug( CompressedGraphicMemory+=i; ); + + VideoFree(graphic); + + return sprite; +} + +/** +** Init sprite +*/ +global void InitSprite(void) +{ + switch( VideoDepth ) { + case 8: + GraphicSprite8Type.Draw=VideoDraw8to8; + GraphicSprite8Type.DrawClip=VideoDraw8to8Clip; + GraphicSprite8Type.DrawX=VideoDraw8to8X; + GraphicSprite8Type.DrawClipX=VideoDraw8to8ClipX; + break; + + case 15: + case 16: + GraphicSprite8Type.Draw=VideoDraw8to16; + GraphicSprite8Type.DrawClip=VideoDraw8to16Clip; + GraphicSprite8Type.DrawX=VideoDraw8to16X; + GraphicSprite8Type.DrawClipX=VideoDraw8to16ClipX; + break; + + case 24: + GraphicSprite8Type.Draw=VideoDraw8to24; + GraphicSprite8Type.DrawClip=VideoDraw8to24Clip; + GraphicSprite8Type.DrawX=VideoDraw8to24X; + GraphicSprite8Type.DrawClipX=VideoDraw8to24ClipX; + // FIXME: need real 24bpp mode!! + // break; + + case 32: + GraphicSprite8Type.Draw=VideoDraw8to32; + GraphicSprite8Type.DrawClip=VideoDraw8to32Clip; + GraphicSprite8Type.DrawX=VideoDraw8to32X; + GraphicSprite8Type.DrawClipX=VideoDraw8to32ClipX; + break; + + default: + DebugLevel0(__FUNCTION__": unsupported %d bpp\n",VideoDepth); + abort(); + } + + GraphicSprite8Type.Free=FreeSprite8; +} + +//@}