001: /*
002: * @(#)ColorUtils.java 9/19/2006
003: *
004: * Copyright 2002 - 2006 JIDE Software Inc. All rights reserved.
005: */
006:
007: package com.jidesoft.utils;
008:
009: import javax.swing.plaf.ColorUIResource;
010: import java.awt.*;
011:
012: /**
013: * Several useful methods for Color.
014: */
015: public class ColorUtils {
016:
017: /**
018: * Gets a derived color from an existing color. The derived color is either lighter
019: * or darker version of the given color with the same hue.
020: *
021: * @param color the given color.
022: * @param ratio the ratio.
023: * @return the derived color.
024: */
025: public static Color getDerivedColor(Color color, float ratio) {
026: if (color != null) {
027: float[] hsl = RGBtoHSL(color);
028: if (hsl[2] < 0.4) {
029: hsl[2] = 0.4f;
030: }
031: if (ratio > 0.5) {
032: hsl[2] += (1f - hsl[2]) * 2 * (ratio - 0.5);
033: } else {
034: hsl[2] -= hsl[2] * 2 * (0.5 - ratio);
035: }
036: int colorRGB = HSLtoRGB(hsl);
037: return new ColorUIResource(colorRGB);
038: } else {
039: return null;
040: }
041: }
042:
043: /**
044: * Converts a color from RBG to HSL color space.
045: *
046: * @param colorRGB
047: * @return color space in HSL.
048: */
049: public static float[] RGBtoHSL(Color colorRGB) {
050: float r, g, b, h, s, l; //this function works with floats between 0 and 1
051: r = colorRGB.getRed() / 256.0f;
052: g = colorRGB.getGreen() / 256.0f;
053: b = colorRGB.getBlue() / 256.0f;
054:
055: // Then, minColor and maxColor are defined. Mincolor is the value of the color component with
056: // the smallest value, while maxColor is the value of the color component with the largest value.
057: // These two variables are needed because the Lightness is defined as (minColor + maxColor) / 2.
058:
059: float maxColor = Math.max(r, Math.max(g, b));
060: float minColor = Math.min(r, Math.min(g, b));
061:
062: // If minColor equals maxColor, we know that R=G=B and thus the color is a shade of gray.
063: // This is a trivial case, hue can be set to anything, saturation has to be set to 0 because
064: // only then it's a shade of gray, and lightness is set to R=G=B, the shade of the gray.
065:
066: //R == G == B, so it's a shade of gray
067: if (r == g && g == b) {
068: h = 0.0f; //it doesn't matter what value it has
069: s = 0.0f;
070: l = r; //doesn't matter if you pick r, g, or b
071: }
072:
073: // If minColor is not equal to maxColor, we have a real color instead of a shade of gray, so more calculations are needed:
074:
075: // Lightness (l) is now set to it's definition of (minColor + maxColor)/2.
076: // Saturation (s) is then calculated with a different formula depending if light is in the first half of the second half. This is because the HSL model can be represented as a double cone, the first cone has a black tip and corresponds to the first half of lightness values, the second cone has a white tip and contains the second half of lightness values.
077: // Hue (h) is calculated with a different formula depending on which of the 3 color components is the dominating one, and then normalized to a number between 0 and 1.
078:
079: else {
080: l = (minColor + maxColor) / 2;
081:
082: if (l < 0.5)
083: s = (maxColor - minColor) / (maxColor + minColor);
084: else
085: s = (maxColor - minColor)
086: / (2.0f - maxColor - minColor);
087:
088: if (r == maxColor)
089: h = (g - b) / (maxColor - minColor);
090: else if (g == maxColor)
091: h = 2.0f + (b - r) / (maxColor - minColor);
092: else
093: h = 4.0f + (r - g) / (maxColor - minColor);
094:
095: h /= 6; //to bring it to a number between 0 and 1
096: if (h < 0)
097: h++;
098: }
099:
100: // Finally, H, S and L are calculated out of h, s and l as integers between 0 and 255 and "returned"
101: // as the result. Returned, because H, S and L were passed by reference to the function.
102:
103: float[] hsl = new float[3];
104: hsl[0] = h;
105: hsl[1] = s;
106: hsl[2] = l;
107: return hsl;
108: }
109:
110: /**
111: * Converts from HSL color space to RGB color.
112: *
113: * @param hsl
114: * @return the RGB color.
115: */
116: public static int HSLtoRGB(float[] hsl) {
117: float r, g, b, h, s, l; //this function works with floats between 0 and 1
118: float temp1, temp2, tempr, tempg, tempb;
119: h = hsl[0];
120: s = hsl[1];
121: l = hsl[2];
122:
123: // Then follows a trivial case: if the saturation is 0, the color will be a grayscale color,
124: // and the calculation is then very simple: r, g and b are all set to the lightness.
125:
126: //If saturation is 0, the color is a shade of gray
127: if (s == 0) {
128: r = g = b = l;
129: }
130: // If the saturation is higher than 0, more calculations are needed again.
131: // red, green and blue are calculated with the formulas defined in the code.
132: // If saturation > 0, more complex calculations are needed
133: else {
134: //Set the temporary values
135: if (l < 0.5)
136: temp2 = l * (1 + s);
137: else
138: temp2 = (l + s) - (l * s);
139: temp1 = 2 * l - temp2;
140: tempr = h + 1.0f / 3.0f;
141: if (tempr > 1)
142: tempr--;
143: tempg = h;
144: tempb = h - 1.0f / 3.0f;
145: if (tempb < 0)
146: tempb++;
147:
148: //Red
149: if (tempr < 1.0 / 6.0)
150: r = temp1 + (temp2 - temp1) * 6.0f * tempr;
151: else if (tempr < 0.5)
152: r = temp2;
153: else if (tempr < 2.0 / 3.0)
154: r = temp1 + (temp2 - temp1) * ((2.0f / 3.0f) - tempr)
155: * 6.0f;
156: else
157: r = temp1;
158:
159: //Green
160: if (tempg < 1.0 / 6.0)
161: g = temp1 + (temp2 - temp1) * 6.0f * tempg;
162: else if (tempg < 0.5)
163: g = temp2;
164: else if (tempg < 2.0 / 3.0)
165: g = temp1 + (temp2 - temp1) * ((2.0f / 3.0f) - tempg)
166: * 6.0f;
167: else
168: g = temp1;
169:
170: //Blue
171: if (tempb < 1.0 / 6.0)
172: b = temp1 + (temp2 - temp1) * 6.0f * tempb;
173: else if (tempb < 0.5)
174: b = temp2;
175: else if (tempb < 2.0 / 3.0)
176: b = temp1 + (temp2 - temp1) * ((2.0f / 3.0f) - tempb)
177: * 6.0f;
178: else
179: b = temp1;
180: }
181:
182: // And finally, the results are returned as integers between 0 and 255.
183:
184: int result = 0;
185: result += ((int) (r * 255) & 0xFF) << 16;
186: result += ((int) (g * 255) & 0xFF) << 8;
187: result += ((int) (b * 255) & 0xFF);
188:
189: return result;
190: }
191:
192: final static float OFFSET_180 = 180f;
193: final static float OFFSET_100 = 100f;
194:
195: public static int[] calculateDifferent(float[] from, float[] to) {
196: int[] diff = new int[3];
197: diff[0] = floatToInteger(from[0], to[0], OFFSET_180, true);
198: diff[1] = floatToInteger(from[1], to[1], OFFSET_100, false);
199: diff[2] = floatToInteger(from[2], to[2], OFFSET_100, false);
200: return diff;
201: }
202:
203: public static float[] applyDifference(float[] from, int[] diff) {
204: float[] to = new float[3];
205: to[0] = integerToFloat(from[0], diff[0], OFFSET_180, true);
206: to[1] = integerToFloat(from[1], diff[1], OFFSET_100, false);
207: to[2] = integerToFloat(from[2], diff[2], OFFSET_100, false);
208: return to;
209: }
210:
211: private static int floatToInteger(float f, float f2, float offset,
212: boolean rotate) {
213: if (rotate) {
214: int i = (int) ((f2 - f) * 2 * offset);
215: if (i > offset) {
216: return i - (int) (2 * offset);
217: } else if (i < -offset) {
218: return i + (int) (2 * offset);
219: } else {
220: return i;
221: }
222: } else {
223: if (f != 0.0f) {
224: return (int) ((f2 - f) * offset / f);
225: } else {
226: return (int) ((f2 - f) * offset);
227: }
228: }
229: }
230:
231: private static float integerToFloat(float f, int i, float offset,
232: boolean rotate) {
233: if (rotate) {
234: float v = f + i / (2 * offset);
235: if (v < 0.0f) {
236: return v + 1.0f;
237: } else if (v > 1.0f) {
238: return v - 1.0f;
239: } else {
240: return v;
241: }
242: } else {
243: if (i > 0) {
244: return f + (1.0f - f) * i / offset;
245: } else {
246: return f + f * i / offset;
247: }
248: }
249: }
250:
251: /**
252: * Simply calls new Color(color, hasalpha) for each color in colors and returns
253: * all of them.
254: *
255: * @param hasalpha
256: * @param colors
257: * @return
258: */
259: public static Color[] toColors(boolean hasalpha, int... colors) {
260: Color[] result = new Color[colors.length];
261: for (int i = 0; i < colors.length; i++) {
262: result[i] = new Color(colors[i], hasalpha);
263: }
264: return result;
265: }
266: }
|