Java Tips Weblog

  • Blog Stats

    • 2,569,553 hits
  • Categories

  • Archives

HSL Color

Posted by Rob Camick on July 5, 2009

The Color class in Java uses Red, Green and Blue (RGB) values to specify a Color. I don’t know about you, but I have no idea how to manipulate a given Color to return a related Color. For example, how would you go about returning a darker or lighter Color? Sure the Color API supports brighter() and darker() methods, but they don’t seem to work that well and you can’t control the degree of brightness or darkness. The API also supports a HSB (Hue, Saturation, Brightness) color space which seemed promising, but didn’t quite return the results I was looking for.

So, I searched the web and found out about the HSL (Hue, Saturation, Luminance) color space. As you can guess from its name you still need to specify 3 values to completely define a unique color:

  • hue – represents the color (think colors of the rainbow). Its value is specified in degrees from 0 – 360. Red is 0, green is 120 and blue is 240.
  • saturation – represents the purity of the color. Its value is specified as a percentage. 100 percent is fully saturated and 0 percent is gray.
  • luminance – represents the brighness of the color. Its value is specified as a percent. 100 percent is white and 0 percent is black.

Each value has its own meaning and therefore you generally only need to change a single value to create a new color that is somehow related to the old color. This is far easier for me to understand then trying to guess how to manipulate RGB values to create a new color.

However, knowing the HSL values doesn’t do us much good because Java only knows how to work with RGB Color objects. So I created the HSLColor class to support conversions between the two color spaces. It also provides methods that enable easy manipulation of the HSL values while returning a Color object that can be used by Java Components. The easiest way to create a HSLColor object is to provide it with a Color object. Although if you know the HSL values you want to use you can create an HSLColor object directly. Once this is done you can manipulate the HSL values using any of the following methods:

  • adjustHue – specify the degree of a color
  • adjustLuminance – specify an absolute percentage of luminance
  • adjustSaturation – specify an absolute percentage of saturation
  • adjustShade – adjusting the shade will give you a darker color. You control how much darker the color should be by specifying the adjustmnt percent.
  • adjustTone – adjusting the tone will give you a lighter color. You control how much lighter the color should be by specifying the adjustment percent.
  • getComplementary – a complementary color differs from its base color by 180 degrees.

The HSLColor class might come in handy when you want to use a color theme for your component. Maybe you want to have a light background and dark foreground (or vice versa). So instead of just using white and black you can use a different shade and tone of a specific color. Something like:

HSLColor base = new HSLColor( Color.RED );
component.setBackground( base.adjustTone(30) );
component.setForeground( base.adjustShade(30) );

Now, whatever base color you choose the background will be 30% lighter and the foreground 30% darker. The following image gives an example of how the tone changes for various percentages.

HSL-Color

Check out the Webstart demo so you can click on the other tabs to get an idea of how the colors change as the different HSL values are adjusted. Hopefully this will make it easier for you to decide on a color theme. You may be able to choose a theme simply by specifying a base color and then use the HSLColor class to adjust the HSL values for other related colors.

Get The Code

HSLColor.java
HSLColorDemo.java (Demo Code)
RelativeLayout.java (needed for demo)

Related Reading

Conversion From RGB to HSL Overview

