Browse Source

Merge pull request #2371 from AvaloniaUI/expose-skcanvas

Custom draw operations, exposed SkCanvas and reduced Skia api visibility
pull/2449/head
Nikita Tsukanov 7 years ago
committed by GitHub
parent
commit
03076a46c9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      samples/RenderDemo/MainWindow.xaml
  2. 119
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  3. 7
      src/Avalonia.Visuals/Media/DrawingContext.cs
  4. 7
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  5. 39
      src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs
  6. 9
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  7. 7
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  8. 2
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  9. 2
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  10. 2
      src/Skia/Avalonia.Skia/GeometryImpl.cs
  11. 2
      src/Skia/Avalonia.Skia/GlRenderTarget.cs
  12. 10
      src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs
  13. 2
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  14. 2
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  15. 2
      src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
  16. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  17. 2
      src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
  18. 2
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  19. 3
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  20. 2
      src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

3
samples/RenderDemo/MainWindow.xaml

@ -33,6 +33,9 @@
<TabItem Header="Drawing">
<pages:DrawingPage/>
</TabItem>
<TabItem Header="SkCanvas">
<pages:CustomSkiaPage/>
</TabItem>
</TabControl>
</DockPanel>
</Window>

119
samples/RenderDemo/Pages/CustomSkiaPage.cs

@ -0,0 +1,119 @@
using System;
using System.Diagnostics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Skia;
using Avalonia.Threading;
using SkiaSharp;
namespace RenderDemo.Pages
{
public class CustomSkiaPage : Control
{
public CustomSkiaPage()
{
ClipToBounds = true;
}
class CustomDrawOp : ICustomDrawOperation
{
private readonly FormattedText _noSkia;
public CustomDrawOp(Rect bounds, FormattedText noSkia)
{
_noSkia = noSkia;
Bounds = bounds;
}
public void Dispose()
{
// No-op
}
public Rect Bounds { get; }
public bool HitTest(Point p) => false;
public bool Equals(ICustomDrawOperation other) => false;
static Stopwatch St = Stopwatch.StartNew();
public void Render(IDrawingContextImpl context)
{
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
if (canvas == null)
context.DrawText(Brushes.Black, new Point(), _noSkia.PlatformImpl);
else
{
canvas.Save();
// create the first shader
var colors = new SKColor[] {
new SKColor(0, 255, 255),
new SKColor(255, 0, 255),
new SKColor(255, 255, 0),
new SKColor(0, 255, 255)
};
var sx = Animate(100, 2, 10);
var sy = Animate(1000, 5, 15);
var lightPosition = new SKPoint(
(float)(Bounds.Width / 2 + Math.Cos(St.Elapsed.TotalSeconds) * Bounds.Width / 4),
(float)(Bounds.Height / 2 + Math.Sin(St.Elapsed.TotalSeconds) * Bounds.Height / 4));
using (var sweep =
SKShader.CreateSweepGradient(new SKPoint((int)Bounds.Width / 2, (int)Bounds.Height / 2), colors,
null))
using(var turbulence = SKShader.CreatePerlinNoiseFractalNoise(0.05f, 0.05f, 4, 0))
using(var shader = SKShader.CreateCompose(sweep, turbulence, SKBlendMode.SrcATop))
using(var blur = SKImageFilter.CreateBlur(Animate(100, 2, 10), Animate(100, 5, 15)))
using (var paint = new SKPaint
{
Shader = shader,
ImageFilter = blur
})
canvas.DrawPaint(paint);
using (var pseudoLight = SKShader.CreateRadialGradient(
lightPosition,
(float) (Bounds.Width/3),
new [] {
new SKColor(255, 200, 200, 100),
SKColors.Transparent,
new SKColor(40,40,40, 220),
new SKColor(20,20,20, (byte)Animate(100, 200,220)) },
new float[] { 0.3f, 0.3f, 0.8f, 1 },
SKShaderTileMode.Clamp))
using (var paint = new SKPaint
{
Shader = pseudoLight
})
canvas.DrawPaint(paint);
canvas.Restore();
}
}
static int Animate(int d, int from, int to)
{
var ms = (int)(St.ElapsedMilliseconds / d);
var diff = to - from;
var range = diff * 2;
var v = ms % range;
if (v > diff)
v = range - v;
var rv = v + from;
if (rv < from || rv > to)
throw new Exception("WTF");
return rv;
}
}
public override void Render(DrawingContext context)
{
var noSkia = new FormattedText()
{
Text = "Current rendering API is not Skia"
};
context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia));
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
}
}
}

7
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Visuals.Media.Imaging;
@ -131,6 +132,12 @@ namespace Avalonia.Media
}
}
/// <summary>
/// Draws a custom drawing operation
/// </summary>
/// <param name="custom">custom operation</param>
public void Custom(ICustomDrawOperation custom) => PlatformImpl.Custom(custom);
/// <summary>
/// Draws text.
/// </summary>

7
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Media;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
@ -139,5 +140,11 @@ namespace Avalonia.Platform
/// Pops the latest pushed geometry clip.
/// </summary>
void PopGeometryClip();
/// <summary>
/// Adds a custom draw operation
/// </summary>
/// <param name="custom">Custom draw operation</param>
void Custom(ICustomDrawOperation custom);
}
}

39
src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs

@ -0,0 +1,39 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
internal sealed class CustomDrawOperation : DrawOperation
{
public Matrix Transform { get; }
public ICustomDrawOperation Custom { get; }
public CustomDrawOperation(ICustomDrawOperation custom, Matrix transform)
: base(custom.Bounds, transform, null)
{
Transform = transform;
Custom = custom;
}
public override bool HitTest(Point p)
{
return Custom.HitTest(p * Transform);
}
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
Custom.Render(context);
}
public override void Dispose() => Custom.Dispose();
public bool Equals(Matrix transform, ICustomDrawOperation custom) =>
Transform == transform && Custom?.Equals(custom) == true;
}
public interface ICustomDrawOperation : IDrawOperation, IEquatable<ICustomDrawOperation>
{
}
}

9
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -165,6 +165,15 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
public void Custom(ICustomDrawOperation custom)
{
var next = NextDrawAs<CustomDrawOperation>();
if (next == null || !next.Item.Equals(Transform, custom))
Add(new CustomDrawOperation(custom, Transform));
else
++_drawOperationindex;
}
/// <inheritdoc/>
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)

7
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -9,6 +9,7 @@ using System.Threading;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Rendering.Utilities;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
@ -19,7 +20,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia based drawing context.
/// </summary>
public class DrawingContextImpl : IDrawingContextImpl
internal class DrawingContextImpl : IDrawingContextImpl, ISkiaDrawingContextImpl
{
private IDisposable[] _disposables;
private readonly Vector _dpi;
@ -99,6 +100,8 @@ namespace Avalonia.Skia
/// </summary>
public SKCanvas Canvas { get; }
SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas;
/// <inheritdoc />
public void Clear(Color color)
{
@ -296,6 +299,8 @@ namespace Avalonia.Skia
Canvas.Restore();
}
public void Custom(ICustomDrawOperation custom) => custom.Render(this);
/// <inheritdoc />
public void PushOpacityMask(IBrush mask, Rect bounds)
{

2
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -13,7 +13,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia formatted text implementation.
/// </summary>
public class FormattedTextImpl : IFormattedTextImpl
internal class FormattedTextImpl : IFormattedTextImpl
{
public FormattedTextImpl(
string text,

2
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -13,7 +13,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia render target that renders to a framebuffer surface. No gpu acceleration available.
/// </summary>
public class FramebufferRenderTarget : IRenderTarget
internal class FramebufferRenderTarget : IRenderTarget
{
private readonly IFramebufferPlatformSurface _platformSurface;
private SKImageInfo _currentImageInfo;

2
src/Skia/Avalonia.Skia/GeometryImpl.cs

@ -11,7 +11,7 @@ namespace Avalonia.Skia
/// <summary>
/// A Skia implementation of <see cref="IGeometryImpl"/>.
/// </summary>
public abstract class GeometryImpl : IGeometryImpl
internal abstract class GeometryImpl : IGeometryImpl
{
private PathCache _pathCache;

2
src/Skia/Avalonia.Skia/GlRenderTarget.cs

@ -8,7 +8,7 @@ using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
public class GlRenderTarget : IRenderTarget
internal class GlRenderTarget : IRenderTarget
{
private readonly GRContext _grContext;
private IGlPlatformSurfaceRenderTarget _surface;

10
src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs

@ -0,0 +1,10 @@
using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia
{
public interface ISkiaDrawingContextImpl : IDrawingContextImpl
{
SKCanvas SkCanvas { get; }
}
}

2
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -12,7 +12,7 @@ namespace Avalonia.Skia
/// <summary>
/// Immutable Skia bitmap.
/// </summary>
public class ImmutableBitmap : IDrawableBitmapImpl
internal class ImmutableBitmap : IDrawableBitmapImpl
{
private readonly SKImage _image;

2
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -15,7 +15,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia platform render interface.
/// </summary>
public class PlatformRenderInterface : IPlatformRenderInterface
internal class PlatformRenderInterface : IPlatformRenderInterface
{
private GRContext GrContext { get; }

2
src/Skia/Avalonia.Skia/StreamGeometryImpl.cs

@ -10,7 +10,7 @@ namespace Avalonia.Skia
/// <summary>
/// A Skia implementation of a <see cref="IStreamGeometryImpl"/>.
/// </summary>
public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
internal class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
{
private Rect _bounds;
private readonly SKPath _effectivePath;

2
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -14,7 +14,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia render target that writes to a surface.
/// </summary>
public class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
{
private readonly SKSurface _surface;
private readonly SKCanvas _canvas;

2
src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs

@ -9,7 +9,7 @@ namespace Avalonia.Skia
/// <summary>
/// A Skia implementation of a <see cref="ITransformedGeometryImpl"/>.
/// </summary>
public class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl
internal class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl
{
/// <summary>
/// Initializes a new instance of the <see cref="TransformedGeometryImpl"/> class.

2
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -13,7 +13,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia based writeable bitmap.
/// </summary>
public class WriteableBitmapImpl : IWriteableBitmapImpl, IDrawableBitmapImpl
internal class WriteableBitmapImpl : IWriteableBitmapImpl, IDrawableBitmapImpl
{
private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc;
private readonly SKBitmap _bitmap;

3
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using SharpDX;
using SharpDX.Direct2D1;
@ -508,5 +509,7 @@ namespace Avalonia.Direct2D1.Media
{
PopLayer();
}
public void Custom(ICustomDrawOperation custom) => custom.Render(this);
}
}

2
src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

@ -9,7 +9,7 @@ using DWrite = SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
{
public class FormattedTextImpl : IFormattedTextImpl
internal class FormattedTextImpl : IFormattedTextImpl
{
public FormattedTextImpl(
string text,

Loading…
Cancel
Save