|
|
@@ -10,6 +10,7 @@ using Avalonia.Rendering.SceneGraph;
|
|
|
using Avalonia.Rendering.Utilities;
|
|
|
using Avalonia.Utilities;
|
|
|
using Avalonia.Media.Imaging;
|
|
|
+using JetBrains.Annotations;
|
|
|
using SkiaSharp;
|
|
|
|
|
|
namespace Avalonia.Skia
|
|
|
@@ -17,7 +18,7 @@ namespace Avalonia.Skia
|
|
|
/// <summary>
|
|
|
/// Skia based drawing context.
|
|
|
/// </summary>
|
|
|
- internal class DrawingContextImpl : IDrawingContextImpl, ISkiaDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport
|
|
|
+ internal class DrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport
|
|
|
{
|
|
|
private IDisposable[] _disposables;
|
|
|
private readonly Vector _dpi;
|
|
|
@@ -38,7 +39,8 @@ namespace Avalonia.Skia
|
|
|
private readonly SKPaint _fillPaint = new SKPaint();
|
|
|
private readonly SKPaint _boxShadowPaint = new SKPaint();
|
|
|
private static SKShader s_acrylicNoiseShader;
|
|
|
- private readonly ISkiaGpuRenderSession _session;
|
|
|
+ private readonly ISkiaGpuRenderSession _session;
|
|
|
+ private bool _leased = false;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Context create info.
|
|
|
@@ -83,6 +85,47 @@ namespace Avalonia.Skia
|
|
|
public ISkiaGpuRenderSession CurrentSession;
|
|
|
}
|
|
|
|
|
|
+ class SkiaLeaseFeature : ISkiaSharpApiLeaseFeature
|
|
|
+ {
|
|
|
+ private readonly DrawingContextImpl _context;
|
|
|
+
|
|
|
+ public SkiaLeaseFeature(DrawingContextImpl context)
|
|
|
+ {
|
|
|
+ _context = context;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ISkiaSharpApiLease Lease()
|
|
|
+ {
|
|
|
+ _context.CheckLease();
|
|
|
+ return new ApiLease(_context);
|
|
|
+ }
|
|
|
+
|
|
|
+ class ApiLease : ISkiaSharpApiLease
|
|
|
+ {
|
|
|
+ private DrawingContextImpl _context;
|
|
|
+ private readonly SKMatrix _revertTransform;
|
|
|
+
|
|
|
+ public ApiLease(DrawingContextImpl context)
|
|
|
+ {
|
|
|
+ _revertTransform = context.Canvas.TotalMatrix;
|
|
|
+ _context = context;
|
|
|
+ _context._leased = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public SKCanvas SkCanvas => _context.Canvas;
|
|
|
+ public GRContext GrContext => _context.GrContext;
|
|
|
+ public SKSurface SkSurface => _context.Surface;
|
|
|
+ public double CurrentOpacity => _context._currentOpacity;
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ _context.Canvas.SetMatrix(_revertTransform);
|
|
|
+ _context._leased = false;
|
|
|
+ _context = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Create new drawing context.
|
|
|
/// </summary>
|
|
|
@@ -123,20 +166,23 @@ namespace Avalonia.Skia
|
|
|
public SKCanvas Canvas { get; }
|
|
|
public SKSurface Surface { get; }
|
|
|
|
|
|
- SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas;
|
|
|
- SKSurface ISkiaDrawingContextImpl.SkSurface => Surface;
|
|
|
- GRContext ISkiaDrawingContextImpl.GrContext => _grContext;
|
|
|
- double ISkiaDrawingContextImpl.CurrentOpacity => _currentOpacity;
|
|
|
-
|
|
|
+ private void CheckLease()
|
|
|
+ {
|
|
|
+ if (_leased)
|
|
|
+ throw new InvalidOperationException("The underlying graphics API is currently leased");
|
|
|
+ }
|
|
|
+
|
|
|
/// <inheritdoc />
|
|
|
public void Clear(Color color)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Clear(color.ToSKColor());
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
var drawableImage = (IDrawableBitmapImpl)source.Item;
|
|
|
var s = sourceRect.ToSKRect();
|
|
|
var d = destRect.ToSKRect();
|
|
|
@@ -157,6 +203,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
PushOpacityMask(opacityMask, opacityMaskRect);
|
|
|
DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default);
|
|
|
PopOpacityMask();
|
|
|
@@ -165,6 +212,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void DrawLine(IPen pen, Point p1, Point p2)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
using (var paint = CreatePaint(_strokePaint, pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
|
|
|
{
|
|
|
if (paint.Paint is object)
|
|
|
@@ -177,6 +225,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
var impl = (GeometryImpl) geometry;
|
|
|
var size = geometry.Bounds.Size;
|
|
|
|
|
|
@@ -260,6 +309,7 @@ namespace Avalonia.Skia
|
|
|
{
|
|
|
if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0)
|
|
|
return;
|
|
|
+ CheckLease();
|
|
|
|
|
|
var rc = rect.Rect.ToSKRect();
|
|
|
var isRounded = rect.IsRounded;
|
|
|
@@ -296,6 +346,7 @@ namespace Avalonia.Skia
|
|
|
{
|
|
|
if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0)
|
|
|
return;
|
|
|
+ CheckLease();
|
|
|
// Arbitrary chosen values
|
|
|
// On OSX Skia breaks OpenGL context when asked to draw, e. g. (0, 0, 623, 6666600) rect
|
|
|
if (rect.Rect.Height > 8192 || rect.Rect.Width > 8192)
|
|
|
@@ -421,7 +472,8 @@ namespace Avalonia.Skia
|
|
|
{
|
|
|
if (rect.Height <= 0 || rect.Width <= 0)
|
|
|
return;
|
|
|
-
|
|
|
+ CheckLease();
|
|
|
+
|
|
|
var rc = rect.ToSKRect();
|
|
|
|
|
|
if (brush != null)
|
|
|
@@ -447,6 +499,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Size))
|
|
|
{
|
|
|
var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
|
|
|
@@ -459,18 +512,21 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public IDrawingContextLayerImpl CreateLayer(Size size)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
return CreateRenderTarget(size, true);
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
public void PushClip(Rect clip)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Save();
|
|
|
Canvas.ClipRect(clip.ToSKRect());
|
|
|
}
|
|
|
|
|
|
public void PushClip(RoundedRect clip)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Save();
|
|
|
Canvas.ClipRoundRect(clip.ToSKRoundRect(), antialias:true);
|
|
|
}
|
|
|
@@ -478,12 +534,14 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PopClip()
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Restore();
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
public void PushOpacity(double opacity)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
_opacityStack.Push(_currentOpacity);
|
|
|
_currentOpacity *= opacity;
|
|
|
}
|
|
|
@@ -491,6 +549,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PopOpacity()
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
_currentOpacity = _opacityStack.Pop();
|
|
|
}
|
|
|
|
|
|
@@ -499,6 +558,7 @@ namespace Avalonia.Skia
|
|
|
{
|
|
|
if(_disposed)
|
|
|
return;
|
|
|
+ CheckLease();
|
|
|
try
|
|
|
{
|
|
|
if (_grContext != null)
|
|
|
@@ -523,6 +583,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PushGeometryClip(IGeometryImpl clip)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Save();
|
|
|
Canvas.ClipPath(((GeometryImpl)clip).EffectivePath, SKClipOperation.Intersect, true);
|
|
|
}
|
|
|
@@ -530,12 +591,14 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PopGeometryClip()
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
Canvas.Restore();
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
_blendingModeStack.Push(_currentBlendingMode);
|
|
|
_currentBlendingMode = blendingMode;
|
|
|
}
|
|
|
@@ -543,14 +606,20 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PopBitmapBlendMode()
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
_currentBlendingMode = _blendingModeStack.Pop();
|
|
|
}
|
|
|
|
|
|
- public void Custom(ICustomDrawOperation custom) => custom.Render(this);
|
|
|
+ public void Custom(ICustomDrawOperation custom)
|
|
|
+ {
|
|
|
+ CheckLease();
|
|
|
+ custom.Render(this);
|
|
|
+ }
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
public void PushOpacityMask(IBrush mask, Rect bounds)
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
// TODO: This should be disposed
|
|
|
var paint = new SKPaint();
|
|
|
|
|
|
@@ -561,6 +630,7 @@ namespace Avalonia.Skia
|
|
|
/// <inheritdoc />
|
|
|
public void PopOpacityMask()
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
using (var paint = new SKPaint { BlendMode = SKBlendMode.DstIn })
|
|
|
{
|
|
|
Canvas.SaveLayer(paint);
|
|
|
@@ -580,6 +650,7 @@ namespace Avalonia.Skia
|
|
|
get { return _currentTransform; }
|
|
|
set
|
|
|
{
|
|
|
+ CheckLease();
|
|
|
if (_currentTransform == value)
|
|
|
return;
|
|
|
|
|
|
@@ -596,6 +667,14 @@ namespace Avalonia.Skia
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ [CanBeNull]
|
|
|
+ public object GetFeature(Type t)
|
|
|
+ {
|
|
|
+ if (t == typeof(ISkiaSharpApiLeaseFeature))
|
|
|
+ return new SkiaLeaseFeature(this);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Configure paint wrapper for using gradient brush.
|
|
|
/// </summary>
|