23 Responses to “HSL Color”

  1. Tim said

    Brilliant! Just what I was looking for!

  2. Corey said

    This is just great! Thanks for sharing! I have been working at trying to do the same thing through guesswork using RGB and it was such a pain. HSL was the way to go for sure!

  3. Snow White said

    very awesome. I was about to write something like this and I googled for some formula info and came across this :D

  4. Peter said

    Thank you! This code is such a great help.

  5. Bigt24 said

    Thank you!

  6. meh said

    This is just what I was looking for. Thank You!

  7. new HslColor(new HslColor(48.0,100.0, 50.0).adjustHue(60.0)).getSaturation() returns 100.0000002

    And after this any adjusting throws an error Illegal argument saturation out of range. Can u help me??

    new HslColor(new HslColor(48.0,100.0, 50.0).adjustHue(180.0)).getSaturation() returns 100.0000002

    • Rob Camick said

      Thanks. It looks like the Color object was being created with a Red value of 1.0000001 which was causing a problem when using the Color object to create a new HSLColor object. I added a check at the bottom of the toRGB() method:

      float r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f)));
      float g = Math.max(0, HueToRGB(p, q, h));
      float b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f)));
      
      r = Math.min(r, 1.0f); // added
      g = Math.min(g, 1.0f); //added
      b = Math.min(b, 1.0f); //added
      
      return new Color(r, g, b, alpha);
      

      Let me know if you have further problems. I’ll update the source in a couple of days.

  8. Hi, Great, just what I was looking for. Err. I have added a slightly modified version. Just spelling mistakes in the javadoc stuff mainly. A few @paran -> @param and I added a couple of missing @returns.

  9. I was looking for just exactly this. Thanks!

  10. Anonymous said

    Thanks alot!
    You didn’t mention a license in the header. Is it ok to be used in a commercial context?

  11. Frederik Heick said

    I have found some issues, mostly I think it is regarding float rounding issues.
    I just want to mention it. Mayby there is some form of solution for this.

    The following java code:
    ————————
    float h=45;
    float s=88;
    float l=99;
    HSLColor hslColor1 = new HSLColor(h,s,l);
    System.out.println(“hslColor1 – RGB:”+hslColor1.getRGB());
    System.out.println(“hslColor1 – HSL:”+hslColor1.toString());
    System.out.println(“”);
    //
    HSLColor hslColor2 = new HSLColor(hslColor1.getRGB());
    System.out.println(“hslColor2 – RGB:”+hslColor2.getRGB());
    System.out.println(“hslColor2 – HSL:”+hslColor2.toString());
    System.out.println(“”);

    Color color1 = new Color(255,254,250);
    HSLColor hslColor3 = new HSLColor(color1);
    System.out.println(“hslColor3 – RGB:”+hslColor3.getRGB());
    System.out.println(“hslColor3 – HSL:”+hslColor3.toString());
    System.out.println(“”);
    —————————-

    Yields the following result:
    —————————-
    hslColor1 – RGB:java.awt.Color[r=255,g=254,b=250]
    hslColor1 – HSL:HSLColor[h=45.0,s=88.0,l=99.0,alpha=1.0]

    hslColor2 – RGB:java.awt.Color[r=255,g=254,b=250]
    hslColor2 – HSL:HSLColor[h=45.00009,s=88.000046,l=99.0,alpha=1.0]

    hslColor3 – RGB:java.awt.Color[r=255,g=254,b=250]
    hslColor3 – HSL:HSLColor[h=48.0,s=100.0,l=99.01961,alpha=1.0]
    —————————-

    So the Color([r=255,g=254,b=250])
    can give different HSL results dependent on rounding issues.

    • Rob Camick said

      When I try a couple of converters I found online (http://serennu.com/colour/hsltorgb.php, http://www.workwithcolor.com/color-converter-01.htm) they seem to agree with the conversions for hslColor1/3. So the question is why is the hslColor2 different?

      This relates to how the Color object is created. You can create an Color object using “int” RGB values or “float” RGB values. My code is using the “float” RGB values because all the calculations use floats, so I assume this to be the most accurate since no rounding is done.

      If you want the hslColor2 conversion to match the hslColor3 conversion then you can change the toRGB() method to use:

      //return new Color(r, g, b, alpha);
      return new Color((int)(r*255+0.5), (int)(g*255+0.5), (int)(b*255+0.5), (int)(alpha*255+0.5));

      The above code is actually the code the Color class uses when creating a Color object with float values. Now the returned Color object will be created with int values so the hsl conversion will match.

  12. Cole Henrich said

    AMAZING THANK YOU SO MUCH!!!!!!!!!!

  13. Anonymous said

    Your code should be a built-in java library!

Leave a reply to Rob Camick Cancel reply