Browse Source

Merge branch 'master' into features/NetAnalyzers/CA1847

pull/9190/head
workgroupengineering 4 years ago
committed by GitHub
parent
commit
600aa3ad91
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  2. 16
      samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
  3. 33
      src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
  4. 53
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  5. 24
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
  6. 117
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
  7. 53
      src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
  8. 71
      src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
  9. 34
      src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
  10. 33
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
  11. 33
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml

8
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
<UserControl.Resources>
<pc:ThirdComponentConverter x:Key="ThirdComponent" />
</UserControl.Resources>
<Grid ColumnDefinitions="Auto,10,Auto,10,Auto"
<Grid x:Name="LayoutRoot"
ColumnDefinitions="Auto,10,Auto"
RowDefinitions="Auto,Auto">
<ColorView Grid.Column="0"
Grid.Row="0"
ColorSpectrumShape="Ring" />
<ColorPicker Grid.Column="0"
Grid.Row="1"
HsvColor="hsv(120, 1, 1)"
Margin="0,50,0,0">
<ColorPicker.Palette>
<controls:FlatColorPalette />
@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
</Grid>
<Grid Grid.Column="4"
Grid.Row="0">
</Grid>
</Grid>
</UserControl>

16
samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs

@ -1,6 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
@ -9,6 +11,20 @@ namespace ControlCatalog.Pages
public ColorPickerPage()
{
InitializeComponent();
var layoutRoot = this.GetControl<Grid>("LayoutRoot");
// ColorPicker added from code-behind
var colorPicker = new ColorPicker()
{
Color = Colors.Blue,
Margin = new Thickness(0, 50, 0, 0),
HorizontalAlignment = HorizontalAlignment.Center,
};
Grid.SetColumn(colorPicker, 2);
Grid.SetRow(colorPicker, 1);
layoutRoot.Children.Add(colorPicker);
}
private void InitializeComponent()

33
src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs

@ -1,4 +1,6 @@
namespace Avalonia.Controls
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
{
/// <summary>
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@ -11,8 +13,33 @@
/// </summary>
public ColorPicker() : base()
{
// Completely ignore property changes here
// The ColorView in the control template is responsible to manage this
}
/// <inheritdoc/>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
// Until this point the ColorPicker itself is responsible to process property updates.
// This, for example, syncs Color with HsvColor and updates primitive controls.
//
// However, when the template is created, hand-off this change processing to the
// ColorView within the control template itself. Remember ColorPicker derives from
// ColorView so we don't want two instances of the same logic fighting each other.
// It is best to hand-off to the ColorView in the control template because that is the
// primary point of user-interaction for the overall control. It also simplifies binding.
//
// Keep in mind this hand-off is not possible until the template controls are created
// which is done after the ColorPicker is instantiated. The ColorPicker must still
// process updates before the template is applied to ensure all property changes in
// XAML or object initializers are handled correctly. Otherwise, there can be bugs
// such as setting the Color property doesn't work because the HsvColor is never updated
// and then the Color value is lost once the template loads (and the template ColorView
// takes over).
//
// In order to complete this hand-off, completely ignore property changes here in the
// ColorPicker. This means the ColorView in the control template is now responsible to
// process property changes and handle primary calculations.
base.ignorePropertyChanged = true;
}
}

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

