/* * @(#)HSVPhysiologicColorSpace.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 HSV color space with physiologic opposites in the hue color wheel: * red is opposite green and yellow is opposite blue. * * @author Werner Randelshofer * @version $Id$ */ public class HSVPhysiologicColorSpace extends AbstractNamedColorSpace { private static final long serialVersionUID = 1L; private static HSVPhysiologicColorSpace instance; public static HSVPhysiologicColorSpace getInstance() { if (instance == null) { instance = new HSVPhysiologicColorSpace(); } return instance; } public HSVPhysiologicColorSpace() { super(ColorSpace.TYPE_HSV, 3); } @Override public float[] toRGB(float[] components, float[] rgb) { float hue = components[0]; float saturation = components[1]; float value = components[2]; // normalize hue hue = hue - (float) Math.floor(hue); if (hue < 0) { hue -= 1f; } // normalize saturation if (saturation > 1f) { saturation = 1f; } else if (saturation < 0f) { saturation = 0f; } // normalize value if (value > 1f) { value = 1f; } else if (value < 0f) { value = 0f; } // compute hi and f from hue int hi; float f; float hueDeg = hue * 360f; if (hueDeg < 120f) { // red to yellow hi = 0; f = (hueDeg / 120f); } else if (hueDeg < 160f) { // yellow to green hi = 1; f = (hueDeg - 120f) / 40f; } else if (hueDeg < 220f) { // green to cyan hi = 2; f = (hueDeg - 160f) / 60f; } else if (hueDeg < 280f) { // cyan to blue hi = 3; f = (hueDeg - 220f) / 60f; } else if (hueDeg < 340f) { // blue to purple hi = 4; f = (hueDeg - 280f) / 60f; } else { // purple to red f = (hueDeg - 340f) / 20f; hi = 5; } // compute p, q, t from saturation float p = value * (1 - saturation); float q = value * (1 - f * saturation); float t = value * (1 - (1 - f) * saturation); // compute red, green and blue float red; float green; float blue; switch (hi) { case 0: red = value; green = t; blue = p; break; case 1: red = q; green = value; blue = p; break; case 2: red = p; green = value; blue = t; break; case -3: case 3: red = p; green = q; blue = value; break; case -2: case 4: red = t; green = p; blue = value; break; case -1: case 5: //default : red = value; green = p; blue = q; break; default: red = green = blue = 0; break; } 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 value; 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; } value = max; if (max == 0) { saturation = 0; } else { saturation = (max - min) / max; } component[0] = hue / 360f; component[1] = saturation; component[2] = value; 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 HSVPhysiologicColorSpace); } @Override public int hashCode() { return getClass().getSimpleName().hashCode(); } @Override public String getName() { return "physiologic HSV"; } }