Browse Source

Added path clipping infrastructure. Got Skia implementation working.

Jeremy Koritzinsky 9 years ago
parent
commit
7969504431

+ 18 - 3
src/Avalonia.SceneGraph/Media/DrawingContext.cs

@@ -127,7 +127,8 @@ namespace Avalonia.Media
                 Matrix,
                 Opacity,
                 Clip,
-                MatrixContainer
+                MatrixContainer,
+                GeometryClip
             }
 
             public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
@@ -149,10 +150,12 @@ namespace Avalonia.Media
                 _context._states.Pop();
                 if (_type == PushedStateType.Matrix)
                     _context.CurrentTransform = _matrix;
-                else if(_type == PushedStateType.Clip)
+                else if (_type == PushedStateType.Clip)
                     _context._impl.PopClip();
-                else if(_type == PushedStateType.Opacity)
+                else if (_type == PushedStateType.Opacity)
                     _context._impl.PopOpacity();
+                else if (_type == PushedStateType.GeometryClip)
+                    _context._impl.PopGeometryClip();
                 else if (_type == PushedStateType.MatrixContainer)
                 {
                     var cont = _context._transformContainers.Pop();
@@ -174,6 +177,18 @@ namespace Avalonia.Media
             return new PushedState(this, PushedState.PushedStateType.Clip);
         }
 
+        /// <summary>
+        /// Pushes a clip geometry.
+        /// </summary>
+        /// <param name="clip">The clip geometry.</param>
+        /// <returns>A disposable used to undo the clip geometry.</returns>
+        public PushedState PushGeometryClip(Geometry clip)
+        {
+            Contract.Requires<ArgumentNullException>(clip != null);
+            _impl.PushGeometryClip(clip);
+            return new PushedState(this, PushedState.PushedStateType.GeometryClip);
+        }
+
         /// <summary>
         /// Pushes an opacity value.
         /// </summary>

+ 8 - 2
src/Avalonia.SceneGraph/Media/IDrawingContext.cs

@@ -69,7 +69,6 @@ namespace Avalonia.Media
         /// Pushes a clip rectange.
         /// </summary>
         /// <param name="clip">The clip rectangle.</param>
-        /// <returns>A disposable used to undo the clip rectangle.</returns>
         void PushClip(Rect clip);
 
         void PopClip();
@@ -78,9 +77,16 @@ namespace Avalonia.Media
         /// Pushes an opacity value.
         /// </summary>
         /// <param name="opacity">The opacity.</param>
-        /// <returns>A disposable used to undo the opacity.</returns>
         void PushOpacity(double opacity);
 
         void PopOpacity();
+
+        /// <summary>
+        /// Pushes a clip geometry.
+        /// </summary>
+        /// <param name="clip">The clip geometry.</param>
+        void PushGeometryClip(Geometry clip);
+
+        void PopGeometryClip();
     }
 }

+ 25 - 2
src/Avalonia.SceneGraph/Media/PathMarkupParser.cs

@@ -34,6 +34,12 @@ namespace Avalonia.Media
             { 'z', Command.Close },
         };
 
+        private static readonly Dictionary<char, FillRule> FillRules = new Dictionary<char, FillRule>
+        {
+            {'0', FillRule.EvenOdd },
+            {'1', FillRule.NonZero }
+        };
+
         private StreamGeometry _geometry;
 
         private readonly StreamGeometryContext _context;
@@ -90,8 +96,7 @@ namespace Avalonia.Media
                     switch (command)
                     {
                         case Command.FillRule:
-                            // TODO: Implement.
-                            reader.Read();
+                            _context.SetFillRule(ReadFillRule(reader));
                             break;
 
                         case Command.Move:
@@ -226,6 +231,24 @@ namespace Avalonia.Media
             }
         }
 
+        private static FillRule ReadFillRule(StringReader reader)
+        {
+            int i = reader.Read();
+            if (i == -1)
+            {
+                throw new InvalidDataException("Invalid fill rule");
+            }
+            char c = (char)i;
+            FillRule rule;
+
+            if (!FillRules.TryGetValue(c, out rule))
+            {
+                throw new InvalidDataException("Invalid fill rule");
+            }
+
+            return rule;
+        }
+
         private static double ReadDouble(StringReader reader)
         {
             ReadWhitespace(reader);

+ 1 - 0
src/Avalonia.SceneGraph/Rendering/RendererMixin.cs

@@ -119,6 +119,7 @@ namespace Avalonia.Rendering
                 using (context.PushPostTransform(m))
                 using (context.PushOpacity(opacity))
                 using (clipToBounds ? context.PushClip(bounds) : default(DrawingContext.PushedState))
+                using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState))
                 using (context.PushTransformContainer())
                 {
                     visual.Render(context);

+ 15 - 0
src/Avalonia.SceneGraph/Visual.cs

@@ -39,6 +39,12 @@ namespace Avalonia
         public static readonly StyledProperty<bool> ClipToBoundsProperty =
             AvaloniaProperty.Register<Visual, bool>(nameof(ClipToBounds));
 
+        /// <summary>
+        /// Defines the <see cref="Clip"/> property.
+        /// </summary>
+        public static readonly StyledProperty<Geometry> ClipProperty =
+            AvaloniaProperty.Register<Visual, Geometry>(nameof(Clip));
+
         /// <summary>
         /// Defines the <see cref="IsVisibleProperty"/> property.
         /// </summary>
@@ -127,6 +133,15 @@ namespace Avalonia
             set { SetValue(ClipToBoundsProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the geometry clip for this visual.
+        /// </summary>
+        public Geometry Clip
+        {
+            get { return GetValue(ClipProperty); }
+            set { SetValue(ClipProperty, value); }
+        }
+
         /// <summary>
         /// Gets a value indicating whether this scene graph node and all its parents are visible.
         /// </summary>

+ 5 - 0
src/Avalonia.SceneGraph/VisualTree/IVisual.cs

@@ -41,6 +41,11 @@ namespace Avalonia.VisualTree
         /// </summary>
         bool ClipToBounds { get; set; }
 
+        /// <summary>
+        /// Gets or sets the geometry clip for this visual.
+        /// </summary>
+        Geometry Clip { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether this scene graph node is attached to a visual root.
         /// </summary>

+ 11 - 0
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -288,6 +288,17 @@ namespace Avalonia.Skia
         {
         }
 
+        public void PushGeometryClip(Geometry clip)
+        {
+            Canvas.Save();
+            Canvas.ClipPath(((StreamGeometryImpl)clip.PlatformImpl).EffectivePath);
+        }
+
+        public void PopGeometryClip()
+        {
+            Canvas.Restore();
+        }
+
         private Matrix _currentTransform = Matrix.Identity;
 
         public Matrix Transform