Skip to content

Commit

Permalink
perf: Add new ArrayList to avoid extra copy creating bitmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
robloo committed Sep 9, 2020
1 parent 6a54235 commit f97af39
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 24 deletions.
87 changes: 87 additions & 0 deletions src/Uno.UI/Microsoft/UI/Xaml/Controls/ColorPicker/ArrayList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;

namespace Microsoft.UI.Xaml.Controls
{
/// <summary>
/// Simplified implementation of List<typeparamref name="T"/> that supports direct access to the internal array.
/// </summary>
/// <remarks>
/// This is needed in the ColorPicker to avoid an extra copy when converting a list of bytes into a WritableBitmap.
/// The performance gains justify creation of this class and a slight deviation from WinUI.
/// This class is not considered safe to use outside of the ColorPicker so it is not in a generic location.
/// </remarks>
/// <typeparam name="T">The type of elements in the list.</typeparam>
internal class ArrayList<T>
{
private T[] _list;

public ArrayList()
{
_list = System.Array.Empty<T>();
}

public ArrayList(int capacity)
{
if (capacity < 0)
{
throw new ArgumentOutOfRangeException("Capacity cannot be less than zero.");
}
else if (capacity == 0)
{
_list = System.Array.Empty<T>();
}
else
{
_list = new T[capacity];
}
}

internal T[] Array
{
get => _list;
}

public int Capacity
{
set
{
if (value < Count)
{
throw new IndexOutOfRangeException("Cannot resize the list smaller than it was.");
}

if (value != Count)
{
if (value > 0)
{
T[] newList = new T[value];

if (Count > 0)
{
System.Array.Copy(_list, 0, newList, 0, Count);
}

_list = newList;
}
else
{
_list = System.Array.Empty<T>();
}
}
}
}

public int Count { get; private set; } = 0;

public void Add(T item)
{
if (Count == _list.Length)
{
System.Array.Resize(ref _list, _list.Length * 2);;
}

_list[Count] = item;
Count++;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public static async void CreateCheckeredBackgroundAsync(
int width,
int height,
Color checkerColor,
List<byte> bgraCheckeredPixelData,
ArrayList<byte> bgraCheckeredPixelData,
IAsyncAction asyncActionToAssign,
CoreDispatcher dispatcherHelper,
Action<WriteableBitmap> completedFunction)
Expand Down Expand Up @@ -460,7 +460,7 @@ await dispatcherHelper.RunAsync(CoreDispatcherPriority.Normal, () =>
public static WriteableBitmap CreateBitmapFromPixelData(
int pixelWidth,
int pixelHeight,
List<byte> bgraPixelData)
ArrayList<byte> bgraPixelData)
{
// IBufferByteAccess isn't included in any WinMD file, because its sole method - Buffer() -
// allows direct pointer access, which isn't applicable to C#. In C#, there's a separate ToStream()
Expand All @@ -470,10 +470,10 @@ public static WriteableBitmap CreateBitmapFromPixelData(

// Uno Doc:
// Since Uno uses C#, the method of converting to a bitmap was changed to what is below.
// WARNING: The .ToArray() copy is a performance hit. It would be better to just pass arrays to this method.
// A new 'ArrayList' class is used to avoid an extra copy using List<T>.ToArray() here.
using (Stream stream = bitmap.PixelBuffer.AsStream())
{
_ = stream.WriteAsync(bgraPixelData.ToArray(), 0, bgraPixelData.Count);
_ = stream.WriteAsync(bgraPixelData.Array, 0, bgraPixelData.Count);
}

// Uno Doc: The following code is not supported in C# and is removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ private void CreateColorPreviewCheckeredBackground()
{
int width = (int)Math.Round(colorPreviewRectangleGrid.ActualWidth);
int height = (int)Math.Round(colorPreviewRectangleGrid.ActualHeight);
List<byte> bgraCheckeredPixelData = new List<byte>();
ArrayList<byte> bgraCheckeredPixelData = new ArrayList<byte>();
var strongThis = this;

ColorHelpers.CreateCheckeredBackgroundAsync(
Expand Down Expand Up @@ -1380,7 +1380,7 @@ private void CreateAlphaSliderCheckeredBackground()
{
int width = (int)Math.Round(alphaSliderBackgroundRectangle.ActualWidth);
int height = (int)Math.Round(alphaSliderBackgroundRectangle.ActualHeight);
List<byte> bgraCheckeredPixelData = new List<byte>();
ArrayList<byte> bgraCheckeredPixelData = new ArrayList<byte>();
var strongThis = this;

ColorHelpers.CreateCheckeredBackgroundAsync(
Expand Down
39 changes: 21 additions & 18 deletions src/Uno.UI/Microsoft/UI/Xaml/Controls/ColorPicker/ColorSpectrum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,12 +1016,15 @@ private async void CreateBitmapsAndColorMap()

// The middle 4 are only needed and used in the case of hue as the third dimension.
// Saturation and luminosity need only a min and max.
List<byte> bgraMinPixelData = new List<byte>();
List<byte> bgraMiddle1PixelData = new List<byte>();
List<byte> bgraMiddle2PixelData = new List<byte>();
List<byte> bgraMiddle3PixelData = new List<byte>();
List<byte> bgraMiddle4PixelData = new List<byte>();
List<byte> bgraMaxPixelData = new List<byte>();

byte[] test = Array.Empty<byte>();

ArrayList<byte> bgraMinPixelData = new ArrayList<byte>();
ArrayList<byte> bgraMiddle1PixelData = new ArrayList<byte>();
ArrayList<byte> bgraMiddle2PixelData = new ArrayList<byte>();
ArrayList<byte> bgraMiddle3PixelData = new ArrayList<byte>();
ArrayList<byte> bgraMiddle4PixelData = new ArrayList<byte>();
ArrayList<byte> bgraMaxPixelData = new ArrayList<byte>();
List<Hsv> newHsvValues = new List<Hsv>();

// Uno Docs: size_t not available in C# so types were changed
Expand Down Expand Up @@ -1212,12 +1215,12 @@ private void FillPixelForBox(
double maxSaturation,
double minValue,
double maxValue,
List<byte> bgraMinPixelData,
List<byte> bgraMiddle1PixelData,
List<byte> bgraMiddle2PixelData,
List<byte> bgraMiddle3PixelData,
List<byte> bgraMiddle4PixelData,
List<byte> bgraMaxPixelData,
ArrayList<byte> bgraMinPixelData,
ArrayList<byte> bgraMiddle1PixelData,
ArrayList<byte> bgraMiddle2PixelData,
ArrayList<byte> bgraMiddle3PixelData,
ArrayList<byte> bgraMiddle4PixelData,
ArrayList<byte> bgraMaxPixelData,
List<Hsv> newHsvValues)
{
double hMin = minHue;
Expand Down Expand Up @@ -1372,12 +1375,12 @@ private void FillPixelForRing(
double maxSaturation,
double minValue,
double maxValue,
List<byte> bgraMinPixelData,
List<byte> bgraMiddle1PixelData,
List<byte> bgraMiddle2PixelData,
List<byte> bgraMiddle3PixelData,
List<byte> bgraMiddle4PixelData,
List<byte> bgraMaxPixelData,
ArrayList<byte> bgraMinPixelData,
ArrayList<byte> bgraMiddle1PixelData,
ArrayList<byte> bgraMiddle2PixelData,
ArrayList<byte> bgraMiddle3PixelData,
ArrayList<byte> bgraMiddle4PixelData,
ArrayList<byte> bgraMaxPixelData,
List<Hsv> newHsvValues)
{
double hMin = minHue;
Expand Down

0 comments on commit f97af39

Please sign in to comment.