Home > Back-end >  How to calculate and display the resulting background color for superimposed backgrounds (Eyedropper
How to calculate and display the resulting background color for superimposed backgrounds (Eyedropper

Time:09-15

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>

Sample App

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:

result


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:

  • Related