How to calculate required hue-rotate to generate specific colour?
Asked Answered
L

6

92

I have a white image that I am using as a background for a div, and I would like to colour to match the themes main colour. I am aware I can do:

filter: sepia() saturate(10000%) hue-rotate(30deg);

and cycle through hue-rotate to find a colour, but is it possible to calculate this value in advance? Given that the specified hex value is quite dark, I imagine I will need to include the invert(%) filter as well.

Given a hex value of #689d94 what math do I need to do to calculate the desired hue-rotate and invert value to convert my white background image into the same colour?

Edit

Here's a snippet of a div with a white background image being filtered green. The trick here, is it is the whole of the div that is being filtered, not just the image. If I was to enter some text into the div the text colour would turn green as well.

div {
  background:url(http://richard.parnaby-king.co.uk/basket.svg) no-repeat scroll 0 0 transparent;
  background-size:5em;
  width:5em;
  height:5em;
  -webkit-filter: invert(25%) sepia() saturate(100000%) hue-rotate(174deg);
  filter: invert(25%) sepia() saturate(100000%) hue-rotate(174deg);
}
<div></div>
<p style="background: #689d94">​</p>
Linebacker answered 13/3, 2015 at 16:12 Comment(2)
Is it possible to use an in-line svg filter?Linebacker
Re: “Is it possible to use an in-line svg filter?”YES, absolutely — see my answer below.Philhellene
S
10

The only way to get the exact match is to use an SVG color matrix filter.

For RGB color #689d94, which is rgb(104, 157, 148), divide each primary color's value by 255:

Put these weights into the SVG <filter> matrix (5ᵗʰ column in the first 3 rows):

<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <filter id="689d94" color-interpolation-filters="sRGB">
      <feColorMatrix type="matrix" 
        values="0 0 0 0 0.40784 
                0 0 0 0 0.61569 
                0 0 0 0 0.58039 
                0 0 0 1 0"/>
    </filter>
  </defs>
</svg>

The <filter> has to have id (I used the RGB hex code 689d94), so we can use it as a reference.

Since some browsers (e.g. Firefox) don't see/use the SVG filter if the display property of the SVG element is set to none, and having this SVG element in HTML code would inconveniently occupy some space, the best way is to convert this SVG into a pure inline CSS filter.

To get an inline filter value, take the above listed SVG code, transform it into a single line by remove line breaks and unnecessary spaces, then prepend url('data:image/svg+xml, and append the previously mentioned id as #689d94'):

div {
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="71.063" height="60.938"><path d="M33.938 0l-16.97 19.906H1.625L0 21.781v8.781l1.25 1.407h4.781l5.875 28.969h46.969l6.188-28.97h4.687l1.313-1.343v-8.844L69.5 19.906H54.656L37.312 0h-3.375zm1.593 7.594l9.594 12.312H26.25l9.281-12.312zm-20.281 16s-.405 2.9 1.594 3.844c1.998.942 4.406.03 4.406.03-1.666 2.763-3.638 3.551-5.469 2.688-3.312-1.562-.531-6.562-.531-6.562zm41.188.031s2.749 4.969-.563 6.531c-2.487 1.162-4.848-1.541-5.438-2.656 0 0 2.377.88 4.375-.063 1.999-.942 1.625-3.812 1.625-3.812z"/></svg>') no-repeat; // optimized from http://richard.parnaby-king.co.uk/basket.svg
  background-size: 100%;
  display: inline-block;
  height: 5em;
  width: 5em;
}
#colored {
  filter: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><defs><filter id="689d94" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0 0 0 0 0.40784 0 0 0 0 0.61569 0 0 0 0 0.58039 0 0 0 1 0"/></filter></defs></svg>#689d94');
  margin-left: 20px;
}
<!-- No <svg> in HTML; pure CSS -->
<div></div><div id="colored"></div>
<p style="background: #689d94">​</p>
Solidify answered 24/5, 2022 at 20:56 Comment(1)
"the best way is to convert this SVG into a pure inline CSS filter." Note that this doesn't work on Safari; known webkit bug. See: #67635331 , bugs.webkit.org/show_bug.cgi?id=104169Phionna
L
103

The key in this case is to define an initial color. White nor black or any gray-scale is technically an actual color - you can't saturate or rotate it. You'll have to "colorize" it somehow, and the sepia filter is the only filter which do some form of colorizing.

It would be easier if your image was pure 100% red. Then you could just add the target degree directly and adjust saturation and lightness using HSL for target. For a white color start point the first step is to convert and define an intermediate color so we can saturate and rotate it later on.

Lets first darken the white image and apply sepia to get a "base" color we can work with:

filter: brightness(50%) sepia(1);

