From 35d31c418b4663081547c2eafac331ec2ae546a0 Mon Sep 17 00:00:00 2001 From: adelikat Date: Tue, 5 Aug 2014 00:18:55 +0000 Subject: [PATCH] Temporarily hijack the NES nametable viewer for a gdi experiment --- .../BizHawk.Client.EmuHawk.csproj | 1 + .../CustomControls/GDITextRenderer.cs | 281 ++++++++++++++++++ .../tools/NES/NESNameTableViewer.Designer.cs | 4 +- .../tools/NES/NESNameTableViewer.cs | 7 +- .../tools/NES/NameTableViewer.cs | 42 ++- 5 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/CustomControls/GDITextRenderer.cs diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index ba4645f2f1..0b4ed773f0 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -443,6 +443,7 @@ Component + Component diff --git a/BizHawk.Client.EmuHawk/CustomControls/GDITextRenderer.cs b/BizHawk.Client.EmuHawk/CustomControls/GDITextRenderer.cs new file mode 100644 index 0000000000..a827490ab7 --- /dev/null +++ b/BizHawk.Client.EmuHawk/CustomControls/GDITextRenderer.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace BizHawk.Client.EmuHawk.CustomControls +{ + /// + /// Wrapper for GDI text rendering functions
+ /// This class is not thread-safe as GDI function should be called from the UI thread. + ///
+ public sealed class NativeTextRenderer : IDisposable + { + #region Fields and Consts + + /// + /// used for calculation. + /// + private static readonly int[] _charFit = new int[1]; + + /// + /// used for calculation. + /// + private static readonly int[] _charFitWidth = new int[1000]; + + /// + /// cache of all the font used not to create same font again and again + /// + private static readonly Dictionary>> _fontsCache = new Dictionary>>(StringComparer.InvariantCultureIgnoreCase); + + /// + /// The wrapped WinForms graphics object + /// + private readonly Graphics _g; + + /// + /// the initialized HDC used + /// + private IntPtr _hdc; + + #endregion + + + /// + /// Init. + /// + public NativeTextRenderer(Graphics g) + { + _g = g; + + var clip = _g.Clip.GetHrgn(_g); + + _hdc = _g.GetHdc(); + SetBkMode(_hdc, 1); + + SelectClipRgn(_hdc, clip); + + DeleteObject(clip); + } + + /// + /// Measure the width and height of string when drawn on device context HDC + /// using the given font . + /// + /// the string to measure + /// the font to measure string with + /// the size of the string + public Size MeasureString(string str, Font font) + { + SetFont(font); + + var size = new Size(); + GetTextExtentPoint32(_hdc, str, str.Length, ref size); + return size; + } + + /// + /// Measure the width and height of string when drawn on device context HDC + /// using the given font .
+ /// Restrict the width of the string and get the number of characters able to fit in the restriction and + /// the width those characters take. + ///
+ /// the string to measure + /// the font to measure string with + /// the max width to render the string in + /// the number of characters that will fit under restriction + /// + /// the size of the string + public Size MeasureString(string str, Font font, float maxWidth, out int charFit, out int charFitWidth) + { + SetFont(font); + + var size = new Size(); + GetTextExtentExPoint(_hdc, str, str.Length, (int)Math.Round(maxWidth), _charFit, _charFitWidth, ref size); + charFit = _charFit[0]; + charFitWidth = charFit > 0 ? _charFitWidth[charFit - 1] : 0; + return size; + } + + /// + /// Draw the given string using the given font and foreground color at given location. + /// + /// the string to draw + /// the font to use to draw the string + /// the text color to set + /// the location to start string draw (top-left) + public void DrawString(String str, Font font, Color color, Point point) + { + SetFont(font); + SetTextColor(color); + + TextOut(_hdc, point.X, point.Y, str, str.Length); + } + + /// + /// Draw the given string using the given font and foreground color at given location.
+ /// See [http://msdn.microsoft.com/en-us/library/windows/desktop/dd162498(v=vs.85).aspx][15]. + ///
+ /// the string to draw + /// the font to use to draw the string + /// the text color to set + /// the rectangle in which the text is to be formatted + /// The method of formatting the text + public void DrawString(String str, Font font, Color color, Rectangle rect, TextFormatFlags flags) + { + SetFont(font); + SetTextColor(color); + + var rect2 = new Rect(rect); + DrawText(_hdc, str, str.Length, ref rect2, (uint)flags); + } + + /// + /// Release current HDC to be able to use methods. + /// + public void Dispose() + { + if (_hdc != IntPtr.Zero) + { + SelectClipRgn(_hdc, IntPtr.Zero); + _g.ReleaseHdc(_hdc); + _hdc = IntPtr.Zero; + } + } + + + #region Private methods + + /// + /// Set a resource (e.g. a font) for the specified device context. + /// + private void SetFont(Font font) + { + SelectObject(_hdc, GetCachedHFont(font)); + } + + /// + /// Get cached unmanaged font handle for given font.
+ ///
+ /// the font to get unmanaged font handle for + /// handle to unmanaged font + private static IntPtr GetCachedHFont(Font font) + { + IntPtr hfont = IntPtr.Zero; + Dictionary> dic1; + if (_fontsCache.TryGetValue(font.Name, out dic1)) + { + Dictionary dic2; + if (dic1.TryGetValue(font.Size, out dic2)) + { + dic2.TryGetValue(font.Style, out hfont); + } + else + { + dic1[font.Size] = new Dictionary(); + } + } + else + { + _fontsCache[font.Name] = new Dictionary>(); + _fontsCache[font.Name][font.Size] = new Dictionary(); + } + + if (hfont == IntPtr.Zero) + { + _fontsCache[font.Name][font.Size][font.Style] = hfont = font.ToHfont(); + } + + return hfont; + } + + /// + /// Set the text color of the device context. + /// + private void SetTextColor(Color color) + { + int rgb = (color.B & 0xFF) << 16 | (color.G & 0xFF) << 8 | color.R; + SetTextColor(_hdc, rgb); + } + + [DllImport("gdi32.dll")] + private static extern int SetBkMode(IntPtr hdc, int mode); + + [DllImport("gdi32.dll")] + private static extern int SelectObject(IntPtr hdc, IntPtr hgdiObj); + + [DllImport("gdi32.dll")] + private static extern int SetTextColor(IntPtr hdc, int color); + + [DllImport("gdi32.dll", EntryPoint = "GetTextExtentPoint32W")] + private static extern int GetTextExtentPoint32(IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] string str, int len, ref Size size); + + [DllImport("gdi32.dll", EntryPoint = "GetTextExtentExPointW")] + private static extern bool GetTextExtentExPoint(IntPtr hDc, [MarshalAs(UnmanagedType.LPWStr)]string str, int nLength, int nMaxExtent, int[] lpnFit, int[] alpDx, ref Size size); + + [DllImport("gdi32.dll", EntryPoint = "TextOutW")] + private static extern bool TextOut(IntPtr hdc, int x, int y, [MarshalAs(UnmanagedType.LPWStr)] string str, int len); + + [DllImport("user32.dll", EntryPoint = "DrawTextW")] + private static extern int DrawText(IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] string str, int len, ref Rect rect, uint uFormat); + + [DllImport("gdi32.dll")] + private static extern int SelectClipRgn(IntPtr hdc, IntPtr hrgn); + + [DllImport("gdi32.dll")] + private static extern bool DeleteObject(IntPtr hObject); + + // ReSharper disable NotAccessedField.Local + private struct Rect + { + private int _left; + private int _top; + private int _right; + private int _bottom; + + public Rect(Rectangle r) + { + _left = r.Left; + _top = r.Top; + _bottom = r.Bottom; + _right = r.Right; + } + } + // ReSharper restore NotAccessedField.Local + + #endregion + } + + /// + /// See [http://msdn.microsoft.com/en-us/library/windows/desktop/dd162498(v=vs.85).aspx][15] + /// + [Flags] + public enum TextFormatFlags : uint + { + Default = 0x00000000, + Center = 0x00000001, + Right = 0x00000002, + VCenter = 0x00000004, + Bottom = 0x00000008, + WordBreak = 0x00000010, + SingleLine = 0x00000020, + ExpandTabs = 0x00000040, + TabStop = 0x00000080, + NoClip = 0x00000100, + ExternalLeading = 0x00000200, + CalcRect = 0x00000400, + NoPrefix = 0x00000800, + Internal = 0x00001000, + EditControl = 0x00002000, + PathEllipsis = 0x00004000, + EndEllipsis = 0x00008000, + ModifyString = 0x00010000, + RtlReading = 0x00020000, + WordEllipsis = 0x00040000, + NoFullWidthCharBreak = 0x00080000, + HidePrefix = 0x00100000, + ProfixOnly = 0x00200000, + } +} diff --git a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.Designer.cs b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.Designer.cs index ba30821d73..6b6bbb3dd5 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.Designer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.Designer.cs @@ -93,7 +93,7 @@ // this.NameTableView.BackColor = System.Drawing.Color.Transparent; this.NameTableView.ContextMenuStrip = this.contextMenuStrip1; - this.NameTableView.Location = new System.Drawing.Point(17, 19); + this.NameTableView.Location = new System.Drawing.Point(6, 19); this.NameTableView.Name = "NameTableView"; this.NameTableView.Size = new System.Drawing.Size(512, 480); this.NameTableView.TabIndex = 0; @@ -108,7 +108,7 @@ this.SaveImageClipboardMenuItem, this.RefreshImageContextMenuItem}); this.contextMenuStrip1.Name = "contextMenuStrip1"; - this.contextMenuStrip1.Size = new System.Drawing.Size(248, 92); + this.contextMenuStrip1.Size = new System.Drawing.Size(248, 70); // // ScreenshotAsContextMenuItem // diff --git a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs index 3ba1e41d6d..02a7945385 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs @@ -37,7 +37,7 @@ namespace BizHawk.Client.EmuHawk _nes = Global.Emulator as NES; RefreshRate.Value = Global.Config.NESNameTableRefreshRate; - Generate(true); + //Generate(true); } #region Public API @@ -50,7 +50,7 @@ namespace BizHawk.Client.EmuHawk if (Global.Emulator is NES) { _nes = Global.Emulator as NES; - Generate(true); + //Generate(true); } else { @@ -62,7 +62,8 @@ namespace BizHawk.Client.EmuHawk { if (Global.Emulator is NES) { - (Global.Emulator as NES).ppu.NTViewCallback = _callback; + //(Global.Emulator as NES).ppu.NTViewCallback = _callback; + NameTableView.Invalidate(); } else { diff --git a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs index 90e4a9f09e..8eeda6bd13 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs @@ -5,6 +5,9 @@ using System.Drawing.Imaging; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.WinFormExtensions; +using System.Text; +using System; +using BizHawk.Client.EmuHawk.CustomControls; namespace BizHawk.Client.EmuHawk { @@ -22,10 +25,10 @@ namespace BizHawk.Client.EmuHawk SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.DoubleBuffer, true); SetStyle(ControlStyles.SupportsTransparentBackColor, true); - SetStyle(ControlStyles.Opaque, true); + //SetStyle(ControlStyles.Opaque, true); Size = new Size(256, 224); BackColor = Color.Transparent; - Paint += NameTableViewer_Paint; + //Paint += NameTableViewer_Paint; } public enum WhichNametable @@ -35,6 +38,41 @@ namespace BizHawk.Client.EmuHawk public WhichNametable Which = WhichNametable.NT_ALL; + protected override void OnPaint(PaintEventArgs e) + { + + using (var ntr = new NativeTextRenderer(e.Graphics)) + { + for (int y = 0; y < 16; y++) + { + StringBuilder sb = new StringBuilder(); + Random r = new Random((int)DateTime.Now.Ticks); + for (int i = 0; i < 64; i++) + { + sb.Append((char)r.Next(0, 255)); + } + + ntr.DrawString(sb.ToString(), this.Font, Color.Black, new Point(15, y * 30)); + //e.Graphics.DrawString(sb.ToString(), this.Font, Brushes.Black, new Point(15, y * 30)); + } + } + + /* + for (int y = 0; y < 16; y++) + { + StringBuilder sb = new StringBuilder(); + Random r = new Random((int)DateTime.Now.Ticks); + for (int i = 0; i < 64; i++) + { + sb.Append((char)r.Next(0, 255)); + } + + e.Graphics.DrawString(sb.ToString(), this.Font, Brushes.Black, new Point(15, y * 30)); + } + */ + //base.OnPaint(e); + } + private void NameTableViewer_Paint(object sender, PaintEventArgs e) { e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;