Skip to content

Commit aef3227

Browse files
committed
Refactor the AccentAnalzyer as a palette sampling tool
- Trimmed colorfulness info from AccentColorInfo - Renamed AccentColorInfo to PaletteColor - Removed colorfulness calculations for AccentAnalyzer - Renamed AccentAnalyzer to ColorPaletteSampler - Replaced statically enumerated PaletteSelector properties with index bindable SelectedColors with dynamic min count - Replaced phrase 'prominence' with 'weight' and 'fraction'
1 parent 4e2e14b commit aef3227

19 files changed

+362
-442
lines changed

components/ColorAnalyzer/samples/AccentAnalyzerSample.xaml

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
mc:Ignorable="d">
1212

1313
<Page.Resources>
14-
<helpers:AccentAnalyzer x:Name="AccentAnalyzer"
15-
Source="{x:Bind AccentedImage}">
16-
<helpers:AccentPalette x:Name="AccentPalette" />
17-
<helpers:ProminencePalette x:Name="ProminencePalette" />
18-
<helpers:BasePalette x:Name="BasePalette" />
19-
</helpers:AccentAnalyzer>
14+
<helpers:ColorPaletteSampler x:Name="ColorPaletteSample"
15+
Source="{x:Bind SampledImage}">
16+
<helpers:AccentColorPaletteSelector x:Name="AccentPalette" MinColorCount="3"/>
17+
<helpers:ColorWeightPaletteSelector x:Name="WeightedColorPalette" MinColorCount="1"/>
18+
<helpers:BaseColorPaletteSelector x:Name="BasePalette" MinColorCount="1"/>
19+
</helpers:ColorPaletteSampler>
2020
</Page.Resources>
2121

2222
<Grid>
@@ -27,19 +27,17 @@
2727

2828
<StackPanel Padding="20"
2929
VerticalAlignment="Center">
30-
<Image x:Name="AccentedImage"
30+
<Image x:Name="SampledImage"
3131
HorizontalAlignment="Center"
3232
Source="/ColorAnalyzerExperiment.Samples/Assets/StockImages/Bloom.jpg"
3333
Stretch="Uniform">
3434
<interactivity:Interaction.Behaviors>
3535
<interactivity:EventTriggerBehavior EventName="ImageOpened">
36-
<interactivity:CallMethodAction MethodName="UpdateAccent"
37-
TargetObject="{x:Bind AccentAnalyzer}" />
36+
<interactivity:CallMethodAction MethodName="UpdatePalette"
37+
TargetObject="{x:Bind ColorPaletteSample}" />
3838
</interactivity:EventTriggerBehavior>
3939
</interactivity:Interaction.Behaviors>
4040
</Image>
41-
<TextBlock HorizontalAlignment="Center"
42-
Text="{x:Bind AccentAnalyzer.Colorfulness, Mode=OneWay}" />
4341
</StackPanel>
4442

