#include "std.h" #include "gxcanvas.h" #include "gxgraphics.h" #include "gxruntime.h" #include "asmcoder.h" #define DEBUG_BITMASK static int canvas_cnt; static DDBLTFX bltfx={sizeof(DDBLTFX)}; extern gxRuntime *gx_runtime; static unsigned FWMS[]={ 0xffffffff,0x7fffffff,0x3fffffff,0x1fffffff, 0x0fffffff,0x07ffffff,0x03ffffff,0x01ffffff, 0x00ffffff,0x007fffff,0x003fffff,0x001fffff, 0x000fffff,0x0007ffff,0x0003ffff,0x0001ffff, 0x0000ffff,0x00007fff,0x00003fff,0x00001fff, 0x00000fff,0x000007ff,0x000003ff,0x000001ff, 0x000000ff,0x0000007f,0x0000003f,0x0000001f, 0x0000000f,0x00000007,0x00000003,0x00000001}; static unsigned LWMS[]={ 0x80000000,0xc0000000,0xe0000000,0xf0000000, 0xf8000000,0xfc000000,0xfe000000,0xff000000, 0xff800000,0xffc00000,0xffe00000,0xfff00000, 0xfff80000,0xfffc0000,0xfffe0000,0xffff0000, 0xffff8000,0xffffc000,0xffffe000,0xfffff000, 0xfffff800,0xfffffc00,0xfffffe00,0xffffff00, 0xffffff80,0xffffffc0,0xffffffe0,0xfffffff0, 0xfffffff8,0xfffffffc,0xfffffffe,0xffffffff}; static void calcShifts( unsigned mask,unsigned char *shr,unsigned char *shl ){ if( mask ){ for( *shl=0;!(mask&1);++*shl,mask>>=1 ){} for( *shr=8;mask&1;--*shr,mask>>=1 ){} }else *shr=*shl=0; } struct Rect : public RECT{ Rect(){ } Rect( int x,int y,int w,int h ){ left=x;top=y;right=x+w;bottom=y+h; } }; static bool clip( const RECT &viewport,RECT *d ){ if( d->right<=d->left || d->bottom<=d->top || d->left>=viewport.right || d->right<=viewport.left || d->top>=viewport.bottom || d->bottom<=viewport.top ) return false; if( d->leftleft=viewport.left; if( d->right>viewport.right ) d->right=viewport.right; if( d->toptop=viewport.top; if( d->bottom>viewport.bottom ) d->bottom=viewport.bottom; return true; } static bool clip( const RECT &viewport,RECT *d,RECT *s ){ if( d->right<=d->left || d->bottom<=d->top || d->left>=viewport.right || d->right<=viewport.left || d->top>=viewport.bottom || d->bottom<=viewport.top ) return false; int dx,dy; if( (dx=viewport.left-d->left)>0 ){ d->left+=dx;s->left+=dx; } if( (dx=viewport.right-d->right)<0 ){ d->right+=dx;s->right+=dx; } if( (dy=viewport.top-d->top)>0 ){ d->top+=dy;s->top+=dy; } if( (dy=viewport.bottom-d->bottom)<0 ){ d->bottom+=dy;s->bottom+=dy; } return true; } gxCanvas::gxCanvas( gxGraphics *g,IDirectDrawSurface7 *s,int f ): graphics(g),main_surf(s),surf(0),z_surf(0),flags(f),cube_mode(CUBEMODE_REFLECTION|CUBESPACE_WORLD), t_surf(0),cm_mask(0),locked_cnt(0),mod_cnt(0),remip_cnt(0){ if( flags & CANVAS_TEX_CUBE ){ cube_surfs[2]=main_surf; for( int k=0;k<6;++k ){ if( k==2 ) continue; DWORD n; switch( k ){ case 0:n=DDSCAPS2_CUBEMAP_NEGATIVEX;break; case 1:n=DDSCAPS2_CUBEMAP_POSITIVEZ;break; case 2:n=DDSCAPS2_CUBEMAP_POSITIVEX;break; case 3:n=DDSCAPS2_CUBEMAP_NEGATIVEZ;break; case 4:n=DDSCAPS2_CUBEMAP_POSITIVEY;break; case 5:n=DDSCAPS2_CUBEMAP_NEGATIVEY;break; default:return; } DDSCAPS2 caps={0}; caps.dwCaps2=DDSCAPS2_CUBEMAP|n; main_surf->GetAttachedSurface( &caps,&cube_surfs[k] ); } surf=cube_surfs[1]; }else{ surf=main_surf; memset( cube_surfs,0,sizeof(cube_surfs) ); } DDSURFACEDESC2 desc={sizeof(desc)}; surf->GetSurfaceDesc( &desc ); format.setFormat( desc.ddpfPixelFormat ); clip_rect.left=clip_rect.top=0; clip_rect.right=desc.dwWidth; clip_rect.bottom=desc.dwHeight; cm_pitch=(clip_rect.right+31)/32+1; setMask( 0 ); setColor( ~0 ); setClsColor( 0 ); setOrigin( 0,0 ); setHandle( 0,0 ); setFont( graphics->getDefaultFont() ); setViewport( 0,0,getWidth(),getHeight() ); if( flags & gxCanvas::CANVAS_TEXTURE ) ddUtil::buildMipMaps( surf ); } gxCanvas::~gxCanvas(){ delete[] cm_mask; if( locked_cnt ) surf->Unlock( 0 ); if( t_surf ) t_surf->Release(); releaseZBuffer(); main_surf->Release(); } void gxCanvas::backup()const{ if( flags & CANVAS_TEX_CUBE ) return; if( !t_surf ){ DDSURFACEDESC2 desc={sizeof(desc)}; if( surf->GetSurfaceDesc(&desc)<0 ) return; if( desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY ) return; DDSURFACEDESC2 t_desc={sizeof(t_desc)}; t_desc.dwFlags=DDSD_CAPS|DDSD_PIXELFORMAT|DDSD_WIDTH|DDSD_HEIGHT; t_desc.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY; t_desc.dwWidth=desc.dwWidth;t_desc.dwHeight=desc.dwHeight; t_desc.ddpfPixelFormat=desc.ddpfPixelFormat; if( graphics->dirDraw->CreateSurface( &t_desc,&t_surf,0 )<0 ){ t_surf=0; return; } } if( t_surf->Blt( 0,surf,0,DDBLT_WAIT,0 )<0 ) return; } void gxCanvas::restore()const{ if( !t_surf ) return; if( surf->Blt( 0,t_surf,0,DDBLT_WAIT,0 )<0 ) return; } ddSurf *gxCanvas::getSurface()const{ return surf; } ddSurf *gxCanvas::getTexSurface()const{ if( mod_cnt==remip_cnt ) return main_surf; ddUtil::buildMipMaps( surf ); remip_cnt=mod_cnt; return main_surf; } bool gxCanvas::clip( RECT *d )const{ return ::clip( viewport,d ); } bool gxCanvas::clip( RECT *d,RECT *s )const{ return ::clip( viewport,d,s ); } void gxCanvas::updateBitMask( const RECT &r )const{ int w=r.right-r.left;if( w<=0 ) return; int h=r.bottom-r.top;if( h<=0 ) return; lock(); RECT t=r; t.left&=~31; t.right=(t.right+31)&~31; w=(t.right-t.left)/32; unsigned char *src_row=locked_surf+t.top*locked_pitch+t.left*format.getPitch(); unsigned *dest_row=cm_mask+t.top*cm_pitch+t.left/32; unsigned mask_argb=format.toARGB( mask_surf ) & 0xffffff; #ifdef DEBUG_BITMASK unsigned *cm_mask_end=cm_mask+cm_pitch*clip_rect.bottom; #endif while( h-- ){ unsigned *dest=dest_row; unsigned char *src=src_row; for( int c=0;c=cm_mask_end ){ gx_runtime->debugError( "gxCanvas::updateBitMask dest out of range" ); } #endif *dest++=mask; } dest_row+=cm_pitch; src_row+=locked_pitch; } unlock(); } void gxCanvas::setModify( int n ){ mod_cnt=n; } int gxCanvas::getModify()const{ return mod_cnt; } bool gxCanvas::attachZBuffer(){ if( z_surf ) return true; DDSURFACEDESC2 desc={sizeof(desc)}; desc.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT; desc.dwWidth=getWidth(); desc.dwHeight=getHeight(); desc.ddsCaps.dwCaps=DDSCAPS_ZBUFFER|DDSCAPS_VIDEOMEMORY; desc.ddpfPixelFormat=graphics->zbuffFmt; if( graphics->dirDraw->CreateSurface( &desc,&z_surf,0 )<0 ) return false; surf->AddAttachedSurface( z_surf ); return true; } void gxCanvas::releaseZBuffer(){ if( !z_surf ) return; surf->DeleteAttachedSurface( 0,z_surf ); z_surf->Release(); z_surf=0; } void gxCanvas::damage( const RECT &r )const{ ++mod_cnt;if( cm_mask ) updateBitMask( r ); } void gxCanvas::setFont( gxFont *f ){ font=f; } void gxCanvas::setMask( unsigned argb ){ mask_surf=format.fromARGB( argb ); } void gxCanvas::setColor( unsigned argb ){ argb|=0xff000000; color_argb=argb; color_surf=format.fromARGB( argb ); } void gxCanvas::setClsColor( unsigned argb ){ argb|=0xff000000; clsColor_surf=format.fromARGB( argb ); } void gxCanvas::setOrigin( int x,int y ){ origin_x=x;origin_y=y; } void gxCanvas::setHandle( int x,int y ){ handle_x=x;handle_y=y; } void gxCanvas::setViewport( int x,int y,int w,int h ){ Rect r( x,y,w,h ); if( !::clip( clip_rect,&r ) ) r=Rect(0,0,0,0); viewport=r; } //renderering primitives void gxCanvas::cls(){ bltfx.dwFillColor=clsColor_surf; surf->Blt( &viewport,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); damage( viewport ); } void gxCanvas::plot( int x,int y ){ x+=origin_x;if( x=viewport.right ) return; y+=origin_y;if( y=viewport.bottom ) return; bltfx.dwFillColor=color_surf; Rect dest( x,y,1,1 ); surf->Blt( &dest,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); damage( dest ); } void gxCanvas::line( int x0,int y0,int x1,int y1 ){ int ddf,padj,sadj; int dx,dy,sx,sy,ax,ay; x0+=origin_x;y0+=origin_y; x1+=origin_x;y1+=origin_y; int cx0,cx1,cy0,cy1,clip0,clip1; cx0=viewport.left; cx1=viewport.right-1; cy0=viewport.top; cy1=viewport.bottom-1; while( true ){ clip0=0;clip1=0; if(y0>cy1)clip0|=1;else if(y0cx1)clip0|=4;else if(x0cy1)clip1|=1;else if(y1cx1)clip1|=4;else if(x1=0) {sx=1;ax=dx;} else {sx=-1;ax=-dx;} if (dy>=0) {sy=1;ay=dy;} else {sy=-1;ay=-dy;} lock(); if( ax>ay ){ ddf=-ax;sadj=ax+ax;padj=ay+ay; while( ax-->=0 ){ setPixelFast( x0,y0,color_argb ); x0+=sx;ddf+=padj;if( ddf>=0 ){ y0+=sy;ddf-=sadj; } } }else{ ddf=-ay;sadj=ay+ay;padj=ax+ax; while( ay-->=0 ){ setPixelFast( x0,y0,color_argb ); y0+=sy;ddf+=padj;if( ddf>=0 ){ x0+=sx;ddf-=sadj; } } } unlock(); } void gxCanvas::rect( int x,int y,int w,int h,bool solid ){ x+=origin_x;y+=origin_y; Rect dest( x,y,w,h ); if( !clip( &dest ) ) return; bltfx.dwFillColor=color_surf; if( solid ){ surf->Blt( &dest,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); damage( dest ); return; } Rect r1( x,y,w,1 );if( clip( &r1 ) ){ surf->Blt( &r1,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); } Rect r2( x,y,1,h );if( clip( &r2 ) ){ surf->Blt( &r2,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); } Rect r3( x+w-1,y,1,h );if( clip( &r3 ) ){ surf->Blt( &r3,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); } Rect r4( x,y+h-1,w,1 );if( clip( &r4 ) ){ surf->Blt( &r4,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); } damage( dest ); } void gxCanvas::oval( int x1,int y1,int w,int h,bool solid ){ x1+=origin_x;y1+=origin_y; Rect dest( x1,y1,w,h ); if( !clip( &dest ) ) return; bltfx.dwFillColor=color_surf; float xr=w*.5f,yr=h*.5f,ar=(float)w/(float)h; float cx=x1+xr+.5f,cy=y1+yr-.5f,rsq=yr*yr,y; if( solid ){ y=dest.top-cy; for( int t=dest.top;t=viewport.right || xb<=viewport.left ) continue; Rect dr;dr.top=t;dr.bottom=t+1; dr.left=xaviewport.right ? viewport.right : xb; surf->Blt( &dr,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); } damage( dest ); return; } int p_xa,p_xb,t,hh=floor(cy); p_xa=p_xb=cx; t=dest.top;y=t-cy; if( dest.top>y1 ){ --t;--y; } for( ;t<=hh;++y,++t ){ float x=sqrt( rsq-y*y )*ar; int xa=floor( cx-x ),xb=floor( cx+x ); Rect r1( xa,t,p_xa-xa,1 );if( r1.right<=r1.left ) r1.right=r1.left+1; if( clip( &r1 ) ) surf->Blt( &r1,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); Rect r2( p_xb,t,xb-p_xb,1 );if( r2.left>=r2.right ) r2.left=r2.right-1; if( clip( &r2 ) ) surf->Blt( &r2,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); p_xa=xa;p_xb=xb; } p_xa=p_xb=cx; t=dest.bottom-1;y=t-cy; if( dest.bottomhh;--y,--t ){ float x=sqrt( rsq-y*y )*ar; int xa=floor( cx-x ),xb=floor( cx+x ); Rect r1( xa,t,p_xa-xa,1 );if( r1.right<=r1.left ) r1.right=r1.left+1; if( clip( &r1 ) ) surf->Blt( &r1,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); Rect r2( p_xb,t,xb-p_xb,1 );if( r2.left>=r2.right ) r2.left=r2.right-1; if( clip( &r2 ) ) surf->Blt( &r2,0,0,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx ); p_xa=xa;p_xb=xb; } damage( dest ); } void gxCanvas::blit( int x,int y,gxCanvas *src,int src_x,int src_y,int src_w,int src_h,bool solid ){ x+=origin_x-src->handle_x; y+=origin_y-src->handle_y; Rect dest_r( x,y,src_w,src_h ),src_r( src_x,src_y,src_w,src_h ); if( !clip( &dest_r,&src_r ) ) return; if( !::clip( src->clip_rect,&src_r,&dest_r ) ) return; if( solid ){ surf->Blt( &dest_r,src->surf,&src_r,DDBLT_WAIT,0 ); }else{ bltfx.ddckSrcColorkey.dwColorSpaceLowValue= bltfx.ddckSrcColorkey.dwColorSpaceHighValue=src->mask_surf; surf->Blt( &dest_r,src->surf,&src_r,DDBLT_WAIT|DDBLT_KEYSRCOVERRIDE,&bltfx ); } damage( dest_r ); } void gxCanvas::text( int x,int y,const string &t ){ int ty=y+origin_y; if( ty>=viewport.bottom ) return; if( ty+font->getHeight()<=viewport.top ) return; int tx=x+origin_x; if( tx>=viewport.right ) return; int b=0,w; while( bcharWidth( t[b] ))<=viewport.left ){ tx+=w;x+=w;++b; } int e=b; while( echarWidth( t[e] );++e; } if( e>b ) font->render( this,format.toARGB( color_surf ),x,y,t.substr( b,e-b ) ); } int gxCanvas::getWidth()const{ return clip_rect.right; } int gxCanvas::getHeight()const{ return clip_rect.bottom; } int gxCanvas::getDepth()const{ return format.getDepth(); } void gxCanvas::getOrigin( int *x,int *y )const{ *x=origin_x;*y=origin_y; } void gxCanvas::getHandle( int *x,int *y )const{ *x=handle_x;*y=handle_y; } void gxCanvas::getViewport( int *x,int *y,int *w,int *h )const{ *x=viewport.left;*y=viewport.top; *w=viewport.right-viewport.left;*h=viewport.bottom-viewport.top; } unsigned gxCanvas::getMask()const{ return format.toARGB( mask_surf ); } unsigned gxCanvas::getColor()const{ return format.toARGB( color_surf ); } unsigned gxCanvas::getClsColor()const{ return format.toARGB( clsColor_surf ); } bool gxCanvas::collide( int x1,int y1,const gxCanvas *i2,int x2,int y2,bool solid )const{ x1-=handle_x;x2-=i2->handle_x; if( x1+clip_rect.right<=x2 || x1>=x2+i2->clip_rect.right ) return false; y1-=handle_y;y2-=i2->handle_y; if( y1+clip_rect.bottom<=y2 || y1>=y2+i2->clip_rect.bottom ) return false; if( solid ) return true; if( !cm_mask ){ cm_mask=d_new unsigned[cm_pitch*clip_rect.bottom]; updateBitMask( clip_rect ); } if( !i2->cm_mask ){ i2->cm_mask=d_new unsigned[i2->cm_pitch*i2->clip_rect.bottom]; i2->updateBitMask( i2->clip_rect ); } const gxCanvas *i1=this; //to keep me sane! if( x1>x2 ){ std::swap( x1,x2 ); std::swap( y1,y2 ); std::swap( i1,i2 ); } Rect r1,r2,ir; r1.left=x1;r1.top=y1;r1.right=x1+i1->clip_rect.right;r1.bottom=y1+i1->clip_rect.bottom; r2.left=x2;r2.top=y2;r2.right=x2+i2->clip_rect.right;r2.bottom=y2+i2->clip_rect.bottom; ir.left=r1.left>r2.left ? r1.left : r2.left; ir.right=r1.rightr2.top ? r1.top : r2.top; ir.bottom=r1.bottomcm_mask,*s2=i2->cm_mask; int i1_pitch=i1->cm_pitch,i2_pitch=i2->cm_pitch; s1+=(ir.top-r1.top)*i1_pitch; s2+=(ir.top-r2.top)*i2_pitch; int startx=ir.left-r1.left; int stopx=ir.right-r1.left-1; int shr=startx&31; int shl=32-shr; int cnt=stopx/32-startx/32; unsigned lwm=LWMS[stopx&31]; #ifdef DEBUG_BITMASK unsigned *cm_mask_end1=i1->cm_mask + i1_pitch*i1->clip_rect.bottom; unsigned *cm_mask_end2=i2->cm_mask + i2_pitch*i2->clip_rect.bottom; #endif s1+=startx/32; for( int y=ir.top;ycm_mask || row2cm_mask ){ gx_runtime->debugError( "gxCanvas::collide row underflow" ); } if( row1>=cm_mask_end1 || row2>=cm_mask_end2 ){ gx_runtime->debugError( "gxCanvas::collide row overflow" ); } #endif unsigned n=*row2++; if( ((n>>shr)|p) & *row1++ ) return true; p=shl<32 ? n<cm_mask || row2cm_mask ){ gx_runtime->debugError( "gxCanvas::collide row underflow" ); } if( row1>=cm_mask_end1 || row2>=cm_mask_end2 ){ gx_runtime->debugError( "gxCanvas::collide row overflow" ); } #endif if( ((*row2>>shr)|p) & *row1 & lwm ) return true; s1+=i1_pitch;s2+=i2_pitch; } return false; } bool gxCanvas::rect_collide( int x1,int y1,int x2,int y2,int w2,int h2,bool solid )const{ x1-=handle_x;if( x1+clip_rect.right<=x2 || x1>=x2+w2 ) return false; y1-=handle_y;if( y1+clip_rect.bottom<=y2 || y1>=y2+h2 ) return false; if( solid ) return true; Rect r1( x1,y1,clip_rect.right,clip_rect.bottom ),r2( x2,y2,w2,h2 ),ir; ir.left=r1.left>r2.left ? r1.left : r2.left; ir.right=r1.rightr2.top ? r1.top : r2.top; ir.bottom=r1.bottomLock( 0,&desc,DDLOCK_WAIT|DDLOCK_NOSYSLOCK,0 )<0 ){ --locked_cnt; return false; } locked_pitch=desc.lPitch; locked_surf=(unsigned char*)desc.lpSurface; lock_mod_cnt=mod_cnt; } return true; } void gxCanvas::unlock()const{ if( locked_cnt==1 ){ if( lock_mod_cnt!=mod_cnt && cm_mask ) updateBitMask( clip_rect ); surf->Unlock( 0 ); } --locked_cnt; } void gxCanvas::setPixel( int x,int y,unsigned argb ){ x+=origin_x;if( x=viewport.right ) return; y+=origin_y;if( y=viewport.bottom ) return; lock(); setPixelFast( x,y,argb ); unlock(); } unsigned gxCanvas::getPixel( int x,int y )const{ x+=origin_x;if( x=viewport.right ) return format.toARGB( mask_surf ); y+=origin_y;if( y=viewport.bottom ) return format.toARGB( mask_surf ); lock(); unsigned p=getPixelFast( x,y ); unlock(); return p; } void gxCanvas::copyPixelFast( int x,int y,gxCanvas *src,int src_x,int src_y ){ switch( format.getDepth() ){ case 16: *(short*)(locked_surf+y*locked_pitch+x*2)= *(short*)(src->locked_surf+src_y*src->locked_pitch+src_x*2); break; case 24:{ unsigned char *p=locked_surf+y*locked_pitch+x*3; unsigned char *t=src->locked_surf+src_y*src->locked_pitch+src_x*3; *(short*)p=*(short*)t;*(char*)(p+2)=*(char*)(t+2);} break; case 32: *(int*)(locked_surf+y*locked_pitch+x*4)= *(int*)(src->locked_surf+src_y*src->locked_pitch+src_x*4); break; } } void gxCanvas::copyPixel( int x,int y,gxCanvas *src,int src_x,int src_y ){ x+=origin_x;if( x=viewport.right ) return; y+=origin_y;if( y=viewport.bottom ) return; src_x+=src->origin_x;if( src_xviewport.left || src_x>=src->viewport.right ) return; src_y+=src->origin_y;if( src_yviewport.top || src_y>=src->viewport.bottom ) return; lock(); src->lock(); copyPixelFast( x,y,src,src_x,src_y ); src->unlock(); unlock(); } void gxCanvas::setCubeMode( int mode ){ cube_mode=mode; } void gxCanvas::setCubeFace( int face ){ getTexSurface(); surf=cube_surfs[face]; }