From 516fe302fe32410bd87e2644f14234fa9c2cadcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Thu, 13 Jan 2022 17:46:43 +0100 Subject: [PATCH] [Android] Changes to improve Border control drawing performance (#3620) * Changes to improve Border control drawing performance * Avoid to draw unnecesary Path --- src/Core/src/Graphics/MauiDrawable.Android.cs | 96 ++++++++++--------- .../Handlers/Border/BorderHandler.Android.cs | 5 + .../src/Platform/Android/StrokeExtensions.cs | 51 +++++----- 3 files changed, 78 insertions(+), 74 deletions(-) diff --git a/src/Core/src/Graphics/MauiDrawable.Android.cs b/src/Core/src/Graphics/MauiDrawable.Android.cs index 3b2edc91f..97e2689b9 100644 --- a/src/Core/src/Graphics/MauiDrawable.Android.cs +++ b/src/Core/src/Graphics/MauiDrawable.Android.cs @@ -23,12 +23,11 @@ namespace Microsoft.Maui.Graphics bool _disposed; + ARect? _bounds; int _width; int _height; Path? _clipPath; - Path? _maskPath; - APaint? _maskPaint; APaint? _borderPaint; IShape? _shape; @@ -60,6 +59,9 @@ namespace Microsoft.Maui.Graphics public void SetBackgroundColor(AColor? backgroundColor) { + if (_backgroundColor == backgroundColor) + return; + _backgroundColor = backgroundColor; InvalidateSelf(); @@ -98,6 +100,9 @@ namespace Microsoft.Maui.Graphics public void SetBackground(LinearGradientPaint linearGradientPaint) { + if (_background == linearGradientPaint) + return; + _invalidatePath = true; _backgroundColor = null; @@ -109,6 +114,9 @@ namespace Microsoft.Maui.Graphics public void SetBackground(RadialGradientPaint radialGradientPaint) { + if (_background == radialGradientPaint) + return; + _invalidatePath = true; _backgroundColor = null; @@ -130,6 +138,9 @@ namespace Microsoft.Maui.Graphics public void SetBorderShape(IShape? shape) { + if (_shape == shape) + return; + _invalidatePath = true; _shape = shape; @@ -140,6 +151,9 @@ namespace Microsoft.Maui.Graphics public void SetBorderColor(AColor? borderColor) { + if (_borderColor == borderColor) + return; + _borderColor = borderColor; InvalidateSelf(); @@ -178,6 +192,9 @@ namespace Microsoft.Maui.Graphics public void SetBorderBrush(LinearGradientPaint linearGradientPaint) { + if (_stroke == linearGradientPaint) + return; + _invalidatePath = true; _borderColor = null; @@ -189,6 +206,9 @@ namespace Microsoft.Maui.Graphics public void SetBorderBrush(RadialGradientPaint radialGradientPaint) { + if (_stroke == radialGradientPaint) + return; + _invalidatePath = true; _borderColor = null; @@ -210,9 +230,14 @@ namespace Microsoft.Maui.Graphics public void SetBorderWidth(double strokeWidth) { + float strokeThickness = (float)(strokeWidth * _density); + + if (_strokeThickness == strokeThickness) + return; + _invalidatePath = true; - _strokeThickness = (float)(strokeWidth * _density); + _strokeThickness = strokeThickness; InitializeBorderIfNeeded(); InvalidateSelf(); @@ -238,6 +263,9 @@ namespace Microsoft.Maui.Graphics public void SetBorderMiterLimit(float strokeMiterLimit) { + if (_strokeMiterLimit == strokeMiterLimit) + return; + _strokeMiterLimit = strokeMiterLimit; InvalidateSelf(); @@ -260,6 +288,9 @@ namespace Microsoft.Maui.Graphics break; } + if (_strokeLineJoin == aLineJoin) + return; + _strokeLineJoin = aLineJoin; InvalidateSelf(); @@ -282,6 +313,9 @@ namespace Microsoft.Maui.Graphics break; } + if (_strokeLineCap == aLineCap) + return; + _strokeLineCap = aLineCap; InvalidateSelf(); @@ -289,18 +323,23 @@ namespace Microsoft.Maui.Graphics protected override void OnBoundsChange(ARect? bounds) { - if (bounds != null) + if (_bounds != bounds) { - var width = bounds.Width(); - var height = bounds.Height(); + _bounds = bounds; - if (_width == width && _height == height) - return; + if (_bounds != null) + { + var width = _bounds.Width(); + var height = _bounds.Height(); - _invalidatePath = true; + if (_width == width && _height == height) + return; - _width = width; - _height = height; + _invalidatePath = true; + + _width = width; + _height = height; + } } base.OnBoundsChange(bounds); @@ -352,13 +391,6 @@ namespace Microsoft.Maui.Graphics { _clipPath.Reset(); _clipPath.Set(clipPath); - - if (_maskPath != null && HasBorder()) - { - _maskPath.Reset(); - _maskPath.AddRect(0, 0, _width, _height, Path.Direction.Cw!); - _maskPath.InvokeOp(_clipPath, Path.Op.Difference!); - } } } } @@ -374,9 +406,6 @@ namespace Microsoft.Maui.Graphics if (_clipPath != null && _borderPaint != null) canvas.DrawPath(_clipPath, _borderPaint); - if (_maskPath != null && _maskPaint != null) - canvas.DrawPath(_maskPath, _maskPaint); - canvas.RestoreToCount(saveCount); } else @@ -419,19 +448,6 @@ namespace Microsoft.Maui.Graphics { if (disposing) { - if (_maskPath != null) - { - _maskPath.Dispose(); - _maskPath = null; - } - - if (_maskPaint != null) - { - _maskPaint.SetXfermode(null); - _maskPaint.Dispose(); - _maskPaint = null; - } - if (_borderPaint != null) { _borderPaint.Dispose(); @@ -455,18 +471,6 @@ namespace Microsoft.Maui.Graphics return; } - if (_maskPath == null) - _maskPath = new Path(); - - if (_maskPaint == null) - { - _maskPaint = new APaint(PaintFlags.AntiAlias); - _maskPaint.SetStyle(APaint.Style.FillAndStroke); - - PorterDuffXfermode porterDuffClearMode = new PorterDuffXfermode(PorterDuff.Mode.Clear); - _maskPaint.SetXfermode(porterDuffClearMode); - } - if (_borderPaint == null) { _borderPaint = new APaint(PaintFlags.AntiAlias); diff --git a/src/Core/src/Handlers/Border/BorderHandler.Android.cs b/src/Core/src/Handlers/Border/BorderHandler.Android.cs index 12b418fd2..42f34e484 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Android.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Android.cs @@ -17,6 +17,10 @@ namespace Microsoft.Maui.Handlers CrossPlatformArrange = VirtualView.CrossPlatformArrange }; + // We only want to use a hardware layer for the entering view because its quite likely + // the view will invalidate several times the Drawable (Draw). + viewGroup.SetLayerType(Android.Views.LayerType.Hardware, null); + return viewGroup; } @@ -51,6 +55,7 @@ namespace Microsoft.Maui.Handlers { // If we're being disconnected from the xplat element, then we should no longer be managing its chidren nativeView.RemoveAllViews(); + base.DisconnectHandler(nativeView); } } diff --git a/src/Core/src/Platform/Android/StrokeExtensions.cs b/src/Core/src/Platform/Android/StrokeExtensions.cs index ea011213c..3e4edc1f5 100644 --- a/src/Core/src/Platform/Android/StrokeExtensions.cs +++ b/src/Core/src/Platform/Android/StrokeExtensions.cs @@ -8,9 +8,9 @@ namespace Microsoft.Maui.Platform public static void UpdateStrokeShape(this AView nativeView, IBorder border) { var borderShape = border.Shape; - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; - if (background == null && borderShape == null) + if (mauiDrawable == null && borderShape == null) return; nativeView.UpdateMauiDrawable(border); @@ -19,82 +19,83 @@ namespace Microsoft.Maui.Platform public static void UpdateStroke(this AView nativeView, IBorder border) { var stroke = border.Stroke; - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; - if (background == null && stroke.IsNullOrEmpty()) + if (mauiDrawable == null && stroke.IsNullOrEmpty()) return; nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderBrush(border.Stroke); } public static void UpdateStrokeThickness(this AView nativeView, IBorder border) { - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder) + if (mauiDrawable == null && !hasBorder) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderWidth(border.StrokeThickness); } public static void UpdateStrokeDashPattern(this AView nativeView, IBorder border) { var strokeDashPattern = border.StrokeDashPattern; - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder && (strokeDashPattern == null || strokeDashPattern.Length == 0)) + if (mauiDrawable == null && !hasBorder && (strokeDashPattern == null || strokeDashPattern.Length == 0)) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderDash(border.StrokeDashPattern, border.StrokeDashOffset); } public static void UpdateStrokeDashOffset(this AView nativeView, IBorder border) { - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder) + if (mauiDrawable == null && !hasBorder) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderDash(border.StrokeDashPattern, border.StrokeDashOffset); } public static void UpdateStrokeMiterLimit(this AView nativeView, IBorder border) { - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder) + if (mauiDrawable == null && !hasBorder) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderMiterLimit(border.StrokeMiterLimit); } public static void UpdateStrokeLineCap(this AView nativeView, IBorder border) { - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder) + if (mauiDrawable == null && !hasBorder) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderLineCap(border.StrokeLineCap); } public static void UpdateStrokeLineJoin(this AView nativeView, IBorder border) { - MauiDrawable? background = nativeView.Background as MauiDrawable; + MauiDrawable? mauiDrawable = nativeView.Background as MauiDrawable; bool hasBorder = border.Shape != null && border.Stroke != null; - if (background == null && !hasBorder) + if (mauiDrawable == null && !hasBorder) return; - nativeView.UpdateMauiDrawable(border); + mauiDrawable?.SetBorderLineJoin(border.StrokeLineJoin); } internal static void UpdateMauiDrawable(this AView nativeView, IBorder border) @@ -114,12 +115,6 @@ namespace Microsoft.Maui.Platform } mauiDrawable.SetBackground(border.Background); - mauiDrawable.SetBorderBrush(border.Stroke); - mauiDrawable.SetBorderWidth(border.StrokeThickness); - mauiDrawable.SetBorderDash(border.StrokeDashPattern, border.StrokeDashOffset); - mauiDrawable.SetBorderMiterLimit(border.StrokeMiterLimit); - mauiDrawable.SetBorderLineJoin(border.StrokeLineJoin); - mauiDrawable.SetBorderLineCap(border.StrokeLineCap); mauiDrawable.SetBorderShape(border.Shape); } }