|
|
|
@ -7,6 +7,7 @@ using Avalonia.Media.Immutable; |
|
|
|
using Avalonia.Rendering; |
|
|
|
using Avalonia.Rendering.Composition; |
|
|
|
using Avalonia.Rendering.Composition.Drawing; |
|
|
|
using Avalonia.Rendering.Composition.Drawing.Nodes; |
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.UnitTests; |
|
|
|
using Moq; |
|
|
|
@ -146,5 +147,457 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph |
|
|
|
|
|
|
|
Assert.Null(ctx.Context.GetRenderResults()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void HitTest_Through_PushTransform_Maps_Coordinates() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateTranslation(50, 50))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
Assert.True(rd.HitTest(new Point(55, 55))); |
|
|
|
Assert.False(rd.HitTest(new Point(5, 5))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void HitTest_Through_PushClip_Restricts_To_Clip_Region() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushClip(new Rect(0, 0, 10, 10))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 100, 100))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(rd.HitTest(new Point(50, 50))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void HitTest_Through_Nested_Transforms_Composes_Outer_To_Inner() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateTranslation(20, 20))) |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateScale(2, 2))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
Assert.True(rd.HitTest(new Point(30, 30))); |
|
|
|
Assert.False(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(rd.HitTest(new Point(45, 45))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Bounds_Union_Across_Multiple_Top_Level_Draws() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(20, 20, 10, 10))); |
|
|
|
|
|
|
|
Assert.Equal(new Rect(0, 0, 30, 30), ctx.GetBounds()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Bounds_Reflect_PushTransform_Translation() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateTranslation(50, 50))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
|
|
|
|
Assert.Equal(new Rect(50, 50, 10, 10), ctx.GetBounds()); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Identity_PushTransform_Does_Not_Wrap_Children() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushTransform(Matrix.Identity)) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
ctx.ForceRender(); |
|
|
|
Assert.Equal(new Rect(0, 0, 10, 10), rd.Server.Bounds?.ToRect()); |
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Opacity_One_Push_Does_Not_Wrap_Children() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushOpacity(1)) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
ctx.ForceRender(); |
|
|
|
Assert.Equal(new Rect(0, 0, 10, 10), rd.Server.Bounds?.ToRect()); |
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Empty_Push_Between_Real_Draws_Does_Not_Affect_Siblings() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateTranslation(100, 100))) |
|
|
|
{ |
|
|
|
} |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(40, 40, 10, 10))); |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
ctx.ForceRender(); |
|
|
|
Assert.Equal(new Rect(0, 0, 50, 50), rd.Server.Bounds?.ToRect()); |
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.True(rd.HitTest(new Point(45, 45))); |
|
|
|
Assert.False(rd.HitTest(new Point(25, 25))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void NoOp_Inner_Push_Inside_Real_Outer_Push_Is_Stripped() |
|
|
|
{ |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushTransform(Matrix.CreateTranslation(20, 20))) |
|
|
|
{ |
|
|
|
using (ctx.Context.PushOpacity(1)) |
|
|
|
{ |
|
|
|
} |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
} |
|
|
|
|
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
ctx.ForceRender(); |
|
|
|
Assert.Equal(new Rect(20, 20, 10, 10), rd.Server.Bounds?.ToRect()); |
|
|
|
Assert.True(rd.HitTest(new Point(25, 25))); |
|
|
|
Assert.False(rd.HitTest(new Point(5, 5))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Non_Immutable_Brush_Is_AddRefed_Once_And_Released_On_Dispose() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(brush, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
Assert.Equal(0, brush.ReleaseCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
Assert.Equal(1, brush.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Non_Immutable_Brush_Used_Multiple_Times_Is_AddRefed_Once() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(brush, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
ctx.Context.DrawRectangle(brush, null, new RoundedRect(new Rect(20, 20, 10, 10))); |
|
|
|
ctx.Context.DrawEllipse(brush, null, new Rect(40, 40, 10, 10)); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Non_Immutable_Pen_Is_AddRefed_Once_And_Released_On_Dispose() |
|
|
|
{ |
|
|
|
var pen = new TrackingPen(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawLine(pen, new Point(0, 0), new Point(10, 10)); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, pen.AddRefCount); |
|
|
|
Assert.Equal(0, pen.ReleaseCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, pen.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Immutable_Brush_Is_Not_Registered_As_Server_Resource() |
|
|
|
{ |
|
|
|
var brush = new ImmutableTrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(brush, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(0, brush.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Immutable_Pen_Is_Not_Registered_As_Server_Resource() |
|
|
|
{ |
|
|
|
var pen = new ImmutableTrackingPen(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawLine(pen, new Point(0, 0), new Point(10, 10)); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(0, pen.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Custom_Draw_Operation_Disposed_When_RenderData_Disposed_Before_Commit() |
|
|
|
{ |
|
|
|
var op = new TrackingCustomOp(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.Custom(op); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(0, op.DisposeCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, op.DisposeCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void GlyphRun_Node_Releases_Reference_On_Direct_Dispose() |
|
|
|
{ |
|
|
|
var glyphRunRef = RefCountable.Create(Mock.Of<IGlyphRunImpl>()); |
|
|
|
Assert.Equal(1, glyphRunRef.RefCount); |
|
|
|
|
|
|
|
var node = new RenderDataGlyphRunNode { GlyphRun = glyphRunRef.Clone() }; |
|
|
|
Assert.Equal(2, glyphRunRef.RefCount); |
|
|
|
|
|
|
|
node.Dispose(); |
|
|
|
Assert.Equal(1, glyphRunRef.RefCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void GlyphRun_Node_Disposed_When_Containing_Push_Node_Disposed() |
|
|
|
{ |
|
|
|
var glyphRunRef = RefCountable.Create(Mock.Of<IGlyphRunImpl>()); |
|
|
|
var glyphNode = new RenderDataGlyphRunNode { GlyphRun = glyphRunRef.Clone() }; |
|
|
|
Assert.Equal(2, glyphRunRef.RefCount); |
|
|
|
|
|
|
|
var pushNode = new RenderDataOpacityNode { Opacity = 0.5 }; |
|
|
|
pushNode.Children.Add(glyphNode); |
|
|
|
|
|
|
|
pushNode.Dispose(); |
|
|
|
Assert.Equal(1, glyphRunRef.RefCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void PushOpacityMask_Brush_Is_AddRefed_Once_And_Released_On_Dispose() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushOpacityMask(brush, new Rect(0, 0, 100, 100))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
Assert.Equal(0, brush.ReleaseCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void PushOpacityMask_HitTest_Recurses_Into_Children() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
using (ctx.Context.PushOpacityMask(brush, new Rect(0, 0, 100, 100))) |
|
|
|
ctx.Context.DrawRectangle(Brushes.Black, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(rd.HitTest(new Point(50, 50))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void PushGeometryClip_HitTest_Restricts_By_FillContains() |
|
|
|
{ |
|
|
|
var geomMock = new Mock<IGeometryImpl>(); |
|
|
|
geomMock.Setup(g => g.FillContains(new Point(5, 5))).Returns(true); |
|
|
|
geomMock.Setup(g => g.FillContains(new Point(50, 50))).Returns(false); |
|
|
|
|
|
|
|
var node = new RenderDataGeometryClipNode { Geometry = geomMock.Object }; |
|
|
|
node.Children.Add(new RenderDataRectangleNode |
|
|
|
{ |
|
|
|
ServerBrush = Brushes.Black, |
|
|
|
Rect = new RoundedRect(new Rect(0, 0, 100, 100)) |
|
|
|
}); |
|
|
|
|
|
|
|
Assert.True(node.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(node.HitTest(new Point(50, 50))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Geometry_Node_AddRefs_Brush_And_Pen() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var pen = new TrackingPen(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawGeometry(brush, pen, Mock.Of<IGeometryImpl>()); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
Assert.Equal(1, pen.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush.ReleaseCount); |
|
|
|
Assert.Equal(1, pen.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Geometry_Node_HitTest_Uses_FillContains_When_Brush_Set() |
|
|
|
{ |
|
|
|
var geomMock = new Mock<IGeometryImpl>(); |
|
|
|
geomMock.Setup(g => g.FillContains(new Point(5, 5))).Returns(true); |
|
|
|
geomMock.Setup(g => g.FillContains(new Point(50, 50))).Returns(false); |
|
|
|
|
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawGeometry(Brushes.Black, null, geomMock.Object); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(rd.HitTest(new Point(50, 50))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Geometry_Node_HitTest_Uses_StrokeContains_When_Pen_Set() |
|
|
|
{ |
|
|
|
var pen = new ImmutablePen(Brushes.Black, 1); |
|
|
|
var geomMock = new Mock<IGeometryImpl>(); |
|
|
|
geomMock.Setup(g => g.StrokeContains(pen, new Point(5, 5))).Returns(true); |
|
|
|
geomMock.Setup(g => g.StrokeContains(pen, new Point(50, 50))).Returns(false); |
|
|
|
|
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawGeometry(null, pen, geomMock.Object); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.True(rd.HitTest(new Point(5, 5))); |
|
|
|
Assert.False(rd.HitTest(new Point(50, 50))); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void PushRenderOptions_Forwards_Push_And_Pop_To_Drawing_Context_Impl() |
|
|
|
{ |
|
|
|
var mockImpl = new Mock<IDrawingContextImpl>(); |
|
|
|
var ctx = new RenderDataNodeRenderContext(mockImpl.Object); |
|
|
|
var opts = new RenderOptions { EdgeMode = EdgeMode.Aliased }; |
|
|
|
var node = new RenderDataRenderOptionsNode { RenderOptions = opts }; |
|
|
|
|
|
|
|
node.Push(ref ctx); |
|
|
|
node.Pop(ref ctx); |
|
|
|
|
|
|
|
mockImpl.Verify(x => x.PushRenderOptions(opts), Times.Once); |
|
|
|
mockImpl.Verify(x => x.PopRenderOptions(), Times.Once); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void PushTextOptions_Forwards_Push_And_Pop_To_Drawing_Context_Impl() |
|
|
|
{ |
|
|
|
var mockImpl = new Mock<IDrawingContextImpl>(); |
|
|
|
var ctx = new RenderDataNodeRenderContext(mockImpl.Object); |
|
|
|
var opts = new TextOptions { TextRenderingMode = TextRenderingMode.Antialias }; |
|
|
|
var node = new RenderDataTextOptionsNode { TextOptions = opts }; |
|
|
|
|
|
|
|
node.Push(ref ctx); |
|
|
|
node.Pop(ref ctx); |
|
|
|
|
|
|
|
mockImpl.Verify(x => x.PushTextOptions(opts), Times.Once); |
|
|
|
mockImpl.Verify(x => x.PopTextOptions(), Times.Once); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Two_Distinct_Brushes_Are_AddRefed_Separately() |
|
|
|
{ |
|
|
|
var brush1 = new TrackingBrush(); |
|
|
|
var brush2 = new TrackingBrush(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(brush1, null, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
ctx.Context.DrawRectangle(brush2, null, new RoundedRect(new Rect(20, 20, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush1.AddRefCount); |
|
|
|
Assert.Equal(1, brush2.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush1.ReleaseCount); |
|
|
|
Assert.Equal(1, brush2.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Brush_And_Pen_On_Same_Draw_Are_Both_AddRefed_Once() |
|
|
|
{ |
|
|
|
var brush = new TrackingBrush(); |
|
|
|
var pen = new TrackingPen(); |
|
|
|
var ctx = new TestContext(_services); |
|
|
|
ctx.Context.DrawRectangle(brush, pen, new RoundedRect(new Rect(0, 0, 10, 10))); |
|
|
|
var rd = ctx.Context.GetRenderResults()!; |
|
|
|
|
|
|
|
Assert.Equal(1, brush.AddRefCount); |
|
|
|
Assert.Equal(1, pen.AddRefCount); |
|
|
|
|
|
|
|
rd.Dispose(); |
|
|
|
Assert.Equal(1, brush.ReleaseCount); |
|
|
|
Assert.Equal(1, pen.ReleaseCount); |
|
|
|
} |
|
|
|
|
|
|
|
sealed class TrackingBrush : IBrush, ICompositionRenderResource<IBrush> |
|
|
|
{ |
|
|
|
public int AddRefCount; |
|
|
|
public int ReleaseCount; |
|
|
|
public double Opacity => 1; |
|
|
|
public ITransform? Transform => null; |
|
|
|
public RelativePoint TransformOrigin => default; |
|
|
|
public void AddRefOnCompositor(Compositor c) => AddRefCount++; |
|
|
|
public void ReleaseOnCompositor(Compositor c) => ReleaseCount++; |
|
|
|
public IBrush GetForCompositor(Compositor c) => this; |
|
|
|
} |
|
|
|
|
|
|
|
sealed class TrackingPen : IPen, ICompositionRenderResource<IPen> |
|
|
|
{ |
|
|
|
public int AddRefCount; |
|
|
|
public int ReleaseCount; |
|
|
|
public IBrush? Brush => Brushes.Black; |
|
|
|
public IDashStyle? DashStyle => null; |
|
|
|
public PenLineCap LineCap => PenLineCap.Flat; |
|
|
|
public PenLineJoin LineJoin => PenLineJoin.Miter; |
|
|
|
public double MiterLimit => 10; |
|
|
|
public double Thickness => 1; |
|
|
|
public void AddRefOnCompositor(Compositor c) => AddRefCount++; |
|
|
|
public void ReleaseOnCompositor(Compositor c) => ReleaseCount++; |
|
|
|
public IPen GetForCompositor(Compositor c) => this; |
|
|
|
} |
|
|
|
|
|
|
|
sealed class ImmutableTrackingBrush : IImmutableBrush, ICompositionRenderResource<IBrush> |
|
|
|
{ |
|
|
|
public int AddRefCount; |
|
|
|
public double Opacity => 1; |
|
|
|
public ITransform? Transform => null; |
|
|
|
public RelativePoint TransformOrigin => default; |
|
|
|
public void AddRefOnCompositor(Compositor c) => AddRefCount++; |
|
|
|
public void ReleaseOnCompositor(Compositor c) { } |
|
|
|
public IBrush GetForCompositor(Compositor c) => this; |
|
|
|
} |
|
|
|
|
|
|
|
sealed class ImmutableTrackingPen : ImmutablePen, ICompositionRenderResource<IPen> |
|
|
|
{ |
|
|
|
public int AddRefCount; |
|
|
|
public ImmutableTrackingPen() : base(Brushes.Black, 1) { } |
|
|
|
public void AddRefOnCompositor(Compositor c) => AddRefCount++; |
|
|
|
public void ReleaseOnCompositor(Compositor c) { } |
|
|
|
public IPen GetForCompositor(Compositor c) => this; |
|
|
|
} |
|
|
|
|
|
|
|
sealed class TrackingCustomOp : ICustomDrawOperation |
|
|
|
{ |
|
|
|
public int DisposeCount; |
|
|
|
public Rect Bounds => new Rect(0, 0, 10, 10); |
|
|
|
public bool HitTest(Point p) => false; |
|
|
|
public void Render(ImmediateDrawingContext context) { } |
|
|
|
public bool Equals(ICustomDrawOperation? other) => ReferenceEquals(this, other); |
|
|
|
public void Dispose() => DisposeCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|