Browse Source

Merge branch 'master' into fixes/geometry-impls

pull/2375/head
Nikita Tsukanov 7 years ago
committed by GitHub
parent
commit
5bee648871
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      nukebuild/Build.cs
  2. 3
      samples/RenderDemo/MainWindow.xaml
  3. 119
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  4. 7
      src/Avalonia.Visuals/Media/DrawingContext.cs
  5. 7
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  6. 39
      src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs
  7. 9
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  8. 42
      src/Skia/Avalonia.Skia/CustomRenderTarget.cs
  9. 7
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  10. 2
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  11. 2
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  12. 2
      src/Skia/Avalonia.Skia/GeometryImpl.cs
  13. 2
      src/Skia/Avalonia.Skia/GlRenderTarget.cs
  14. 26
      src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs
  15. 29
      src/Skia/Avalonia.Skia/ICustomSkiaRenderSession.cs
  16. 19
      src/Skia/Avalonia.Skia/ICustomSkiaRenderTarget.cs
  17. 10
      src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs
  18. 2
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  19. 32
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  20. 5
      src/Skia/Avalonia.Skia/SkiaApplicationExtensions.cs
  21. 19
      src/Skia/Avalonia.Skia/SkiaOptions.cs
  22. 10
      src/Skia/Avalonia.Skia/SkiaPlatform.cs
  23. 2
      src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
  24. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  25. 2
      src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
  26. 2
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  27. 3
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  28. 2
      src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

8
nukebuild/Build.cs

@ -122,6 +122,14 @@ partial class Build : NukeBuild
foreach(var fw in frameworks)
{
if (fw.StartsWith("net4")
&& RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
&& Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
{
Information($"Skipping {fw} tests on Linux - https://github.com/mono/mono/issues/13969");
continue;
}
Information("Running for " + fw);
DotNetTest(c =>
{

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)

42
src/Skia/Avalonia.Skia/CustomRenderTarget.cs

@ -0,0 +1,42 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.Skia
{
/// <summary>
/// Adapts <see cref="ICustomSkiaRenderTarget"/> to be used within Skia rendering pipeline.
/// </summary>
internal class CustomRenderTarget : IRenderTarget
{
private readonly ICustomSkiaRenderTarget _renderTarget;
public CustomRenderTarget(ICustomSkiaRenderTarget renderTarget)
{
_renderTarget = renderTarget;
}
public void Dispose()
{
_renderTarget.Dispose();
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
ICustomSkiaRenderSession session = _renderTarget.BeginRendering();
var nfo = new DrawingContextImpl.CreateInfo
{
GrContext = session.GrContext,
Canvas = session.Canvas,
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = true
};
return new DrawingContextImpl(nfo, session);
}
}
}

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;

26
src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs

@ -0,0 +1,26 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using SkiaSharp;
namespace Avalonia.Skia
{
/// <summary>
/// Custom Skia gpu instance.
/// </summary>
public interface ICustomSkiaGpu
{
/// <summary>
/// Skia GrContext used.
/// </summary>
GRContext GrContext { get; }
/// <summary>
/// Attempts to create custom render target from given surfaces.
/// </summary>
/// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ICustomSkiaRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces);
}
}

29
src/Skia/Avalonia.Skia/ICustomSkiaRenderSession.cs

@ -0,0 +1,29 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using SkiaSharp;
namespace Avalonia.Skia
{
/// <summary>
/// Custom render session for Skia render target.
/// </summary>
public interface ICustomSkiaRenderSession : IDisposable
{
/// <summary>
/// GrContext used by this session.
/// </summary>
GRContext GrContext { get; }
/// <summary>
/// Canvas that will be used to render.
/// </summary>
SKCanvas Canvas { get; }
/// <summary>
/// Scaling factor.
/// </summary>
double ScaleFactor { get; }
}
}

19
src/Skia/Avalonia.Skia/ICustomSkiaRenderTarget.cs

@ -0,0 +1,19 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Skia
{
/// <summary>
/// Custom Skia render target.
/// </summary>
public interface ICustomSkiaRenderTarget : IDisposable
{
/// <summary>
/// Start rendering to this render target.
/// </summary>
/// <returns></returns>
ICustomSkiaRenderSession BeginRendering();
}
}

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;

32
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -15,14 +15,25 @@ namespace Avalonia.Skia
/// <summary>
/// Skia platform render interface.
/// </summary>
public class PlatformRenderInterface : IPlatformRenderInterface
internal class PlatformRenderInterface : IPlatformRenderInterface
{
private readonly ICustomSkiaGpu _customSkiaGpu;
private GRContext GrContext { get; }
public IEnumerable<string> InstalledFontNames => SKFontManager.Default.FontFamilies;
public PlatformRenderInterface()
public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu)
{
if (customSkiaGpu != null)
{
_customSkiaGpu = customSkiaGpu;
GrContext = _customSkiaGpu.GrContext;
return;
}
var gl = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
if (gl != null)
{
@ -32,12 +43,11 @@ namespace Avalonia.Skia
? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc))
: GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc)))
{
GrContext = GRContext.Create(GRBackend.OpenGL, iface);
}
}
}
/// <inheritdoc />
public IFormattedTextImpl CreateFormattedText(
string text,
@ -104,13 +114,23 @@ namespace Avalonia.Skia
DisableTextLcdRendering = false,
GrContext = GrContext
};
return new SurfaceRenderTarget(createInfo);
}
/// <inheritdoc />
public virtual IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
if (_customSkiaGpu != null)
{
ICustomSkiaRenderTarget customRenderTarget = _customSkiaGpu.TryCreateRenderTarget(surfaces);
if (customRenderTarget != null)
{
return new CustomRenderTarget(customRenderTarget);
}
}
foreach (var surface in surfaces)
{
if (surface is IGlPlatformSurface glSurface && GrContext != null)

5
src/Skia/Avalonia.Skia/SkiaApplicationExtensions.cs

@ -20,8 +20,9 @@ namespace Avalonia
/// <returns>Configure builder.</returns>
public static T UseSkia<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.UseRenderingSubsystem(() => SkiaPlatform.Initialize(), "Skia");
return builder;
return builder.UseRenderingSubsystem(() => SkiaPlatform.Initialize(
AvaloniaLocator.Current.GetService<SkiaOptions>() ?? new SkiaOptions()),
"Skia");
}
}
}

19
src/Skia/Avalonia.Skia/SkiaOptions.cs

@ -0,0 +1,19 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Skia;
namespace Avalonia
{
/// <summary>
/// Options for Skia rendering subsystem.
/// </summary>
public class SkiaOptions
{
/// <summary>
/// Custom gpu factory to use. Can be used to customize behavior of Skia renderer.
/// </summary>
public Func<ICustomSkiaGpu> CustomGpuFactory { get; set; }
}
}

10
src/Skia/Avalonia.Skia/SkiaPlatform.cs

@ -15,8 +15,14 @@ namespace Avalonia.Skia
/// </summary>
public static void Initialize()
{
var renderInterface = new PlatformRenderInterface();
Initialize(new SkiaOptions());
}
public static void Initialize(SkiaOptions options)
{
var customGpu = options.CustomGpuFactory?.Invoke();
var renderInterface = new PlatformRenderInterface(customGpu);
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(renderInterface);
}

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