I am working on creating a dark theme ResourceDictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Primary" Color="#008080"/>
<SolidColorBrush x:Key="PrimaryVariant" Color="#4DCECF"/>
<SolidColorBrush x:Key="Secondary" Color="#B894F6"/>
<SolidColorBrush x:Key="SecondaryVariant" Color="#9A66F4"/>
<SolidColorBrush x:Key="Background" Color="#131313"/>
<SolidColorBrush x:Key="Elevation1" Color="Black"/> <!--Placeholder-->
<SolidColorBrush x:Key="Elevation2" Color="Black"/> <!--Placeholder-->
<SolidColorBrush x:Key="Elevation3" Color="Black"/> <!--Placeholder-->
<SolidColorBrush x:Key="Elevation4" Color="Black"/> <!--Placeholder-->
<SolidColorBrush x:Key="OnPrimary" Color="Black"/>
<SolidColorBrush x:Key="OnSecondary" Color="Black"/>
<SolidColorBrush x:Key="OnBackground" Color="White"/>
</ResourceDictionary>
I'm trying to calculate the color hex code for different elevations. To do this, I am using my target background color (i.e. #131313) and then overlaying a partially transparent white layer.
<Grid Grid.Row="1"
Grid.Column="2">
<Border Background="{DynamicResource Background}"/>
<Border Background="#0DFFFFFF"
Style="{StaticResource ElevationStyle}">
<TextBlock Text="5%"
Style="{StaticResource TextBlockStyle}"/>
</Border>
</Grid>
So here I have 2 superimposed borders, one with a #131313 background fill and the other with a #0DFFFFFF background. The resulting color:
#131313 #0DFFFFFF = #1F1F1F
Is there a way to automatically calculate the resulting superimposed color so that I can see the results of the new superimposed values if I change the base background color from #131313 to something else?
I would like to be able to display this value as well. Something like
<Grid Grid.Row="1"
Grid.Column="2">
<Border Background="{DynamicResource Background}"/>
<Border Background="#0DFFFFFF"
Style="{StaticResource ElevationStyle}">
<TextBlock Text="5%"
Style="{StaticResource TextBlockStyle}"/>
<TextBlock Text="**<Show the hex code here>**"
Style="{StaticResource TextBlockStyle}"/>
</Border>
</Grid>
CodePudding user response:
You could combine colors like this:
public static class ColorCombiner
{
public static Color Combine(Color source, Color added, int addedAmountPercentage)
{
//the Alpha channel of the added color converted to double (0-1)
var addedTransparencyAmount = (double)added.A / 255;
//the added amount percentage converted to double (0-1)
var addedAmount = (double)addedAmountPercentage / 100;
//combined alpha and amount percentage
var amount = addedTransparencyAmount * addedAmount;
//blend channels
var r = BlendChannel(source.R, added.R, amount);
var g = BlendChannel(source.G, added.G, amount);
var b = BlendChannel(source.B, added.B, amount);
//create resulting color
return Color.FromRgb(r, g, b);
}
private static byte BlendChannel(byte source, byte added, double addedAmount)
{
//blend channel: if addedAmount is 0.60, use 60% of added color and 40% of source color
var sourceAmount = 1d - addedAmount;
var result = (source * sourceAmount) (added * addedAmount);
return Convert.ToByte(result);
}
}
Here's what that results in:
Here's the full code for the demo:
ColorCombiner.cs
using System;
using System.Windows.Media;
namespace WpfSuperimposedColors;
public static class ColorCombiner
{
public static Color Combine(Color source, Color added, int addedAmountPercentage)
{
//the Alpha channel of the added color converted to double (0-1)
var addedTransparencyAmount = (double)added.A / 255;
//the added amount percentage converted to double (0-1)
var addedAmount = (double)addedAmountPercentage / 100;
//combined alpha and amount percentage
var amount = addedTransparencyAmount * addedAmount;
//blend channels
var r = BlendChannel(source.R, added.R, amount);
var g = BlendChannel(source.G, added.G, amount);
var b = BlendChannel(source.B, added.B, amount);
//create resulting color
return Color.FromRgb(r, g, b);
}
private static byte BlendChannel(byte source, byte added, double addedAmount)
{
//blend channel: if addedAmount is 0.60, use 60% of added color and 40% of source color
var sourceAmount = 1d - addedAmount;
var result = (source * sourceAmount) (added * addedAmount);
return Convert.ToByte(result);
}
}
Models
using System.Windows.Media;
using System.Collections.Generic;
namespace WpfSuperimposedColors;
public record ColoringItem(string Name, SolidColorBrush Brush, string Hex)
{
public ColoringItem(string Name, Color Color) : this(Name, new SolidColorBrush(Color), GetHex(Color)) { }
private static string GetHex(Color c)
{
if (c.A < 255)
{
return $"#{c.A:X2}{c.R:X2}{c.G:X2}{c.B:X2}";
}
return $"#{c.R:X2}{c.G:X2}{c.B:X2}";
}
}
public record ColoringSection(ColoringItem Source, ColoringItem Added, IEnumerable<ColoringItem> Results);
MainWindow.xaml.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Media;
namespace WpfSuperimposedColors;
public partial class MainWindow : Window
{
public ObservableCollection<ColoringSection> Items { get; } = new();
public MainWindow()
{
InitializeComponent();
//#2d00b3
var primary = Color.FromRgb(45, 0, 179);
//#2d00b3 at 50% transparency
var primary50 = Color.FromArgb(127, 45, 0, 179);
Items.Add(CreateDemoSection(Colors.White, "White", primary, "Primary"));
Items.Add(CreateDemoSection(Colors.White, "White", primary50, "Half Transparent Primary"));
Items.Add(CreateDemoSection(Colors.Black, "Black", primary, "Primary"));
Items.Add(CreateDemoSection(Colors.Black, "Black", primary50, "Half Transparent Primary"));
DataContext = this;
}
private ColoringSection CreateDemoSection(Color baseColor, string baseName, Color additionColor, string additionName)
{
var baseItem = new ColoringItem(baseName, baseColor);
var additionItem = new ColoringItem(additionName, additionColor);
var items = new List<ColoringItem>();
for (int amountPercentage = 5; amountPercentage <= 100; amountPercentage = 5)
{
var color = ColorCombiner.Combine(baseColor, additionColor, amountPercentage);
items.Add(new ColoringItem($"{amountPercentage}%", color));
}
return new ColoringSection(baseItem, additionItem, items);
}
}
MainWindow.xaml
<Window
x:Class="WpfSuperimposedColors.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfSuperimposedColors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<ScrollViewer Padding="5">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ColoringSection}">
<StackPanel>
<TextBlock Margin="5,15,5,5" FontSize="18">
<Run Text="{Binding Added.Name}" />
<Run Text="{Binding Added.Hex, StringFormat='({0})'}" />
<Run Text="on" />
<Run Text="{Binding Source.Name}" />
<Run Text="background" />
</TextBlock>
<WrapPanel>
<Border
Width="80"
Height="60"
Margin="5"
Background="{Binding Source.Brush}"
BorderBrush="Black"
BorderThickness="1" />
<Border
Width="80"
Height="60"
Margin="5"
Background="{Binding Added.Brush}"
BorderBrush="Black"
BorderThickness="1" />
</WrapPanel>
<ItemsControl Margin="5" ItemsSource="{Binding Results}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Width="80"
Height="60"
Background="{Binding Brush}">
<Border
Padding="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="#20000000"
CornerRadius="5">
<StackPanel>
<TextBlock
Foreground="#fff"
Text="{Binding Name}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
Foreground="#fff"
Text="{Binding Hex}"
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>
</Border>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Window>
EDIT
I've put together a little color blending library for this task exactly, it's called DarkColors.
Here's what it can do: