示例#1
0
        private SkiaGeometrySource2D GetGeometry(Size finalSize)
        {
            var strokeThickness = StrokeThickness;
            var radiusX         = RadiusX;
            var radiusY         = RadiusY;

            var offset = new Vector2((float)(strokeThickness * 0.5), (float)(strokeThickness * 0.5));
            var size   = new Vector2((float)finalSize.Width, (float)finalSize.Height);

            SkiaGeometrySource2D geometry;

            if (radiusX == 0 || radiusY == 0)
            {
                // Simple rectangle
                geometry = new SkiaGeometrySource2D(
                    CompositionGeometry.BuildRectangleGeometry(
                        offset,
                        size));
            }
            else
            {
                // Complex rectangle
                geometry = new SkiaGeometrySource2D(
                    CompositionGeometry.BuildRoundedRectangleGeometry(
                        offset,
                        size,
                        new Vector2((float)radiusX, (float)radiusY)));
            }

            return(geometry);
        }
示例#2
0
        private SkiaGeometrySource2D GetGeometry(Size finalSize)
        {
            var geometry = new SkiaGeometrySource2D();

            geometry.Geometry.AddOval(new SKRect(0, 0, (float)finalSize.Width, (float)finalSize.Height));

            return(geometry);
        }
示例#3
0
        private SkiaGeometrySource2D GetGeometry(Rect renderingArea)
        {
            var geometry = new SkiaGeometrySource2D();

            geometry.Geometry.AddOval(new SKRect((float)renderingArea.X, (float)renderingArea.Y, (float)renderingArea.Right, (float)renderingArea.Bottom));

            return(geometry);
        }
示例#4
0
        private static CompositionPath GetRoundedRect(CornerRadius cornerRadius, CornerRadius innerCornerRadius, Rect area, Rect insetArea)
        {
            var geometrySource = new SkiaGeometrySource2D();

            GetRoundedPath(cornerRadius, area, geometrySource);
            GetRoundedPath(innerCornerRadius, insetArea, geometrySource);
            geometrySource.Geometry.FillType = SKPathFillType.EvenOdd;
            return(new CompositionPath(geometrySource));
        }
示例#5
0
        private SkiaGeometrySource2D ToGeometrySource2D(PathGeometry geometry)
        {
            var skiaGeometry = new SkiaGeometrySource2D();

            foreach (PathFigure figure in geometry.Figures)
            {
                skiaGeometry.Geometry.MoveTo((float)figure.StartPoint.X, (float)figure.StartPoint.Y);

                foreach (PathSegment segment in figure.Segments)
                {
                    if (segment is LineSegment lineSegment)
                    {
                        skiaGeometry.Geometry.LineTo((float)lineSegment.Point.X, (float)lineSegment.Point.Y);
                    }
                    else if (segment is BezierSegment bezierSegment)
                    {
                        skiaGeometry.Geometry.CubicTo(
                            (float)bezierSegment.Point1.X, (float)bezierSegment.Point1.Y,
                            (float)bezierSegment.Point2.X, (float)bezierSegment.Point2.Y,
                            (float)bezierSegment.Point3.X, (float)bezierSegment.Point3.Y);
                    }
                    else if (segment is QuadraticBezierSegment quadraticBezierSegment)
                    {
                        skiaGeometry.Geometry.QuadTo(
                            (float)quadraticBezierSegment.Point1.X, (float)quadraticBezierSegment.Point1.Y,
                            (float)quadraticBezierSegment.Point2.X, (float)quadraticBezierSegment.Point2.Y);
                    }
                    else if (segment is ArcSegment arcSegment)
                    {
                        skiaGeometry.Geometry.ArcTo(
                            (float)arcSegment.Size.Width, (float)arcSegment.Size.Height,
                            (float)arcSegment.RotationAngle,
                            arcSegment.IsLargeArc ? SkiaSharp.SKPathArcSize.Large : SkiaSharp.SKPathArcSize.Small,
                            (arcSegment.SweepDirection == SweepDirection.Clockwise ? SkiaSharp.SKPathDirection.Clockwise : SkiaSharp.SKPathDirection.CounterClockwise),
                            (float)arcSegment.Point.X, (float)arcSegment.Point.Y);
                    }
                }

                if (figure.IsClosed)
                {
                    skiaGeometry.Geometry.Close();
                }
            }

            skiaGeometry.Geometry.FillType = geometry.FillRule.ToSkiaFillType();

            return(skiaGeometry);
        }
示例#6
0
        internal override SkiaGeometrySource2D GetGeometry(Size finalSize)
        {
            var area = new Rect(0, 0, finalSize.Width, finalSize.Height);

            switch (Stretch)
            {
            default:
            case Stretch.None:
                break;

            case Stretch.Fill:
                area = new Rect(0, 0, finalSize.Width, finalSize.Height);
                break;

            case Stretch.Uniform:
                area = (area.Height > area.Width)
                                                ? (new Rect((float)area.X, (float)area.Y, (float)area.Width, (float)area.Width))
                                                : (new Rect((float)area.X, (float)area.Y, (float)area.Height, (float)area.Height));
                break;

            case Stretch.UniformToFill:
                area = (area.Height > area.Width)
                                                ? (new Rect((float)area.X, (float)area.Y, (float)area.Height, (float)area.Height))
                                                : (new Rect((float)area.X, (float)area.Y, (float)area.Width, (float)area.Width));
                break;
            }

            var shrinkValue = -ActualStrokeThickness / 2;

            if (area != Rect.Empty)
            {
                area.Inflate(shrinkValue, shrinkValue);
            }

            var geometry = new SkiaGeometrySource2D();

            if (Math.Max(RadiusX, RadiusY) > 0)
            {
                geometry.Geometry.AddRoundRect(area.ToSKRect(), (float)RadiusX, (float)RadiusY);
            }
            else
            {
                geometry.Geometry.AddRect(area.ToSKRect());
            }

            return(geometry);
        }
示例#7
0
        private SkiaGeometrySource2D GetGeometry(Size finalSize)
        {
            var area = new Rect(0, 0, finalSize.Width, finalSize.Height);

            var geometry = new SkiaGeometrySource2D();

            if (Math.Max(RadiusX, RadiusY) > 0)
            {
                geometry.Geometry.AddRoundRect(area.ToSKRect(), (float)RadiusX, (float)RadiusY);
            }
            else
            {
                geometry.Geometry.AddRect(area.ToSKRect());
            }

            return(geometry);
        }
示例#8
0
 private Rect GetPathBoundingBox(SkiaGeometrySource2D path)
 => path.Geometry.Bounds.ToRect();
示例#9
0
        private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state)
        {
            var parent          = owner.Visual;
            var compositor      = parent.Compositor;
            var area            = owner.LayoutRound(state.Area);
            var background      = state.Background;
            var borderThickness = owner.LayoutRound(state.BorderThickness);
            var borderBrush     = state.BorderBrush;
            var cornerRadius    = state.CornerRadius;

            var disposables = new CompositeDisposable();
            var sublayers   = new List <Visual>();

            var heightOffset = ((float)borderThickness.Top / 2) + ((float)borderThickness.Bottom / 2);
            var widthOffset  = ((float)borderThickness.Left / 2) + ((float)borderThickness.Right / 2);
            var halfWidth    = (float)area.Width / 2;
            var halfHeight   = (float)area.Height / 2;
            var adjustedArea = area;

            adjustedArea = adjustedArea.DeflateBy(borderThickness);

            if (cornerRadius != CornerRadius.None)
            {
                var maxOuterRadius = Math.Max(0, Math.Min(halfWidth - widthOffset, halfHeight - heightOffset));
                var maxInnerRadius = Math.Max(0, Math.Min(halfWidth, halfHeight));

                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxOuterRadius),
                    Math.Min(cornerRadius.TopRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomLeft, maxOuterRadius));

                var innerCornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxInnerRadius),
                    Math.Min(cornerRadius.TopRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomLeft, maxInnerRadius));

                var borderShape     = compositor.CreateSpriteShape();
                var backgroundShape = compositor.CreateSpriteShape();
                var outerShape      = compositor.CreateSpriteShape();

                // Border brush
                Brush.AssignAndObserveBrush(borderBrush, compositor, brush => borderShape.FillBrush = brush)
                .DisposeWith(disposables);

                // Background brush
                if (background is ImageBrush imgBackground)
                {
                    adjustedArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground);
                }
                else
                {
                    Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush)
                    .DisposeWith(disposables);
                }

                var borderPath     = GetRoundedRect(cornerRadius, innerCornerRadius, area, adjustedArea);
                var backgroundPath = GetRoundedPath(cornerRadius, adjustedArea);
                var outerPath      = GetRoundedPath(cornerRadius, area);

                backgroundShape.Geometry = compositor.CreatePathGeometry(backgroundPath);
                borderShape.Geometry     = compositor.CreatePathGeometry(borderPath);
                outerShape.Geometry      = compositor.CreatePathGeometry(outerPath);

                var borderVisual     = compositor.CreateShapeVisual();
                var backgroundVisual = compositor.CreateShapeVisual();
                backgroundVisual.Shapes.Add(backgroundShape);
                borderVisual.Shapes.Add(borderShape);

                sublayers.Add(backgroundVisual);
                sublayers.Add(borderVisual);
                parent.Children.InsertAtBottom(backgroundVisual);
                parent.Children.InsertAtTop(borderVisual);

                owner.ClippingIsSetByCornerRadius = cornerRadius != CornerRadius.None;
                if (owner.ClippingIsSetByCornerRadius)
                {
                    parent.Clip = compositor.CreateGeometricClip(outerShape.Geometry);
                }
            }
            else
            {
                var shapeVisual = compositor.CreateShapeVisual();

                var backgroundShape = compositor.CreateSpriteShape();

                var backgroundArea = area;

                // Background brush
                if (background is ImageBrush imgBackground)
                {
                    backgroundArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, backgroundArea, imgBackground);
                }
                else
                {
                    Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush)
                    .DisposeWith(disposables);

                    // This is required because changing the CornerRadius changes the background drawing
                    // implementation and we don't want a rectangular background behind a rounded background.
                    Disposable.Create(() => backgroundShape.FillBrush = null)
                    .DisposeWith(disposables);
                }

                var geometrySource = new SkiaGeometrySource2D();
                var geometry       = geometrySource.Geometry;

                geometry.AddRect(backgroundArea.ToSKRect());

                backgroundShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(geometrySource));

                shapeVisual.Shapes.Add(backgroundShape);

                if (borderThickness != Thickness.Empty)
                {
                    Action <Action <CompositionSpriteShape, SKPath> > createLayer = builder =>
                    {
                        var spriteShape = compositor.CreateSpriteShape();
                        var geometry    = new SkiaGeometrySource2D();

                        // Border brush
                        Brush.AssignAndObserveBrush(borderBrush, compositor, brush => spriteShape.StrokeBrush = brush)
                        .DisposeWith(disposables);

                        builder(spriteShape, geometry.Geometry);
                        spriteShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(geometry));

                        shapeVisual.Shapes.Add(spriteShape);
                    };

                    if (borderThickness.Top != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Top;
                            var StrokeThicknessAdjust = (float)(borderThickness.Top / 2);
                            path.MoveTo((float)(area.X + borderThickness.Left), (float)(area.Y + StrokeThicknessAdjust));
                            path.LineTo((float)(area.X + area.Width - borderThickness.Right), (float)(area.Y + StrokeThicknessAdjust));
                            path.Close();
                        });
                    }

                    if (borderThickness.Bottom != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Bottom;
                            var StrokeThicknessAdjust = borderThickness.Bottom / 2;
                            path.MoveTo((float)(area.X + (float)borderThickness.Left), (float)(area.Y + area.Height - StrokeThicknessAdjust));
                            path.LineTo((float)(area.X + area.Width - (float)borderThickness.Right), (float)(area.Y + area.Height - StrokeThicknessAdjust));
                            path.Close();
                        });
                    }

                    if (borderThickness.Left != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Left;
                            var StrokeThicknessAdjust = borderThickness.Left / 2;
                            path.MoveTo((float)(area.X + StrokeThicknessAdjust), (float)area.Y);
                            path.LineTo((float)(area.X + StrokeThicknessAdjust), (float)(area.Y + area.Height));
                            path.Close();
                        });
                    }

                    if (borderThickness.Right != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Right;
                            var StrokeThicknessAdjust = borderThickness.Right / 2;
                            path.MoveTo((float)(area.X + area.Width - StrokeThicknessAdjust), (float)area.Y);
                            path.LineTo((float)(area.X + area.Width - StrokeThicknessAdjust), (float)(area.Y + area.Height));
                            path.Close();
                        });
                    }
                }

                sublayers.Add(shapeVisual);

                // Must be inserted below the other subviews, which may happen when
                // the current view has subviews.
                parent.Children.InsertAtBottom(shapeVisual);
            }

            disposables.Add(() =>
            {
                owner.ClippingIsSetByCornerRadius = false;

                foreach (var sv in sublayers)
                {
                    parent.Children.Remove(sv);
                    sv.Dispose();
                }
            }
                            );

            compositor.InvalidateRender();

            return(disposables);
        }
示例#10
0
        /// <summary>
        /// Creates a rounded-rectangle path from the nominated bounds and corner radius.
        /// </summary>
        private static CompositionPath GetRoundedPath(CornerRadius cornerRadius, Rect area, SkiaGeometrySource2D geometrySource = null)
        {
            geometrySource ??= new SkiaGeometrySource2D();
            var geometry = geometrySource.Geometry;

            // How ArcTo works:
            // http://www.twistedape.me.uk/blog/2013/09/23/what-arctopointdoes/

            geometry.MoveTo((float)area.GetMidX(), (float)area.Y);
            geometry.ArcTo((float)area.Right, (float)area.Top, (float)area.Right, (float)area.GetMidY(), (float)cornerRadius.TopRight);
            geometry.ArcTo((float)area.Right, (float)area.Bottom, (float)area.GetMidX(), (float)area.Bottom, (float)cornerRadius.BottomRight);
            geometry.ArcTo((float)area.Left, (float)area.Bottom, (float)area.Left, (float)area.GetMidY(), (float)cornerRadius.BottomLeft);
            geometry.ArcTo((float)area.Left, (float)area.Top, (float)area.GetMidX(), (float)area.Top, (float)cornerRadius.TopLeft);

            geometry.Close();

            return(new CompositionPath(geometrySource));
        }
示例#11
0
        private static IDisposable InnerCreateLayer(
            ContainerVisual parent,
            Rect area,
            Brush background,
            Thickness borderThickness,
            Brush borderBrush,
            CornerRadius cornerRadius)
        {
            var disposables = new CompositeDisposable();
            var subVisuals  = new List <Visual>();

            var adjustedLineWidth       = borderThickness.Top;
            var adjustedLineWidthOffset = adjustedLineWidth / 2;

            var adjustedArea = area;

            adjustedArea.Inflate(-adjustedLineWidthOffset, -adjustedLineWidthOffset);

            var compositor = parent.Compositor;

            var shapeVisual = compositor.CreateShapeVisual();

            parent.Children.InsertAtBottom(shapeVisual);

            var spriteShape = compositor.CreateSpriteShape();

            SkiaGeometrySource2D BuildGeometry()
            {
                var maxRadius = Math.Max(0, Math.Min((float)area.Width / 2 - adjustedLineWidthOffset, (float)area.Height / 2 - adjustedLineWidthOffset));

                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxRadius),
                    Math.Min(cornerRadius.TopRight, maxRadius),
                    Math.Min(cornerRadius.BottomRight, maxRadius),
                    Math.Min(cornerRadius.BottomLeft, maxRadius));

                var geometry = new SkiaGeometrySource2D();

                Brush.AssignAndObserveBrush(borderBrush, color => spriteShape.StrokeBrush = compositor.CreateColorBrush(color))
                .DisposeWith(disposables);

                geometry.Geometry.MoveTo((float)adjustedArea.GetMidX(), (float)adjustedArea.Y);
                geometry.Geometry.ArcTo((float)adjustedArea.Right, (float)adjustedArea.Top, (float)adjustedArea.Right, (float)adjustedArea.GetMidY(), (float)cornerRadius.TopRight);
                geometry.Geometry.ArcTo((float)adjustedArea.Right, (float)adjustedArea.Bottom, (float)adjustedArea.GetMidX(), (float)adjustedArea.Bottom, (float)cornerRadius.BottomRight);
                geometry.Geometry.ArcTo((float)adjustedArea.Left, (float)adjustedArea.Bottom, (float)adjustedArea.Left, (float)adjustedArea.GetMidY(), (float)cornerRadius.BottomLeft);
                geometry.Geometry.ArcTo((float)adjustedArea.Left, (float)adjustedArea.Top, (float)adjustedArea.GetMidX(), (float)adjustedArea.Top, (float)cornerRadius.TopLeft);

                geometry.Geometry.Close();

                if (background is LinearGradientBrush lgbBackground)
                {
                    //var fillMask = new CAShapeLayer()
                    //{
                    //	Path = path,
                    //	Frame = area,
                    //	// We only use the fill color to create the mask area
                    //	FillColor = _Color.White.CGColor,
                    //};

                    //// We reduce the adjustedArea again so that the gradient is inside the border (like in Windows)
                    //adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    //CreateLinearGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, lgbBackground, fillMask);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => spriteShape.FillBrush = compositor.CreateColorBrush(color))
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    //var uiImage = imgBackground.ImageSource?.ImageData;
                    //if (uiImage != null && uiImage.Size != CGSize.Empty)
                    //{
                    //	var fillMask = new CAShapeLayer()
                    //	{
                    //		Path = path,
                    //		Frame = area,
                    //		// We only use the fill color to create the mask area
                    //		FillColor = _Color.White.CGColor,
                    //	};

                    //	// We reduce the adjustedArea again so that the image is inside the border (like in Windows)
                    //	adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    //	CreateImageBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
                    //}
                }
                else
                {
                    spriteShape.FillBrush = null;
                }

                return(geometry);
            }

            spriteShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(BuildGeometry()));

            shapeVisual.Size   = new Vector2((float)area.Width, (float)area.Height);
            shapeVisual.Offset = new Vector3(0, 0, 0);

            shapeVisual.Shapes.Add(spriteShape);

            subVisuals.Add(shapeVisual);

            disposables.Add(() =>
            {
                foreach (var sv in subVisuals)
                {
                    parent.Children.Remove(sv);
                    sv.Dispose();
                }
            });

            Window.Current.QueueInvalidateRender();

            return(disposables);
        }