This will produce RGB color value of approximately:

rgb(178, 160, 128)

Step two is to convert that to HSL color-space which gives us:

hsl(38, 24.5%, 60%);

Base color result

div {
  background:url(http://richard.parnaby-king.co.uk/basket.svg) no-repeat;
  background-size:5em;
  width:5em;
  height:5em;
  -webkit-filter: brightness(50%) sepia(1);
  filter: brightness(50%) sepia(1);
}
<div></div>

Converting base color to target color

These two first steps are static and its result will be reused every time we need to find a target adjustment (the actual value of sepia is defined in the SVG Filters specification).

Now we need to calculate what we need to apply to this base color to get target color. First convert target color, for example #689d94 as given in the question, to HSL:

hsl(170, 21.3%, 51.2%);

Then we have to calculate the difference between those. Hue is calculated by simply subtracting base from target. The same for Saturation and Lightness, but as we assume 100% of the base value we need to subtract the result from 100% to end up with a diff for the accumulated values:

H:  170 - 38             ->  132°
S:  100 + (24.5 - 21.3)  ->  103.2%  (relative to base 100% =  3.2%)
L:  100 + (51.2 - 60.0)  ->   91.2%  (relative to base 100% = -8.8%)

Convert those values to a filter-string by appending it to the existing filter, then set it on the div:

/*      ------ base color ------  -------  new target -------------------------------*/
filter: brightness(50%) sepia(1)  hue-rotate(132deg) saturate(103.2%) brightness(91.2%);

And to set it you would probably do something like this assuming filter and divElement are already declared:

...
filter = "brightness(0.5) sepia(1) hue-rotate(132deg) saturate(103.2%) brightness(91.2%)";
divElement.style.filter = filter;
divElement.style.webkitFilter = filter;

Note that there is likely rounding errors as RGB is represented as integer, while HSL is floating point, so the actual result may not be exact, but it should get pretty close.

Live example

div {
  background:url(http://richard.parnaby-king.co.uk/basket.svg) no-repeat;
  background-size:5em;
  width:5em;
  height:5em;
  -webkit-filter: 
      brightness(50%) sepia(1) hue-rotate(132deg) saturate(103.2%) brightness(91.2%);
  filter: 
      brightness(50%) sepia(1) hue-rotate(132deg) saturate(103.2%) brightness(91.2%);
}
<div></div>
<span style="font:14px sans-serif;padding:7px;color:#fff;background:#689d94">
Target color</span>

Viable alternative options are:

  • Predefine SVGs with the color already set.
  • Work with HSL/RGB directly in JavaScript and modify the SVG tree with the color directly for the shape rather than using filters. Filters are expensive performance wise, especially if many are chained as here and they are in addition a dominant part of a page. They are neither supported in all browsers.
Lambertson answered 30/4, 2015 at 3:21 Comment(3)
This is a nice attempt but totally misleading.Lungan
This answer is wrong. There is a precise and elegant solution for this.Philhellene
re: "It would be easier if your image was pure 100% red" it can be! brightness(0) invert(30%) sepia(100%) saturate(10000%) will get you to #ff0000.Haeckel
T
35

The accepted answer is wrong. Hue-rotate does not conserve saturation or brightness and you have to do crazy math to come up with the correct

[Update: OP changed their original answer to a new solution, so the observations above are no longer true]

values. The far easier way - which will result in a correct result - is to do a CSS filter that references an SVG filter. The feColorMatrix primitive in SVG filters allows you to pick a color directly.

Take your color #424242 - divide each RGB channel value by #FF and put them in the fifth column, first three rows of your color matrix. In this case, hex #42 is 68 in decimal, so divide that by 255 (#FF in decimal) and you'll get 0.257 - which you put in the fifth column, first three rows.

div {
  background:url(http://richard.parnaby-king.co.uk/basket.svg) no-repeat scroll 0 0 transparent;
  background-size:5em;
  width:5em;
  height:5em;
  -webkit-filter: url(#colorize);
  filter: url(#colorize);
}
<div>
  </div>

<svg>
<defs>
<filter id="colorize" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 .257
                                 0 0 0 0 .257
                                 0 0 0 0 .257
                                 0 0 0 1 0"/>
 
/filter>
</defs>
</svg>
Transvalue answered 3/10, 2017 at 2:22 Comment(0)
S
10

The only way to get the exact match is to use an SVG color matrix filter.

For RGB color #689d94, which is rgb(104, 157, 148), divide each primary color's value by 255:

Put these weights into the SVG <filter> matrix (5ᵗʰ column in the first 3 rows):

<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <filter id="689d94" color-interpolation-filters="sRGB">
      <feColorMatrix type="matrix" 
        values="0 0 0 0 0.40784 
                0 0 0 0 0.61569 
                0 0 0 0 0.58039 
                0 0 0 1 0"/>
    </filter>
  </defs>
</svg>

The <filter> has to have id (I used the RGB hex code 689d94), so we can use it as a reference.

Since some browsers (e.g. Firefox) don't see/use the SVG filter if the display property of the SVG element is set to none, and having this SVG element in HTML code would inconveniently occupy some space, the best way is to convert this SVG into a pure inline CSS filter.

To get an inline filter value, take the above listed SVG code, transform it into a single line by remove line breaks and unnecessary spaces, then prepend url('data:image/svg+xml, and append the previously mentioned id as #689d94'):

div {
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="71.063" height="60.938"><path d="M33.938 0l-16.97 19.906H1.625L0 21.781v8.781l1.25 1.407h4.781l5.875 28.969h46.969l6.188-28.97h4.687l1.313-1.343v-8.844L69.5 19.906H54.656L37.312 0h-3.375zm1.593 7.594l9.594 12.312H26.25l9.281-12.312zm-20.281 16s-.405 2.9 1.594 3.844c1.998.942 4.406.03 4.406.03-1.666 2.763-3.638 3.551-5.469 2.688-3.312-1.562-.531-6.562-.531-6.562zm41.188.031s2.749 4.969-.563 6.531c-2.487 1.162-4.848-1.541-5.438-2.656 0 0 2.377.88 4.375-.063 1.999-.942 1.625-3.812 1.625-3.812z"/></svg>') no-repeat; // optimized from http://richard.parnaby-king.co.uk/basket.svg
  background-size: 100%;
  display: inline-block;
  height: 5em;
  width: 5em;
}
#colored {
  filter: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><defs><filter id="689d94" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0 0 0 0 0.40784 0 0 0 0 0.61569 0 0 0 0 0.58039 0 0 0 1 0"/></filter></defs></svg>#689d94');
  margin-left: 20px;
}
<!-- No <svg> in HTML; pure CSS -->
<div></div><div id="colored"></div>
<p style="background: #689d94">​</p>
Solidify answered 24/5, 2022 at 20:56 Comment(1)
"the best way is to convert this SVG into a pure inline CSS filter." Note that this doesn't work on Safari; known webkit bug. See: #67635331 , bugs.webkit.org/show_bug.cgi?id=104169Phionna
P
5

Hex Color To CSS Filter Converter

Use this website to calculate the filter: https://isotropic.co/tool/hex-color-to-css-filter/

Property answered 23/9, 2022 at 8:22 Comment(0)
R
0

Here is an extension method in C# to do this (thanks Ωmega for the knowledge):

public static class ColorExtensions
{
    public static string ToSvgFilter(this string value)
    {
        Color color = ColorTranslator.FromHtml(value);
        int r = Convert.ToInt16(color.R);
        int g = Convert.ToInt16(color.G);
        int b = Convert.ToInt16(color.B);

        decimal converted_r = (decimal)r / 255;
        decimal converted_g = (decimal)g / 255;
        decimal converted_b = (decimal)b / 255;

        return $"url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\"><defs><filter id=\"{value.Replace("#", "")}\" color-interpolation-filters=\"sRGB\"><feColorMatrix type=\"matrix\" values=\"0 0 0 0 {converted_r} 0 0 0 0 {converted_g} 0 0 0 0 {converted_b} 0 0 0 1 0\"/></filter></defs></svg>{value}')";
    }
}

Here is the usage:

    protected override async Task OnInitializedAsync()
    {
        _menuItemSvgFilter = "#f20c96".ToSvgFilter();            

        await base.OnInitializedAsync();
    }

Here is the razor:

<NavLink class="menu-font" href="inboundorders">
     <img src="/img/svg_icon/ICON_ORDER.svg" /> Orders
</NavLink>

and I added just this CSS to the razor at the bottom:

<style type="text/css">
.menu-font img 
{
    filter: @_menuItemSvgFilter;
}
</style>
Raiment answered 25/1, 2023 at 21:8 Comment(0)
B
-1

If svg are being used then ...

You can open svg files with some text editor copy and paste to html file then change path color as required.

In below example code... I just changed the path color of center ring. Hope this helps..

        var imgg =document.getElementById("path");
        imgg.style="fill:#424242";
   
<html>
<body>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="imgg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 296.838 296.838" style="enable-background:new 0 0 296.838 296.838;" xml:space="preserve" width="512px" height="512px">
<g>
	<path  d="M58.733,64.566L41.763,47.596C14.832,74.526,0,110.333,0,148.419s14.832,73.893,41.763,100.823l16.971-16.971   C36.335,209.874,24,180.095,24,148.419S36.335,86.964,58.733,64.566z" fill="#91DC5A"/>
	<path d="M82.137,81.969c-17.75,17.748-27.525,41.348-27.525,66.45s9.775,48.702,27.525,66.45l16.971-16.971   c-13.218-13.216-20.496-30.788-20.496-49.479s7.278-36.264,20.496-49.48L82.137,81.969z" fill="#91DC5A"/>
	<path d="M255.075,47.596l-16.971,16.971c22.399,22.397,34.733,52.177,34.733,83.853s-12.335,61.455-34.733,83.852l16.971,16.971   c26.931-26.931,41.763-62.737,41.763-100.823S282.006,74.526,255.075,47.596z" fill="#91DC5A"/>
	<path d="M214.701,81.969L197.73,98.939c13.218,13.216,20.496,30.788,20.496,49.48s-7.278,36.264-20.496,49.479l16.971,16.971   c17.75-17.748,27.525-41.348,27.525-66.45S232.451,99.717,214.701,81.969z" fill="#91DC5A"/>
	<path id="path" d="M148.586,114.789c-8.607,0-17.212,3.284-23.78,9.851c-13.131,13.133-13.131,34.424,0,47.559   c6.568,6.566,15.174,9.851,23.78,9.851c8.606,0,17.212-3.284,23.779-9.851c13.131-13.135,13.131-34.426,0-47.559   C165.798,118.073,157.192,114.789,148.586,114.789z M155.395,155.228c-2.454,2.454-5.319,2.821-6.809,2.821   c-1.489,0-4.356-0.367-6.808-2.818c-3.755-3.756-3.755-9.867-0.003-13.619c2.455-2.455,5.321-2.822,6.811-2.822   c1.489,0,4.354,0.367,6.808,2.82C159.147,145.363,159.147,151.475,155.395,155.228z" fill="#91DC5A"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
    
    
</body>
</html>

For background image

        var myimg='url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 296.838 296.838" style="enable-background:new 0 0 296.838 296.838;" xml:space="preserve" width="512px" height="512px"><g><path  d="M58.733,64.566L41.763,47.596C14.832,74.526,0,110.333,0,148.419s14.832,73.893,41.763,100.823l16.971-16.971   C36.335,209.874,24,180.095,24,148.419S36.335,86.964,58.733,64.566z" fill="#91DC5A"/><path d="M82.137,81.969c-17.75,17.748-27.525,41.348-27.525,66.45s9.775,48.702,27.525,66.45l16.971-16.971   c-13.218-13.216-20.496-30.788-20.496-49.479s7.278-36.264,20.496-49.48L82.137,81.969z" fill="#91DC5A"/><path d="M255.075,47.596l-16.971,16.971c22.399,22.397,34.733,52.177,34.733,83.853s-12.335,61.455-34.733,83.852l16.971,16.971   c26.931-26.931,41.763-62.737,41.763-100.823S282.006,74.526,255.075,47.596z" fill="#91DC5A"/><path d="M214.701,81.969L197.73,98.939c13.218,13.216,20.496,30.788,20.496,49.48s-7.278,36.264-20.496,49.479l16.971,16.971   c17.75-17.748,27.525-41.348,27.525-66.45S232.451,99.717,214.701,81.969z" fill="#91DC5A"/><path d="M148.586,114.789c-8.607,0-17.212,3.284-23.78,9.851c-13.131,13.133-13.131,34.424,0,47.559   c6.568,6.566,15.174,9.851,23.78,9.851c8.606,0,17.212-3.284,23.779-9.851c13.131-13.135,13.131-34.426,0-47.559   C165.798,118.073,157.192,114.789,148.586,114.789z M155.395,155.228c-2.454,2.454-5.319,2.821-6.809,2.821   c-1.489,0-4.356-0.367-6.808-2.818c-3.755-3.756-3.755-9.867-0.003-13.619c2.455-2.455,5.321-2.822,6.811-2.822   c1.489,0,4.354,0.367,6.808,2.82C159.147,145.363,159.147,151.475,155.395,155.228z" fill="#91DC5A"/></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></svg> \')';
        
        document.getElementById("mydiv").style.backgroundImage =myimg ;  
        
        
        
        //changing color according to theme .. new theme color :#424242
        myimg=myimg.replace(/#91DC5A/g,"#424242");
       document.getElementById("mydiv").style.backgroundImage =myimg ; 
             div {

  background-size:5em;
  width:5em;
  height:5em;
  
}
<html>
<body>

    
    <div id="mydiv"></div>
<span style="font:14px sans-serif;padding:7px;color:#fff;background:#689d94">
Target color</span>
   
  
    
</body>
</html>
Buonaparte answered 28/6, 2017 at 13:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.