Tuesday, January 10, 2012

Convert RGB to HSL and vice-versa in Java

I wanted to add colored labels to my Android application and I wanted to make it such that the users need only to select one color, the background color, and the foreground color would be the same color, only lighter or darker.

I thought I needed a RGB to HSL (Hue, Saturation, Lightness) converter, but Android SDK only provides RGB to HSV value, which has some similarity but different.

Based on the above Wikipedia article, I wrote the Java code to convert between RGB and HSL. Hopefully this can be useful to your project.
  • rgb is an int (with alpha ignored) 0xrrggbb
  • hsl is an array of 3 floats:
    • [0] is hue, 0..360
    • [1] is saturation, 0..1
    • [2] is lightness, 0..1
public static void rgbToHsl(int rgb, float[] hsl) {
 float r = ((0x00ff0000 & rgb) >> 16) / 255.f;
 float g = ((0x0000ff00 & rgb) >> 8) / 255.f;
 float b = ((0x000000ff & rgb)) / 255.f;
 float max = Math.max(Math.max(r, g), b);
 float min = Math.min(Math.min(r, g), b);
 float c = max - min;
 
 float h_ = 0.f;
 if (c == 0) {
  h_ = 0;
 } else if (max == r) {
  h_ = (float)(g-b) / c;
  if (h_ < 0) h_ += 6.f;
 } else if (max == g) {
  h_ = (float)(b-r) / c + 2.f;
 } else if (max == b) {
  h_ = (float)(r-g) / c + 4.f;
 }
 float h = 60.f * h_;
 
 float l = (max + min) * 0.5f;
 
 float s;
 if (c == 0) {
  s = 0.f;
 } else {
  s = c / (1 - Math.abs(2.f * l - 1.f));
 }
 
 hsl[0] = h;
 hsl[1] = s;
 hsl[2] = l;
}

public static int hslToRgb(float[] hsl) {
 float h = hsl[0];
 float s = hsl[1];
 float l = hsl[2];
 
 float c = (1 - Math.abs(2.f * l - 1.f)) * s;
 float h_ = h / 60.f;
 float h_mod2 = h_;
 if (h_mod2 >= 4.f) h_mod2 -= 4.f;
 else if (h_mod2 >= 2.f) h_mod2 -= 2.f;
 
 float x = c * (1 - Math.abs(h_mod2 - 1));
 float r_, g_, b_;
 if (h_ < 1)      { r_ = c; g_ = x; b_ = 0; }
 else if (h_ < 2) { r_ = x; g_ = c; b_ = 0; }
 else if (h_ < 3) { r_ = 0; g_ = c; b_ = x; }
 else if (h_ < 4) { r_ = 0; g_ = x; b_ = c; }
 else if (h_ < 5) { r_ = x; g_ = 0; b_ = c; }
 else             { r_ = c; g_ = 0; b_ = x; }
 
 float m = l - (0.5f * c); 
 int r = (int)((r_ + m) * (255.f) + 0.5f);
 int g = (int)((g_ + m) * (255.f) + 0.5f);
 int b = (int)((b_ + m) * (255.f) + 0.5f);
 return r << 16 | g << 8 | b;
}

4 comments:

  1. > if (h_ < 0) h_ += 6.f;

    That line is only in the first case of rgbToHsl - is it supposed to be like that, or should it be for every case?

    ReplyDelete
  2. Only the first case. Because when max==r, h_ can range between -1.0 to +1.0, but we want to normalize (-1.0 to 0.0) to (+5.0 to +6.0).

    ReplyDelete
  3. This code do not work for me (Eclipse Juno, JDK 1.7). I changed the lines
    float r = ((0x00ff0000 & rgb) >> 16) / 255.f;
    float g = ((0x0000ff00 & rgb) >> 8) / 255.f;
    loat b = ((0x000000ff & rgb)) / 255.f;

    with :
    int ir = ( rgb >> 24 ) & 0xFF;
    int ig = ( rgb >> 16 ) & 0xFF;
    int ib = ( rgb >> 8 ) & 0xFF;
    float r = (float)ir / 255.f;
    float g = (float)ig / 255.f;
    float b = (float)ib / 255.f;

    ReplyDelete
  4. code does not work either
    the / 60.f makes all color the same

    ReplyDelete