1059178a8c7cc80848e5594a9287be91bd924831aChris Banes/* 2711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Copyright 2015 The Android Open Source Project 3059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 4059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5059178a8c7cc80848e5594a9287be91bd924831aChris Banes * you may not use this file except in compliance with the License. 6059178a8c7cc80848e5594a9287be91bd924831aChris Banes * You may obtain a copy of the License at 7059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 8059178a8c7cc80848e5594a9287be91bd924831aChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 10059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Unless required by applicable law or agreed to in writing, software 11059178a8c7cc80848e5594a9287be91bd924831aChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12059178a8c7cc80848e5594a9287be91bd924831aChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13059178a8c7cc80848e5594a9287be91bd924831aChris Banes * See the License for the specific language governing permissions and 14059178a8c7cc80848e5594a9287be91bd924831aChris Banes * limitations under the License. 15059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 16059178a8c7cc80848e5594a9287be91bd924831aChris Banes 17711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banespackage android.support.v4.graphics; 18059178a8c7cc80848e5594a9287be91bd924831aChris Banes 19059178a8c7cc80848e5594a9287be91bd924831aChris Banesimport android.graphics.Color; 20059178a8c7cc80848e5594a9287be91bd924831aChris Banes 217aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes/** 22711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * A set of color-related utility methods, building upon those available in {@code Color}. 237aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes */ 24711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banespublic class ColorUtils { 25059178a8c7cc80848e5594a9287be91bd924831aChris Banes 26f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10; 27f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private static final int MIN_ALPHA_SEARCH_PRECISION = 10; 28f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 29059178a8c7cc80848e5594a9287be91bd924831aChris Banes private ColorUtils() {} 30059178a8c7cc80848e5594a9287be91bd924831aChris Banes 31059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 32f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Composite two potentially translucent colors over each other and returns the result. 33f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 34711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static int compositeColors(int foreground, int background) { 35eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int bgAlpha = Color.alpha(background); 36eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int fgAlpha = Color.alpha(foreground); 37eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int a = compositeAlpha(fgAlpha, bgAlpha); 38f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 39eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int r = compositeComponent(Color.red(foreground), fgAlpha, 40eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes Color.red(background), bgAlpha, a); 41eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int g = compositeComponent(Color.green(foreground), fgAlpha, 42eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes Color.green(background), bgAlpha, a); 43eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes int b = compositeComponent(Color.blue(foreground), fgAlpha, 44eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes Color.blue(background), bgAlpha, a); 45f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 46eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes return Color.argb(a, r, g, b); 47eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes } 48eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 49eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) { 50eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF); 51eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes } 52eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 53eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) { 54eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes if (a == 0) return 0; 55eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF); 56f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 57f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 58f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 59f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Returns the luminance of a color. 60f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * 61f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef 62f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 63711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static double calculateLuminance(int color) { 64f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes double red = Color.red(color) / 255d; 65f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4); 66f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 67f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes double green = Color.green(color) / 255d; 68f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4); 69f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 70f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes double blue = Color.blue(color) / 255d; 71f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4); 72f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 73f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue); 74f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 75f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 76f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 77711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Returns the contrast ratio between {@code foreground} and {@code background}. 78711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * {@code background} must be opaque. 79711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <p> 80711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Formula defined 81711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>. 82f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 83711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static double calculateContrast(int foreground, int background) { 84f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (Color.alpha(background) != 255) { 85f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes throw new IllegalArgumentException("background can not be translucent"); 86f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 87f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (Color.alpha(foreground) < 255) { 88f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // If the foreground is translucent, composite the foreground over the background 89f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes foreground = compositeColors(foreground, background); 90f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 91f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 92f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes final double luminance1 = calculateLuminance(foreground) + 0.05; 93f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes final double luminance2 = calculateLuminance(background) + 0.05; 94f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 95f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // Now return the lighter luminance divided by the darker luminance 96f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2); 97f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 98f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 99f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 100711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Calculates the minimum alpha value which can be applied to {@code foreground} so that would 101711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * have a contrast value of at least {@code minContrastRatio} when compared to 102711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * {@code background}. 103f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * 104711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @param foreground the foreground color. 105711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @param background the background color. Should be opaque. 106711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @param minContrastRatio the minimum contrast ratio. 107711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @return the alpha value in the range 0-255, or -1 if no value could be calculated. 108059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 109711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static int calculateMinimumAlpha(int foreground, int background, 110711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes float minContrastRatio) { 111f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (Color.alpha(background) != 255) { 112f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes throw new IllegalArgumentException("background can not be translucent"); 113f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 114f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 115f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // First lets check that a fully opaque foreground has sufficient contrast 1167aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes int testForeground = setAlphaComponent(foreground, 255); 117f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes double testRatio = calculateContrast(testForeground, background); 118f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (testRatio < minContrastRatio) { 119f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // Fully opaque foreground does not have sufficient contrast, return error 120f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return -1; 121f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 122f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 123f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // Binary search to find a value with the minimum value which provides sufficient contrast 124f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes int numIterations = 0; 125f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes int minAlpha = 0; 126f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes int maxAlpha = 255; 127f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 128f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS && 129f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) { 130f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes final int testAlpha = (minAlpha + maxAlpha) / 2; 131f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 1327aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes testForeground = setAlphaComponent(foreground, testAlpha); 133f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes testRatio = calculateContrast(testForeground, background); 134f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 135f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (testRatio < minContrastRatio) { 136f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes minAlpha = testAlpha; 137f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } else { 138f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes maxAlpha = testAlpha; 139f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 140f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 141f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes numIterations++; 142f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 143f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 144f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes // Conservatively return the max of the range of possible alphas, which is known to pass. 145f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return maxAlpha; 146059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 147059178a8c7cc80848e5594a9287be91bd924831aChris Banes 1487aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes /** 149711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Convert RGB components to HSL (hue-saturation-lightness). 1507aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * <ul> 151711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[0] is Hue [0 .. 360)</li> 152711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[1] is Saturation [0...1]</li> 153711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[2] is Lightness [0...1]</li> 1547aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * </ul> 1557aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * 1567aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @param r red component value [0..255] 1577aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @param g green component value [0..255] 1587aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @param b blue component value [0..255] 1597aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @param hsl 3 element array which holds the resulting HSL components. 1607aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes */ 161711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static void RGBToHSL(int r, int g, int b, float[] hsl) { 162059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float rf = r / 255f; 163059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float gf = g / 255f; 164059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float bf = b / 255f; 165059178a8c7cc80848e5594a9287be91bd924831aChris Banes 166059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float max = Math.max(rf, Math.max(gf, bf)); 167059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float min = Math.min(rf, Math.min(gf, bf)); 168059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float deltaMaxMin = max - min; 169059178a8c7cc80848e5594a9287be91bd924831aChris Banes 170059178a8c7cc80848e5594a9287be91bd924831aChris Banes float h, s; 171059178a8c7cc80848e5594a9287be91bd924831aChris Banes float l = (max + min) / 2f; 172059178a8c7cc80848e5594a9287be91bd924831aChris Banes 173059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (max == min) { 174059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Monochromatic 175059178a8c7cc80848e5594a9287be91bd924831aChris Banes h = s = 0f; 176059178a8c7cc80848e5594a9287be91bd924831aChris Banes } else { 177059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (max == rf) { 178059178a8c7cc80848e5594a9287be91bd924831aChris Banes h = ((gf - bf) / deltaMaxMin) % 6f; 179059178a8c7cc80848e5594a9287be91bd924831aChris Banes } else if (max == gf) { 180059178a8c7cc80848e5594a9287be91bd924831aChris Banes h = ((bf - rf) / deltaMaxMin) + 2f; 181059178a8c7cc80848e5594a9287be91bd924831aChris Banes } else { 182059178a8c7cc80848e5594a9287be91bd924831aChris Banes h = ((rf - gf) / deltaMaxMin) + 4f; 183059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 184059178a8c7cc80848e5594a9287be91bd924831aChris Banes 185711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes s = deltaMaxMin / (1f - Math.abs(2f * l - 1f)); 186059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 187059178a8c7cc80848e5594a9287be91bd924831aChris Banes 188c929274ecb2495a428f214393c787360c3433438Chris Banes h = (h * 60f) % 360f; 189c929274ecb2495a428f214393c787360c3433438Chris Banes if (h < 0) { 190c929274ecb2495a428f214393c787360c3433438Chris Banes h += 360f; 191c929274ecb2495a428f214393c787360c3433438Chris Banes } 192c929274ecb2495a428f214393c787360c3433438Chris Banes 193c929274ecb2495a428f214393c787360c3433438Chris Banes hsl[0] = constrain(h, 0f, 360f); 194c929274ecb2495a428f214393c787360c3433438Chris Banes hsl[1] = constrain(s, 0f, 1f); 195c929274ecb2495a428f214393c787360c3433438Chris Banes hsl[2] = constrain(l, 0f, 1f); 196059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 197059178a8c7cc80848e5594a9287be91bd924831aChris Banes 1987aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes /** 199711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Convert the ARGB color to its HSL (hue-saturation-lightness) components. 2007aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * <ul> 201711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[0] is Hue [0 .. 360)</li> 202711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[1] is Saturation [0...1]</li> 203711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[2] is Lightness [0...1]</li> 204711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * </ul> 205711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * 206711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @param color the ARGB color to convert. The alpha component is ignored. 207711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * @param hsl 3 element array which holds the resulting HSL components. 208711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes */ 209711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static void colorToHSL(int color, float[] hsl) { 210711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl); 211711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes } 212711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes 213711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes /** 214711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * Convert HSL (hue-saturation-lightness) components to a RGB color. 215711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <ul> 216711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[0] is Hue [0 .. 360)</li> 217711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[1] is Saturation [0...1]</li> 218711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes * <li>hsl[2] is Lightness [0...1]</li> 2197aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * </ul> 2207aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * If hsv values are out of range, they are pinned. 2217aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * 2227aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @param hsl 3 element array which holds the input HSL components. 2237aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes * @return the resulting RGB color 2247aa3688908034b80e3e4a86b62f4cf2fc9ecc3a5Chris Banes */ 225711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static int HSLToColor(float[] hsl) { 226059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float h = hsl[0]; 227059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float s = hsl[1]; 228059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float l = hsl[2]; 229059178a8c7cc80848e5594a9287be91bd924831aChris Banes 230059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float c = (1f - Math.abs(2 * l - 1f)) * s; 231059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float m = l - 0.5f * c; 232059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f)); 233059178a8c7cc80848e5594a9287be91bd924831aChris Banes 234059178a8c7cc80848e5594a9287be91bd924831aChris Banes final int hueSegment = (int) h / 60; 235059178a8c7cc80848e5594a9287be91bd924831aChris Banes 236059178a8c7cc80848e5594a9287be91bd924831aChris Banes int r = 0, g = 0, b = 0; 237059178a8c7cc80848e5594a9287be91bd924831aChris Banes 238059178a8c7cc80848e5594a9287be91bd924831aChris Banes switch (hueSegment) { 239059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 0: 240059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * (c + m)); 241059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * (x + m)); 242059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * m); 243059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 244059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 1: 245059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * (x + m)); 246059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * (c + m)); 247059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * m); 248059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 249059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 2: 250059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * m); 251059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * (c + m)); 252059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * (x + m)); 253059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 254059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 3: 255059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * m); 256059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * (x + m)); 257059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * (c + m)); 258059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 259059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 4: 260059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * (x + m)); 261059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * m); 262059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * (c + m)); 263059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 264059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 5: 265059178a8c7cc80848e5594a9287be91bd924831aChris Banes case 6: 266059178a8c7cc80848e5594a9287be91bd924831aChris Banes r = Math.round(255 * (c + m)); 267059178a8c7cc80848e5594a9287be91bd924831aChris Banes g = Math.round(255 * m); 268059178a8c7cc80848e5594a9287be91bd924831aChris Banes b = Math.round(255 * (x + m)); 269059178a8c7cc80848e5594a9287be91bd924831aChris Banes break; 270059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 271059178a8c7cc80848e5594a9287be91bd924831aChris Banes 272c929274ecb2495a428f214393c787360c3433438Chris Banes r = constrain(r, 0, 255); 273c929274ecb2495a428f214393c787360c3433438Chris Banes g = constrain(g, 0, 255); 274c929274ecb2495a428f214393c787360c3433438Chris Banes b = constrain(b, 0, 255); 275059178a8c7cc80848e5594a9287be91bd924831aChris Banes 276059178a8c7cc80848e5594a9287be91bd924831aChris Banes return Color.rgb(r, g, b); 277059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 278059178a8c7cc80848e5594a9287be91bd924831aChris Banes 279f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 280f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Set the alpha component of {@code color} to be {@code alpha}. 281f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 282711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes public static int setAlphaComponent(int color, int alpha) { 283711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes if (alpha < 0 || alpha > 255) { 284711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes throw new IllegalArgumentException("alpha must be between 0 and 255."); 285711c3df64595e2404ff6aa642ee5303f510e1dcbChris Banes } 286f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return (color & 0x00ffffff) | (alpha << 24); 287f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 288f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 289c929274ecb2495a428f214393c787360c3433438Chris Banes private static float constrain(float amount, float low, float high) { 290c929274ecb2495a428f214393c787360c3433438Chris Banes return amount < low ? low : (amount > high ? high : amount); 291c929274ecb2495a428f214393c787360c3433438Chris Banes } 292c929274ecb2495a428f214393c787360c3433438Chris Banes 293c929274ecb2495a428f214393c787360c3433438Chris Banes private static int constrain(int amount, int low, int high) { 294c929274ecb2495a428f214393c787360c3433438Chris Banes return amount < low ? low : (amount > high ? high : amount); 295c929274ecb2495a428f214393c787360c3433438Chris Banes } 296c929274ecb2495a428f214393c787360c3433438Chris Banes 297059178a8c7cc80848e5594a9287be91bd924831aChris Banes} 298