Browse Source

simplyfy acrylic brush based on avalonias capabilities.

feature/test-branch
Dan Walmsley 6 years ago
parent
commit
1ec1fafe91
  1. 38
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  2. 92
      src/Avalonia.Visuals/Media/ExperimentalAcrylicBrush.cs
  3. 4
      src/Avalonia.Visuals/Media/IExperimentalAcrylicBrush.cs
  4. 15
      src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicBrush.cs
  5. 3
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs

38
samples/ControlCatalog/Pages/AcrylicPage.xaml

@ -8,13 +8,11 @@
<ExperimentalAcrylicBrush
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</UserControl.Background>
<Border Padding="20" HorizontalAlignment="Center">
<StackPanel Spacing="20">
<Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="1" />
<Slider Name="TintLuminositySlider" Minimum="0" Maximum="1" Value="0" />
<StackPanel Orientation="Horizontal" Spacing="20">
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
@ -32,7 +30,6 @@
<ExperimentalAcrylicBrush
TintColor="#00FF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -42,7 +39,6 @@
<ExperimentalAcrylicBrush
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -54,7 +50,6 @@
<ExperimentalAcrylicBrush
TintColor="#0000FF"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -64,7 +59,6 @@
<ExperimentalAcrylicBrush
TintColor="#FFFF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -72,9 +66,37 @@
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="#FFFFFF"
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="20">
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="Black"
TintOpacity="{Binding #TintOpacitySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="#7F7F7F"
TintOpacity="{Binding #TintOpacitySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="White"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>

92
src/Avalonia.Visuals/Media/ExperimentalAcrylicBrush.cs

@ -73,32 +73,6 @@ namespace Avalonia.Media
public float Value { get; set; }
}
public static Color FromHsv(HsvColor color)
{
var i = (float)Math.Floor(color.Hue * 6f);
var f = color.Hue * 6f - i;
var p = color.Value * (1f - color.Saturation);
var q = color.Value * (1f - f * color.Saturation);
var t = color.Value * (1f - (1f - f) * color.Saturation);
switch (i % 6)
{
case 0:
return new Color(255, (byte)(255.0 * color.Value), (byte)(255.0 * t), (byte)(255.0 * p));
case 1:
return new Color(255, (byte)(255.0 * q), (byte)(255.0 * color.Value), (byte)(255.0 * p));
case 2:
return new Color(255, (byte)(255.0 * p), (byte)(255.0 * color.Value), (byte)(255.0 * t));
case 3:
return new Color(255, (byte)(255.0 * p), (byte)(255.0 * q), (byte)(255.0 * color.Value));
case 4:
return new Color(255, (byte)(255.0 * t), (byte)(255.0 * p), (byte)(255.0 * color.Value));
default:
case 5:
return new Color(255, (byte)(255.0 * color.Value), (byte)(255.0 * p), (byte)(255.0 * q));
}
}
public static HsvColor RgbToHsv(Color color)
{
var r = color.R /255.0f;
@ -140,65 +114,20 @@ namespace Avalonia.Media
return new HsvColor { Hue = h, Saturation = s, Value = v };
}
public Color GetLuminosityColor ()
{
return GetLuminosityColor(TintColor, TintLuminosityOpacity);
}
Color GetLuminosityColor(Color tintColor, double? luminosityOpacity)
{
var rgbTintColor = tintColor;
// If luminosity opacity is specified, just use the values as is
if (luminosityOpacity.HasValue)
{
return new Color((byte)(255.0 * Math.Min(1.0, Math.Max(0.0, luminosityOpacity.Value))), tintColor.R, tintColor.G, tintColor.B);
}
else
{
// To create the Luminosity blend input color without luminosity opacity,
// we're taking the TintColor input, converting to HSV, and clamping the V between these values
const double minHsvV = 0.125;
const double maxHsvV = 0.965;
var hsvTintColor = RgbToHsv(rgbTintColor);
var clampedHsvV = Math.Max(Math.Min(hsvTintColor.Value, minHsvV), maxHsvV);
var hsvLuminosityColor = hsvTintColor;
hsvLuminosityColor.Value = (float)clampedHsvV;
var rgbLuminosityColor = FromHsv(hsvLuminosityColor);
// Now figure out luminosity opacity
// Map original *tint* opacity to this range
const double minLuminosityOpacity = 0.15;
const double maxLuminosityOpacity = 1.03;
double luminosityOpacityRangeMax = maxLuminosityOpacity - minLuminosityOpacity;
double mappedTintOpacity = ((tintColor.A / 255.0) * luminosityOpacityRangeMax) + minLuminosityOpacity;
// Finally, combine the luminosity opacity and the HsvV-clamped tint color
return new Color((byte)(255.0 * Math.Min(mappedTintOpacity, 1.0)), rgbLuminosityColor.R, rgbLuminosityColor.G, rgbLuminosityColor.B);
}
}
public Color GetEffectiveTintColor()
{
var tintColor = TintColor;
double tintOpacity = TintOpacity;
// Update tintColor's alpha with the combined opacity value
// If LuminosityOpacity was specified, we don't intervene into users parameters
if (false)//TintLuminosityOpacity() != nullptr)
double tintOpacityModifier = GetTintOpacityModifier(tintColor);
if (false) // non-acrylic blue // TODO detect blur level.
{
//tintColor.A = static_cast<uint8_t>(round(tintColor.A * tintOpacity));
tintColor = new Color((byte)(Math.Round(tintColor.A * (((tintOpacity * tintOpacityModifier) * 0.25) + 0.75))), tintColor.R, tintColor.G, tintColor.B);
}
else
{
double tintOpacityModifier = GetTintOpacityModifier(tintColor);
tintColor = new Color((byte)(Math.Round(tintColor.A * tintOpacity * tintOpacityModifier)), tintColor.R, tintColor.G, tintColor.B);
}
@ -213,19 +142,14 @@ namespace Avalonia.Media
// the effect increases linearly as luminosity deviates from 50%. After this effect is calculated, we cancel it out
// linearly as saturation increases from zero.
const double midPoint = 0.50; // Mid point of HsvV range that these calculations are based on. This is here for easy tuning.
const double midPoint = 0.5; // Mid point of HsvV range that these calculations are based on. This is here for easy tuning.
const double whiteMaxOpacity = 0.45; // 100% luminosity
const double midPointMaxOpacity = 0.90; // 50% luminosity
const double blackMaxOpacity = 0.85; // 0% luminosity
const double whiteMaxOpacity = 0.40; // 100% luminosity
const double midPointMaxOpacity = 0.50; // 50% luminosity
const double blackMaxOpacity = 0.84; // 0% luminosity
var hsv = RgbToHsv(tintColor);
if(tintColor == Colors.Red)
{
}
double opacityModifier = midPointMaxOpacity;
if (hsv.Value != midPoint)

4
src/Avalonia.Visuals/Media/IExperimentalAcrylicBrush.cs

@ -10,12 +10,8 @@ namespace Avalonia.Media
double TintOpacity { get; }
double? TintLuminosityOpacity { get; }
Color FallbackColor { get; }
Color GetEffectiveTintColor();
Color GetLuminosityColor();
}
}

