/* * @(#)HSLColorSpace.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; import static java.lang.Math.*; /** * A HSL color space with additive complements in the hue color wheel: red is * opposite cyan, magenta is opposite green, blue is opposite yellow. * * @author Werner Randelshofer * @version $Id$ */ public class HSLColorSpace extends AbstractNamedColorSpace { private static final long serialVersionUID = 1L; private static HSLColorSpace instance; public static HSLColorSpace getInstance() { if (instance == null) { instance = new HSLColorSpace(); } return instance; } public HSLColorSpace() { 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]; // 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; // normalize hue to -1..+1 float hk = hue - (float) Math.floor(hue); // / 360f; // compute red, green and blue float red = hk + 1f / 3f; float green = hk; float blue = hk - 1f / 3f; // normalize rgb values if (red < 0) { red = red + 1f; } else if (red > 1) { red = red - 1f; } if (green < 0) { green = green + 1f; } else if (green > 1) { green = green - 1f; } if (blue < 0) { blue = blue + 1f; } else if (blue > 1) { blue = blue - 1f; } // adjust rgb values 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]=clamp(red,0,1); rgb[1]=clamp(green,0,1); rgb[2]=clamp(blue,0,1); 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) { hue = 60f * (g - b) / (max - min); } else if (max == r && g < b) { hue = 60f * (g - b) / (max - min) + 360f; } else if (max == g) { hue = 60f * (b - r) / (max - min) + 120f; } else /*if (max == b)*/ { hue = 60f * (r - g) / (max - min) + 240f; } 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 HSLColorSpace); } @Override public int hashCode() { return getClass().getSimpleName().hashCode(); } @Override public String getName() { return "HSL"; } private static float clamp(float v, float minv, float maxv) { return max(minv,min(v,maxv)); } }