@ -2,6 +2,7 @@
using Avalonia.Controls.Metadata;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives
protected bool ignorePropertyChanged = false;
private WriteableBitmap? _backgroundBitmap;
/// <summary>
/// Initializes a new instance of the <see cref="ColorSlider"/> class.
/// </summary>
@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
}
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
ArrayList<byte> bgraPixelData = await ColorPickerHelpers.CreateComponentBitmapAsync(
pixelWidth,
pixelHeight,
Orientation,
@ -108,9 +123,27 @@ namespace Avalonia.Controls.Primitives
IsAlphaMaxForced,
IsSaturationValueMaxForced);
if (bitmap != null)
if (bgraPixelData != null)
{
Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight));
if (_backgroundBitmap != null)
{
// TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
//
// 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, bgraPixelData);
// TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
//_backgroundBitmap?.Dispose();
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
}
else
{
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
}
Background = new ImageBrush(_backgroundBitmap);
}
}
}
@ -350,11 +383,11 @@ namespace Avalonia.Controls.Primitives
return;
}
// Always keep the two color properties in sync
if (change.Property == ColorProperty)
{
ignorePropertyChanged = true;
// Always keep the two color properties in sync
HsvColor = Color.ToHsv();
SetColorToSliderValues();
@ -367,7 +400,10 @@ namespace Avalonia.Controls.Primitives
ignorePropertyChanged = false;
}
else if (change.Property == ColorModelProperty)
else if (change.Property == ColorComponentProperty ||
change.Property == ColorModelProperty ||
change.Property == IsAlphaMaxForcedProperty ||
change.Property == IsSaturationValueMaxForcedProperty)
{
ignorePropertyChanged = true;
@ -381,6 +417,7 @@ namespace Avalonia.Controls.Primitives
{
ignorePropertyChanged = true;
// Always keep the two color properties in sync
Color = HsvColor.ToRgb();
SetColorToSliderValues();
@ -399,7 +436,13 @@ namespace Avalonia.Controls.Primitives
}
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();
UpdatePseudoClasses();
}
else if (change.Property == ValueProperty ||
change.Property == MinimumProperty ||

24
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs

@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
/// <summary>
/// Defines the <see cref="ThirdComponent"/> property.
/// </summary>
public static readonly StyledProperty<ColorComponent> ThirdComponentProperty =
AvaloniaProperty.Register<ColorSpectrum, ColorComponent>(
nameof(ThirdComponent),
ColorComponent.Component3); // Value
/// <summary>
/// Gets or sets the currently selected color in the RGB color model.
/// </summary>
@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
/// <summary>
/// Gets the third HSV color component that is NOT displayed by the spectrum.
/// This is automatically calculated from the <see cref="Components"/> property.
/// </summary>
/// <remarks>
/// This property should be used for any external color slider that represents the
/// third component of the color. Note that this property uses the generic
/// <see cref="ColorComponent"/> type instead of the more accurate <see cref="HsvComponent"/>
/// to allow direct usage by the generalized color sliders.
/// </remarks>
public ColorComponent ThirdComponent
{
get => GetValue(ThirdComponentProperty);
private set => SetValue(ThirdComponentProperty, value);
}
}
}

117
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs

