/* * @(#)HSLPhysiologicColorSpace.java * * Copyright (c) 2010 The authors and contributors of JHotDraw. * * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. */ package org.jhotdraw.color; import java.awt.color.ColorSpace; /** * A HSL color space with physiologic opposites in the hue color wheel: red is * opposite green and yellow is opposite blue. * * @author Werner Randelshofer * @version $Id: HSLPhysiologicColorSpace.java 717 2010-11-21 12:30:57Z rawcoder * $ */ public class HSLPhysiologicColorSpace extends AbstractNamedColorSpace { private static final long serialVersionUID = 1L; private static HSLPhysiologicColorSpace instance; public static HSLPhysiologicColorSpace getInstance() { if (instance == null) { instance = new HSLPhysiologicColorSpace(); } return instance; } public HSLPhysiologicColorSpace() { super(ColorSpace.TYPE_HSV, 3); } @Override public float[] toRGB(float[] components, float[] rgb) { float hue = components[0]; float saturation = components[1]; float lightness = components[2]; // normalize hue hue = hue - (float) Math.floor(hue); if (hue < 0) { hue = 1f + hue; } // normalize saturation if (saturation > 1f) { saturation = 1f; } else if (saturation < 0f) { saturation = 0f; } // normalize value if (lightness > 1f) { lightness = 1f; } else if (lightness < 0f) { lightness = 0f; } float hueDeg = hue * 360f; if (hueDeg < 0) { hueDeg += 360f; } // compute hi and f from hue // float f; float hk = hue - (float) Math.floor(hue); // / 360f; if (hueDeg < 120f) { // red to yellow hk /= 2f; } else if (hueDeg < 160f) { // yellow to green hk = (hk - 120f / 360f) * 3f / 2f + 60f / 360f; } else if (hueDeg < 220f) { // green to cyan hk = (hk - 160f / 360f) + 120f / 360f; } else if (hueDeg < 280f) { // cyan to blue hk = (hk - 220f / 360f) + 180f / 360f; } else if (hueDeg < 340f) { // blue to purple hk = (hk - 280f / 360f) + 240f / 360f; } else { // purple to red hk = (hk - 340f / 360f) * 3f + 300f / 360f; } // compute p and q from saturation and lightness float q; if (lightness < 0.5f) { q = lightness * (1f + saturation); } else { q = lightness + saturation - (lightness * saturation); } float p = 2f * lightness - q; // compute red, green and blue float red = hk + 1f / 3f; float green = hk; float blue = hk - 1f / 3f; if (red < 0) { red = red + 1f; } if (green < 0) { green = green + 1f; } if (blue < 0) { blue = blue + 1f; } if (red > 1) { red = red - 1f; } if (green > 1) { green = green - 1f; } if (blue > 1) { blue = blue - 1f; } if (red < 1f / 6f) { red = p + ((q - p) * 6 * red); } else if (red < 0.5f) { red = q; } else if (red < 2f / 3f) { red = p + ((q - p) * 6 * (2f / 3f - red)); } else { red = p; } if (green < 1f / 6f) { green = p + ((q - p) * 6 * green); } else if (green < 0.5f) { green = q; } else if (green < 2f / 3f) { green = p + ((q - p) * 6 * (2f / 3f - green)); } else { green = p; } if (blue < 1f / 6f) { blue = p + ((q - p) * 6 * blue); } else if (blue < 0.5f) { blue = q; } else if (blue < 2f / 3f) { blue = p + ((q - p) * 6 * (2f / 3f - blue)); } else { blue = p; } rgb[0] = red; rgb[1] = green; rgb[2] = blue; return rgb; } @Override public float[] fromRGB(float[] rgbvalue, float[] component) { float r = rgbvalue[0]; float g = rgbvalue[1]; float b = rgbvalue[2]; float max = Math.max(Math.max(r, g), b); float min = Math.min(Math.min(r, g), b); float hue; float saturation; float luminance; if (max == min) { hue = 0; } else if (max == r && g >= b) { // red to yellow hue = 120f * (g - b) / (max - min); } else if (max == r) { // red to purple hue = 20f * (g - b) / (max - min) + 360f; } else if (max == g && r >= b) { // yellow to green hue = 40f * (b - r) / (max - min) + 120f + 40f; } else if (max == g) { // green to cyan hue = 60f * (b - r) / (max - min) + 120f + 40f; } else if (g >= r) { // cyan to blue hue = 60f * (r - g) / (max - min) + 240f + 40f; } else { // blue to purple hue = 60f * (r - g) / (max - min) + 240f + 40f; } luminance = (max + min) / 2f; if (max == min) { saturation = 0; } else if (luminance <= 0.5f) { saturation = (max - min) / (max + min); } else /* if (lightness > 0.5f)*/ { saturation = (max - min) / (2 - (max + min)); } component[0] = hue / 360f; component[1] = saturation; component[2] = luminance; return component; } @Override public String getName(int idx) { switch (idx) { case 0: return "Hue"; case 1: return "Saturation"; case 2: return "Lightness"; default: throw new IllegalArgumentException("index must be between 0 and 2:" + idx); } } @Override public float getMaxValue(int component) { return 1f; } @Override public float getMinValue(int component) { return 0f; } @Override public boolean equals(Object o) { return (o instanceof HSLPhysiologicColorSpace); } @Override public int hashCode() { return getClass().getSimpleName().hashCode(); } @Override public String getName() { return "physiologic HSL"; } }