Skip to content

Commit

Permalink
fix(Studio): Incorrect rendering order on GTK
Browse files Browse the repository at this point in the history
  • Loading branch information
psyGamer committed Nov 7, 2024
1 parent b82cafd commit f45de44
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 54 deletions.
69 changes: 43 additions & 26 deletions Studio/CelesteStudio.GTK/SkiaDrawableHandler.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
using Cairo;
using CelesteStudio.Controls;
using CelesteStudio.Util;
using Eto.GtkSharp.Forms;
using SkiaSharp;
using SkiaSharp.Views.Gtk;
using System;

namespace CelesteStudio.GTK;

public class SkiaDrawableHandler : GtkPanel<Gtk.EventBox, SkiaDrawable, SkiaDrawable.ICallback>, SkiaDrawable.IHandler {
private Gtk.Box content = null!;

protected override WeakConnector CreateConnector() => new SkiaDrawableConnector();
protected new SkiaDrawableConnector Connector => (SkiaDrawableConnector)base.Connector;

public void Create() {
Control = new SkiaEventBox(Widget);
Control = new Gtk.EventBox();
Control.Events |= Gdk.EventMask.ExposureMask;
Control.CanFocus = false;
Control.CanDefault = true;
Expand All @@ -21,18 +24,39 @@ public void Create() {
Control.Add(content);
}

private class SkiaEventBox(SkiaDrawable drawable) : Gtk.EventBox {
protected override void Initialize() {
base.Initialize();
Control.Drawn += Connector.HandleDrawn;
Control.ButtonPressEvent += Connector.HandleDrawableButtonPressEvent;
}

protected class SkiaDrawableConnector : GtkPanelEventConnector {
private new SkiaDrawableHandler? Handler => (SkiaDrawableHandler)base.Handler;

private SKBitmap? bitmap;
private SKSurface? surface;
private ImageSurface? imageSurface;

protected override bool OnDrawn(Context cr) {
if (base.OnDrawn(cr)) {
return true;
public void HandleDrawableButtonPressEvent(object o, Gtk.ButtonPressEventArgs args) {
var handler = Handler;
if (handler == null) {
return;
}

if (handler.CanFocus) {
handler.Control.GrabFocus();
}
}

[GLib.ConnectBefore]
public void HandleDrawn(object o, Gtk.DrawnArgs args) {
if (Handler == null) {
return;
}

var drawable = Handler.Widget;
int width = drawable.DrawWidth, height = drawable.DrawHeight;
if (width != bitmap?.Width || height != bitmap?.Height) {
if (surface == null || imageSurface == null || width != bitmap?.Width || height != bitmap?.Height) {
var colorType = SKImageInfo.PlatformColorType;

bitmap?.Dispose();
Expand All @@ -47,26 +71,15 @@ protected override bool OnDrawn(Context cr) {
imageSurface = new ImageSurface(pixels, Format.Argb32, bitmap.Width, bitmap.Height, bitmap.Width * 4);
}

using (new SKAutoCanvasRestore(surface!.Canvas, true)) {
var canvas = surface.Canvas;
using (new SKAutoCanvasRestore(canvas, true)) {
canvas.Clear(drawable.BackgroundColor.ToSkia());
canvas.Translate(-drawable.DrawX, -drawable.DrawY);
drawable.Draw(surface);
}

imageSurface!.MarkDirty();
cr.SetSourceSurface(imageSurface, drawable.DrawX, drawable.DrawY);
cr.Paint();

return false;
}

protected override void Dispose(bool disposing) {
bitmap?.Dispose();
bitmap = null;

surface?.Dispose();
surface = null;

imageSurface?.Dispose();
imageSurface = null;
args.Cr.SetSourceSurface(imageSurface, drawable.DrawX, drawable.DrawY);
args.Cr.Paint();
}
}

Expand All @@ -75,8 +88,12 @@ public bool CanFocus {
set => Control.CanFocus = value;
}

protected override void SetContainerContent(Gtk.Widget containerContent)
{
protected override void SetContainerContent(Gtk.Widget containerContent) {
content.Add(containerContent);
}

protected override void SetBackgroundColor(Eto.Drawing.Color? color) {
// Handled by ourselves
Invalidate(false);
}
}
10 changes: 7 additions & 3 deletions Studio/CelesteStudio.Mac/SkiaDrawableHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CelesteStudio.Controls;
using CelesteStudio.Util;
using Eto.Mac.Forms;
using MonoMac.AppKit;
using MonoMac.CoreGraphics;
Expand Down Expand Up @@ -35,7 +36,7 @@ public override void DrawRect(CGRect dirtyRect) {

// Allocate a memory for the drawing process
nuint infoLength = (nuint)info.BytesSize;
if (bitmapData?.Length != infoLength) {
if (surface == null || bitmapData?.Length != infoLength) {
dataProvider?.Dispose();
bitmapData?.Dispose();

Expand All @@ -46,10 +47,13 @@ public override void DrawRect(CGRect dirtyRect) {
surface = SKSurface.Create(info, bitmapData.MutableBytes, info.RowBytes);
}

using (new SKAutoCanvasRestore(surface!.Canvas, true)) {
var canvas = surface.Canvas;
using (new SKAutoCanvasRestore(canvas, true)) {
canvas.Clear(drawable.BackgroundColor.ToSkia());
canvas.Translate(-drawable.DrawX, -drawable.DrawY);
drawable.Draw(surface);
}
surface.Canvas.Flush();
canvas.Flush();

using var image = new CGImage(
info.Width, info.Height,
Expand Down
6 changes: 5 additions & 1 deletion Studio/CelesteStudio.WPF/SkiaDrawableHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CelesteStudio.Controls;
using CelesteStudio.Util;
using System;
using System.Windows;
using System.Windows.Media;
Expand Down Expand Up @@ -44,10 +45,13 @@ protected override void OnRender(DrawingContext drawingContext) {

bitmap.Lock();

var canvas = surface.Canvas;
using (new SKAutoCanvasRestore(surface.Canvas, true)) {
canvas.Clear(drawable.BackgroundColor.ToSkia());
canvas.Translate(-drawable.DrawX, -drawable.DrawY);
drawable.Draw(surface);
}
surface.Canvas.Flush();
canvas.Flush();

bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
drawingContext.DrawImage(bitmap, new Rect(drawable.DrawX, drawable.DrawY, width / dpiX, height / dpiY));
Expand Down
12 changes: 6 additions & 6 deletions Studio/CelesteStudio/Dialog/ChangelogDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private class ContentDrawable(string title, Dictionary<string, List<string>> cat
: SystemColors.ControlText.ToSkia();

public override void Draw(SKSurface surface) {
surface.Canvas.Clear();
var canvas = surface.Canvas;

var textColor = TextColor;

Expand All @@ -46,27 +46,27 @@ public override void Draw(SKSurface surface) {
float yOffset = 0.0f;
// Title
foreach (string line in WrapLines(title, Width, titlePaint)) {
surface.Canvas.DrawText(line, Width / 2.0f, yOffset + titleFont.Offset(), titlePaint);
canvas.DrawText(line, Width / 2.0f, yOffset + titleFont.Offset(), titlePaint);
yOffset += titleFont.LineHeight();
}
yOffset += titleFont.LineHeight() * 0.25f;
surface.Canvas.DrawLine(0.0f, yOffset, Width, yOffset, titlePaint);
canvas.DrawLine(0.0f, yOffset, Width, yOffset, titlePaint);
yOffset += titleFont.LineHeight() * 0.5f;

foreach ((string categoryName, var entries) in categories) {
// Heading
foreach (string line in WrapLines(categoryName, Width, headingPaint)) {
surface.Canvas.DrawText(line, 0.0f, yOffset + headingFont.Offset(), headingPaint);
canvas.DrawText(line, 0.0f, yOffset + headingFont.Offset(), headingPaint);
yOffset += headingFont.LineHeight();
}
yOffset += headingFont.LineHeight() * 0.5f;

// Entries
foreach (string entry in entries) {
surface.Canvas.DrawCircle(10.0f, yOffset + entryFont.LineHeight() / 2.0f, 2.5f, entryPaint);
canvas.DrawCircle(10.0f, yOffset + entryFont.LineHeight() / 2.0f, 2.5f, entryPaint);

foreach (string line in WrapLines(entry, Width, entryPaint)) {
surface.Canvas.DrawText(line, 20.0f, yOffset + entryFont.Offset(), entryPaint);
canvas.DrawText(line, 20.0f, yOffset + entryFont.Offset(), entryPaint);
yOffset += entryFont.LineHeight();
}
}
Expand Down
4 changes: 2 additions & 2 deletions Studio/CelesteStudio/Dialog/FontDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public FontPreview() {
}

public override void Draw(SKSurface surface) {
surface.Canvas.Clear();
var canvas = surface.Canvas;

if (highlighter == null)
return;
Expand All @@ -50,7 +50,7 @@ public override void Draw(SKSurface surface) {
float yPos = 0.0f;
float maxWidth = 0.0f;
foreach (var line in previewText) {
highlighter.DrawLine(surface.Canvas, 0.0f, yPos, line);
highlighter.DrawLine(canvas, 0.0f, yPos, line);
maxWidth = Math.Max(maxWidth, font.MeasureWidth(line));
yPos += font.LineHeight();
}
Expand Down
2 changes: 0 additions & 2 deletions Studio/CelesteStudio/Editing/Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3626,8 +3626,6 @@ private void UpdateMouseCursor(PointF location, Keys modifiers) {

public override void Draw(SKSurface surface) {
var canvas = surface.Canvas;
canvas.Clear();
canvas.Translate(-scrollablePosition.X, -scrollablePosition.Y);

using var strokePaint = new SKPaint();
strokePaint.Style = SKPaintStyle.Stroke;
Expand Down
14 changes: 7 additions & 7 deletions Studio/CelesteStudio/Editing/GameInfoPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace CelesteStudio.Editing;
public sealed class GameInfo : Panel {
private sealed class SubpixelIndicator : SkiaDrawable {
public override void Draw(SKSurface surface) {
surface.Canvas.Clear();
var canvas = surface.Canvas;

var remainder = CommunicationWrapper.PlayerPositionRemainder;

Expand Down Expand Up @@ -49,11 +49,11 @@ public override void Draw(SKSurface surface) {
string top = subpixelTop.ToFormattedString(vDecimals);
string bottom = subpixelBottom.ToFormattedString(vDecimals);

surface.Canvas.DrawText(left, x - rectPadding - font.MeasureWidth(left), y + (rectSize - textHeight) / 2.0f + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);
surface.Canvas.DrawText(right, x + rectPadding + rectSize, y + (rectSize - textHeight) / 2.0f + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);
canvas.DrawText(left, x - rectPadding - font.MeasureWidth(left), y + (rectSize - textHeight) / 2.0f + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);
canvas.DrawText(right, x + rectPadding + rectSize, y + (rectSize - textHeight) / 2.0f + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);

surface.Canvas.DrawText(top, MathF.Round(x + (rectSize - font.MeasureWidth(top)) / 2.0f), MathF.Round(y - rectPadding - textHeight + font.Offset()), font, Settings.Instance.Theme.StatusFgPaint);
surface.Canvas.DrawText(bottom, x + (rectSize - font.MeasureWidth(bottom)) / 2.0f, y + rectPadding + rectSize + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);
canvas.DrawText(top, MathF.Round(x + (rectSize - font.MeasureWidth(top)) / 2.0f), MathF.Round(y - rectPadding - textHeight + font.Offset()), font, Settings.Instance.Theme.StatusFgPaint);
canvas.DrawText(bottom, x + (rectSize - font.MeasureWidth(bottom)) / 2.0f, y + rectPadding + rectSize + font.Offset(), font, Settings.Instance.Theme.StatusFgPaint);

int boxThickness = Math.Max(1, (int)Math.Round(rectSize / 20.0f));
float dotThickness = boxThickness * 1.25f;
Expand All @@ -63,8 +63,8 @@ public override void Draw(SKSurface surface) {
boxPaint.Style = SKPaintStyle.Stroke;
boxPaint.StrokeWidth = boxThickness;

surface.Canvas.DrawRect(x, y, rectSize, rectSize, boxPaint);
surface.Canvas.DrawRect(x + (rectSize - dotThickness) * subpixelLeft, y + (rectSize - dotThickness) * subpixelTop, dotThickness, dotThickness, Settings.Instance.Theme.SubpixelIndicatorDotPaint);
canvas.DrawRect(x, y, rectSize, rectSize, boxPaint);
canvas.DrawRect(x + (rectSize - dotThickness) * subpixelLeft, y + (rectSize - dotThickness) * subpixelTop, dotThickness, dotThickness, Settings.Instance.Theme.SubpixelIndicatorDotPaint);

Width = (int)((textWidth + rectPadding + indicatorPadding) * 2.0f + rectSize);
Height = (int)((textHeight + rectPadding + indicatorPadding) * 2.0f + rectSize);
Expand Down
13 changes: 6 additions & 7 deletions Studio/CelesteStudio/Editing/PopupMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,15 @@ public ContentDrawable(PopupMenu menu) {
public override int DrawHeight => menu.Height;

public override void Draw(SKSurface surface) {
surface.Canvas.Clear();
surface.Canvas.Translate(-menu.ScrollPosition.X, -menu.ScrollPosition.Y);
var canvas = surface.Canvas;

if (menu.shownEntries.Length == 0) {
return;
}

var backgroundRect = new SKRect(menu.ScrollPosition.X, menu.ScrollPosition.Y, menu.ScrollPosition.X + menu.Width, menu.ScrollPosition.Y + menu.Height);
surface.Canvas.ClipRoundRect(new SKRoundRect(backgroundRect, Settings.Instance.Theme.PopupMenuBorderRounding), antialias: true);
surface.Canvas.DrawRect(backgroundRect, Settings.Instance.Theme.PopupMenuBgPaint);
canvas.ClipRoundRect(new SKRoundRect(backgroundRect, Settings.Instance.Theme.PopupMenuBorderRounding), antialias: true);
canvas.DrawRect(backgroundRect, Settings.Instance.Theme.PopupMenuBgPaint);

var font = FontManager.SKPopupFont;
int maxDisplayLen = menu.shownEntries.Select(entry => entry.DisplayText.Length).Aggregate(Math.Max);
Expand All @@ -76,7 +75,7 @@ public override void Draw(SKSurface surface) {

// Highlight selected entry
if (row == menu.SelectedEntry && !entry.Disabled) {
surface.Canvas.DrawRoundRect(
canvas.DrawRoundRect(
x: Settings.Instance.Theme.PopupMenuBorderPadding,
y: row * height + Settings.Instance.Theme.PopupMenuBorderPadding + Settings.Instance.Theme.PopupMenuEntrySpacing / 2.0f,
w: width,
Expand All @@ -86,11 +85,11 @@ public override void Draw(SKSurface surface) {
Settings.Instance.Theme.PopupMenuSelectedPaint);
}

surface.Canvas.DrawText(entry.DisplayText,
canvas.DrawText(entry.DisplayText,
x: Settings.Instance.Theme.PopupMenuBorderPadding + Settings.Instance.Theme.PopupMenuEntryHorizontalPadding,
y: Settings.Instance.Theme.PopupMenuBorderPadding + row * height + Settings.Instance.Theme.PopupMenuEntryVerticalPadding + Settings.Instance.Theme.PopupMenuEntrySpacing / 2.0f + font.Offset(),
font, entry.Disabled ? Settings.Instance.Theme.PopupMenuFgDisabledPaint : Settings.Instance.Theme.PopupMenuFgPaint);
surface.Canvas.DrawText(entry.ExtraText,
canvas.DrawText(entry.ExtraText,
x: Settings.Instance.Theme.PopupMenuBorderPadding + Settings.Instance.Theme.PopupMenuEntryHorizontalPadding + font.CharWidth() * (maxDisplayLen + DisplayExtraPadding),
y: Settings.Instance.Theme.PopupMenuBorderPadding + row * height + Settings.Instance.Theme.PopupMenuEntryVerticalPadding + Settings.Instance.Theme.PopupMenuEntrySpacing / 2.0f + font.Offset(),
font, Settings.Instance.Theme.PopupMenuFgExtraPaint);
Expand Down

0 comments on commit f45de44

Please sign in to comment.