@ -73,6 +73,8 @@ namespace Avalonia.Controls.Primitives
private WriteableBitmap? _saturationMaximumBitmap;
private WriteableBitmap? _valueBitmap;
private WriteableBitmap? _minBitmap;
private WriteableBitmap? _maxBitmap;
// Fields used by UpdateEllipse() to ensure that it's using the data
// associated with the last call to CreateBitmapsAndColorMap(),
@ -95,7 +97,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Initializes a new instance of the <see cref="ColorSpectrum"/> class.
/// </summary>
public ColorSpectrum()
public ColorSpectrum() : base()
{
_shapeFromLastBitmapCreation = Shape;
_componentsFromLastBitmapCreation = Components;
@ -171,6 +173,18 @@ namespace Avalonia.Controls.Primitives
{
base.OnAttachedToVisualTree(e);
// If the color was updated while this ColorSpectrum was not part of the visual tree,
// the selection ellipse may be in an incorrect position. This is because the spectrum
// renders based on layout scaling to avoid color banding; however, layout scale is only
// available when the control is attached to the visual tree. The ColorSpectrum's color
// may be updated from code-behind or from binding with another control when it's not
// part of the visual tree.
//
// See discussion: https://github.com/AvaloniaUI/Avalonia/discussions/9077
//
// To work-around this issue the selection ellipse is refreshed here.
UpdateEllipse();
// OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here
}
@ -489,6 +503,23 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == ComponentsProperty)
{
// Calculate and update the ThirdComponent value
switch (Components)
{
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
ThirdComponent = (ColorComponent)HsvComponent.Value;
break;
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
ThirdComponent = (ColorComponent)HsvComponent.Saturation;
break;
case ColorSpectrumComponents.SaturationValue:
case ColorSpectrumComponents.ValueSaturation:
ThirdComponent = (ColorComponent)HsvComponent.Hue;
break;
}
CreateBitmapsAndColorMap();
}
@ -588,6 +619,10 @@ namespace Avalonia.Controls.Primitives
RaiseColorChanged();
}
/// <summary>
/// Updates the selected <see cref="HsvColor"/> and <see cref="Color"/> based on a point within the color spectrum.
/// </summary>
/// <param name="point">The point on the spectrum representing the color.</param>
private void UpdateColorFromPoint(PointerPoint point)
{
// If we haven't initialized our HSV value array yet, then we should just ignore any user input -
@ -664,6 +699,9 @@ namespace Avalonia.Controls.Primitives
UpdateColor(hsvAtPoint);
}
/// <summary>
/// Updates the position of the selection ellipse on the spectrum which indicates the selected color.
/// </summary>
private void UpdateEllipse()
{
if (_selectionEllipsePanel == null)
@ -832,6 +870,8 @@ namespace Avalonia.Controls.Primitives
}
// Remember the bitmap size follows physical device pixels
// Warning: LayoutHelper.GetLayoutScale() doesn't work unless the control is visible
// This will not be true in all cases if the color is updated from another control or code-behind
var scale = LayoutHelper.GetLayoutScale(this);
Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2));
Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2));
@ -973,13 +1013,13 @@ namespace Avalonia.Controls.Primitives
// 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>();
List<Hsv> newHsvValues = new List<Hsv>();
ArrayList<byte> bgraMinPixelData;
ArrayList<byte> bgraMiddle1PixelData;
ArrayList<byte> bgraMiddle2PixelData;
ArrayList<byte> bgraMiddle3PixelData;
ArrayList<byte> bgraMiddle4PixelData;
ArrayList<byte> bgraMaxPixelData;
List<Hsv> newHsvValues;
// In Avalonia, Bounds returns the actual device-independent pixel size of a control.
// However, this is not necessarily the size of the control rendered on a display.
@ -990,20 +1030,27 @@ namespace Avalonia.Controls.Primitives
int pixelDimension = (int)Math.Round(minDimension * scale);
var pixelCount = pixelDimension * pixelDimension;
var pixelDataSize = pixelCount * 4;
bgraMinPixelData.Capacity = pixelDataSize;
bgraMinPixelData = new ArrayList<byte>(pixelDataSize);
bgraMaxPixelData = new ArrayList<byte>(pixelDataSize);
newHsvValues = new List<Hsv>(pixelCount);
// We'll only save pixel data for the middle bitmaps if our third dimension is hue.
if (components == ColorSpectrumComponents.ValueSaturation ||
components == ColorSpectrumComponents.SaturationValue)
{
bgraMiddle1PixelData.Capacity = pixelDataSize;
bgraMiddle2PixelData.Capacity = pixelDataSize;
bgraMiddle3PixelData.Capacity = pixelDataSize;
bgraMiddle4PixelData.Capacity = pixelDataSize;
bgraMiddle1PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle2PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle3PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle4PixelData = new ArrayList<byte>(pixelDataSize);
}
else
{
bgraMiddle1PixelData = new ArrayList<byte>(0);
bgraMiddle2PixelData = new ArrayList<byte>(0);
bgraMiddle3PixelData = new ArrayList<byte>(0);
bgraMiddle4PixelData = new ArrayList<byte>(0);
}
bgraMaxPixelData.Capacity = pixelDataSize;
newHsvValues.Capacity = pixelCount;
await Task.Run(() =>
{
@ -1056,28 +1103,28 @@ namespace Avalonia.Controls.Primitives
ColorSpectrumComponents components2 = Components;
WriteableBitmap minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
WriteableBitmap maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
_minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
_maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
switch (components2)
{
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
_saturationMinimumBitmap = minBitmap;
_saturationMaximumBitmap = maxBitmap;
_saturationMinimumBitmap = _minBitmap;
_saturationMaximumBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
_valueBitmap = maxBitmap;
_valueBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.ValueSaturation:
case ColorSpectrumComponents.SaturationValue:
_hueRedBitmap = minBitmap;
_hueRedBitmap = _minBitmap;
_hueYellowBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle1PixelData, pixelWidth, pixelHeight);
_hueGreenBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle2PixelData, pixelWidth, pixelHeight);
_hueCyanBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle3PixelData, pixelWidth, pixelHeight);
_hueBlueBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle4PixelData, pixelWidth, pixelHeight);
_huePurpleBitmap = maxBitmap;
_huePurpleBitmap = _maxBitmap;
break;
}
@ -1111,12 +1158,12 @@ namespace Avalonia.Controls.Primitives
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;
@ -1271,12 +1318,12 @@ namespace Avalonia.Controls.Primitives
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;

