Browse Source

use proper corrections. and blend with mid range gray to allow for readability.

feature/test-branch
Dan Walmsley 6 years ago
parent
commit
98e7b14023
  1. 22
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  2. 131
      src/Avalonia.Visuals/Media/ExperimentalAcrylicBrush.cs
  3. 4
      src/Avalonia.Visuals/Media/IExperimentalAcrylicBrush.cs
  4. 3
      src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicBrush.cs
  5. 10
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs

22
samples/ControlCatalog/Pages/AcrylicPage.xaml

@ -3,10 +3,21 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.AcrylicPage">
x:Class="ControlCatalog.Pages.AcrylicPage">
<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="1" />
<Border Height="200" Width="660" CornerRadius="5" BoxShadow="2 2 16 0 Black">
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="Red"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
<StackPanel Orientation="Horizontal" Spacing="20">
<Border Height="200" Width="200" CornerRadius="5" BoxShadow="2 2 16 0 Black">
@ -24,6 +35,7 @@
<ExperimentalAcrylicBrush
TintColor="#00FF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -33,6 +45,7 @@
<ExperimentalAcrylicBrush
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -44,6 +57,7 @@
<ExperimentalAcrylicBrush
TintColor="#0000FF"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -53,6 +67,7 @@
<ExperimentalAcrylicBrush
TintColor="#FFFF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -62,6 +77,7 @@
<ExperimentalAcrylicBrush
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -73,6 +89,7 @@
<ExperimentalAcrylicBrush
TintColor="White"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -82,6 +99,7 @@
<ExperimentalAcrylicBrush
TintColor="#3c3c3c"
TintOpacity="{Binding #TintOpacitySlider.Value}"
TintLuminosityOpacity="{Binding #TintLuminositySlider.Value}"
BackgroundSource="Digger" />
</Border.Background>
</Border>
@ -90,7 +108,7 @@
<Border.Background>
<ExperimentalAcrylicBrush
TintColor="{StaticResource SystemChromeAltHighColor}"
TintOpacity="0.8"
TintOpacity="0.8"
BackgroundSource="Digger" />
</Border.Background>
</Border>

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

