Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor DisplaySurface locking as used by ApiHawk (and Lua) #2575

Merged
merged 3 commits into from
Jan 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 117 additions & 48 deletions src/BizHawk.Client.Common/Api/Classes/GuiApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ public sealed class GuiApi : IGuiApi

private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);

private DisplaySurface _clientSurface;

private DisplaySurface _GUISurface;

private (int Left, int Top, int Right, int Bottom) _padding = (0, 0, 0, 0);

private DisplaySurfaceID? _usingSurfaceID = null;

public bool HasGUISurface => _GUISurface != null;

public GuiApi(Action<string> logCallback, IDisplayManagerForApi displayManager)
Expand All @@ -55,9 +59,9 @@ public GuiApi(Action<string> logCallback, IDisplayManagerForApi displayManager)

private Pen GetPen(Color color) => _pens.TryGetValue(color, out var p) ? p : (_pens[color] = new Pen(color));

private Graphics GetGraphics()
private Graphics GetGraphics(DisplaySurfaceID? surfaceID)
{
var g = _GUISurface?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
var g = GetRelevantSurface(surfaceID)?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
var (tx, ty) = Emulator.ScreenLogicalOffsets();
if (tx != 0 || ty != 0)
{
Expand All @@ -75,25 +79,94 @@ private Graphics GetGraphics()

public void SetAttributes(ImageAttributes a) => _attributes = a;

public void DrawNew(string name, bool clear)
private DisplaySurface GetRelevantSurface(DisplaySurfaceID? surfaceID) => (surfaceID ?? _usingSurfaceID) switch
{
DisplaySurfaceID.EmuCore => _GUISurface,
DisplaySurfaceID.Client => _clientSurface,
_ => throw new Exception()
};

private void LockSurface(DisplaySurfaceID surfaceID)
{
switch (surfaceID)
{
case DisplaySurfaceID.EmuCore:
if (_GUISurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous");
_GUISurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true);
break;
case DisplaySurfaceID.Client:
if (_clientSurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous");
_clientSurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true);
break;
default:
throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID));
}
}

private void UnlockSurface(DisplaySurfaceID surfaceID)
{
switch (surfaceID)
{
case DisplaySurfaceID.EmuCore:
if (_GUISurface != null) _displayManager.UnlockApiHawkSurface(_GUISurface);
_GUISurface = null;
break;
case DisplaySurfaceID.Client:
if (_clientSurface != null) _displayManager.UnlockApiHawkSurface(_clientSurface);
_clientSurface = null;
break;
default:
throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID));
}
}

public void WithSurface(DisplaySurfaceID surfaceID, Action drawingCallsFunc)
{
LockSurface(surfaceID);
_usingSurfaceID = surfaceID;
try
{
drawingCallsFunc();
}
finally
{
_usingSurfaceID = null;
UnlockSurface(surfaceID);
}
}

public void LockEmuSurfaceLua()
{
try
{
DrawFinish();
_GUISurface = _displayManager.LockLuaSurface(name, clear);
UnlockSurface(DisplaySurfaceID.EmuCore);
LockSurface(DisplaySurfaceID.EmuCore);
}
catch (InvalidOperationException ex)
{
LogCallback(ex.ToString());
}
}

public void DrawFinish()
public void UnlockEmuSurfaceLua() => UnlockSurface(DisplaySurfaceID.EmuCore);

public void DrawNew(string name, bool clear)
{
if (_GUISurface != null) _displayManager.UnlockLuaSurface(_GUISurface);
_GUISurface = null;
switch (name)
{
case null:
case "emu":
LogCallback("the `DrawNew(\"emu\")` function has been deprecated");
return;
case "native":
throw new InvalidOperationException("the ability to draw in the margins with `DrawNew(\"native\")` has been removed");
default:
throw new InvalidOperationException("invalid surface name");
}
}

public void DrawFinish() => LogCallback("the `DrawFinish()` function has been deprecated");

public void SetPadding(int all) => _padding = (all, all, all, all);

public void SetPadding(int x, int y) => _padding = (x / 2, y / 2, x / 2 + x & 1, y / 2 + y & 1);
Expand All @@ -104,11 +177,7 @@ public void DrawFinish()

public void AddMessage(string message) => _displayManager.OSD.AddMessage(message);

public void ClearGraphics()
{
_GUISurface.Clear();
DrawFinish();
}
public void ClearGraphics(DisplaySurfaceID? surfaceID = null) => GetRelevantSurface(surfaceID).Clear();

public void ClearText() => _displayManager.OSD.ClearGuiText();

Expand Down Expand Up @@ -138,11 +207,11 @@ public void SetDefaultPixelFont(string fontfamily)
}
}

public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null)
public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null, DisplaySurfaceID? surfaceID = null)
{
try
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
}
Expand All @@ -152,11 +221,11 @@ public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = nu
}
}