53
src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs

@ -1,53 +0,0 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace Avalonia.Controls.Primitives.Converters
{
/// <summary>
/// Gets the third <see cref="ColorComponent"/> corresponding with a given
/// <see cref="ColorSpectrumComponents"/> that represents the other two components.
/// </summary>
/// <remarks>
/// This is a highly-specialized converter for the color picker.
/// </remarks>
public class ThirdComponentConverter : IValueConverter
{
/// <inheritdoc/>
public object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (value is ColorSpectrumComponents components)
{
// Note: Alpha is not relevant here
switch (components)
{
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
return (ColorComponent)HsvComponent.Value;
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
return (ColorComponent)HsvComponent.Saturation;
case ColorSpectrumComponents.SaturationValue:
case ColorSpectrumComponents.ValueSaturation:
return (ColorComponent)HsvComponent.Hue;
}
}
return AvaloniaProperty.UnsetValue;
}
/// <inheritdoc/>
public object? ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
return AvaloniaProperty.UnsetValue;
}
}
}

71
src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs

@ -0,0 +1,71 @@
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// A thin wrapper over an <see cref="System.Array"/> that allows some additional list-like functionality.
/// </summary>
/// <remarks>
/// This is only for internal ColorPicker-related functionality and should not be used elsewhere.
/// It is added for performance to enjoy the simplicity of the IList.Add() method without requiring
/// an additional copy to turn a list into an array for bitmaps.
/// </remarks>
/// <typeparam name="T">The type of items in the array.</typeparam>
internal class ArrayList<T>
{
private int _nextIndex = 0;
/// <summary>
/// Initializes a new instance of the <see cref="ArrayList{T}"/> class.
/// </summary>
public ArrayList(int capacity)
{
Capacity = capacity;
Array = new T[capacity];
}
/// <summary>
/// Provides access to the underlying array by index.
/// This exists for simplification and the <see cref="Array"/> property
/// may also be used.
/// </summary>
/// <param name="i">The index of the item to get or set.</param>
/// <returns>The item at the given index.</returns>
public T this[int i]
{
get => Array[i];
set => Array[i] = value;
}
/// <summary>
/// Gets the underlying array.
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the fixed capacity/size of the array.
/// This must be set during construction.
/// </summary>
public int Capacity { get; private set; }
/// <summary>
/// Adds the given item to the array at the next available index.
/// WARNING: This must be used carefully and only once, in sequence.
/// </summary>
/// <param name="item">The item to add.</param>
public void Add(T item)
{
if (_nextIndex >= 0 &&
_nextIndex < Capacity)
{
Array[_nextIndex] = item;
_nextIndex++;
}
else
{
// If necessary an exception could be thrown here
// throw new IndexOutOfRangeException();
}
return;
}
}
}

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

