BizHawk/BizHawk.Client.EmuHawk/GraphicsImplementations/IGL_GdiPlus.cs

409 lines
11 KiB
C#

using System;
using System.IO;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using BizHawk.Bizware.BizwareGL;
using sd = System.Drawing;
using sdi = System.Drawing.Imaging;
//TODO - maybe a layer to cache Graphics parameters (notably, filtering) ?
namespace BizHawk.Client.EmuHawk
{
public class IGL_GdiPlus : IGL
{
// rendering state
private RenderTarget _currRenderTarget;
public IGL_GdiPlus()
{
MyBufferedGraphicsContext = new BufferedGraphicsContext();
}
void IDisposable.Dispose()
{
}
public void Clear(ClearBufferMask mask)
{
var g = GetCurrentGraphics();
if((mask & ClearBufferMask.ColorBufferBit) != 0)
{
g.Clear(_currentClearColor);
}
}
public string API => "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 GDIPTextureWrapper;
tw.Dispose();
}
public Shader CreateFragmentShader(bool cg, string source, string entry, bool required) => null;
public Shader CreateVertexShader(bool cg, string source, string entry, bool required) => null;
public void SetBlendState(IBlendState rsBlend)
{
//TODO for real
}
class MyBlendState : IBlendState { }
static readonly MyBlendState _rsBlendNoneVerbatim = new MyBlendState(), _rsBlendNoneOpaque = new MyBlendState(), _rsBlendNormal = new MyBlendState();
public IBlendState BlendNoneCopy => _rsBlendNoneVerbatim;
public IBlendState BlendNoneOpaque => _rsBlendNoneOpaque;
public IBlendState BlendNormal => _rsBlendNormal;
public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
{
return null;
}
public void FreePipeline(Pipeline pipeline) {}
public VertexLayout CreateVertexLayout() => 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 GDIPTextureWrapper;
if (pname == TextureParameterName.TextureMinFilter)
tw.MinFilter = (TextureMinFilter)param;
if (pname == TextureParameterName.TextureMagFilter)
tw.MagFilter = (TextureMagFilter)param;
}
public Texture2d LoadTexture(sd.Bitmap bitmap)
{
var sdBitmap = (Bitmap)bitmap.Clone();
GDIPTextureWrapper tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
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) => null;
public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height)
{
// TODO - need to rip the texture data. we had code for that somewhere...
return null;
}
public void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
{
var tw = tex.Opaque as GDIPTextureWrapper;
bmp.ToSysdrawingBitmap(tw.SDBitmap);
}
public Texture2d LoadTexture(BitmapBuffer bmp)
{
// definitely needed (by TextureFrugalizer at least)
var sdBitmap = bmp.ToSysdrawingBitmap();
var tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
return new Texture2d(this, tw, bmp.Width, bmp.Height);
}
public BitmapBuffer ResolveTexture2d(Texture2d tex)
{
var tw = tex.Opaque as GDIPTextureWrapper;
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 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;
/// <summary>
/// the control associated with this render target (if any)
/// </summary>
public GLControlWrapper_GdiPlus Control;
/// <summary>
/// the offscreen render target, if that's what this is representing
/// </summary>
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 GDIPTextureWrapper;
r = Target.Texture2d.Rectangle;
refGraphics = Graphics.FromImage(tw.SDBitmap);
}
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
var rtw = new RenderTargetWrapper(this) { 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)
{
var tw = new GDIPTextureWrapper
{
SDBitmap = new Bitmap(w, h, sdi.PixelFormat.Format32bppArgb)
};
var tex = new Texture2d(this, tw, w, h);
var 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)
{
//don't 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 GDIPTextureWrapper;
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;
} //class IGL_GdiPlus
}