#include "std.h" #include "gxgraphics.h" #include "gxruntime.h" extern gxRuntime *gx_runtime; gxGraphics::gxGraphics( gxRuntime *rt,IDirectDraw7 *dd,IDirectDrawSurface7 *fs,IDirectDrawSurface7 *bs,bool d3d ): runtime(rt),dirDraw(dd),dir3d(0),dir3dDev(0),def_font(0),gfx_lost(false),dummy_mesh(0){ dirDraw->QueryInterface( IID_IDirectDraw,(void**)&ds_dirDraw ); front_canvas=d_new gxCanvas( this,fs,0 ); back_canvas=d_new gxCanvas( this,bs,0 ); front_canvas->cls(); back_canvas->cls(); def_font=loadFont( "courier",12,0 ); front_canvas->setFont( def_font ); back_canvas->setFont( def_font ); memset(&primFmt,0,sizeof(primFmt)); primFmt.dwSize=sizeof(primFmt); fs->GetPixelFormat( &primFmt ); //are we fullscreen? _gamma=0; if( fs!=bs ){ if( fs->QueryInterface( IID_IDirectDrawGammaControl,(void**)&_gamma )>=0 ){ if( _gamma->GetGammaRamp( 0,&_gammaRamp )<0 ) _gamma=0; } } if( !_gamma ){ for( int k=0;k<256;++k ) _gammaRamp.red[k]=_gammaRamp.blue[k]=_gammaRamp.green[k]=k; } } gxGraphics::~gxGraphics(){ if( _gamma ) _gamma->Release(); #ifdef PRO while( scene_set.size() ) freeScene( *scene_set.begin() ); #endif while( movie_set.size() ) closeMovie( *movie_set.begin() ); while( font_set.size() ) freeFont( *font_set.begin() ); while( canvas_set.size() ) freeCanvas( *canvas_set.begin() ); set::iterator it; for( it=font_res.begin();it!=font_res.end();++it ) RemoveFontResource( (*it).c_str() ); font_res.clear(); delete back_canvas; delete front_canvas; ds_dirDraw->Release(); dirDraw->RestoreDisplayMode(); dirDraw->Release(); } void gxGraphics::setGamma( int r,int g,int b,float dr,float dg,float db ){ _gammaRamp.red[r&255]=dr*257.0f; _gammaRamp.green[g&255]=dg*257.0f; _gammaRamp.blue[b&255]=db*257.0f; } void gxGraphics::updateGamma( bool calibrate ){ if( !_gamma ) return; _gamma->SetGammaRamp( calibrate ? DDSGR_CALIBRATE : 0,&_gammaRamp ); } void gxGraphics::getGamma( int r,int g,int b,float *dr,float *dg,float *db ){ *dr=_gammaRamp.red[r&255]/257.0f; *dg=_gammaRamp.green[g&255]/257.0f; *db=_gammaRamp.blue[b&255]/257.0f; } void gxGraphics::backup(){ } bool gxGraphics::restore(){ while( dirDraw->TestCooperativeLevel()!=DD_OK ){ if( dirDraw->TestCooperativeLevel()==DDERR_WRONGMODE ) return false; Sleep( 100 ); } if( back_canvas->getSurface()->IsLost()==DD_OK ) return true; dirDraw->RestoreAllSurfaces(); //restore all canvases set::iterator it; for( it=canvas_set.begin();it!=canvas_set.end();++it ){ (*it)->restore(); } #ifdef PRO //restore all meshes (b3d surfaces) set::iterator mesh_it; for( mesh_it=mesh_set.begin();mesh_it!=mesh_set.end();++mesh_it ){ (*mesh_it)->restore(); } if( dir3d ) dir3d->EvictManagedTextures(); #endif return true; } gxCanvas *gxGraphics::getFrontCanvas()const{ return front_canvas; } gxCanvas *gxGraphics::getBackCanvas()const{ return back_canvas; } gxFont *gxGraphics::getDefaultFont()const{ return def_font; } void gxGraphics::vwait(){ dirDraw->WaitForVerticalBlank( DDWAITVB_BLOCKBEGIN,0 ); } void gxGraphics::flip( bool v ){ runtime->flip( v ); } void gxGraphics::copy( gxCanvas *dest,int dx,int dy,int dw,int dh,gxCanvas *src,int sx,int sy,int sw,int sh ){ RECT r={ dx,dy,dx+dw,dy+dh }; ddUtil::copy( dest->getSurface(),dx,dy,dw,dh,src->getSurface(),sx,sy,sw,sh ); dest->damage( r ); } int gxGraphics::getScanLine()const{ DWORD t=0; dirDraw->GetScanLine( &t ); return t; } int gxGraphics::getTotalVidmem()const{ DDCAPS caps={sizeof(caps)}; dirDraw->GetCaps( &caps,0 ); return caps.dwVidMemTotal; } int gxGraphics::getAvailVidmem()const{ DDCAPS caps={sizeof(caps)}; dirDraw->GetCaps( &caps,0 ); return caps.dwVidMemFree; } gxMovie *gxGraphics::openMovie( const string &file,int flags ){ IAMMultiMediaStream *iam_stream; if( CoCreateInstance( CLSID_AMMultiMediaStream,NULL,CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream,(void **)&iam_stream )==S_OK ){ if( iam_stream->Initialize( STREAMTYPE_READ,AMMSF_NOGRAPHTHREAD,NULL )==S_OK ){ if( iam_stream->AddMediaStream( ds_dirDraw,&MSPID_PrimaryVideo,0,NULL )==S_OK ){ iam_stream->AddMediaStream( NULL,&MSPID_PrimaryAudio,AMMSF_ADDDEFAULTRENDERER,NULL ); WCHAR *path=new WCHAR[ file.size()+1 ]; MultiByteToWideChar( CP_ACP,0,file.c_str(),-1,path,sizeof(WCHAR)*(file.size()+1) ); int n=iam_stream->OpenFile( path,0 ); delete path; if( n==S_OK ){ gxMovie *movie=d_new gxMovie( this,iam_stream ); movie_set.insert( movie ); return movie; } } } iam_stream->Release(); } return 0; } gxMovie *gxGraphics::verifyMovie( gxMovie *m ){ return movie_set.count( m ) ? m : 0; } void gxGraphics::closeMovie( gxMovie *m ){ if( movie_set.erase( m ) ) delete m; } gxCanvas *gxGraphics::createCanvas( int w,int h,int flags ){ ddSurf *s=ddUtil::createSurface( w,h,flags,this ); if( !s ) return 0; gxCanvas *c=d_new gxCanvas( this,s,flags ); canvas_set.insert( c ); c->cls(); return c; } gxCanvas *gxGraphics::loadCanvas( const string &f,int flags ){ ddSurf *s=ddUtil::loadSurface( f,flags,this ); if( !s ) return 0; gxCanvas *c=d_new gxCanvas( this,s,flags ); canvas_set.insert( c ); return c; } gxCanvas *gxGraphics::verifyCanvas( gxCanvas *c ){ return canvas_set.count( c ) || c==front_canvas || c==back_canvas ? c : 0; } void gxGraphics::freeCanvas( gxCanvas *c ){ if( canvas_set.erase( c ) ) delete c; } int gxGraphics::getWidth()const{ return front_canvas->getWidth(); } int gxGraphics::getHeight()const{ return front_canvas->getHeight(); } int gxGraphics::getDepth()const{ return front_canvas->getDepth(); } gxFont *gxGraphics::loadFont( const string &f,int height,int flags ){ int bold=flags & gxFont::FONT_BOLD ? FW_BOLD : FW_REGULAR; int italic=flags & gxFont::FONT_ITALIC ? 1 : 0; int underline=flags & gxFont::FONT_UNDERLINE ? 1 : 0; int strikeout=0; string t; int n=f.find('.'); if( n!=string::npos ){ t=fullfilename(f); if( !font_res.count(t) && AddFontResource( t.c_str() ) ) font_res.insert( t ); t=filenamefile( f.substr(0,n) ); }else{ t=f; } //save and turn off font smoothing.... BOOL smoothing=FALSE; SystemParametersInfo( SPI_GETFONTSMOOTHING,0,&smoothing,0 ); SystemParametersInfo( SPI_SETFONTSMOOTHING,FALSE,0,0 ); HFONT hfont=CreateFont( height,0,0,0, bold,italic,underline,strikeout, ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE,t.c_str() ); if( !hfont ){ //restore font smoothing SystemParametersInfo( SPI_SETFONTSMOOTHING,smoothing,0,0 ); return 0; } HDC hdc=CreateCompatibleDC( 0 ); HFONT pfont=(HFONT)SelectObject( hdc,hfont ); TEXTMETRIC tm={0}; if( !GetTextMetrics( hdc,&tm ) ){ SelectObject( hdc,pfont ); DeleteDC( hdc ); DeleteObject( hfont ); SystemParametersInfo( SPI_SETFONTSMOOTHING,smoothing,0,0 ); return 0; } height=tm.tmHeight; int first=tm.tmFirstChar,last=tm.tmLastChar; int sz=last-first+1; int *offs=d_new int[sz]; int *widths=d_new int[sz]; int *as=d_new int[sz]; //calc size of canvas to hold font. int x=0,y=0,max_x=0; for( int k=0;kgetWidth() ){ x=0;y+=height; } offs[k]=(x<<16)|y; widths[k]=w; x+=w;if( x>max_x ) max_x=x; } SelectObject( hdc,pfont ); DeleteDC( hdc ); int cw=max_x,ch=y+height; if( gxCanvas *c=createCanvas( cw,ch,0 ) ){ ddSurf *surf=c->getSurface(); HDC surf_hdc; if( surf->GetDC( &surf_hdc )>=0 ){ HFONT pfont=(HFONT)SelectObject( surf_hdc,hfont ); SetBkColor( surf_hdc,0x000000 ); SetTextColor( surf_hdc,0xffffff ); for( int k=0;k>16)&0xffff,y=offs[k]&0xffff; char t=k+first; RECT rect={x,y,x+widths[k],y+height}; ExtTextOut( surf_hdc,x+as[k],y,ETO_CLIPPED,&rect,&t,1,0 ); } SelectObject( surf_hdc,pfont ); surf->ReleaseDC( surf_hdc ); DeleteObject( hfont ); delete[] as; c->backup(); gxFont *font=d_new gxFont( this,c,tm.tmMaxCharWidth,height,first,last+1,tm.tmDefaultChar,offs,widths ); font_set.insert( font ); //restore font smoothing SystemParametersInfo( SPI_SETFONTSMOOTHING,smoothing,0,0 ); return font; }else{ } freeCanvas( c ); }else{ } DeleteObject( hfont ); delete[] as; delete[] widths; delete[] offs; //restore font smoothing SystemParametersInfo( SPI_SETFONTSMOOTHING,smoothing,0,0 ); return 0; } gxFont *gxGraphics::verifyFont( gxFont *f ){ return font_set.count( f ) ? f : 0; } void gxGraphics::freeFont( gxFont *f ){ if( font_set.erase( f ) ) delete f; } ////////////// // 3D STUFF // ////////////// #ifdef PRO static int maxDevType; static HRESULT CALLBACK enumDevice( char *desc,char *name,D3DDEVICEDESC7 *devDesc,void *context ){ gxGraphics *g=(gxGraphics*)context; int t=0; GUID guid=devDesc->deviceGUID; if( guid==IID_IDirect3DRGBDevice ) t=1; else if( guid==IID_IDirect3DHALDevice ) t=2; else if( guid==IID_IDirect3DTnLHalDevice ) t=3; if( t>maxDevType ){ g->dir3dDevDesc=*devDesc; maxDevType=t; } return D3DENUMRET_OK; } static HRESULT CALLBACK enumZbuffFormat( LPDDPIXELFORMAT format,void *context ){ gxGraphics *g=(gxGraphics*)context; if( format->dwZBufferBitDepth==g->primFmt.dwRGBBitCount ){ g->zbuffFmt=*format; return D3DENUMRET_CANCEL; } if( format->dwZBufferBitDepth>g->zbuffFmt.dwZBufferBitDepth ){ if( format->dwZBufferBitDepthprimFmt.dwRGBBitCount ){ g->zbuffFmt=*format; } } return D3DENUMRET_OK; } struct TexFmt{ DDPIXELFORMAT fmt; int bits,a_bits,rgb_bits; }; static int cntBits( int mask ){ int n=0; for( int k=0;k<32;++k ){ if( mask & (1< tex_fmts; static HRESULT CALLBACK enumTextureFormat( DDPIXELFORMAT *fmt,void *p ){ TexFmt t; t.fmt=*fmt; t.bits=fmt->dwRGBBitCount; t.a_bits=(fmt->dwFlags & DDPF_ALPHAPIXELS) ? cntBits(fmt->dwRGBAlphaBitMask) : 0; t.rgb_bits=(fmt->dwFlags & DDPF_RGB) ? cntBits(fmt->dwRBitMask|fmt->dwGBitMask|fmt->dwBBitMask) : 0; tex_fmts.push_back( t ); return D3DENUMRET_OK; } static string itobin( int n ){ string t; for( int k=0;k<32;n<<=1,++k ){ t+=(n&0x80000000) ? '1' : '0'; } return t; } static void debugPF( const DDPIXELFORMAT &pf ){ string t; t="Bits:"+itoa( pf.dwRGBBitCount ); gx_runtime->debugLog( t.c_str() ); t="R Mask:"+itobin( pf.dwRBitMask ); gx_runtime->debugLog( t.c_str() ); t="G Mask:"+itobin( pf.dwGBitMask ); gx_runtime->debugLog( t.c_str() ); t="B Mask:"+itobin( pf.dwBBitMask ); gx_runtime->debugLog( t.c_str() ); t="A Mask:"+itobin( pf.dwRGBAlphaBitMask ); gx_runtime->debugLog( t.c_str() ); } static void pickTexFmts( gxGraphics *g,int hi ){ //texRGBFmt. { int pick=-1,max=0,bits; for( int d=g->primFmt.dwRGBBitCount;d<=32;d+=8 ){ for( int k=0;kd || !t.rgb_bits || t.rgb_bits=bits ) continue; pick=k;max=t.rgb_bits;bits=t.bits; } if( !hi && pick>=0 ) break; } if( pick<0 ) g->texRGBFmt[hi]=g->primFmt; else g->texRGBFmt[hi]=tex_fmts[pick].fmt; } //texAlphaFmt { int pick=-1,max=0,bits; for( int d=g->primFmt.dwRGBBitCount;d<=32;d+=8 ){ for( int k=0;kd || !t.a_bits || t.a_bits=bits ) continue; pick=k;max=t.a_bits;bits=t.bits; } if( !hi && pick>=0 ) break; } if( pick<0 ) g->texAlphaFmt[hi]=g->primFmt; else g->texAlphaFmt[hi]=tex_fmts[pick].fmt; } //texRGBAlphaFmt { int pick=-1,a8rgb8=-1,max=0,bits; for( int d=g->primFmt.dwRGBBitCount;d<=32;d+=8 ){ for( int k=0;kd || !t.a_bits || !t.rgb_bits || t.a_bits=bits ) continue; pick=k;max=t.a_bits;bits=t.bits; } if( !hi && pick>=0 ) break; } if( pick<0 ) pick=a8rgb8; if( pick<0 ) g->texRGBAlphaFmt[hi]=g->primFmt; else g->texRGBAlphaFmt[hi]=tex_fmts[pick].fmt; } //texRGBMaskFmt... { int pick=-1,max=0,bits; for( int d=g->primFmt.dwRGBBitCount;d<=32;d+=8 ){ for( int k=0;k=bits ) continue; pick=k;max=t.rgb_bits;bits=t.bits; } if( !hi && pick>=0 ) break; } if( pick<0 ) g->texRGBMaskFmt[hi]=g->primFmt; else g->texRGBMaskFmt[hi]=tex_fmts[pick].fmt; } } gxScene *gxGraphics::createScene( int flags ){ if( scene_set.size() ) return 0; //get d3d if( dirDraw->QueryInterface( IID_IDirect3D7,(void**)&dir3d )>=0 ){ //enum devices maxDevType=0; if( dir3d->EnumDevices( enumDevice,this )>=0 && maxDevType>1 ){ //enum zbuffer formats zbuffFmt.dwZBufferBitDepth=0; if( dir3d->EnumZBufferFormats( dir3dDevDesc.deviceGUID,enumZbuffFormat,this )>=0 ){ //create zbuff for back buffer if( back_canvas->attachZBuffer() ){ //create 3d device if( dir3d->CreateDevice( dir3dDevDesc.deviceGUID,back_canvas->getSurface(),&dir3dDev )>=0 ){ //enum texture formats tex_fmts.clear(); if( dir3dDev->EnumTextureFormats( enumTextureFormat,this )>=0 ){ pickTexFmts( this,0 ); pickTexFmts( this,1 ); tex_fmts.clear(); #ifdef BETA gx_runtime->debugLog( "Texture RGB format:" ); debugPF( texRGBFmt ); gx_runtime->debugLog( "Texture Alpha format:" ); debugPF( texAlphaFmt ); gx_runtime->debugLog( "Texture RGB Alpha format:" ); debugPF( texRGBAlphaFmt ); gx_runtime->debugLog( "Texture RGB Mask format:" ); debugPF( texRGBMaskFmt ); gx_runtime->debugLog( "Texture Primary format:" ); debugPF( primFmt ); string ts="ZBuffer Bit Depth:"+itoa( zbuffFmt.dwZBufferBitDepth ); gx_runtime->debugLog( ts.c_str() ); #endif gxScene *scene=d_new gxScene( this,back_canvas ); scene_set.insert( scene ); dummy_mesh=createMesh( 8,12,0 ); return scene; } dir3dDev->Release(); dir3dDev=0; } back_canvas->releaseZBuffer(); } } } dir3d->Release(); dir3d=0; } return 0; } gxScene *gxGraphics::verifyScene( gxScene *s ){ return scene_set.count( s ) ? s : 0; } void gxGraphics::freeScene( gxScene *scene ){ if( !scene_set.erase( scene ) ) return; dummy_mesh=0; while( mesh_set.size() ) freeMesh( *mesh_set.begin() ); back_canvas->releaseZBuffer(); if( dir3dDev ){ dir3dDev->Release();dir3dDev=0; } if( dir3d ){ dir3d->Release();dir3d=0; } delete scene; } gxMesh *gxGraphics::createMesh( int max_verts,int max_tris,int flags ){ static const int VTXFMT= D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX2| D3DFVF_TEXCOORDSIZE2(0)|D3DFVF_TEXCOORDSIZE2(1); int vbflags=0; //XP or less? if( runtime->osinfo.dwMajorVersion<6 ){ vbflags|=D3DVBCAPS_WRITEONLY; } D3DVERTEXBUFFERDESC desc={ sizeof(desc),vbflags,VTXFMT,max_verts }; IDirect3DVertexBuffer7 *buff; if( dir3d->CreateVertexBuffer( &desc,&buff,0 )<0 ) return 0; WORD *indices=d_new WORD[max_tris*3]; gxMesh *mesh=d_new gxMesh( this,buff,indices,max_verts,max_tris ); mesh_set.insert( mesh ); return mesh; } gxMesh *gxGraphics::verifyMesh( gxMesh *m ){ return mesh_set.count( m ) ? m : 0; } void gxGraphics::freeMesh( gxMesh *mesh ){ if( mesh_set.erase( mesh ) ) delete mesh; } #endif