|
|
|
@ -1,6 +1,10 @@ |
|
|
|
using Avalonia.Collections; |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using Avalonia.Media.Imaging; |
|
|
|
using Avalonia.Metadata; |
|
|
|
using Avalonia.Platform; |
|
|
|
using Avalonia.Rendering.SceneGraph; |
|
|
|
using Avalonia.Utilities; |
|
|
|
|
|
|
|
namespace Avalonia.Media |
|
|
|
{ |
|
|
|
@ -18,6 +22,14 @@ namespace Avalonia.Media |
|
|
|
public static readonly StyledProperty<IBrush> OpacityMaskProperty = |
|
|
|
AvaloniaProperty.Register<DrawingGroup, IBrush>(nameof(OpacityMask)); |
|
|
|
|
|
|
|
public static readonly DirectProperty<DrawingGroup, DrawingCollection> ChildrenProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<DrawingGroup, DrawingCollection>( |
|
|
|
nameof(Children), |
|
|
|
o => o.Children, |
|
|
|
(o, v) => o.Children = v); |
|
|
|
|
|
|
|
private DrawingCollection _children = new DrawingCollection(); |
|
|
|
|
|
|
|
public double Opacity |
|
|
|
{ |
|
|
|
get => GetValue(OpacityProperty); |
|
|
|
@ -42,8 +54,23 @@ namespace Avalonia.Media |
|
|
|
set => SetValue(OpacityMaskProperty, value); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the collection that contains the child geometries.
|
|
|
|
/// </summary>
|
|
|
|
[Content] |
|
|
|
public AvaloniaList<Drawing> Children { get; } = new AvaloniaList<Drawing>(); |
|
|
|
public DrawingCollection Children |
|
|
|
{ |
|
|
|
get => _children; |
|
|
|
set |
|
|
|
{ |
|
|
|
SetAndRaise(ChildrenProperty, ref _children, value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public DrawingContext Open() |
|
|
|
{ |
|
|
|
return new DrawingContext(new DrawingGroupDrawingContext(this)); |
|
|
|
} |
|
|
|
|
|
|
|
public override void Draw(DrawingContext context) |
|
|
|
{ |
|
|
|
@ -75,5 +102,394 @@ namespace Avalonia.Media |
|
|
|
|
|
|
|
return rect; |
|
|
|
} |
|
|
|
|
|
|
|
private class DrawingGroupDrawingContext : IDrawingContextImpl |
|
|
|
{ |
|
|
|
private readonly DrawingGroup _drawingGroup; |
|
|
|
private readonly IPlatformRenderInterface _platformRenderInterface = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>(); |
|
|
|
|
|
|
|
private Matrix _transform; |
|
|
|
|
|
|
|
private bool _disposed; |
|
|
|
|
|
|
|
// Root drawing created by this DrawingContext.
|
|
|
|
//
|
|
|
|
// If there is only a single child of the root DrawingGroup, _rootDrawing
|
|
|
|
// will reference the single child, and the root _currentDrawingGroup
|
|
|
|
// value will be null. Otherwise, _rootDrawing will reference the
|
|
|
|
// root DrawingGroup, and be the same value as the root _currentDrawingGroup.
|
|
|
|
//
|
|
|
|
// Either way, _rootDrawing always references the root drawing.
|
|
|
|
protected Drawing? _rootDrawing; |
|
|
|
|
|
|
|
// Current DrawingGroup that new children are added to
|
|
|
|
protected DrawingGroup? _currentDrawingGroup; |
|
|
|
|
|
|
|
// Previous values of _currentDrawingGroup
|
|
|
|
private Stack<DrawingGroup?>? _previousDrawingGroupStack; |
|
|
|
|
|
|
|
public DrawingGroupDrawingContext(DrawingGroup drawingGroup) |
|
|
|
{ |
|
|
|
_drawingGroup = drawingGroup; |
|
|
|
} |
|
|
|
|
|
|
|
public Matrix Transform |
|
|
|
{ |
|
|
|
get => _transform; |
|
|
|
set |
|
|
|
{ |
|
|
|
_transform = value; |
|
|
|
PushTransform(new MatrixTransform(value)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect) |
|
|
|
{ |
|
|
|
if ((brush == null) && (pen == null)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Instantiate the geometry
|
|
|
|
var geometry = _platformRenderInterface.CreateEllipseGeometry(rect); |
|
|
|
|
|
|
|
// Add Drawing to the Drawing graph
|
|
|
|
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry) |
|
|
|
{ |
|
|
|
if (((brush == null) && (pen == null)) || (geometry == null)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) |
|
|
|
{ |
|
|
|
if (foreground == null || glyphRun == null) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Add a GlyphRunDrawing to the Drawing graph
|
|
|
|
GlyphRunDrawing glyphRunDrawing = new GlyphRunDrawing |
|
|
|
{ |
|
|
|
Foreground = foreground, |
|
|
|
GlyphRun = glyphRun, |
|
|
|
}; |
|
|
|
|
|
|
|
// Add Drawing to the Drawing graph
|
|
|
|
AddDrawing(glyphRunDrawing); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawLine(IPen pen, Point p1, Point p2) |
|
|
|
{ |
|
|
|
if (pen == null) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Instantiate the geometry
|
|
|
|
var geometry = _platformRenderInterface.CreateLineGeometry(p1, p2); |
|
|
|
|
|
|
|
// Add Drawing to the Drawing graph
|
|
|
|
AddNewGeometryDrawing(null, pen, new PlatformGeometry(geometry)); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default) |
|
|
|
{ |
|
|
|
if ((brush == null) && (pen == null)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Instantiate the geometry
|
|
|
|
var geometry = _platformRenderInterface.CreateRectangleGeometry(rect.Rect); |
|
|
|
|
|
|
|
// Add Drawing to the Drawing graph
|
|
|
|
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); |
|
|
|
} |
|
|
|
|
|
|
|
public void Clear(Color color) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public IDrawingContextLayerImpl CreateLayer(Size size) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void Custom(ICustomDrawOperation custom) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PopBitmapBlendMode() |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PopClip() |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PopGeometryClip() |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PopOpacity() |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PopOpacityMask() |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushClip(Rect clip) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushClip(RoundedRect clip) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushGeometryClip(IGeometryImpl clip) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushOpacity(double opacity) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void PushOpacityMask(IBrush mask, Rect bounds) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
// Dispose may be called multiple times without throwing
|
|
|
|
// an exception.
|
|
|
|
if (!_disposed) |
|
|
|
{ |
|
|
|
// Match any outstanding Push calls with a Pop
|
|
|
|
if (_previousDrawingGroupStack != null) |
|
|
|
{ |
|
|
|
int stackCount = _previousDrawingGroupStack.Count; |
|
|
|
for (int i = 0; i < stackCount; i++) |
|
|
|
{ |
|
|
|
Pop(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Call CloseCore with the root DrawingGroup's children
|
|
|
|
DrawingCollection rootChildren; |
|
|
|
|
|
|
|
if (_currentDrawingGroup != null) |
|
|
|
{ |
|
|
|
// If we created a root DrawingGroup because multiple elements
|
|
|
|
// exist at the root level, provide it's Children collection
|
|
|
|
// directly.
|
|
|
|
rootChildren = _currentDrawingGroup.Children; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// Create a new DrawingCollection if we didn't create a
|
|
|
|
// root DrawingGroup because the root level only contained
|
|
|
|
// a single child.
|
|
|
|
//
|
|
|
|
// This collection is needed by DrawingGroup.Open because
|
|
|
|
// Open always replaces it's Children collection. It isn't
|
|
|
|
// strictly needed for Append, but always using a collection
|
|
|
|
// simplifies the TransactionalAppend implementation (i.e.,
|
|
|
|
// a seperate implemention isn't needed for a single element)
|
|
|
|
rootChildren = new DrawingCollection(); |
|
|
|
|
|
|
|
//
|
|
|
|
// We may need to opt-out of inheritance through the new Freezable.
|
|
|
|
// This is controlled by this.CanBeInheritanceContext.
|
|
|
|
//
|
|
|
|
if (_rootDrawing != null) |
|
|
|
{ |
|
|
|
rootChildren.Add(_rootDrawing); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Inform our derived classes that Close was called
|
|
|
|
_drawingGroup.Children = rootChildren; |
|
|
|
|
|
|
|
_disposed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Pop
|
|
|
|
/// </summary>
|
|
|
|
private void Pop() |
|
|
|
{ |
|
|
|
// Verify that Pop hasn't been called too many times
|
|
|
|
if ((_previousDrawingGroupStack == null) || (_previousDrawingGroupStack.Count == 0)) |
|
|
|
{ |
|
|
|
throw new InvalidOperationException("DrawingGroupStack count missmatch."); |
|
|
|
} |
|
|
|
|
|
|
|
// Restore the previous value of the current drawing group
|
|
|
|
_currentDrawingGroup = _previousDrawingGroupStack.Pop(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// PushTransform -
|
|
|
|
/// Push a Transform which will apply to all drawing operations until the corresponding
|
|
|
|
/// Pop.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="transform"> The Transform to push. </param>
|
|
|
|
private void PushTransform(Transform transform) |
|
|
|
{ |
|
|
|
// Instantiate a new drawing group and set it as the _currentDrawingGroup
|
|
|
|
var drawingGroup = PushNewDrawingGroup(); |
|
|
|
|
|
|
|
// Set the transform on the new DrawingGroup
|
|
|
|
drawingGroup.Transform = transform; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Creates a new DrawingGroup for a Push* call by setting the
|
|
|
|
/// _currentDrawingGroup to a newly instantiated DrawingGroup,
|
|
|
|
/// and saving the previous _currentDrawingGroup value on the
|
|
|
|
/// _previousDrawingGroupStack.
|
|
|
|
/// </summary>
|
|
|
|
private DrawingGroup PushNewDrawingGroup() |
|
|
|
{ |
|
|
|
// Instantiate a new drawing group
|
|
|
|
DrawingGroup drawingGroup = new DrawingGroup(); |
|
|
|
|
|
|
|
// Add it to the drawing graph, like any other Drawing
|
|
|
|
AddDrawing(drawingGroup); |
|
|
|
|
|
|
|
// Lazily allocate the stack when it is needed because many uses
|
|
|
|
// of DrawingDrawingContext will have a depth of one.
|
|
|
|
if (null == _previousDrawingGroupStack) |
|
|
|
{ |
|
|
|
_previousDrawingGroupStack = new Stack<DrawingGroup?>(2); |
|
|
|
} |
|
|
|
|
|
|
|
// Save the previous _currentDrawingGroup value.
|
|
|
|
//
|
|
|
|
// If this is the first call, the value of _currentDrawingGroup
|
|
|
|
// will be null because AddDrawing doesn't create a _currentDrawingGroup
|
|
|
|
// for the first drawing. Having null on the stack is valid, and simply
|
|
|
|
// denotes that this new DrawingGroup is the first child in the root
|
|
|
|
// DrawingGroup. It is also possible for the first value on the stack
|
|
|
|
// to be non-null, which means that the root DrawingGroup has other
|
|
|
|
// children.
|
|
|
|
_previousDrawingGroupStack.Push(_currentDrawingGroup); |
|
|
|
|
|
|
|
// Set this drawing group as the current one so that subsequent drawing's
|
|
|
|
// are added as it's children until Pop is called.
|
|
|
|
_currentDrawingGroup = drawingGroup; |
|
|
|
|
|
|
|
return drawingGroup; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Contains the functionality common to GeometryDrawing operations of
|
|
|
|
/// instantiating the GeometryDrawing, setting it's Freezable state,
|
|
|
|
/// and Adding it to the Drawing Graph.
|
|
|
|
/// </summary>
|
|
|
|
private void AddNewGeometryDrawing(IBrush? brush, IPen? pen, Geometry? geometry) |
|
|
|
{ |
|
|
|
if (geometry == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(geometry)); |
|
|
|
} |
|
|
|
|
|
|
|
// Instantiate the GeometryDrawing
|
|
|
|
GeometryDrawing geometryDrawing = new GeometryDrawing |
|
|
|
{ |
|
|
|
// We may need to opt-out of inheritance through the new Freezable.
|
|
|
|
// This is controlled by this.CanBeInheritanceContext.
|
|
|
|
Brush = brush, |
|
|
|
Pen = pen, |
|
|
|
Geometry = geometry |
|
|
|
}; |
|
|
|
|
|
|
|
// Add it to the drawing graph
|
|
|
|
AddDrawing(geometryDrawing); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Adds a new Drawing to the DrawingGraph.
|
|
|
|
///
|
|
|
|
/// This method avoids creating a DrawingGroup for the common case
|
|
|
|
/// where only a single child exists in the root DrawingGroup.
|
|
|
|
/// </summary>
|
|
|
|
private void AddDrawing(Drawing newDrawing) |
|
|
|
{ |
|
|
|
if (newDrawing == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(newDrawing)); |
|
|
|
} |
|
|
|
|
|
|
|
if (_rootDrawing == null) |
|
|
|
{ |
|
|
|
// When a DrawingGroup is set, it should be made the root if
|
|
|
|
// a root drawing didnt exist.
|
|
|
|
Contract.Requires<NotSupportedException>(_currentDrawingGroup == null); |
|
|
|
|
|
|
|
// If this is the first Drawing being added, avoid creating a DrawingGroup
|
|
|
|
// and set this drawing as the root drawing. This optimizes the common
|
|
|
|
// case where only a single child exists in the root DrawingGroup.
|
|
|
|
_rootDrawing = newDrawing; |
|
|
|
} |
|
|
|
else if (_currentDrawingGroup == null) |
|
|
|
{ |
|
|
|
// When the second drawing is added at the root level, set a
|
|
|
|
// DrawingGroup as the root and add both drawings to it.
|
|
|
|
|
|
|
|
// Instantiate the DrawingGroup
|
|
|
|
_currentDrawingGroup = new DrawingGroup(); |
|
|
|
|
|
|
|
// Add both Children
|
|
|
|
_currentDrawingGroup.Children.Add(_rootDrawing); |
|
|
|
_currentDrawingGroup.Children.Add(newDrawing); |
|
|
|
|
|
|
|
// Set the new DrawingGroup as the current
|
|
|
|
_rootDrawing = _currentDrawingGroup; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// If there already is a current drawing group, then simply add
|
|
|
|
// the new drawing too it.
|
|
|
|
_currentDrawingGroup.Children.Add(newDrawing); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|