简体   繁体   中英

HSL to RGB converter issue

This converter assumes H, S, and L are contained in the set [0, 1].

But I want H to be between [0, 359] (0 and 359 included) and S & L between [0, 100] (0 and 100 included) and I don't know how to do it (in Java aswell).

I took this converter from here -> (CTRL+F and search "Java implementation of Mohsen's code")

public class testdos {
    public static void main(String[] args) {
        // hslToRgb([0, 359], [0, 100], [0, 100]);
    }

    /**
     * Converts an HSL color value to RGB. Conversion formula adapted from
     * http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are
     * contained in the set [0, 1] and returns r, g, and b in the set [0, 255].
     *
     * @param h The hue
     * @param s The saturation
     * @param l The lightness
     * @return int array, the RGB representation
     */
    public static int[] hslToRgb(float h, float s, float l) {
        // i added this sysout line
        System.out.println("HSL(" + h + "," + s + "," + l + ")");

        float r, g, b;

        if (s == 0f) {
            r = g = b = l; // achromatic
        } else {
            float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
            float p = 2 * l - q;
            r = hueToRgb(p, q, h + 1f / 3f);
            g = hueToRgb(p, q, h);
            b = hueToRgb(p, q, h - 1f / 3f);
        }
        int[] rgb = { to255(r), to255(g), to255(b) };

        // and i added this to print it, don't know if its the best way to do it
        System.out.print("RGB(");
        for (int i = 0; i < rgb.length; i++) {
            if ((i + 1) != rgb.length) {
                System.out.print(rgb[i] + ",");
            }
            else {
                System.out.print(rgb[i]);
            }
        }
        System.out.println(")");

        return rgb;
    }

    public static int to255(float v) {
        return (int) Math.min(255, 256 * v);
    }

    /** Helper method that converts hue to rgb */
    public static float hueToRgb(float p, float q, float t) {
        if (t < 0f)
            t += 1f;
        if (t > 1f)
            t -= 1f;
        if (t < 1f / 6f)
            return p + (q - p) * 6f * t;
        if (t < 1f / 2f)
            return q;
        if (t < 2f / 3f)
            return p + (q - p) * (2f / 3f - t) * 6f;
        return p;
    }
}

The hslToRgb() method you provided accepts three parameters of float data type. The value range that must be provided to all of these parameters is: 0.0f to 1.0f :

int[] hslToRgb(float h, float s, float l) { ... }

Instead you want to supply a specific range values to these parameters which in turn makes the method much easier to utilize in layman's terms with respect to what needs to actually be supplied.

H  (0° to 360°  as in Degrees)
S  (0% to 100%  as in Percent)
L  (0% to 100%  as in Percent)

It's all a matter of doing a little division. This can be done as the parameters are supplied to the method or built into the method which appears to be what you want. The latter is somewhat more advantageous since you don't need to remember the formula (regardless of how simple it is) to carry out those divisions when applying the arguments to the hslToRgb() method.

Doing the math before passing arguments:

// Hue is: 65°   Saturation is: 36%   Lightness is: 87%
float hue =        (float) 65/360;
float saturation = (float) 36/100;
float lightness  = (float) 87/100;
int[] rgb = hslToRgb(hue, saturation, lightness); 

Doing the math when passing arguments:

// Hue is: 65°   Saturation is: 36%   Lightness is: 87%
int[] rgb = hslToRgb((float)65/360, (float)36/100, (float)87/100); 

Who cares: Let the method figure it out:

// Hue is: 65°   Saturation is: 36%   Lightness is: 87%
int[] rgb = hslToRgb(65, 36, 87);

This last example requires a little bit of code to be added to the hslToRgb() method. It's nothing major but before we get into that you need to be sure to realize that for color accuracy, Hue, Saturation, and Lightness arguments can be given respectively in floating point degree and floating point percentage values. In other words, supplying arguments such as:

int[] rgb = hslToRgb(65, 36, 87);

               AND

int[] rgb = hslToRgb(65.8, 36.2, 87.5);

are to be considered valid calls with valid arguments. With the slight code addition to the hslToRgb() method, either or can be supplied, for example:

int[] rgb = hslToRgb(0.1806f, 36, 87.5);

This is a valid call with valid arguments and will produce the very same RGB values as the other above examples.

Modifying the hslToRgb() method:

Simply add these three lines of code to the top of the hslToRGB() method:

public static int[] hslToRGB(float h, float s, float l) {
    if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }
    if (s > 1.0f) { s = (s < 0 ? 0 : s > 100 ? 100 : s) / 100; }
    if (l > 1.0f) { l = (l < 0 ? 0 : l > 100 ? 100 : l) / 100; }

    // .... the rest of method code here ....
}

