Browse Source

Implement WriteableBitmap caching/reuse and disposal in ColorSlider

pull/9140/head
robloo 4 years ago
parent
commit
4b43cb3662
  1. 42
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  2. 19
      src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs

42
src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs

@ -2,6 +2,7 @@
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives
protected bool ignorePropertyChanged = false; protected bool ignorePropertyChanged = false;
private WriteableBitmap? _backgroundBitmap;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ColorSlider"/> class. /// Initializes a new instance of the <see cref="ColorSlider"/> class.
/// </summary> /// </summary>
@ -38,6 +41,26 @@ namespace Avalonia.Controls.Primitives
{ {
} }
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
// Bitmaps were released when detached from the visual tree so they must be re-built
UpdateBackground();
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
// Clean-up all bitmaps
// https://github.com/AvaloniaUI/Avalonia/issues/9051
_backgroundBitmap?.Dispose();
_backgroundBitmap = null;
}
/// <summary> /// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses. /// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary> /// </summary>
@ -110,7 +133,19 @@ namespace Avalonia.Controls.Primitives
if (bitmap != null) if (bitmap != null)
{ {
Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight)); if (_backgroundBitmap != null)
{
// Re-use the existing WriteableBitmap
// This assumes the height, width and byte counts are the same and must be set to null
// elsewhere if that assumption is ever not true.
ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bitmap);
}
else
{
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight);
}
Background = new ImageBrush(_backgroundBitmap);
} }
} }
} }
@ -399,6 +434,11 @@ namespace Avalonia.Controls.Primitives
} }
else if (change.Property == BoundsProperty) else if (change.Property == BoundsProperty)
{ {
// If the control's overall dimensions have changed the background bitmap size also needs to change.
// This means the existing bitmap must be released to be recreated correctly in UpdateBackground().
_backgroundBitmap?.Dispose();
_backgroundBitmap = null;
UpdateBackground(); UpdateBackground();
} }
else if (change.Property == ValueProperty || else if (change.Property == ValueProperty ||

19
src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs

@ -617,7 +617,6 @@ namespace Avalonia.Controls.Primitives
PixelFormat.Bgra8888, PixelFormat.Bgra8888,
AlphaFormat.Premul); AlphaFormat.Premul);
// Warning: This is highly questionable
using (var frameBuffer = bitmap.Lock()) using (var frameBuffer = bitmap.Lock())
{ {
Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
@ -625,5 +624,23 @@ namespace Avalonia.Controls.Primitives
return bitmap; return bitmap;
} }
/// <summary>
/// Updates the given <see cref="WriteableBitmap"/> with new, raw BGRA pre-multiplied alpha pixel data.
/// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally.
/// </summary>
/// <param name="bitmap">The existing <see cref="WriteableBitmap"/> to update.</param>
/// <param name="bgraPixelData">The bitmap (in raw BGRA pre-multiplied alpha pixels).</param>
public static void UpdateBitmapFromPixelData(
WriteableBitmap bitmap,
IList<byte> bgraPixelData)
{
using (var frameBuffer = bitmap.Lock())
{
Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
}
return;
}
} }
} }

Loading…
Cancel
Save