@ -37,7 +37,7 @@ namespace Avalonia.Controls.Primitives
/// during calculation with the HSVA color model.
/// This will ensure colors are always discernible regardless of saturation/value.</param>
/// <returns>A new bitmap representing a gradient of color component values.</returns>
public static async Task<byte[]> CreateComponentBitmapAsync(
public static async Task<ArrayList<byte>> CreateComponentBitmapAsync(
int width,
int height,
Orientation orientation,
@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
return Array.Empty<byte>();
return new ArrayList<byte>(0);
}
var bitmap = await Task.Run<byte[]>(() =>
var bitmap = await Task.Run<ArrayList<byte>>(() =>
{
int pixelDataIndex = 0;
double componentStep;
byte[] bgraPixelData;
ArrayList<byte> bgraPixelData;
Color baseRgbColor = Colors.White;
Color rgbColor;
int bgraPixelDataHeight;
@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
// Allocate the buffer
// BGRA formatted color components 1 byte each (4 bytes in a pixel)
bgraPixelData = new byte[width * height * 4];
bgraPixelData = new ArrayList<byte>(width * height * 4);
bgraPixelDataHeight = height * 4;
bgraPixelDataWidth = width * 4;
@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="pixelHeight">The pixel height of the bitmap.</param>
/// <returns>A new <see cref="WriteableBitmap"/>.</returns>
public static WriteableBitmap CreateBitmapFromPixelData(
IList<byte> bgraPixelData,
ArrayList<byte> bgraPixelData,
int pixelWidth,
int pixelHeight)
{
@ -617,13 +617,31 @@ namespace Avalonia.Controls.Primitives
PixelFormat.Bgra8888,
AlphaFormat.Premul);
// Warning: This is highly questionable
using (var frameBuffer = bitmap.Lock())
{
Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return bitmap;
}
/// <summary>
/// Updates the given <see cref="WriteableBitmap"/> with new, raw BGRA pre-multiplied alpha pixel data.
/// TODO: THIS METHOD IS CURRENTLY PROVIDED AS REFERENCE BUT CAUSES INTERMITTENT CRASHES IF USED.
/// 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,
ArrayList<byte> bgraPixelData)
{
using (var frameBuffer = bitmap.Lock())
{
Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return;
}
}
}

33
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml

@ -8,7 +8,6 @@
x:CompileBindings="True">
<pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
<pc:ThirdComponentConverter x:Key="ThirdComponentConverter" />
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
<converters:ColorToHexConverter x:Key="ColorToHexConverter" />
<converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
@ -241,23 +240,21 @@
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Border Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Panel>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<ContentPresenter Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Height="{DynamicResource TabItemPipeThickness}"
Margin="0,0,0,2"
@ -370,7 +367,7 @@
IsSaturationValueMaxForced="False"
Orientation="Vertical"
ColorModel="Hsva"
ColorComponent="{Binding Components, ElementName=ColorSpectrum, Converter={StaticResource ThirdComponentConverter}}"
ColorComponent="{Binding ThirdComponent, ElementName=ColorSpectrum}"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum}"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"

33
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml

@ -8,7 +8,6 @@
x:CompileBindings="True">
<pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
<pc:ThirdComponentConverter x:Key="ThirdComponentConverter" />
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
<converters:ColorToHexConverter x:Key="ColorToHexConverter" />
<converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
@ -215,23 +214,21 @@
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Border Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Panel>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<ContentPresenter Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Height="2"
Margin="0,0,0,2"
@ -332,7 +329,7 @@
IsSaturationValueMaxForced="False"
Orientation="Vertical"
ColorModel="Hsva"
ColorComponent="{Binding Components, ElementName=ColorSpectrum, Converter={StaticResource ThirdComponentConverter}}"
ColorComponent="{Binding ThirdComponent, ElementName=ColorSpectrum}"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum}"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"

Loading…
Cancel
Save