4543
<Grid Grid.Column="1"
@@ -64,7 +62,7 @@
6462
Margin="4"
6563
Padding="2">
6664
<Border.Background>
67-
<SolidColorBrush Color="{x:Bind ProminencePalette.Primary, Mode=OneWay}" />
65+
<SolidColorBrush Color="{x:Bind WeightedColorPalette.SelectedColors[0], FallbackValue=Transparent, Mode=OneWay}" />
6866
</Border.Background>
6967
<TextBlock Foreground="Black"
7068
Text="Dominant" />
@@ -76,7 +74,7 @@
7674
Margin="4"
7775
Padding="2">
7876
<Border.Background>
79-
<SolidColorBrush Color="{x:Bind BasePalette.Base, Mode=OneWay}" />
77+
<SolidColorBrush Color="{x:Bind BasePalette.SelectedColors[0], FallbackValue=Transparent, Mode=OneWay}" />
8078
</Border.Background>
8179
<TextBlock Foreground="Black"
8280
Text="Base" />
@@ -88,7 +86,7 @@
8886
Margin="4"
8987
Padding="2">
9088
<Border.Background>
91-
<SolidColorBrush Color="{x:Bind AccentPalette.Primary, Mode=OneWay}" />
89+
<SolidColorBrush Color="{x:Bind AccentPalette.SelectedColors[0], FallbackValue=Transparent, Mode=OneWay}" />
9290
</Border.Background>
9391
<TextBlock Foreground="Black"
9492
Text="Primary" />
@@ -99,7 +97,7 @@
9997
Margin="4"
10098
Padding="2">
10199
<Border.Background>
102-
<SolidColorBrush Color="{x:Bind AccentPalette.Secondary, Mode=OneWay}" />
100+
<SolidColorBrush Color="{x:Bind AccentPalette.SelectedColors[1], FallbackValue=Transparent, Mode=OneWay}" />
103101
</Border.Background>
104102
<TextBlock Foreground="Black"
105103
Text="Secondary" />
@@ -110,7 +108,7 @@
110108
Margin="4"
111109
Padding="2">
112110
<Border.Background>
113-
<SolidColorBrush Color="{x:Bind AccentPalette.Tertiary, Mode=OneWay}" />
111+
<SolidColorBrush Color="{x:Bind AccentPalette.SelectedColors[2], FallbackValue=Transparent, Mode=OneWay}" />
114112
</Border.Background>
115113
<TextBlock Foreground="Black"
116114
Text="Tertiary" />
@@ -123,9 +121,9 @@
123121
<Rectangle.Fill>
124122
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
125123
<GradientStopCollection>
126-
<GradientStop Offset="0" Color="{x:Bind AccentPalette.Primary, Mode=OneWay}" />
127-
<GradientStop Offset="0.74" Color="{x:Bind AccentPalette.Secondary, Mode=OneWay}" />
128-
<GradientStop Offset="1" Color="{x:Bind AccentPalette.Tertiary, Mode=OneWay}" />
124+
<GradientStop Offset="0" Color="{x:Bind AccentPalette.SelectedColors[0], FallbackValue=Transparent, Mode=OneWay}" />
125+
<GradientStop Offset="0.74" Color="{x:Bind AccentPalette.SelectedColors[1], FallbackValue=Transparent, Mode=OneWay}" />
126+
<GradientStop Offset="1" Color="{x:Bind AccentPalette.SelectedColors[2], FallbackValue=Transparent, Mode=OneWay}" />
129127
</GradientStopCollection>
130128
</LinearGradientBrush>
131129
</Rectangle.Fill>

components/ColorAnalyzer/samples/ImageOptionsPane.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ private void GridView_ItemClick(object sender, ItemClickEventArgs e)
3939

4040
private void SetImage(Uri uri)
4141
{
42-
_sample.AccentedImage.Source = new BitmapImage(uri);
42+
_sample.SampledImage.Source = new BitmapImage(uri);
4343
}
4444
}

components/ColorAnalyzer/src/AccentAnalyzer.Properties.cs

Lines changed: 0 additions & 70 deletions
This file was deleted.

components/ColorAnalyzer/src/AccentColorInfo.cs

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Numerics;
6+
using Windows.UI;
7+
8+
namespace CommunityToolkit.WinUI.Helpers;
9+
10+
internal static class ColorExtensions
11+
{
12+
internal static float FindColorfulness(this Color color)
13+
{
14+
var vectorColor = color.ToVector3();
15+
var rg = vectorColor.X - vectorColor.Y;
16+
var yb = ((vectorColor.X + vectorColor.Y) / 2) - vectorColor.Z;
17+
return 0.3f * new Vector2(rg, yb).Length();
18+
}
19+
20+
internal static float FindColorfulness(this Color[] colors)
21+
{
22+
var vectorColors = colors.Select(ToVector3);
23+
24+
// Isolate rg and yb
25+
var rg = vectorColors.Select(x => Math.Abs(x.X - x.Y));
26+
var yb = vectorColors.Select(x => Math.Abs(0.5f * (x.X + x.Y) - x.Z));
27+
28+
// Evaluate rg and yb mean and std
29+
var rg_std = FindStandardDeviation(rg, out var rg_mean);
30+
var yb_std = FindStandardDeviation(yb, out var yb_mean);
31+
32+
// Combine means and standard deviations
33+
var std = new Vector2(rg_mean, yb_mean).Length();
34+
var mean = new Vector2(rg_std, yb_std).Length();
35+
36+
// Return colorfulness
37+
return std + (0.3f * mean);
38+
}
39+
40+
internal static Color ToColor(this Vector3 color)
41+
{
42+
color *= 255;
43+
return Color.FromArgb(255, (byte)(color.X), (byte)(color.Y), (byte)(color.Z));
44+
}
45+
46+
internal static Vector3 ToVector3(this Color color)
47+
{
48+
var vector = new Vector3(color.R, color.G, color.B);
49+
return vector / 255;
50+
}
51+
52+
private static float FindStandardDeviation(IEnumerable<float> data, out float avg)
53+
{
54+
var average = data.Average();
55+
avg = average;
56+
var sumOfSquares = data.Select(x => (x - average) * (x - average)).Sum();
57+
return (float)Math.Sqrt(sumOfSquares / data.Count());
58+
}
59+
}

components/ColorAnalyzer/src/AccentAnalyzer.Clustering.cs renamed to components/ColorAnalyzer/src/ColorPaletteSampler.Clustering.cs

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace CommunityToolkit.WinUI.Helpers;
88

9-
public partial class AccentAnalyzer
9+
public partial class ColorPaletteSampler
1010
{
1111
private static Vector3[] KMeansCluster(Span<Vector3> points, int k, out int[] counts)
1212
{
@@ -121,7 +121,7 @@ private static void CalculateCentroidsAndPrune(ref Span<Vector3> centroids, ref
121121
}
122122

123123
/// <summary>
124-
/// Finds the index of the centroid nearest the point
124+
/// Finds the index of the centroid nearest the point.
125125
/// </summary>
126126
private static int FindNearestClusterIndex(Vector3 point, Span<Vector3> centroids)
127127
{
@@ -146,37 +146,4 @@ private static int FindNearestClusterIndex(Vector3 point, Span<Vector3> centroid
146146

147147
return nearestIndex;
148148
}
149-
150-
internal static float FindColorfulness(Vector3 color)
151-
{
152-
var rg = color.X - color.Y;
153-
var yb = ((color.X + color.Y) / 2) - color.Z;
154-
return 0.3f * new Vector2(rg, yb).Length();
155-
}
156-
157-
internal static float FindColorfulness(Vector3[] colors)
158-
{
159-
// Isolate rg and yb
160-
var rg = colors.Select(x => Math.Abs(x.X - x.Y));
161-
var yb = colors.Select(x => Math.Abs(0.5f * (x.X + x.Y) - x.Z));
162-
163-
// Evaluate rg and yb mean and std
164-
var rg_std = FindStandardDeviation(rg, out var rg_mean);
165-
var yb_std = FindStandardDeviation(yb, out var yb_mean);
166-
167-
// Combine means and standard deviations
168-
var std = new Vector2(rg_mean, yb_mean).Length();
169-
var mean = new Vector2(rg_std, yb_std).Length();
170-
171-
// Return colorfulness
172-
return std + (0.3f * mean);
173-
}
174-
175-
private static float FindStandardDeviation(IEnumerable<float> data, out float avg)
176-
{
177-
var average = data.Average();
178-
avg = average;
179-
var sumOfSquares = data.Select(x => (x - average) * (x - average)).Sum();
180-
return (float)Math.Sqrt(sumOfSquares / data.Count());
181-
}
182149
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.WinUI.Helpers;
6+
7+
public partial class ColorPaletteSampler
8+
{
9+
/// <summary>
10+
/// Gets the <see cref="DependencyProperty"/> for the <see cref="Source"/> property.
11+
/// </summary>
12+
public static readonly DependencyProperty SourceProperty =
13+
DependencyProperty.Register(nameof(Source), typeof(UIElement), typeof(ColorPaletteSampler), new PropertyMetadata(null, OnSourceChanged));
14+
15+
/// <summary>
16+
/// An event fired when the <see cref="Palette"/> and <see cref="PaletteSelectors"/> are updated.
17+
/// </summary>
18+
public event EventHandler? PaletteUpdated;
19+
20+
/// <summary>
21+
/// Gets or sets the <see cref="UIElement"/> source sampled for a color palette.
22+
/// </summary>
23+
public UIElement? Source
24+
{
25+
get => (UIElement)GetValue(SourceProperty);
26+
set => SetValue(SourceProperty, value);
27+
}
28+
29+
/// <summary>
30+
/// The list of <see cref="ColorPaletteSelector"/> to update when the <see cref="Source"/> is set or changed.
31+
/// </summary>
32+
public IList<ColorPaletteSelector> PaletteSelectors { get; set; }
33+
34+
/// <summary>
35+
/// Gets the set of <see cref="PaletteColor"/> extracted on last update.
36+
/// </summary>
37+
/// <remarks>
38+
/// The palette is the set of colors extracted from the <see cref="Source"/> element, and
39+
/// the fraction of the image that each <see cref="PaletteColor"/> covers.
40+
/// </remarks>
41+
public IReadOnlyList<PaletteColor>? Palette { get; private set; }
42+
43+
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
44+
{
45+
if (d is not ColorPaletteSampler analyzer)
46+
return;
47+
48+
_ = analyzer.UpdatePaletteAsync();
49+
}
50+
}

0 commit comments

Comments
 (0)