using System; using System.IO; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; using swf = System.Windows.Forms; using sd = System.Drawing; using sdi = System.Drawing.Imaging; using OpenTK; using OpenTK.Graphics.OpenGL; using BizHawk.Bizware.BizwareGL; //TODO - maybe a layer to cache Graphics parameters (notably, filtering) ? namespace BizHawk.Bizware.BizwareGL.Drivers.GdiPlus { public class IGL_GdiPlus : IGL { //rendering state RenderTarget _CurrRenderTarget; public IGL_GdiPlus() { MyBufferedGraphicsContext = new BufferedGraphicsContext(); } void IDisposable.Dispose() { } public void Clear(OpenTK.Graphics.OpenGL.ClearBufferMask mask) { var g = GetCurrentGraphics(); if((mask & ClearBufferMask.ColorBufferBit) != 0) { g.Clear(_currentClearColor); } } public string API { get { return "GDIPLUS"; } } public IBlendState CreateBlendState(BlendingFactorSrc colorSource, BlendEquationMode colorEquation, BlendingFactorDest colorDest, BlendingFactorSrc alphaSource, BlendEquationMode alphaEquation, BlendingFactorDest alphaDest) { return null; } private sd.Color _currentClearColor = Color.Transparent; public void SetClearColor(sd.Color color) { _currentClearColor = color; } public unsafe void BindArrayData(void* pData) { } public void FreeTexture(Texture2d tex) { var tw = tex.Opaque as TextureWrapper; tw.Dispose(); } public Shader CreateFragmentShader(bool cg, string source, string entry, bool required) { return null; } public Shader CreateVertexShader(bool cg, string source, string entry, bool required) { return null; } public void FreeShader(IntPtr shader) { } public void SetBlendState(IBlendState rsBlend) { //TODO for real } class MyBlendState : IBlendState { } static MyBlendState _rsBlendNoneVerbatim = new MyBlendState(), _rsBlendNoneOpaque = new MyBlendState(), _rsBlendNormal = new MyBlendState(); public IBlendState BlendNoneCopy { get { return _rsBlendNoneVerbatim; } } public IBlendState BlendNoneOpaque { get { return _rsBlendNoneOpaque; } } public IBlendState BlendNormal { get { return _rsBlendNormal; } } public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo) { return null; } public void FreePipeline(Pipeline pipeline) {} public VertexLayout CreateVertexLayout() { return new VertexLayout(this, new IntPtr(0)); } public void SetTextureWrapMode(Texture2d tex, bool clamp) { } public void DrawArrays(PrimitiveType mode, int first, int count) { } public void BindPipeline(Pipeline pipeline) { } public void Internal_FreeShader(Shader shader) { } public void SetPipelineUniform(PipelineUniform uniform, bool value) { } public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose) { } public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose) { } public void SetPipelineUniform(PipelineUniform uniform, Vector4 value) { } public void SetPipelineUniform(PipelineUniform uniform, Vector2 value) { } public void SetPipelineUniform(PipelineUniform uniform, float value) { } public unsafe void SetPipelineUniform(PipelineUniform uniform, Vector4[] values) { } public void SetPipelineUniformSampler(PipelineUniform uniform, Texture2d tex) { } public void TexParameter2d(Texture2d tex, TextureParameterName pname, int param) { var tw = tex.Opaque as TextureWrapper; if (pname == TextureParameterName.TextureMinFilter) tw.MinFilter = (TextureMinFilter)param; if (pname == TextureParameterName.TextureMagFilter) tw.MagFilter = (TextureMagFilter)param; } public Texture2d LoadTexture(sd.Bitmap bitmap) { var sdbmp = (sd.Bitmap)bitmap.Clone(); TextureWrapper tw = new TextureWrapper(); tw.SDBitmap = sdbmp; return new Texture2d(this, tw, bitmap.Width, bitmap.Height); } public Texture2d LoadTexture(Stream stream) { using (var bmp = new BitmapBuffer(stream, new BitmapLoadOptions())) return (this as IGL).LoadTexture(bmp); } public Texture2d CreateTexture(int width, int height) { return null; } public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height) { //TODO - need to rip the texturedata. we had code for that somewhere... return null; } public void LoadTextureData(Texture2d tex, BitmapBuffer bmp) { var tw = tex.Opaque as TextureWrapper; bmp.ToSysdrawingBitmap(tw.SDBitmap); } public Texture2d LoadTexture(BitmapBuffer bmp) { //definitely needed (by TextureFrugalizer at least) var sdbmp = bmp.ToSysdrawingBitmap(); var tw = new TextureWrapper(); tw.SDBitmap = sdbmp; return new Texture2d(this, tw, bmp.Width, bmp.Height); } public unsafe BitmapBuffer ResolveTexture2d(Texture2d tex) { var tw = tex.Opaque as TextureWrapper; var blow = new BitmapLoadOptions() { AllowWrap = false //must be an independent resource }; var bb = new BitmapBuffer(tw.SDBitmap,blow); return bb; } public Texture2d LoadTexture(string path) { //todo //using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) // return (this as IGL).LoadTexture(fs); return null; } public Matrix4 CreateGuiProjectionMatrix(int w, int h) { return CreateGuiProjectionMatrix(new sd.Size(w, h)); } public Matrix4 CreateGuiViewMatrix(int w, int h, bool autoflip) { return CreateGuiViewMatrix(new sd.Size(w, h), autoflip); } public Matrix4 CreateGuiProjectionMatrix(sd.Size dims) { //see CreateGuiViewMatrix for more return Matrix4.Identity; } public Matrix4 CreateGuiViewMatrix(sd.Size dims, bool autoflip) { //on account of gdi+ working internally with a default view exactly like we want, we don't need to setup a new one here //furthermore, we _cant_, without inverting the GuiView and GuiProjection before drawing, to completely undo it //this might be feasible, but its kind of slow and annoying and worse, seemingly numerically unstable //if (autoflip && _CurrRenderTarget != null) //{ // Matrix4 ret = Matrix4.Identity; // ret.M22 = -1; // ret.M42 = dims.Height; // return ret; //} //else return Matrix4.Identity; } public void SetViewport(int x, int y, int width, int height) { } public void SetViewport(int width, int height) { } public void SetViewport(sd.Size size) { SetViewport(size.Width, size.Height); } public void SetViewport(swf.Control control) { } public void BeginControl(GLControlWrapper_GdiPlus control) { CurrentControl = control; } public void EndControl(GLControlWrapper_GdiPlus control) { CurrentControl = null; } public void SwapControl(GLControlWrapper_GdiPlus control) { } public class RenderTargetWrapper { public RenderTargetWrapper(IGL_GdiPlus gdi) { Gdi = gdi; } public void Dispose() { } IGL_GdiPlus Gdi; /// /// the control associated with this render target (if any) /// public GLControlWrapper_GdiPlus Control; /// /// the offscreen render target, if that's what this is representing /// public RenderTarget Target; public BufferedGraphics MyBufferedGraphics; public Graphics refGraphics; //?? hacky? public void CreateGraphics() { Rectangle r; if (Control != null) { r = Control.ClientRectangle; refGraphics = Control.CreateGraphics(); } else { var tw = Target.Texture2d.Opaque as TextureWrapper; r = Target.Texture2d.Rectangle; refGraphics = Graphics.FromImage(tw.SDBitmap); } if (MyBufferedGraphics != null) MyBufferedGraphics.Dispose(); MyBufferedGraphics = Gdi.MyBufferedGraphicsContext.Allocate(refGraphics, r); //MyBufferedGraphics.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed; ////not sure about this stuff... ////it will wreck alpha blending, for one thing //MyBufferedGraphics.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; //MyBufferedGraphics.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; } } public void BeginScene() { } public void EndScene() { //maybe an inconsistent semantic with other implementations.. //but accomplishes the needed goal of getting the current RT to render BindRenderTarget(null); } public IGraphicsControl Internal_CreateGraphicsControl() { var ret = new GLControlWrapper_GdiPlus(this); //create a render target for this control RenderTargetWrapper rtw = new RenderTargetWrapper(this); rtw.Control = ret; ret.RenderTargetWrapper = rtw; return ret; } public void FreeRenderTarget(RenderTarget rt) { var rtw = rt.Opaque as RenderTargetWrapper; rtw.Dispose(); } public unsafe RenderTarget CreateRenderTarget(int w, int h) { TextureWrapper tw = new TextureWrapper(); tw.SDBitmap = new Bitmap(w,h, sdi.PixelFormat.Format32bppArgb); var tex = new Texture2d(this, tw, w, h); RenderTargetWrapper rtw = new RenderTargetWrapper(this); var rt = new RenderTarget(this, rtw, tex); rtw.Target = rt; return rt; } public void BindRenderTarget(RenderTarget rt) { if (_CurrentOffscreenGraphics != null) { _CurrentOffscreenGraphics.Dispose(); _CurrentOffscreenGraphics = null; } _CurrRenderTarget = rt; if (CurrentRenderTargetWrapper != null) { if (CurrentRenderTargetWrapper == CurrentControl.RenderTargetWrapper) { //dont do anything til swapbuffers } else { //CurrentRenderTargetWrapper.MyBufferedGraphics.Render(); } } if (rt == null) { //null means to use the default RT for the current control CurrentRenderTargetWrapper = CurrentControl.RenderTargetWrapper; } else { var tw = rt.Texture2d.Opaque as TextureWrapper; CurrentRenderTargetWrapper = rt.Opaque as RenderTargetWrapper; _CurrentOffscreenGraphics = Graphics.FromImage(tw.SDBitmap); //if (CurrentRenderTargetWrapper.MyBufferedGraphics == null) // CurrentRenderTargetWrapper.CreateGraphics(); } } Graphics _CurrentOffscreenGraphics; public Graphics GetCurrentGraphics() { if (_CurrentOffscreenGraphics != null) return _CurrentOffscreenGraphics; var rtw = CurrentRenderTargetWrapper; return rtw.MyBufferedGraphics.Graphics; } public GLControlWrapper_GdiPlus CurrentControl; public RenderTargetWrapper CurrentRenderTargetWrapper; public BufferedGraphicsContext MyBufferedGraphicsContext; public class TextureWrapper : IDisposable { public sd.Bitmap SDBitmap; public TextureMinFilter MinFilter = TextureMinFilter.Nearest; public TextureMagFilter MagFilter = TextureMagFilter.Nearest; public void Dispose() { if (SDBitmap != null) { SDBitmap.Dispose(); SDBitmap = null; } } } } //class IGL_GdiPlus }