2011-01-11 02:55:51 +00:00
using System ;
using System.Runtime.InteropServices ;
using System.Collections.Generic ;
using System.Drawing ;
using System.Drawing.Imaging ;
using System.IO ;
using System.Windows.Forms ;
using SlimDX ;
using SlimDX.Direct3D9 ;
using Font = SlimDX . Direct3D9 . Font ;
using BizHawk.Core ;
namespace BizHawk.MultiClient
{
public class ImageTexture : IDisposable
{
public Device GraphicsDevice ;
public Texture Texture ;
private int imageWidth ;
public int ImageWidth { get { return imageWidth ; } }
private int imageHeight ;
public int ImageHeight { get { return imageHeight ; } }
private int textureWidth ;
public int TextureWidth { get { return textureWidth ; } }
private int textureHeight ;
public int TextureHeight { get { return textureHeight ; } }
public ImageTexture ( Device graphicsDevice )
{
GraphicsDevice = graphicsDevice ;
}
public void SetImage ( int [ ] data , int width , int height )
{
bool needsRecreating = false ;
if ( Texture = = null )
{
needsRecreating = true ;
}
else
{
var currentTextureSize = Texture . GetLevelDescription ( 0 ) ;
if ( imageWidth ! = width | | imageHeight ! = height )
{
needsRecreating = true ;
}
}
// If we need to recreate the texture, do so.
if ( needsRecreating )
{
if ( Texture ! = null )
{
Texture . Dispose ( ) ;
Texture = null ;
}
// Copy the width/height to member fields.
imageWidth = width ;
imageHeight = height ;
// Round up the width/height to the nearest power of two.
textureWidth = 32 ; textureHeight = 32 ;
while ( textureWidth < imageWidth ) textureWidth < < = 1 ;
while ( textureHeight < imageHeight ) textureHeight < < = 1 ;
// Create a new texture instance.
Texture = new Texture ( GraphicsDevice , textureWidth , textureHeight , 1 , Usage . Dynamic , Format . X8R8G8B8 , Pool . Default ) ;
}
// Copy the image data to the texture.
using ( var Data = Texture . LockRectangle ( 0 , new Rectangle ( 0 , 0 , imageWidth , imageHeight ) , LockFlags . None ) . Data )
{
if ( imageWidth = = textureWidth )
{
// Widths are the same, just dump the data across (easy!)
Data . WriteRange ( data , 0 , imageWidth * imageHeight ) ;
}
else
{
// Widths are different, need a bit of additional magic here to make them fit:
long RowSeekOffset = 4 * ( textureWidth - imageWidth ) ;
for ( int r = 0 , s = 0 ; r < imageHeight ; + + r , s + = imageWidth )
{
Data . WriteRange ( data , s , imageWidth ) ;
Data . Seek ( RowSeekOffset , SeekOrigin . Current ) ;
}
}
Texture . UnlockRectangle ( 0 ) ;
}
}
private bool disposed ;
public void Dispose ( )
{
if ( ! disposed )
{
disposed = true ;
if ( Texture ! = null )
Texture . Dispose ( ) ;
Texture = null ;
GC . SuppressFinalize ( this ) ;
}
}
}
public interface IRenderer : IDisposable
{
void Render ( IVideoProvider video ) ;
bool Resized { get ; set ; }
void AddMessage ( string msg ) ;
2011-03-16 06:30:25 +00:00
decimal FPS { get ; set ; }
2011-01-11 02:55:51 +00:00
}
public class SysdrawingRenderPanel : IRenderer
{
public bool Resized { get ; set ; }
public void Dispose ( ) { }
2011-03-16 06:30:25 +00:00
public decimal FPS { get ; set ; }
2011-01-11 02:55:51 +00:00
public void Render ( IVideoProvider video )
{
Color BackgroundColor = Color . FromArgb ( video . BackgroundColor ) ;
int [ ] data = video . GetVideoBuffer ( ) ;
Bitmap bmp = new Bitmap ( video . BufferWidth , video . BufferHeight , PixelFormat . Format32bppArgb ) ;
BitmapData bmpdata = bmp . LockBits ( new Rectangle ( 0 , 0 , bmp . Width , bmp . Height ) , ImageLockMode . WriteOnly , PixelFormat . Format32bppArgb ) ;
//TODO - this is not very intelligent. no handling of pitch, for instance
Marshal . Copy ( data , 0 , bmpdata . Scan0 , bmp . Width * bmp . Height ) ;
bmp . UnlockBits ( bmpdata ) ;
backingControl . SetBitmap ( bmp ) ;
}
public SysdrawingRenderPanel ( RetainedViewportPanel control )
{
backingControl = control ;
}
RetainedViewportPanel backingControl ;
public void AddMessage ( string msg ) { }
}
public class Direct3DRenderPanel : IRenderer
{
public Color BackgroundColor { get ; set ; }
public bool Resized { get ; set ; }
2011-03-16 06:30:25 +00:00
public decimal FPS { get ; set ; }
2011-01-11 02:55:51 +00:00
private Direct3D d3d ;
private Device Device ;
private Control backingControl ;
public ImageTexture Texture ;
private Sprite Sprite ;
private Font MessageFont ;
public Direct3DRenderPanel ( Direct3D direct3D , Control control )
{
d3d = direct3D ;
backingControl = control ;
2011-01-29 08:42:35 +00:00
control . DoubleClick + = ( o , e ) = > Global . MainForm . ToggleFullscreen ( ) ;
2011-01-11 02:55:51 +00:00
}
private void DestroyDevice ( )
{
if ( Texture ! = null )
{
Texture . Dispose ( ) ;
Texture = null ;
}
if ( Sprite ! = null )
{
Sprite . Dispose ( ) ;
Sprite = null ;
}
if ( MessageFont ! = null )
{
MessageFont . Dispose ( ) ;
MessageFont = null ;
}
2011-02-24 02:35:23 +00:00
if ( Device ! = null )
{
Device . Dispose ( ) ;
Device = null ;
}
2011-01-11 02:55:51 +00:00
}
public void CreateDevice ( )
{
DestroyDevice ( ) ;
2011-02-21 09:48:53 +00:00
var pp = new PresentParameters
2011-01-11 02:55:51 +00:00
{
BackBufferWidth = Math . Max ( 1 , backingControl . ClientSize . Width ) ,
BackBufferHeight = Math . Max ( 1 , backingControl . ClientSize . Height ) ,
DeviceWindowHandle = backingControl . Handle ,
2011-02-21 09:48:53 +00:00
PresentationInterval = Global . Config . DisplayVSync ? PresentInterval . One : PresentInterval . Immediate
} ;
Device = new Device ( d3d , 0 , DeviceType . Hardware , backingControl . Handle , CreateFlags . HardwareVertexProcessing , pp ) ;
2011-01-11 02:55:51 +00:00
Sprite = new Sprite ( Device ) ;
Texture = new ImageTexture ( Device ) ;
MessageFont = new Font ( Device , 16 , 0 , FontWeight . Bold , 1 , false , CharacterSet . Default , Precision . Default , FontQuality . Default , PitchAndFamily . Default | PitchAndFamily . DontCare , "Arial" ) ;
}
public void Render ( )
{
if ( Device = = null | | Resized )
CreateDevice ( ) ;
Resized = false ;
Device . Clear ( ClearFlags . Target , BackgroundColor , 1.0f , 0 ) ;
Device . Present ( Present . DoNotWait ) ;
}
public void Render ( IVideoProvider video )
{
if ( video = = null )
{
Render ( ) ;
return ;
}
if ( Device = = null | | Resized )
CreateDevice ( ) ;
Resized = false ;
BackgroundColor = Color . FromArgb ( video . BackgroundColor ) ;
int [ ] data = video . GetVideoBuffer ( ) ;
Texture . SetImage ( data , video . BufferWidth , video . BufferHeight ) ;
Device . Clear ( ClearFlags . Target , BackgroundColor , 1.0f , 0 ) ;
// figure out scaling factor
2011-01-29 08:42:35 +00:00
float widthScale = ( float ) backingControl . Size . Width / video . BufferWidth ;
float heightScale = ( float ) backingControl . Size . Height / video . BufferHeight ;
float finalScale = Math . Min ( widthScale , heightScale ) ;
2011-01-11 02:55:51 +00:00
Device . BeginScene ( ) ;
Sprite . Begin ( SpriteFlags . None ) ;
Device . SetSamplerState ( 0 , SamplerState . MagFilter , TextureFilter . Point ) ;
Device . SetSamplerState ( 1 , SamplerState . MagFilter , TextureFilter . Point ) ;
Sprite . Transform = Matrix . Scaling ( finalScale , finalScale , 0f ) ;
Sprite . Draw ( Texture . Texture , new Rectangle ( 0 , 0 , video . BufferWidth , video . BufferHeight ) , new Vector3 ( video . BufferWidth / 2f , video . BufferHeight / 2f , 0 ) , new Vector3 ( backingControl . Size . Width / 2f / finalScale , backingControl . Size . Height / 2f / finalScale , 0 ) , Color . White ) ;
Sprite . End ( ) ;
DrawMessages ( ) ;
Device . EndScene ( ) ;
Device . Present ( Present . DoNotWait ) ;
}
2011-02-15 16:41:03 +00:00
/// <summary>
/// Display all screen info objects like fps, frame counter, lag counter, and input display
/// </summary>
public void DrawScreenInfo ( )
{
//TODO: If movie loaded use that frame counter, and also display total movie frame count if read-only
2011-02-28 07:17:57 +00:00
if ( Global . Config . DisplayFrameCounter )
2011-03-20 14:08:35 +00:00
MessageFont . DrawString ( null , MakeFrameCounter ( ) , Global . Config . DispFrameCx , Global . Config . DispFrameCy , new Color4 ( Color . White ) ) ; //TODO: Allow user to set screen coordinates?
2011-02-16 02:42:58 +00:00
if ( Global . Config . DisplayInput )
{
2011-02-26 22:04:17 +00:00
string input = MakeInputDisplay ( ) ;
2011-03-20 14:08:35 +00:00
MessageFont . DrawString ( null , input , Global . Config . DispInpx , Global . Config . DispInpy , new Color4 ( Color . White ) ) ;
2011-02-16 02:42:58 +00:00
}
2011-03-16 06:30:25 +00:00
if ( Global . Config . DisplayFPS )
2011-03-20 14:08:35 +00:00
MessageFont . DrawString ( null , FPS . ToString ( ) + " fps" , Global . Config . DispFPSx , Global . Config . DispFPSy , new Color4 ( Color . White ) ) ;
2011-02-15 16:41:03 +00:00
}
2011-03-03 18:25:06 +00:00
private string MakeFrameCounter ( )
{
if ( Global . MainForm . InputLog . GetMovieMode ( ) = = MOVIEMODE . PLAY ) //TODO: use user movie not input log (input log will never be allowed to be played back)
{
2011-03-03 20:01:36 +00:00
return Global . Emulator . Frame . ToString ( ) + " " + Global . MainForm . InputLog . lastLog . ToString ( ) + "/" + Global . MainForm . InputLog . GetMovieLength ( ) . ToString ( ) ;
2011-03-03 18:25:06 +00:00
}
else
{
return Global . Emulator . Frame . ToString ( ) ;
}
}
2011-01-11 02:55:51 +00:00
private List < UIMessage > messages = new List < UIMessage > ( 5 ) ;
public void AddMessage ( string message )
{
messages . Add ( new UIMessage { Message = message , ExpireAt = DateTime . Now + TimeSpan . FromSeconds ( 2 ) } ) ;
}
private void DrawMessages ( )
{
messages . RemoveAll ( m = > DateTime . Now > m . ExpireAt ) ;
2011-02-15 16:41:03 +00:00
DrawScreenInfo ( ) ;
2011-01-11 02:55:51 +00:00
int line = 1 ;
for ( int i = messages . Count - 1 ; i > = 0 ; i - - , line + + )
{
int x = 3 ;
int y = backingControl . Size . Height - ( line * 18 ) ;
MessageFont . DrawString ( null , messages [ i ] . Message , x + 2 , y + 2 , new Color4 ( Color . Black ) ) ;
MessageFont . DrawString ( null , messages [ i ] . Message , x , y , new Color4 ( Color . White ) ) ;
}
}
private bool disposed ;
public void Dispose ( )
{
if ( disposed = = false )
{
disposed = true ;
DestroyDevice ( ) ;
}
}
2011-02-16 14:59:32 +00:00
2011-02-26 22:04:17 +00:00
public string MakeInputDisplay ( )
2011-02-16 14:59:32 +00:00
{
2011-02-26 22:08:55 +00:00
string tmp = Global . Emulator . GetControllersAsMnemonic ( ) ;
2011-02-26 22:04:17 +00:00
tmp = tmp . Replace ( "." , " " ) ;
tmp = tmp . Replace ( "|" , "" ) ;
return tmp ;
2011-02-16 14:59:32 +00:00
}
2011-01-11 02:55:51 +00:00
}
class UIMessage
{
public string Message ;
public DateTime ExpireAt ;
}
}