19public abstract class BackbufferBase : IDisposable
24 private BackbufferBase()
60 public abstract SKCanvas
Canvas {
get; }
83 protected internal abstract SKImage Snapshot();
103 protected internal abstract void BeginFrame();
130 protected internal abstract void DrawTileFrame(
Tile tile, RectangleF destRectScreen);
151 protected internal abstract void EndFrame();
158 Color =
new SKColor(0, 0, 0, 128),
167 Color = SKColors.White,
177 Color = SKColors.Green,
185 public int Width => Volatile.Read(ref _width);
190 public int Height => Volatile.Read(ref _height);
213 protected internal virtual void RequestResize(
int width,
int height)
234 protected internal event Action<int, int>? SizeChanged;
254 Volatile.Write(ref _width, width);
255 Volatile.Write(ref _height, height);
257 SizeChanged?.Invoke(width, height);
264 protected internal Rectangle DirtyRectangle {
get;
private set; }
266 private SKColor _clearColor = SKColors.Black;
274 BlendMode = SKBlendMode.Src,
275 Style = SKPaintStyle.Fill,
276 FilterQuality = SKFilterQuality.None,
288 _fillPaint.Color = value;
292 internal void ClearRect(Rectangle rectPx)
308 AddToBackbufferDirtyRectangle(rectPx);
313 internal void DrawDrawables(
View view, IEnumerable<IDrawable> drawables, Rectangle clipRect)
315 if (
this is RecordingBackbuffer recording &&
316 drawables is IReadOnlyList<IDrawable> ordered &&
317 recording.TryDrawFixedGridDrawables(view, ordered, clipRect))
323 Canvas.ClipRect(clipRect.ToSKRect());
325 var tiles =
new List<(Tile Tile, WrappedDrawable? Instance)>();
327 foreach (var entry
in drawables)
329 var instance = entry as WrappedDrawable;
330 var drawable = instance?.Owner ?? entry;
331 using var scope = instance?.Enter(view);
332 if (!drawable.Visible)
335 var destRectScreen = drawable.GetDrawLocationScreen(view);
336 if (drawable is
Sprite sprite)
338 destRectScreen = sprite.ApplyJiggleToDestRect(destRectScreen);
341 bool batched =
this is RecordingBackbuffer recorder &&
345 recorder.TryQueueTile(layerTile, destRectScreen);
349 (
this as RecordingBackbuffer)?.FlushTileBatch();
350 drawable.Draw(
this, destRectScreen);
357 RectangleF visualBoundsScreen = drawable is Sprite drawnSprite
358 ? drawnSprite.GetVisualBoundsScreen(destRectScreen)
361 AddToBackbufferDirtyRectangle(visualBoundsScreen.ToPixelAlignedRect());
364 if (drawable is Tile tile)
365 tiles.Add((tile, instance));
368 (
this as RecordingBackbuffer)?.FlushTileBatch();
370 PostDrawTiles(view, tiles);
375 private void PostDrawTiles(View view, List<(Tile Tile, WrappedDrawable? Instance)> tiles)
377 foreach (var entry
in tiles)
379 var tile = entry.Tile;
380 using var scope = entry.Instance?.Enter(view);
381 var layer = tile.SceneLayer;
382 bool drawFog = tile.EnableFog;
383 bool drawGrid = layer.ShowGridLines && tile.Visible && tile.IsPositionFixed;
384 bool drawCollision = layer.ShowCollisionBoxes && tile.Visible && tile.CollisionsEnabled;
386 if (!drawFog && !drawGrid && !drawCollision)
395 && layer.CoordinateSystemType == Gondwana.Drawing.Coordinates.CoordinateSystemTypes.Orthogonal)
397 var worldRect = layer.CoordinateSystem.GetPixelRangeForTile(tile, includeOverhang:
false);
403 var colRectScreen = tile.GetCollisionAreaScreen(view).ToSKRect();
411 if (drawFog || drawGrid)
413 var worldPts = tile.OutlinePointsWorld;
414 var ptsScreen =
new SKPoint[worldPts.Length];
416 for (
int i = 0; i < worldPts.Length; i++)
424 ptsScreen[i] =
new SKPoint(sp.X, sp.Y);
429 using var path =
new SKPath();
430 path.AddPoly(ptsScreen, close:
true);
436 var enclosed =
new SKPoint[ptsScreen.Length + 1];
437 Array.Copy(ptsScreen, enclosed, ptsScreen.Length);
438 enclosed[^1] = ptsScreen[0];
445 var colRectScreen = tile.GetCollisionAreaScreen(view).ToSKRect();
458 protected internal void AddToBackbufferDirtyRectangle(Rectangle area)
463 area.Inflate(area.Width, area.Height);
465 DirtyRectangle = DirtyRectangle.IsEmpty
481 protected internal void ClearDirtyRectangle()
496 public virtual byte[]
ToByteArray(SKEncodedImageFormat format = SKEncodedImageFormat.Png,
int quality = 100)
498 using var image = Snapshot();
499 using var data = image.Encode(format, quality);
500 return data.ToArray();
536 GC.SuppressFinalize(
this);