001/*
002 * $Id: ColorUtilities.java 1496 2006-10-22 03:26:24Z gfx $
003 *
004 * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
005 *
006 * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
007 * Santa Clara, California 95054, U.S.A. All rights reserved.
008 *
009 * Copyright (c) 2006 Romain Guy <romain.guy@mac.com>
010 * All rights reserved.
011 *
012 * Redistribution and use in source and binary forms, with or without
013 * modification, are permitted provided that the following conditions
014 * are met:
015 * 1. Redistributions of source code must retain the above copyright
016 *    notice, this list of conditions and the following disclaimer.
017 * 2. Redistributions in binary form must reproduce the above copyright
018 *    notice, this list of conditions and the following disclaimer in the
019 *    documentation and/or other materials provided with the distribution.
020 * 3. The name of the author may not be used to endorse or promote products
021 *    derived from this software without specific prior written permission.
022 *
023 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
024 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
025 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
026 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
028 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
032 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033 */
034
035package org.jdesktop.swingx.graphics;
036
037import java.awt.Color;
038
039/**
040 * <p><code>ColorUtilities</code> contains a set of tools to perform
041 * common color operations easily.</p>
042 *
043 * @author Romain Guy <romain.guy@mac.com>
044 */
045public class ColorUtilities {
046    private ColorUtilities() {
047    }
048
049    /**
050     * <p>Returns the HSL (Hue/Saturation/Luminance) equivalent of a given
051     * RGB color. All three HSL components are between 0.0 and 1.0.</p>
052     *
053     * @param color the RGB color to convert
054     * @return a new array of 3 floats corresponding to the HSL components
055     */
056    public static float[] RGBtoHSL(Color color) {
057        return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), null);
058    }
059
060    /**
061     * <p>Returns the HSL (Hue/Saturation/Luminance) equivalent of a given
062     * RGB color. All three HSL components are between 0.0 and 1.0.</p>
063     *
064     * @param color the RGB color to convert
065     * @param hsl a pre-allocated array of floats; can be null
066     * @return <code>hsl</code> if non-null, a new array of 3 floats otherwise
067     * @throws IllegalArgumentException if <code>hsl</code> has a length lower
068     *   than 3
069     */
070    public static float[] RGBtoHSL(Color color, float[] hsl) {
071        return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), hsl);
072    }
073
074    /**
075     * <p>Returns the HSL (Hue/Saturation/Luminance) equivalent of a given
076     * RGB color. All three HSL components are between 0.0 and 1.0.</p>
077     *
078     * @param r the red component, between 0 and 255
079     * @param g the green component, between 0 and 255
080     * @param b the blue component, between 0 and 255
081     * @return a new array of 3 floats corresponding to the HSL components
082     */
083    public static float[] RGBtoHSL(int r, int g, int b) {
084        return RGBtoHSL(r, g, b, null);
085    }
086
087    /**
088     * <p>Returns the HSL (Hue/Saturation/Luminance) equivalent of a given
089     * RGB color. All three HSL components are floats between 0.0 and 1.0.</p>
090     *
091     * @param r the red component, between 0 and 255
092     * @param g the green component, between 0 and 255
093     * @param b the blue component, between 0 and 255
094     * @param hsl a pre-allocated array of floats; can be null
095     * @return <code>hsl</code> if non-null, a new array of 3 floats otherwise
096     * @throws IllegalArgumentException if <code>hsl</code> has a length lower
097     *   than 3
098     */
099    public static float[] RGBtoHSL(int r, int g, int b, float[] hsl) {
100        if (hsl == null) {
101            hsl = new float[3];
102        } else if (hsl.length < 3) {
103            throw new IllegalArgumentException("hsl array must have a length of" +
104                                               " at least 3");
105        }
106
107        if (r < 0) r = 0;
108        else if (r > 255) r = 255;
109        if (g < 0) g = 0;
110        else if (g > 255) g = 255;
111        if (b < 0) b = 0;
112        else if (b > 255) b = 255;
113
114        float var_R = (r / 255f);
115        float var_G = (g / 255f);
116        float var_B = (b / 255f);
117
118        float var_Min;
119        float var_Max;
120        float del_Max;
121
122        if (var_R > var_G) {
123            var_Min = var_G;
124            var_Max = var_R;
125        } else {
126            var_Min = var_R;
127            var_Max = var_G;
128        }
129        if (var_B > var_Max) {
130            var_Max = var_B;
131        }
132        if (var_B < var_Min) {
133            var_Min = var_B;
134        }
135
136        del_Max = var_Max - var_Min;
137
138        float H, S, L;
139        L = (var_Max + var_Min) / 2f;
140
141        if (del_Max - 0.01f <= 0.0f) {
142            H = 0;
143            S = 0;
144        } else {
145            if (L < 0.5f) {
146                S = del_Max / (var_Max + var_Min);
147            } else {
148                S = del_Max / (2 - var_Max - var_Min);
149            }
150
151            float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max;
152            float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max;
153            float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max;
154
155            if (var_R == var_Max) {
156                H = del_B - del_G;
157            } else if (var_G == var_Max) {
158                H = (1 / 3f) + del_R - del_B;
159            } else {
160                H = (2 / 3f) + del_G - del_R;
161            }
162            if (H < 0) {
163                H += 1;
164            }
165            if (H > 1) {
166                H -= 1;
167            }
168        }
169
170        hsl[0] = H;
171        hsl[1] = S;
172        hsl[2] = L;
173
174        return hsl;
175    }
176
177    /**
178     * <p>Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance)
179     * color.</p>
180     *
181     * @param h the hue component, between 0.0 and 1.0
182     * @param s the saturation component, between 0.0 and 1.0
183     * @param l the luminance component, between 0.0 and 1.0
184     * @return a new <code>Color</code> object equivalent to the HSL components
185     */
186    public static Color HSLtoRGB(float h, float s, float l) {
187        int[] rgb = HSLtoRGB(h, s, l, null);
188        return new Color(rgb[0], rgb[1], rgb[2]);
189    }
190
191    /**
192     * <p>Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance)
193     * color. All three RGB components are integers between 0 and 255.</p>
194     *
195     * @param h the hue component, between 0.0 and 1.0
196     * @param s the saturation component, between 0.0 and 1.0
197     * @param l the luminance component, between 0.0 and 1.0
198     * @param rgb a pre-allocated array of ints; can be null
199     * @return <code>rgb</code> if non-null, a new array of 3 ints otherwise
200     * @throws IllegalArgumentException if <code>rgb</code> has a length lower
201     *   than 3
202     */
203    public static int[] HSLtoRGB(float h, float s, float l, int[] rgb) {
204        if (rgb == null) {
205            rgb = new int[3];
206        } else if (rgb.length < 3) {
207            throw new IllegalArgumentException("rgb array must have a length of" +
208                                               " at least 3");
209        }
210
211        if (h < 0) h = 0.0f;
212        else if (h > 1.0f) h = 1.0f;
213        if (s < 0) s = 0.0f;
214        else if (s > 1.0f) s = 1.0f;
215        if (l < 0) l = 0.0f;
216        else if (l > 1.0f) l = 1.0f;
217
218        int R, G, B;
219
220        if (s - 0.01f <= 0.0f) {
221            R = (int) (l * 255.0f);
222            G = (int) (l * 255.0f);
223            B = (int) (l * 255.0f);
224        } else {
225            float var_1, var_2;
226            if (l < 0.5f) {
227                var_2 = l * (1 + s);
228            } else {
229                var_2 = (l + s) - (s * l);
230            }
231            var_1 = 2 * l - var_2;
232
233            R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f)));
234            G = (int) (255.0f * hue2RGB(var_1, var_2, h));
235            B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f)));
236        }
237
238        rgb[0] = R;
239        rgb[1] = G;
240        rgb[2] = B;
241
242        return rgb;
243    }
244
245    private static float hue2RGB(float v1, float v2, float vH) {
246        if (vH < 0.0f) {
247            vH += 1.0f;
248        }
249        if (vH > 1.0f) {
250            vH -= 1.0f;
251        }
252        if ((6.0f * vH) < 1.0f) {
253            return (v1 + (v2 - v1) * 6.0f * vH);
254        }
255        if ((2.0f * vH) < 1.0f) {
256            return (v2);
257        }
258        if ((3.0f * vH) < 2.0f) {
259            return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
260        }
261        return (v1);
262    }
263}