|
|
|
@ -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; |
|
|
|
|