public void DrawBeziers(Point[] points, Color? color = null)
public void DrawBeziers(Point[] points, Color? color = null, DisplaySurfaceID? surfaceID = null)
{
try
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
}
Expand All @@ -166,7 +235,7 @@ public void DrawBeziers(Point[] points, Color? color = null)
}
}

public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null)
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
{
try
{
Expand All @@ -192,7 +261,7 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac
y -= y2;
h = Math.Max(y2, 0.1f);
}
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
Expand All @@ -204,11 +273,11 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac
}
}

public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null)
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
{
try
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
var bg = background ?? _defaultBackground;
if (bg != null) g.FillEllipse(GetBrush(bg.Value), x, y, width, height);
g.CompositingMode = _compositingMode;
Expand All @@ -220,7 +289,7 @@ public void DrawEllipse(int x, int y, int width, int height, Color? line = null,
}
}

public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null, DisplaySurfaceID? surfaceID = null)
{
try
{
Expand All @@ -229,7 +298,7 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height =
AddMessage($"File not found: {path}");
return;
}
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawIcon(
width != null && height != null
Expand All @@ -245,9 +314,9 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height =
}
}

public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true)
public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null)
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawImage(
img,
Expand All @@ -260,14 +329,14 @@ public void DrawImage(Image img, int x, int y, int? width = null, int? height =
_attributes
);
}
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true)
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null)
{
if (!File.Exists(path))
{
LogCallback($"File not found: {path}");
return;
}
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
var isCached = _imageCache.ContainsKey(path);
var img = isCached ? _imageCache[path] : Image.FromFile(path);
if (!isCached && cache) _imageCache[path] = img;
Expand All @@ -290,9 +359,9 @@ public void ClearImageCache()
_imageCache.Clear();
}

public void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null)
public void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null)
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawImage(
img,
Expand All @@ -306,14 +375,14 @@ public void DrawImageRegion(Image img, int source_x, int source_y, int source_wi
);
}

public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null)
public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null)
{
if (!File.Exists(path))
{
LogCallback($"File not found: {path}");
return;
}
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawImage(
_imageCache.TryGetValue(path, out var img) ? img : (_imageCache[path] = Image.FromFile(path)),
Expand All @@ -327,33 +396,33 @@ public void DrawImageRegion(string path, int source_x, int source_y, int source_
);
}

public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null, DisplaySurfaceID? surfaceID = null)
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
}

public void DrawAxis(int x, int y, int size, Color? color = null)
public void DrawAxis(int x, int y, int size, Color? color = null, DisplaySurfaceID? surfaceID = null)
{
DrawLine(x + size, y, x - size, y, color ?? _defaultForeground);
DrawLine(x, y + size, x, y - size, color ?? _defaultForeground);
DrawLine(x + size, y, x - size, y, color ?? _defaultForeground, surfaceID: surfaceID);
DrawLine(x, y + size, x, y - size, color ?? _defaultForeground, surfaceID: surfaceID);
}

public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null)
public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.CompositingMode = _compositingMode;
var bg = background ?? _defaultBackground;
if (bg != null) g.FillPie(GetBrush(bg.Value), x, y, width, height, startangle, sweepangle);
g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
}

public void DrawPixel(int x, int y, Color? color = null)
public void DrawPixel(int x, int y, Color? color = null, DisplaySurfaceID? surfaceID = null)
{
try
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
}
catch (Exception)
Expand All @@ -362,11 +431,11 @@ public void DrawPixel(int x, int y, Color? color = null)
}
}

public void DrawPolygon(Point[] points, Color? line = null, Color? background = null)
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
{
try
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
var bg = background ?? _defaultBackground;
if (bg != null) g.FillPolygon(GetBrush(bg.Value), points);
Expand All @@ -377,17 +446,17 @@ public void DrawPolygon(Point[] points, Color? line = null, Color? background =
}
}

public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null)
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
{
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
var w = Math.Max(width, 0.1F);
var h = Math.Max(height, 0.1F);
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
var bg = background ?? _defaultBackground;
if (bg != null) g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
}

public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null)
public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null, DisplaySurfaceID? surfaceID = null)
{
try
{
Expand All @@ -402,7 +471,7 @@ public void DrawString(int x, int y, string message, Color? forecolor = null, Co
_ => FontStyle.Regular
};

using var g = GetGraphics();
using var g = GetGraphics(surfaceID);

// The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse.
// And writing it out with GenericTypographic just made it uglier. :p
Expand Down Expand Up @@ -455,7 +524,7 @@ public void DrawString(int x, int y, string message, Color? forecolor = null, Co
}
}

public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null)
public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null, DisplaySurfaceID? surfaceID = null)
{
try
{
Expand All @@ -479,7 +548,7 @@ public void DrawText(int x, int y, string message, Color? forecolor = null, Colo
index = _defaultPixelFont;
break;
}
using var g = GetGraphics();
using var g = GetGraphics(surfaceID);
var font = new Font(_displayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel);
var sizeOfText = g.MeasureString(
message,
Expand Down
Loading