15
src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicBrush.cs

@ -4,15 +4,11 @@ namespace Avalonia.Media
{
public readonly struct ImmutableExperimentalAcrylicBrush : IExperimentalAcrylicBrush, IEquatable<ImmutableExperimentalAcrylicBrush>
{
private readonly Color luminosityColor;
public ImmutableExperimentalAcrylicBrush(IExperimentalAcrylicBrush brush)
{
luminosityColor = brush.GetLuminosityColor();
{
BackgroundSource = brush.BackgroundSource;
TintColor = brush.GetEffectiveTintColor();
TintOpacity = brush.TintOpacity;
TintLuminosityOpacity = brush.TintLuminosityOpacity;
FallbackColor = brush.FallbackColor;
Opacity = brush.Opacity;
}
@ -23,8 +19,6 @@ namespace Avalonia.Media
public double TintOpacity { get; }
public double? TintLuminosityOpacity { get; }
public Color FallbackColor { get; }
public double Opacity { get; }
@ -37,7 +31,6 @@ namespace Avalonia.Media
Opacity == other.Opacity &&
TintOpacity == other.TintOpacity &&
BackgroundSource == other.BackgroundSource &&
TintLuminosityOpacity == other.TintLuminosityOpacity &&
FallbackColor == other.FallbackColor;
}
@ -58,7 +51,6 @@ namespace Avalonia.Media
int hash = 17;
hash = (hash * 23) + TintColor.GetHashCode();
hash = (hash * 23) + TintLuminosityOpacity.GetHashCode();
hash = (hash * 23) + Opacity.GetHashCode();
hash = (hash * 23) + TintOpacity.GetHashCode();
hash = (hash * 23) + BackgroundSource.GetHashCode();
@ -68,11 +60,6 @@ namespace Avalonia.Media
}
}
public Color GetLuminosityColor()
{
return luminosityColor;
}
public static bool operator ==(ImmutableExperimentalAcrylicBrush left, ImmutableExperimentalAcrylicBrush right)
{
return left.Equals(right);

3
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -714,8 +714,7 @@ namespace Avalonia.Skia
var noiseOpcity = 0.09 * brush.Opacity;
var tintColor = acrylicBrush.GetEffectiveTintColor();
var luminosityColor = acrylicBrush.GetLuminosityColor();
var excl = new SKColor(luminosityColor.R, luminosityColor.G, luminosityColor.B, (byte)(255* (luminosityColor.A /255.0) * 0.1));
var excl = new SKColor(255, 255, 255, (byte)(255 * 0.1));
var tint = new SKColor(tintColor.R, tintColor.G, tintColor.B, (byte)(255 * ((tintColor.A /255.0) * acrylicBrush.Opacity)));
tint = SimpleColorBurn(excl, tint);

Loading…
Cancel
Save