What are these three lines of code doing?

With these lines of code the Hue (h) argument can be supplied as a set [0.0 to 1.0] or a set [0.0 to 360.0] or a set [0 to 360]. The Saturation (s) and Lightness (l) arguments can be supplied as a set [0.0 to 1.0] or a set [0.0 to 100.0] or a set [0 to 100]. If any argument supplied is less than its minimum range (0) then it is defaulted to that arguments specific minimum which for all arguments would be 0. On the same hand, if any argument supplied is greater than its maximum range then it is defaulted to that arguments specific maximum.

Since all three lines of code are basically doing the same thing for each specific argument, we'll explain the first line:

if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }

The condition for the if statement ( h > 1.0f ) checks to see if the supplied Hue (h) argument is within the set of [0.0 to 1.0]. If the argument supplied is greater than 1.0 then it must be supplied as a literal degree value and the if statement condition is true therefore running the code contained within this statement's code block which consists of nested Ternary Operator statements. If the if statement condition is false then the supplied value is merely used. Ultimately, if the condition is true then we want to take the supplied argument and divide it by 360 but first we use the nested Ternary Operator statements to ensure the argument supplied is within valid range (0 to 360):

h < 0 ? 0 :

If the supplied argument (h) is less than 0 (such as an arbitrary value of -22 or -0.202) then the h is set to hold a default of 0 otherwise we check to see if the argument value is greater then 360:

: h > 360 ? 360 :

If the supplied argument (h) is greater than 360 (such as an arbitrary value of 365 or 378.8) then the h is set to hold a default of 360 otherwise it is deemed that the argument supplied is indeed within the proper range and we'll use it therefore dividing the supplied value by 360;

: h) / 360

How you might use this method:

int[] rgb = hslToRGB(65, 36, 87);
System.out.println("RGB is: " + java.util.Arrays.toString(rgb));
String hex = "#" + Integer.toHexString(new Color(rgb[0], rgb[1], rgb[2]).getRGB() & 0x00ffffff);
System.out.println("In Hexadecimal: " + hex.toUpperCase());

Console Output:

RGB is: [232, 234, 210]
In Hexadecimal: #E8EAD2

You divide by the h, s, and l range to get the numbers between 0 and 1. You also start class names with an upper-case character.

Edited to add: Why do I have to start class names with an upper-case character?

Because it's the Java convention, and so you can write code like:

Testdoc testdoc = new TestDoc();

and visually see the difference between the Testdoc class name and the testdoc field name.

Here's the revised code.

public class Testdos {
    public static void main(String[] args) {
        // hslToRgb([0, 359], [0, 100], [0, 100]);
    }

    /**
     * Converts an HSL color value to RGB. Conversion formula adapted from
     * http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are
     * contained in the set [0, 1] and returns r, g, and b in the set [0, 255].
     *
     * @param h The hue
     * @param s The saturation
     * @param l The lightness
     * @return int array, the RGB representation
     */
    public static int[] hslToRgb(float h, float s, float l) {
        // i added this sysout line
        System.out.println("HSL(" + h + "," + s + "," + l + ")");

        h /= 359.0;
        s /= 100.0;
        l /= 100.0;

        float r, g, b;

        if (s == 0f) {
            r = g = b = l; // achromatic
        } else {
            float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
            float p = 2 * l - q;
            r = hueToRgb(p, q, h + 1f / 3f);
            g = hueToRgb(p, q, h);
            b = hueToRgb(p, q, h - 1f / 3f);
        }
        int[] rgb = { to255(r), to255(g), to255(b) };

        // and i added this to print it, don't know if its the best way to do it
        System.out.print("RGB(");
        for (int i = 0; i < rgb.length; i++) {
            if ((i + 1) != rgb.length) {
                System.out.print(rgb[i] + ",");
            } else {
                System.out.print(rgb[i]);
            }
        }
        System.out.println(")");

        return rgb;
    }

    public static int to255(float v) {
        return (int) Math.min(255, 256 * v);
    }

    /** Helper method that converts hue to rgb */
    public static float hueToRgb(float p, float q, float t) {
        if (t < 0f)
            t += 1f;
        if (t > 1f)
            t -= 1f;
        if (t < 1f / 6f)
            return p + (q - p) * 6f * t;
        if (t < 1f / 2f)
            return q;
        if (t < 2f / 3f)
            return p + (q - p) * (2f / 3f - t) * 6f;
        return p;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM