2012-04-16 08:18:41 +00:00
|
|
|
|
using System;
|
2014-02-07 02:36:27 +00:00
|
|
|
|
using System.IO;
|
2013-10-27 22:13:08 +00:00
|
|
|
|
using System.Linq;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2013-11-04 01:39:19 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2013-10-25 00:57:23 +00:00
|
|
|
|
using BizHawk.Client.Common;
|
|
|
|
|
|
2014-01-27 00:02:21 +00:00
|
|
|
|
using BizHawk.Bizware.BizwareGL;
|
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
using OpenTK;
|
|
|
|
|
using OpenTK.Graphics;
|
|
|
|
|
|
2013-11-03 03:54:37 +00:00
|
|
|
|
namespace BizHawk.Client.EmuHawk
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A DisplayManager is destined forevermore to drive the PresentationPanel it gets initialized with.
|
|
|
|
|
/// Its job is to receive OSD and emulator outputs, and produce one single buffer (BitampBuffer? Texture2d?) for display by the PresentationPanel.
|
|
|
|
|
/// Details TBD
|
|
|
|
|
/// </summary>
|
2012-04-16 08:18:41 +00:00
|
|
|
|
public class DisplayManager : IDisposable
|
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
|
|
|
|
public DisplayManager(PresentationPanel presentationPanel)
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
GL = GlobalWin.GL;
|
|
|
|
|
this.presentationPanel = presentationPanel;
|
|
|
|
|
GraphicsControl = this.presentationPanel.GraphicsControl;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//it's sort of important for these to be initialized to something nonzero
|
|
|
|
|
currEmuWidth = currEmuHeight = 1;
|
2012-09-02 14:54:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
Renderer = new GuiRenderer(GL);
|
2014-02-14 00:55:18 +00:00
|
|
|
|
|
2014-02-03 05:27:59 +00:00
|
|
|
|
Video2xFrugalizer = new RenderTargetFrugalizer(GL);
|
2014-02-14 00:55:18 +00:00
|
|
|
|
VideoTextureFrugalizer = new TextureFrugalizer(GL);
|
2012-09-02 14:54:30 +00:00
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
ShaderChainFrugalizers = new RenderTargetFrugalizer[16]; //hacky hardcoded limit.. need some other way to manage these
|
|
|
|
|
for (int i = 0; i < 16; i++)
|
2014-02-03 05:27:59 +00:00
|
|
|
|
{
|
2014-02-07 02:36:27 +00:00
|
|
|
|
ShaderChainFrugalizers[i] = new RenderTargetFrugalizer(GL);
|
2014-02-03 05:27:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
using (var xml = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.Resources.courier16px.fnt"))
|
|
|
|
|
using (var tex = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.Resources.courier16px_0.png"))
|
|
|
|
|
TheOneFont = new StringRenderer(GL, xml, tex);
|
2014-02-03 07:01:31 +00:00
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
var fiHq2x = new FileInfo(System.IO.Path.Combine(PathManager.GetExeDirectoryAbsolute(),"Shaders/BizHawk/hq2x.cgp"));
|
|
|
|
|
if(fiHq2x.Exists)
|
|
|
|
|
using(var stream = fiHq2x.OpenRead())
|
|
|
|
|
ShaderChain_hq2x = new RetroShaderChain(GL,new RetroShaderPreset(stream), System.IO.Path.Combine(PathManager.GetExeDirectoryAbsolute(),"Shaders/BizHawk"));
|
|
|
|
|
var fiScanlines = new FileInfo(System.IO.Path.Combine(PathManager.GetExeDirectoryAbsolute(), "Shaders/BizHawk/BizScanlines.cgp"));
|
|
|
|
|
if (fiScanlines.Exists)
|
|
|
|
|
using (var stream = fiScanlines.OpenRead())
|
|
|
|
|
ShaderChain_scanlines = new RetroShaderChain(GL,new RetroShaderPreset(stream), System.IO.Path.Combine(PathManager.GetExeDirectoryAbsolute(),"Shaders/BizHawk"));
|
2014-02-14 00:55:18 +00:00
|
|
|
|
|
|
|
|
|
LuaSurfaceSets["emu"] = new SwappableDisplaySurfaceSet();
|
|
|
|
|
LuaSurfaceSets["native"] = new SwappableDisplaySurfaceSet();
|
|
|
|
|
LuaSurfaceFrugalizers["emu"] = new TextureFrugalizer(GL);
|
|
|
|
|
LuaSurfaceFrugalizers["native"] = new TextureFrugalizer(GL);
|
2012-09-02 14:54:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public bool Disposed { get; private set; }
|
2012-09-21 18:01:24 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public void Dispose()
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
if (Disposed) return;
|
|
|
|
|
Disposed = true;
|
|
|
|
|
VideoTextureFrugalizer.Dispose();
|
2014-02-14 00:55:18 +00:00
|
|
|
|
foreach (var f in LuaSurfaceFrugalizers.Values)
|
|
|
|
|
f.Dispose();
|
2014-02-07 02:36:27 +00:00
|
|
|
|
foreach (var f in ShaderChainFrugalizers)
|
|
|
|
|
if (f != null)
|
|
|
|
|
f.Dispose();
|
2014-01-28 04:39:27 +00:00
|
|
|
|
}
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
//dont know what to do about this yet
|
|
|
|
|
public bool NeedsToPaint { get; set; }
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//rendering resources:
|
|
|
|
|
IGL GL;
|
|
|
|
|
StringRenderer TheOneFont;
|
|
|
|
|
GuiRenderer Renderer;
|
2012-07-15 07:38:36 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//layer resources
|
|
|
|
|
PresentationPanel presentationPanel; //well, its the final layer's target, at least
|
|
|
|
|
GraphicsControl GraphicsControl; //well, its the final layer's target, at least
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// these variables will track the dimensions of the last frame's (or the next frame? this is confusing) emulator native output size
|
|
|
|
|
/// </summary>
|
|
|
|
|
int currEmuWidth, currEmuHeight;
|
2012-07-15 09:13:46 +00:00
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
TextureFrugalizer VideoTextureFrugalizer;
|
|
|
|
|
Dictionary<string, TextureFrugalizer> LuaSurfaceFrugalizers = new Dictionary<string, TextureFrugalizer>();
|
2014-02-03 05:27:59 +00:00
|
|
|
|
RenderTargetFrugalizer Video2xFrugalizer;
|
2014-02-07 02:36:27 +00:00
|
|
|
|
RenderTargetFrugalizer[] ShaderChainFrugalizers;
|
|
|
|
|
RetroShaderChain ShaderChain_hq2x, ShaderChain_scanlines;
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// This will receive an emulated output frame from an IVideoProvider and run it through the complete frame processing pipeline
|
|
|
|
|
/// Then it will stuff it into the bound PresentationPanel
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void UpdateSource(IVideoProvider videoProvider)
|
|
|
|
|
{
|
|
|
|
|
//wrap the videoprovider data in a BitmapBuffer (no point to refactoring that many IVidepProviders)
|
|
|
|
|
BitmapBuffer bb = new BitmapBuffer(videoProvider.BufferWidth, videoProvider.BufferHeight, videoProvider.GetVideoBuffer());
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//record the size of what we received, since lua and stuff is gonna want to draw onto it
|
|
|
|
|
currEmuWidth = bb.Width;
|
|
|
|
|
currEmuHeight = bb.Height;
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//now, acquire the data sent from the videoProvider into a texture
|
|
|
|
|
var videoTexture = VideoTextureFrugalizer.Get(bb);
|
2013-08-23 02:40:14 +00:00
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
//acquire the lua surfaces as textures
|
2014-01-28 04:39:27 +00:00
|
|
|
|
Texture2d luaEmuTexture = null;
|
2014-02-14 00:55:18 +00:00
|
|
|
|
var luaEmuSurface = LuaSurfaceSets["emu"].GetCurrent();
|
2014-01-28 04:39:27 +00:00
|
|
|
|
if (luaEmuSurface != null)
|
2014-02-14 00:55:18 +00:00
|
|
|
|
luaEmuTexture = LuaSurfaceFrugalizers["emu"].Get(luaEmuSurface);
|
|
|
|
|
|
|
|
|
|
Texture2d luaNativeTexture = null;
|
|
|
|
|
var luaNativeSurface = LuaSurfaceSets["native"].GetCurrent();
|
|
|
|
|
if (luaNativeSurface != null)
|
|
|
|
|
luaNativeTexture = LuaSurfaceFrugalizers["native"].Get(luaNativeSurface);
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
//select shader chain
|
|
|
|
|
RetroShaderChain selectedChain = null;
|
|
|
|
|
if (Global.Config.TargetDisplayFilter == 1 && ShaderChain_hq2x != null && ShaderChain_hq2x.Available)
|
|
|
|
|
selectedChain = ShaderChain_hq2x;
|
|
|
|
|
if (Global.Config.TargetDisplayFilter == 2 && ShaderChain_scanlines != null && ShaderChain_scanlines.Available)
|
|
|
|
|
selectedChain = ShaderChain_scanlines;
|
|
|
|
|
|
|
|
|
|
//run shader chain
|
2014-02-03 05:27:59 +00:00
|
|
|
|
Texture2d currentTexture = videoTexture;
|
2014-02-07 02:36:27 +00:00
|
|
|
|
if (selectedChain != null)
|
2014-02-03 07:01:31 +00:00
|
|
|
|
{
|
2014-02-07 02:36:27 +00:00
|
|
|
|
foreach (var pass in selectedChain.Passes)
|
|
|
|
|
{
|
|
|
|
|
//calculate size of input and output (note, we dont have a distinction between logical size and POW2 buffer size yet, like we should)
|
|
|
|
|
|
|
|
|
|
Size insize = currentTexture.Size;
|
|
|
|
|
Size outsize = insize;
|
|
|
|
|
|
|
|
|
|
//calculate letterboxing scale factors for the current configuration, so that ScaleType.Viewport can do something intelligent
|
|
|
|
|
var LLpass = new LetterboxingLogic(GraphicsControl.Width, GraphicsControl.Height, insize.Width, insize.Height);
|
|
|
|
|
|
|
|
|
|
if (pass.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) { throw new NotImplementedException("ScaleType Absolute"); }
|
|
|
|
|
if (pass.ScaleTypeX == RetroShaderPreset.ScaleType.Viewport) outsize.Width = LLpass.Rectangle.Width;
|
|
|
|
|
if (pass.ScaleTypeX == RetroShaderPreset.ScaleType.Source) outsize.Width = (int)(insize.Width * pass.Scale.X);
|
|
|
|
|
if (pass.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) { throw new NotImplementedException("ScaleType Absolute"); }
|
|
|
|
|
if (pass.ScaleTypeY == RetroShaderPreset.ScaleType.Viewport) outsize.Height = LLpass.Rectangle.Height;
|
|
|
|
|
if (pass.ScaleTypeY == RetroShaderPreset.ScaleType.Source) outsize.Height = (int)(insize.Height * pass.Scale.Y);
|
|
|
|
|
|
|
|
|
|
if (pass.InputFilterLinear)
|
|
|
|
|
videoTexture.SetFilterLinear();
|
|
|
|
|
else
|
|
|
|
|
videoTexture.SetFilterNearest();
|
|
|
|
|
|
|
|
|
|
var rt = ShaderChainFrugalizers[pass.Index].Get(outsize.Width, outsize.Height);
|
|
|
|
|
rt.Bind();
|
|
|
|
|
|
|
|
|
|
var shader = selectedChain.Shaders[pass.Index];
|
|
|
|
|
shader.Bind();
|
|
|
|
|
if(selectedChain == ShaderChain_scanlines)
|
|
|
|
|
shader.Pipeline["uIntensity"].Set(1.0f - Global.Config.TargetScanlineFilterIntensity / 256.0f);
|
|
|
|
|
shader.Run(currentTexture, insize, outsize, true);
|
|
|
|
|
currentTexture = rt.Texture2d;
|
|
|
|
|
}
|
2014-02-03 07:01:31 +00:00
|
|
|
|
}
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//begin drawing to the PresentationPanel:
|
|
|
|
|
GraphicsControl.Begin();
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-02-07 02:36:27 +00:00
|
|
|
|
//1. clear it with the background color that the emulator specified (could we please only clear the necessary letterbox area, to save some time?)
|
2014-01-28 04:39:27 +00:00
|
|
|
|
GL.SetClearColor(Color.FromArgb(videoProvider.BackgroundColor));
|
|
|
|
|
GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit);
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//2. begin 2d rendering
|
|
|
|
|
Renderer.Begin(GraphicsControl.Width, GraphicsControl.Height);
|
|
|
|
|
|
|
|
|
|
//3. figure out how to draw the emulator output content
|
2014-02-03 05:27:59 +00:00
|
|
|
|
var LL = new LetterboxingLogic(GraphicsControl.Width, GraphicsControl.Height, currentTexture.IntWidth, currentTexture.IntHeight);
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
|
|
|
|
//4. draw the emulator content
|
|
|
|
|
Renderer.SetBlendState(GL.BlendNone);
|
|
|
|
|
Renderer.Modelview.Push();
|
|
|
|
|
Renderer.Modelview.Translate(LL.dx, LL.dy);
|
|
|
|
|
Renderer.Modelview.Scale(LL.finalScale);
|
|
|
|
|
if (Global.Config.DispBlurry)
|
|
|
|
|
videoTexture.SetFilterLinear();
|
|
|
|
|
else
|
|
|
|
|
videoTexture.SetFilterNearest();
|
2014-02-03 05:27:59 +00:00
|
|
|
|
Renderer.Draw(currentTexture);
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//4.b draw the "lua emu surface" which is designed for art matching up exactly with the emulator output
|
|
|
|
|
Renderer.SetBlendState(GL.BlendNormal);
|
|
|
|
|
if(luaEmuTexture != null) Renderer.Draw(luaEmuTexture);
|
|
|
|
|
Renderer.Modelview.Pop();
|
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
//5a. draw the native layer content
|
|
|
|
|
//4.b draw the "lua emu surface" which is designed for art matching up exactly with the emulator output
|
|
|
|
|
if (luaNativeTexture != null) Renderer.Draw(luaNativeTexture);
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
//5b. draw the native layer OSD
|
2014-01-28 04:39:27 +00:00
|
|
|
|
MyBlitter myBlitter = new MyBlitter(this);
|
|
|
|
|
myBlitter.ClipBounds = new Rectangle(0, 0, GraphicsControl.Width, GraphicsControl.Height);
|
|
|
|
|
GlobalWin.OSD.Begin(myBlitter);
|
|
|
|
|
GlobalWin.OSD.DrawScreenInfo(myBlitter);
|
|
|
|
|
GlobalWin.OSD.DrawMessages(myBlitter);
|
|
|
|
|
|
|
|
|
|
//6. finished drawing
|
|
|
|
|
Renderer.End();
|
|
|
|
|
|
|
|
|
|
//7. apply the vsync setting (should probably try to avoid repeating this)
|
|
|
|
|
bool vsync = Global.Config.VSyncThrottle || Global.Config.VSync;
|
|
|
|
|
presentationPanel.GraphicsControl.SetVsync(vsync);
|
|
|
|
|
|
|
|
|
|
//7. present and conclude drawing
|
|
|
|
|
presentationPanel.GraphicsControl.SwapBuffers();
|
|
|
|
|
presentationPanel.GraphicsControl.End();
|
|
|
|
|
|
|
|
|
|
//cleanup:
|
|
|
|
|
bb.Dispose();
|
|
|
|
|
NeedsToPaint = false; //??
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
Dictionary<string, DisplaySurface> MapNameToLuaSurface = new Dictionary<string,DisplaySurface>();
|
|
|
|
|
Dictionary<DisplaySurface, string> MapLuaSurfaceToName = new Dictionary<DisplaySurface, string>();
|
|
|
|
|
Dictionary<string, SwappableDisplaySurfaceSet> LuaSurfaceSets = new Dictionary<string, SwappableDisplaySurfaceSet>();
|
2012-04-16 08:18:41 +00:00
|
|
|
|
SwappableDisplaySurfaceSet luaNativeSurfaceSet = new SwappableDisplaySurfaceSet();
|
|
|
|
|
public void SetLuaSurfaceNativePreOSD(DisplaySurface surface) { luaNativeSurfaceSet.SetPending(surface); }
|
2014-02-14 00:55:18 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Locks the requested lua surface name
|
|
|
|
|
/// </summary>
|
|
|
|
|
public DisplaySurface LockLuaSurface(string name)
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-02-14 00:55:18 +00:00
|
|
|
|
if (MapNameToLuaSurface.ContainsKey(name))
|
|
|
|
|
throw new InvalidOperationException("Lua surface is already locked: " + name);
|
|
|
|
|
|
|
|
|
|
SwappableDisplaySurfaceSet sdss;
|
|
|
|
|
if (!LuaSurfaceSets.TryGetValue(name, out sdss))
|
|
|
|
|
{
|
|
|
|
|
sdss = new SwappableDisplaySurfaceSet();
|
|
|
|
|
LuaSurfaceSets.Add(name, sdss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//placeholder logic for more abstracted surface definitions from filter chain
|
2014-01-28 04:39:27 +00:00
|
|
|
|
int currNativeWidth = presentationPanel.NativeSize.Width;
|
|
|
|
|
int currNativeHeight = presentationPanel.NativeSize.Height;
|
2014-02-14 00:55:18 +00:00
|
|
|
|
|
|
|
|
|
int width,height;
|
|
|
|
|
if(name == "emu") { width = currEmuWidth; height = currEmuHeight; }
|
|
|
|
|
else if(name == "native") { width = currNativeWidth; height = currNativeHeight; }
|
|
|
|
|
else throw new InvalidOperationException("Unknown lua surface name: " +name);
|
|
|
|
|
|
|
|
|
|
DisplaySurface ret = sdss.AllocateSurface(width, height);
|
|
|
|
|
MapNameToLuaSurface[name] = ret;
|
|
|
|
|
MapLuaSurfaceToName[ret] = name;
|
|
|
|
|
return ret;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 00:55:18 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Unlocks this DisplaySurface which had better have been locked as a lua surface
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void UnlockLuaSurface(DisplaySurface surface)
|
2012-05-06 07:09:04 +00:00
|
|
|
|
{
|
2014-02-14 00:55:18 +00:00
|
|
|
|
if (!MapLuaSurfaceToName.ContainsKey(surface))
|
|
|
|
|
throw new InvalidOperationException("Surface was not locked as a lua surface");
|
|
|
|
|
string name = MapLuaSurfaceToName[surface];
|
|
|
|
|
MapLuaSurfaceToName.Remove(surface);
|
|
|
|
|
MapNameToLuaSurface.Remove(name);
|
|
|
|
|
LuaSurfaceSets[name].SetPending(surface);
|
2012-05-06 07:09:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//helper classes:
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
class MyBlitter : IBlitter
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
DisplayManager Owner;
|
|
|
|
|
public MyBlitter(DisplayManager dispManager)
|
|
|
|
|
{
|
|
|
|
|
Owner = dispManager;
|
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
class FontWrapper : IBlitterFont
|
|
|
|
|
{
|
|
|
|
|
public FontWrapper(StringRenderer font)
|
|
|
|
|
{
|
|
|
|
|
this.font = font;
|
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public readonly StringRenderer font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IBlitterFont IBlitter.GetFontType(string fontType) { return new FontWrapper(Owner.TheOneFont); }
|
|
|
|
|
void IBlitter.DrawString(string s, IBlitterFont font, Color color, float x, float y)
|
|
|
|
|
{
|
|
|
|
|
var stringRenderer = ((FontWrapper)font).font;
|
|
|
|
|
Owner.Renderer.SetModulateColor(color);
|
|
|
|
|
stringRenderer.RenderString(Owner.Renderer, x, y, s);
|
|
|
|
|
Owner.Renderer.SetModulateColorWhite();
|
|
|
|
|
}
|
|
|
|
|
SizeF IBlitter.MeasureString(string s, IBlitterFont font)
|
|
|
|
|
{
|
|
|
|
|
var stringRenderer = ((FontWrapper)font).font;
|
|
|
|
|
return stringRenderer.Measure(s);
|
|
|
|
|
}
|
|
|
|
|
public Rectangle ClipBounds { get; set; }
|
2012-06-10 23:27:30 +00:00
|
|
|
|
}
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// applies letterboxing logic to figure out how to fit the source dimensions into the target dimensions.
|
|
|
|
|
/// In the future this could also apply rules like integer-only scaling, etc.
|
|
|
|
|
/// TODO - make this work with a output rect instead of float and dx/dy
|
|
|
|
|
/// </summary>
|
|
|
|
|
class LetterboxingLogic
|
2012-07-15 08:50:24 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public LetterboxingLogic(int targetWidth, int targetHeight, int sourceWidth, int sourceHeight)
|
2012-07-15 08:50:24 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
float vw = (float)targetWidth;
|
|
|
|
|
float vh = (float)targetHeight;
|
|
|
|
|
float widthScale = vw / sourceWidth;
|
|
|
|
|
float heightScale = vh / sourceHeight;
|
|
|
|
|
finalScale = Math.Min(widthScale, heightScale);
|
|
|
|
|
dx = (int)((vw - finalScale * sourceWidth) / 2);
|
|
|
|
|
dy = (int)((vh - finalScale * sourceHeight) / 2);
|
2014-02-07 02:36:27 +00:00
|
|
|
|
Rectangle = new Rectangle((int)dx, (int)dy, (int)(finalScale * sourceWidth), (int)(finalScale * sourceHeight));
|
2012-07-15 08:50:24 +00:00
|
|
|
|
}
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// scale to be applied to both x and y
|
|
|
|
|
/// </summary>
|
|
|
|
|
public float finalScale;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// offset
|
|
|
|
|
/// </summary>
|
|
|
|
|
public float dx, dy;
|
2014-02-07 02:36:27 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The destination rectangle
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Rectangle Rectangle;
|
2014-01-28 04:39:27 +00:00
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|