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;
}

Friday, January 6, 2012

Add "screenSize" flag to android:configChanges when targetting API level 13 (Android 3.2) or newer

If you're setting "keyboardHidden|orientation" to android:configChanges in order to prevent activity from restarting itself when the device is rotated, you might be surprised when you find your activity restarts itself when running on Android 3.2 or newer.

The reason is, starting from Android 3.2 (API Level 13), rotated device will also trigger "screenSize" change. From attrs_manifest.xml:



        <!-- The current available screen size has changed.  If applications don't
             target at least {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}
             then the activity will always handle this itself (the change
             will not result in a restart).  This represents a change in the
             currently available size, so will change when the user switches
             between landscape and portrait. -->
        <flag name="screenSize" value="0x0400" />


It means, if your android:targetSdkVersion is 13 or more, your application will receive "screenSize" config change. Therefore, to prevent your activity from restarting, add "screenSize". E.g.

    android:configChanges="keyboardHidden|orientation|screenSize"

Hope this helps you.