@ -5,6 +5,7 @@ namespace Avalonia.Media
public class ExperimentalAcrylicBrush : Brush, IExperimentalAcrylicBrush
{
private Color _effectiveTintColor;
private Color _effectiveLuminosityColor;
static ExperimentalAcrylicBrush()
{
@ -17,11 +18,19 @@ namespace Avalonia.Media
TintColorProperty.Changed.AddClassHandler<ExperimentalAcrylicBrush>((b, e) =>
{
b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
});
TintOpacityProperty.Changed.AddClassHandler<ExperimentalAcrylicBrush>((b, e) =>
{
b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
});
TintLuminosityOpacityProperty.Changed.AddClassHandler<ExperimentalAcrylicBrush>((b, e) =>
{
b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
});
}
@ -37,8 +46,8 @@ namespace Avalonia.Media
public static readonly StyledProperty<double> TintOpacityProperty =
AvaloniaProperty.Register<ExperimentalAcrylicBrush, double>(nameof(TintOpacity));
public static readonly StyledProperty<double> TintLuminosityOpacityProperty =
AvaloniaProperty.Register<ExperimentalAcrylicBrush, double>(nameof(TintLuminosityOpacity));
public static readonly StyledProperty<double?> TintLuminosityOpacityProperty =
AvaloniaProperty.Register<ExperimentalAcrylicBrush, double?>(nameof(TintLuminosityOpacity));
public static readonly StyledProperty<Color> FallbackColorProperty =
AvaloniaProperty.Register<ExperimentalAcrylicBrush, Color>(nameof(FallbackColor));
@ -57,6 +66,8 @@ namespace Avalonia.Media
Color IExperimentalAcrylicBrush.TintColor => _effectiveTintColor;
Color IExperimentalAcrylicBrush.LuminosityColor => _effectiveLuminosityColor;
public double TintOpacity
{
get => GetValue(TintOpacityProperty);
@ -161,9 +172,9 @@ namespace Avalonia.Media
const double midPoint = 0.5; // Mid point of HsvV range that these calculations are based on. This is here for easy tuning.
double whiteMaxOpacity = AdjustOpacity(0.45); // 100% luminosity
double midPointMaxOpacity = AdjustOpacity(0.40); // 50% luminosity
double blackMaxOpacity = AdjustOpacity(0.60); // 0% luminosity
double whiteMaxOpacity = 0.45; // 100% luminosity
double midPointMaxOpacity = 0.90; // 50% luminosity
double blackMaxOpacity = 0.85; // 0% luminosity
var hsv = RgbToHsv(tintColor);
@ -205,5 +216,115 @@ namespace Avalonia.Media
return opacityModifier;
}
Color GetEffectiveLuminosityColor()
{
double tintOpacity = TintOpacity;
// Purposely leaving out tint opacity modifier here because GetLuminosityColor needs the *original* tint opacity set by the user.
var tintColor = new Color((byte)(Math.Round(TintColor.A * tintOpacity)), TintColor.R, TintColor.G, TintColor.B);
double? luminosityOpacity = TintLuminosityOpacity;
return GetLuminosityColor(tintColor, luminosityOpacity);
}
public static Color FromHsv(HsvColor color)
{
float r = 0;
float g = 0;
float b = 0;
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:
r = color.Value;
g = t;
b = p;
break;
case 1:
r = q;
g = color.Value;
b = p;
break;
case 2:
r = p;
g = color.Value;
b = t;
break;
case 3:
r = p;
g = q;
b = color.Value;
break;
case 4:
r = t;
g = p;
b = color.Value;
break;
case 5:
r = color.Value;
g = p;
b = q;
break;
}
return new Color(Trim(r), Trim(g), Trim(b), 255);
}
private static byte Trim(double value)
{
value = Math.Min(Math.Floor(value * 256), 255);
if (value < 0)
{
return 0;
}
else if (value > 255)
{
return 255;
}
return (byte)value;
}
double Luminosity (Color color)
{
return 0.299 * color.R + 0.587 * color.G + 0.114 * color.B;
}
// The tintColor passed into this method should be the original, unmodified color created using user values for TintColor + TintOpacity
Color GetLuminosityColor(Color tintColor, double? luminosityOpacity)
{
var luminosityColor = new Color(255, 127, 127, 127);
var modifier = GetTintOpacityModifier(luminosityColor);
// If luminosity opacity is specified, just use the values as is
if (luminosityOpacity.HasValue)
{
return new Color((byte)(255 * Math.Max(Math.Min(luminosityOpacity.Value * modifier, 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B);
}
else
{
// 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(Trim(Math.Min(mappedTintOpacity * modifier, 1.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B);
}
}
}
}

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

@ -6,7 +6,9 @@ namespace Avalonia.Media
{
AcrylicBackgroundSource BackgroundSource { get; }
Color TintColor { get; }
Color TintColor { get; }
Color LuminosityColor { get; }
double TintOpacity { get; }

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

@ -11,12 +11,15 @@ namespace Avalonia.Media
TintOpacity = brush.TintOpacity;
FallbackColor = brush.FallbackColor;
Opacity = brush.Opacity;
LuminosityColor = brush.LuminosityColor;
}
public AcrylicBackgroundSource BackgroundSource { get; }
public Color TintColor { get; }
public Color LuminosityColor { get; }
public double TintOpacity { get; }
public Color FallbackColor { get; }

10
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -726,8 +726,6 @@ namespace Avalonia.Skia
//tint = Blend(tracingPaper, tint);
if (s_acrylicNoiseShader == null)
{
using(var stream = typeof(DrawingContextImpl).Assembly.GetManifestResourceStream("Avalonia.Skia.Assets.NoiseAsset_256X256_PNG.png"))
@ -736,11 +734,13 @@ namespace Avalonia.Skia
s_acrylicNoiseShader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat)
.WithColorFilter(CreateAlphaColorFilter(noiseOpcity));
}
}
using (var backdrop = SKShader.CreateColor(new SKColor(acrylicBrush.LuminosityColor.R, acrylicBrush.LuminosityColor.G, acrylicBrush.LuminosityColor.B, (byte)Math.Round((acrylicBrush.LuminosityColor.A * 0.75)))))
using (var backdropResult = SKShader.CreateCompose(backdrop, backdrop, SKBlendMode.Luminosity))
using (var tintShader = SKShader.CreateColor(tint))
using (var compose = SKShader.CreateCompose(tintShader, s_acrylicNoiseShader))
using (var effectiveTint = SKShader.CreateCompose (backdropResult, tintShader))
using (var compose = SKShader.CreateCompose(effectiveTint, s_acrylicNoiseShader))
{
paint.Shader = compose;

Loading…
Cancel
Save