| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.graphics; |
| |
| import android.annotation.AnyThread; |
| import android.annotation.ColorInt; |
| import android.annotation.ColorLong; |
| import android.annotation.HalfFloat; |
| import android.annotation.IntRange; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.Size; |
| import android.annotation.SuppressAutoDoc; |
| import android.util.Half; |
| import com.android.internal.util.XmlUtils; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.function.DoubleUnaryOperator; |
| |
| /** |
| * {@usesMathJax} |
| * |
| * <p>The <code>Color</code> class provides methods for creating, converting and |
| * manipulating colors. Colors have three different representations:</p> |
| * <ul> |
| * <li>Color ints, the most common representation</li> |
| * <li>Color longs</li> |
| * <li><code>Color</code> instances</li> |
| * </ul> |
| * <p>The section below describe each representation in detail.</p> |
| * |
| * <h3>Color ints</h3> |
| * <p>Color ints are the most common representation of colors on Android and |
| * have been used since {@link android.os.Build.VERSION_CODES#BASE API level 1}.</p> |
| * |
| * <p>A color int always defines a color in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space using 4 components packed in a single 32 bit integer value:</p> |
| * |
| * <table summary="Color int definition"> |
| * <tr> |
| * <th>Component</th><th>Name</th><th>Size</th><th>Range</th> |
| * </tr> |
| * <tr><td>A</td><td>Alpha</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>R</td><td>Red</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>G</td><td>Green</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>B</td><td>Blue</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * </table> |
| * |
| * <p>The components in this table are listed in encoding order (see below), |
| * which is why color ints are called ARGB colors.</p> |
| * |
| * <h4>Usage in code</h4> |
| * <p>To avoid confusing color ints with arbitrary integer values, it is a |
| * good practice to annotate them with the <code>@ColorInt</code> annotation |
| * found in the Android Support Library.</p> |
| * |
| * <h4>Encoding</h4> |
| * <p>The four components of a color int are encoded in the following way:</p> |
| * <pre class="prettyprint"> |
| * int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff); |
| * </pre> |
| * |
| * <p>Because of this encoding, color ints can easily be described as an integer |
| * constant in source. For instance, opaque blue is <code>0xff0000ff</code> |
| * and yellow is <code>0xffffff00</code>.</p> |
| * |
| * <p>To easily encode color ints, it is recommended to use the static methods |
| * {@link #argb(int, int, int, int)} and {@link #rgb(int, int, int)}. The second |
| * method omits the alpha component and assumes the color is opaque (alpha is 255). |
| * As a convenience this class also offers methods to encode color ints from components |
| * defined in the \([0..1]\) range: {@link #argb(float, float, float, float)} and |
| * {@link #rgb(float, float, float)}.</p> |
| * |
| * <p>Color longs (defined below) can be easily converted to color ints by invoking |
| * the {@link #toArgb(long)} method. This method performs a color space conversion |
| * if needed.</p> |
| * |
| * <p>It is also possible to create a color int by invoking the method {@link #toArgb()} |
| * on a color instance.</p> |
| * |
| * <h4>Decoding</h4> |
| * <p>The four ARGB components can be individually extracted from a color int |
| * using the following expressions:</p> |
| * <pre class="prettyprint"> |
| * int A = (color >> 24) & 0xff; // or color >>> 24 |
| * int R = (color >> 16) & 0xff; |
| * int G = (color >> 8) & 0xff; |
| * int B = (color ) & 0xff; |
| * </pre> |
| * |
| * <p>This class offers convenience methods to easily extract these components:</p> |
| * <ul> |
| * <li>{@link #alpha(int)} to extract the alpha component</li> |
| * <li>{@link #red(int)} to extract the red component</li> |
| * <li>{@link #green(int)} to extract the green component</li> |
| * <li>{@link #blue(int)} to extract the blue component</li> |
| * </ul> |
| * |
| * <h3>Color longs</h3> |
| * <p>Color longs are a representation introduced in |
| * {@link android.os.Build.VERSION_CODES#O Android O} to store colors in different |
| * {@link ColorSpace color spaces}, with more precision than color ints.</p> |
| * |
| * <p>A color long always defines a color using 4 components packed in a single |
| * 64 bit long value. One of these components is always alpha while the other |
| * three components depend on the color space's {@link ColorSpace.Model color model}. |
| * The most common color model is the {@link ColorSpace.Model#RGB RGB} model in |
| * which the components represent red, green and blue values.</p> |
| * |
| * <p class="note"><b>Component ranges:</b> the ranges defined in the tables |
| * below indicate the ranges that can be encoded in a color long. They do not |
| * represent the actual ranges as they may differ per color space. For instance, |
| * the RGB components of a color in the {@link ColorSpace.Named#DISPLAY_P3 Display P3} |
| * color space use the \([0..1]\) range. Please refer to the documentation of the |
| * various {@link ColorSpace.Named color spaces} to find their respective ranges.</p> |
| * |
| * <p class="note"><b>Alpha range:</b> while alpha is encoded in a color long using |
| * a 10 bit integer (thus using a range of \([0..1023]\)), it is converted to and |
| * from \([0..1]\) float values when decoding and encoding color longs.</p> |
| * |
| * <p class="note"><b>sRGB color space:</b> for compatibility reasons and ease of |
| * use, color longs encoding {@link ColorSpace.Named#SRGB sRGB} colors do not |
| * use the same encoding as other color longs.</p> |
| * |
| * <table summary="Color long definition"> |
| * <tr> |
| * <th>Component</th><th>Name</th><th>Size</th><th>Range</th> |
| * </tr> |
| * <tr><td colspan="4">{@link ColorSpace.Model#RGB RGB} color model</td></tr> |
| * <tr><td>R</td><td>Red</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>G</td><td>Green</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>B</td><td>Blue</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr> |
| * <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr> |
| * <tr><td colspan="4">{@link ColorSpace.Named#SRGB sRGB} color space</td></tr> |
| * <tr><td>A</td><td>Alpha</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>R</td><td>Red</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>G</td><td>Green</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>B</td><td>Blue</td><td>8 bits</td><td>\([0..255]\)</td></tr> |
| * <tr><td>X</td><td>Unused</td><td>32 bits</td><td>\(0\)</td></tr> |
| * <tr><td colspan="4">{@link ColorSpace.Model#XYZ XYZ} color model</td></tr> |
| * <tr><td>X</td><td>X</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>Y</td><td>Y</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>Z</td><td>Z</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr> |
| * <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr> |
| * <tr><td colspan="4">{@link ColorSpace.Model#XYZ Lab} color model</td></tr> |
| * <tr><td>L</td><td>L</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>a</td><td>a</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>b</td><td>b</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr> |
| * <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr> |
| * <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr> |
| * <tr><td colspan="4">{@link ColorSpace.Model#CMYK CMYK} color model</td></tr> |
| * <tr><td colspan="4">Unsupported</td></tr> |
| * </table> |
| * |
| * <p>The components in this table are listed in encoding order (see below), |
| * which is why color longs in the RGB model are called RGBA colors (even if |
| * this doesn't quite hold for the special case of sRGB colors).</p> |
| * |
| * <p>The color long encoding relies on half-precision float values (fp16). If you |
| * wish to know more about the limitations of half-precision float values, please |
| * refer to the documentation of the {@link Half} class.</p> |
| * |
| * <h4>Usage in code</h4> |
| * <p>To avoid confusing color longs with arbitrary long values, it is a |
| * good practice to annotate them with the <code>@ColorLong</code> annotation |
| * found in the Android Support Library.</p> |
| * |
| * <h4>Encoding</h4> |
| * |
| * <p>Given the complex nature of color longs, it is strongly encouraged to use |
| * the various methods provided by this class to encode them.</p> |
| * |
| * <p>The most flexible way to encode a color long is to use the method |
| * {@link #pack(float, float, float, float, ColorSpace)}. This method allows you |
| * to specify three color components (typically RGB), an alpha component and a |
| * color space. To encode sRGB colors, use {@link #pack(float, float, float)} |
| * and {@link #pack(float, float, float, float)} which are the |
| * equivalent of {@link #rgb(int, int, int)} and {@link #argb(int, int, int, int)} |
| * for color ints. If you simply need to convert a color int into a color long, |
| * use {@link #pack(int)}.</p> |
| * |
| * <p>It is also possible to create a color long value by invoking the method |
| * {@link #pack()} on a color instance.</p> |
| * |
| * <h4>Decoding</h4> |
| * |
| * <p>This class offers convenience methods to easily extract the components |
| * of a color long:</p> |
| * <ul> |
| * <li>{@link #alpha(long)} to extract the alpha component</li> |
| * <li>{@link #red(long)} to extract the red/X/L component</li> |
| * <li>{@link #green(long)} to extract the green/Y/a component</li> |
| * <li>{@link #blue(long)} to extract the blue/Z/b component</li> |
| * </ul> |
| * |
| * <p>The values returned by these methods depend on the color space encoded |
| * in the color long. The values are however typically in the \([0..1]\) range |
| * for RGB colors. Please refer to the documentation of the various |
| * {@link ColorSpace.Named color spaces} for the exact ranges.</p> |
| * |
| * <h3>Color instances</h3> |
| * <p>Color instances are a representation introduced in |
| * {@link android.os.Build.VERSION_CODES#O Android O} to store colors in different |
| * {@link ColorSpace color spaces}, with more precision than both color ints and |
| * color longs. Color instances also offer the ability to store more than 4 |
| * components if necessary.</p> |
| * |
| * <p>Colors instances are immutable and can be created using one of the various |
| * <code>valueOf</code> methods. For instance:</p> |
| * <pre class="prettyprint"> |
| * // sRGB |
| * Color opaqueRed = Color.valueOf(0xffff0000); // from a color int |
| * Color translucentRed = Color.valueOf(1.0f, 0.0f, 0.0f, 0.5f); |
| * |
| * // Wide gamut color |
| * {@literal @}ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, colorSpaceP3); |
| * Color opaqueYellow = Color.valueOf(p3); // from a color long |
| * |
| * // CIE L*a*b* color space |
| * ColorSpace lab = ColorSpace.get(ColorSpace.Named.LAB); |
| * Color green = Color.valueOf(100.0f, -128.0f, 128.0f, 1.0f, lab); |
| * </pre> |
| * |
| * <p>Color instances can be converted to color ints ({@link #toArgb()}) or |
| * color longs ({@link #pack()}). They also offer easy access to their various |
| * components using the following methods:</p> |
| * <ul> |
| * <li>{@link #alpha()}, returns the alpha component value</li> |
| * <li>{@link #red()}, returns the red component value (or first |
| * component value in non-RGB models)</li> |
| * <li>{@link #green()}, returns the green component value (or second |
| * component value in non-RGB models)</li> |
| * <li>{@link #blue()}, returns the blue component value (or third |
| * component value in non-RGB models)</li> |
| * <li>{@link #getComponent(int)}, returns a specific component value</li> |
| * <li>{@link #getComponents()}, returns all component values as an array</li> |
| * </ul> |
| * |
| * <h3>Color space conversions</h3> |
| * <p>You can convert colors from one color space to another using |
| * {@link ColorSpace#connect(ColorSpace, ColorSpace)} and its variants. However, |
| * the <code>Color</code> class provides a few convenience methods to simplify |
| * the process. Here is a brief description of some of them:</p> |
| * <ul> |
| * <li>{@link #convert(ColorSpace)} to convert a color instance in a color |
| * space to a new color instance in a different color space</li> |
| * <li>{@link #convert(float, float, float, float, ColorSpace, ColorSpace)} to |
| * convert a color from a source color space to a destination color space</li> |
| * <li>{@link #convert(long, ColorSpace)} to convert a color long from its |
| * built-in color space to a destination color space</li> |
| * <li>{@link #convert(int, ColorSpace)} to convert a color int from sRGB |
| * to a destination color space</li> |
| * </ul> |
| * |
| * <p>Please refere to the {@link ColorSpace} documentation for more |
| * information.</p> |
| * |
| * <h3>Alpha and transparency</h3> |
| * <p>The alpha component of a color defines the level of transparency of a |
| * color. When the alpha component is 0, the color is completely transparent. |
| * When the alpha is component is 1 (in the \([0..1]\) range) or 255 (in the |
| * \([0..255]\) range), the color is completely opaque.</p> |
| * |
| * <p>The color representations described above do not use pre-multiplied |
| * color components (a pre-multiplied color component is a color component |
| * that has been multiplied by the value of the alpha component). |
| * For instance, the color int representation of opaque red is |
| * <code>0xffff0000</code>. For semi-transparent (50%) red, the |
| * representation becomes <code>0x80ff0000</code>. The equivalent color |
| * instance representations would be <code>(1.0, 0.0, 0.0, 1.0)</code> |
| * and <code>(1.0, 0.0, 0.0, 0.5)</code>.</p> |
| */ |
| @AnyThread |
| @SuppressAutoDoc |
| public class Color { |
| @ColorInt public static final int BLACK = 0xFF000000; |
| @ColorInt public static final int DKGRAY = 0xFF444444; |
| @ColorInt public static final int GRAY = 0xFF888888; |
| @ColorInt public static final int LTGRAY = 0xFFCCCCCC; |
| @ColorInt public static final int WHITE = 0xFFFFFFFF; |
| @ColorInt public static final int RED = 0xFFFF0000; |
| @ColorInt public static final int GREEN = 0xFF00FF00; |
| @ColorInt public static final int BLUE = 0xFF0000FF; |
| @ColorInt public static final int YELLOW = 0xFFFFFF00; |
| @ColorInt public static final int CYAN = 0xFF00FFFF; |
| @ColorInt public static final int MAGENTA = 0xFFFF00FF; |
| @ColorInt public static final int TRANSPARENT = 0; |
| |
| @NonNull |
| @Size(min = 4, max = 5) |
| private final float[] mComponents; |
| |
| @NonNull |
| private final ColorSpace mColorSpace; |
| |
| /** |
| * Creates a new color instance set to opaque black in the |
| * {@link ColorSpace.Named#SRGB sRGB} color space. |
| * |
| * @see #valueOf(float, float, float) |
| * @see #valueOf(float, float, float, float) |
| * @see #valueOf(float, float, float, float, ColorSpace) |
| * @see #valueOf(float[], ColorSpace) |
| * @see #valueOf(int) |
| * @see #valueOf(long) |
| */ |
| public Color() { |
| // This constructor is required for compatibility with previous APIs |
| mComponents = new float[] { 0.0f, 0.0f, 0.0f, 1.0f }; |
| mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); |
| } |
| |
| /** |
| * Creates a new color instance in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space. |
| * |
| * @param r The value of the red channel, must be in [0..1] range |
| * @param g The value of the green channel, must be in [0..1] range |
| * @param b The value of the blue channel, must be in [0..1] range |
| * @param a The value of the alpha channel, must be in [0..1] range |
| */ |
| private Color(float r, float g, float b, float a) { |
| this(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB)); |
| } |
| |
| /** |
| * Creates a new color instance in the specified color space. The color space |
| * must have a 3 components model. |
| * |
| * @param r The value of the red channel, must be in the color space defined range |
| * @param g The value of the green channel, must be in the color space defined range |
| * @param b The value of the blue channel, must be in the color space defined range |
| * @param a The value of the alpha channel, must be in [0..1] range |
| * @param colorSpace This color's color space, cannot be null |
| */ |
| private Color(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) { |
| mComponents = new float[] { r, g, b, a }; |
| mColorSpace = colorSpace; |
| } |
| |
| /** |
| * Creates a new color instance in the specified color space. |
| * |
| * @param components An array of color components, plus alpha |
| * @param colorSpace This color's color space, cannot be null |
| */ |
| private Color(@Size(min = 4, max = 5) float[] components, @NonNull ColorSpace colorSpace) { |
| mComponents = components; |
| mColorSpace = colorSpace; |
| } |
| |
| /** |
| * Returns this color's color space. |
| * |
| * @return A non-null instance of {@link ColorSpace} |
| */ |
| @NonNull |
| public ColorSpace getColorSpace() { |
| return mColorSpace; |
| } |
| |
| /** |
| * Returns the color model of this color. |
| * |
| * @return A non-null {@link ColorSpace.Model} |
| */ |
| public ColorSpace.Model getModel() { |
| return mColorSpace.getModel(); |
| } |
| |
| /** |
| * Indicates whether this color color is in a wide-gamut color space. |
| * See {@link ColorSpace#isWideGamut()} for a definition of a wide-gamut |
| * color space. |
| * |
| * @return True if this color is in a wide-gamut color space, false otherwise |
| * |
| * @see #isSrgb() |
| * @see ColorSpace#isWideGamut() |
| */ |
| public boolean isWideGamut() { |
| return getColorSpace().isWideGamut(); |
| } |
| |
| /** |
| * Indicates whether this color is in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space. |
| * |
| * @return True if this color is in the sRGB color space, false otherwise |
| * |
| * @see #isWideGamut() |
| */ |
| public boolean isSrgb() { |
| return getColorSpace().isSrgb(); |
| } |
| |
| /** |
| * Returns the number of components that form a color value according |
| * to this color space's color model, plus one extra component for |
| * alpha. |
| * |
| * @return The integer 4 or 5 |
| */ |
| @IntRange(from = 4, to = 5) |
| public int getComponentCount() { |
| return mColorSpace.getComponentCount() + 1; |
| } |
| |
| /** |
| * Packs this color into a color long. See the documentation of this class |
| * for a description of the color long format. |
| * |
| * @return A color long |
| * |
| * @throws IllegalArgumentException If this color's color space has the id |
| * {@link ColorSpace#MIN_ID} or if this color has more than 4 components |
| */ |
| @ColorLong |
| public long pack() { |
| return pack(mComponents[0], mComponents[1], mComponents[2], mComponents[3], mColorSpace); |
| } |
| |
| /** |
| * Converts this color from its color space to the specified color space. |
| * The conversion is done using the default rendering intent as specified |
| * by {@link ColorSpace#connect(ColorSpace, ColorSpace)}. |
| * |
| * @param colorSpace The destination color space, cannot be null |
| * |
| * @return A non-null color instance in the specified color space |
| */ |
| @NonNull |
| public Color convert(@NonNull ColorSpace colorSpace) { |
| ColorSpace.Connector connector = ColorSpace.connect(mColorSpace, colorSpace); |
| float[] color = new float[] { |
| mComponents[0], mComponents[1], mComponents[2], mComponents[3] |
| }; |
| connector.transform(color); |
| return new Color(color, colorSpace); |
| } |
| |
| /** |
| * Converts this color to an ARGB color int. A color int is always in |
| * the {@link ColorSpace.Named#SRGB sRGB} color space. This implies |
| * a color space conversion is applied if needed. |
| * |
| * @return An ARGB color in the sRGB color space |
| */ |
| @ColorInt |
| public int toArgb() { |
| if (mColorSpace.isSrgb()) { |
| return ((int) (mComponents[3] * 255.0f + 0.5f) << 24) | |
| ((int) (mComponents[0] * 255.0f + 0.5f) << 16) | |
| ((int) (mComponents[1] * 255.0f + 0.5f) << 8) | |
| (int) (mComponents[2] * 255.0f + 0.5f); |
| } |
| |
| float[] color = new float[] { |
| mComponents[0], mComponents[1], mComponents[2], mComponents[3] |
| }; |
| // The transformation saturates the output |
| ColorSpace.connect(mColorSpace).transform(color); |
| |
| return ((int) (color[3] * 255.0f + 0.5f) << 24) | |
| ((int) (color[0] * 255.0f + 0.5f) << 16) | |
| ((int) (color[1] * 255.0f + 0.5f) << 8) | |
| (int) (color[2] * 255.0f + 0.5f); |
| } |
| |
| /** |
| * <p>Returns the value of the red component in the range defined by this |
| * color's color space (see {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}).</p> |
| * |
| * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB}, |
| * calling this method is equivalent to <code>getComponent(0)</code>.</p> |
| * |
| * @see #alpha() |
| * @see #red() |
| * @see #green |
| * @see #getComponents() |
| */ |
| public float red() { |
| return mComponents[0]; |
| } |
| |
| /** |
| * <p>Returns the value of the green component in the range defined by this |
| * color's color space (see {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}).</p> |
| * |
| * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB}, |
| * calling this method is equivalent to <code>getComponent(1)</code>.</p> |
| * |
| * @see #alpha() |
| * @see #red() |
| * @see #green |
| * @see #getComponents() |
| */ |
| public float green() { |
| return mComponents[1]; |
| } |
| |
| /** |
| * <p>Returns the value of the blue component in the range defined by this |
| * color's color space (see {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}).</p> |
| * |
| * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB}, |
| * calling this method is equivalent to <code>getComponent(2)</code>.</p> |
| * |
| * @see #alpha() |
| * @see #red() |
| * @see #green |
| * @see #getComponents() |
| */ |
| public float blue() { |
| return mComponents[2]; |
| } |
| |
| /** |
| * Returns the value of the alpha component in the range \([0..1]\). |
| * Calling this method is equivalent to |
| * <code>getComponent(getComponentCount() - 1)</code>. |
| * |
| * @see #red() |
| * @see #green() |
| * @see #blue() |
| * @see #getComponents() |
| * @see #getComponent(int) |
| */ |
| public float alpha() { |
| return mComponents[mComponents.length - 1]; |
| } |
| |
| /** |
| * Returns this color's components as a new array. The last element of the |
| * array is always the alpha component. |
| * |
| * @return A new, non-null array whose size is equal to {@link #getComponentCount()} |
| * |
| * @see #getComponent(int) |
| */ |
| @NonNull |
| @Size(min = 4, max = 5) |
| public float[] getComponents() { |
| return Arrays.copyOf(mComponents, mComponents.length); |
| } |
| |
| /** |
| * Copies this color's components in the supplied array. The last element of the |
| * array is always the alpha component. |
| * |
| * @param components An array of floats whose size must be at least |
| * {@link #getComponentCount()}, can be null |
| * @return The array passed as a parameter if not null, or a new array of length |
| * {@link #getComponentCount()} |
| * |
| * @see #getComponent(int) |
| * |
| * @throws IllegalArgumentException If the specified array's length is less than |
| * {@link #getComponentCount()} |
| */ |
| @NonNull |
| @Size(min = 4) |
| public float[] getComponents(@Nullable @Size(min = 4) float[] components) { |
| if (components == null) { |
| return Arrays.copyOf(mComponents, mComponents.length); |
| } |
| |
| if (components.length < mComponents.length) { |
| throw new IllegalArgumentException("The specified array's length must be at " |
| + "least " + mComponents.length); |
| } |
| |
| System.arraycopy(mComponents, 0, components, 0, mComponents.length); |
| return components; |
| } |
| |
| /** |
| * <p>Returns the value of the specified component in the range defined by |
| * this color's color space (see {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}).</p> |
| * |
| * <p>If the requested component index is {@link #getComponentCount()}, |
| * this method returns the alpha component, always in the range |
| * \([0..1]\).</p> |
| * |
| * @see #getComponents() |
| * |
| * @throws ArrayIndexOutOfBoundsException If the specified component index |
| * is < 0 or >= {@link #getComponentCount()} |
| */ |
| public float getComponent(@IntRange(from = 0, to = 4) int component) { |
| return mComponents[component]; |
| } |
| |
| /** |
| * <p>Returns the relative luminance of this color.</p> |
| * |
| * <p>Based on the formula for relative luminance defined in WCAG 2.0, |
| * W3C Recommendation 11 December 2008.</p> |
| * |
| * @return A value between 0 (darkest black) and 1 (lightest white) |
| * |
| * @throws IllegalArgumentException If the this color's color space |
| * does not use the {@link ColorSpace.Model#RGB RGB} color model |
| */ |
| public float luminance() { |
| if (mColorSpace.getModel() != ColorSpace.Model.RGB) { |
| throw new IllegalArgumentException("The specified color must be encoded in an RGB " + |
| "color space. The supplied color space is " + mColorSpace.getModel()); |
| } |
| |
| DoubleUnaryOperator eotf = ((ColorSpace.Rgb) mColorSpace).getEotf(); |
| double r = eotf.applyAsDouble(mComponents[0]); |
| double g = eotf.applyAsDouble(mComponents[1]); |
| double b = eotf.applyAsDouble(mComponents[2]); |
| |
| return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b))); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| Color color = (Color) o; |
| |
| //noinspection SimplifiableIfStatement |
| if (!Arrays.equals(mComponents, color.mComponents)) return false; |
| return mColorSpace.equals(color.mColorSpace); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = Arrays.hashCode(mComponents); |
| result = 31 * result + mColorSpace.hashCode(); |
| return result; |
| } |
| |
| /** |
| * <p>Returns a string representation of the object. This method returns |
| * a string equal to the value of:</p> |
| * |
| * <pre class="prettyprint"> |
| * "Color(" + r + ", " + g + ", " + b + ", " + a + |
| * ", " + getColorSpace().getName + ')' |
| * </pre> |
| * |
| * <p>For instance, the string representation of opaque black in the sRGB |
| * color space is equal to the following value:</p> |
| * |
| * <pre> |
| * Color(0.0, 0.0, 0.0, 1.0, sRGB IEC61966-2.1) |
| * </pre> |
| * |
| * @return A non-null string representation of the object |
| */ |
| @Override |
| @NonNull |
| public String toString() { |
| StringBuilder b = new StringBuilder("Color("); |
| for (float c : mComponents) { |
| b.append(c).append(", "); |
| } |
| b.append(mColorSpace.getName()); |
| b.append(')'); |
| return b.toString(); |
| } |
| |
| /** |
| * Returns the color space encoded in the specified color long. |
| * |
| * @param color The color long whose color space to extract |
| * @return A non-null color space instance |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| * |
| * @see #red(long) |
| * @see #green(long) |
| * @see #blue(long) |
| * @see #alpha(long) |
| */ |
| @NonNull |
| public static ColorSpace colorSpace(@ColorLong long color) { |
| return ColorSpace.get((int) (color & 0x3fL)); |
| } |
| |
| /** |
| * Returns the red component encoded in the specified color long. |
| * The range of the returned value depends on the color space |
| * associated with the specified color. The color space can be |
| * queried by calling {@link #colorSpace(long)}. |
| * |
| * @param color The color long whose red channel to extract |
| * @return A float value with a range defined by the specified color's |
| * color space |
| * |
| * @see #colorSpace(long) |
| * @see #green(long) |
| * @see #blue(long) |
| * @see #alpha(long) |
| */ |
| public static float red(@ColorLong long color) { |
| if ((color & 0x3fL) == 0L) return ((color >> 48) & 0xff) / 255.0f; |
| return Half.toFloat((short) ((color >> 48) & 0xffff)); |
| } |
| |
| /** |
| * Returns the green component encoded in the specified color long. |
| * The range of the returned value depends on the color space |
| * associated with the specified color. The color space can be |
| * queried by calling {@link #colorSpace(long)}. |
| * |
| * @param color The color long whose green channel to extract |
| * @return A float value with a range defined by the specified color's |
| * color space |
| * |
| * @see #colorSpace(long) |
| * @see #red(long) |
| * @see #blue(long) |
| * @see #alpha(long) |
| */ |
| public static float green(@ColorLong long color) { |
| if ((color & 0x3fL) == 0L) return ((color >> 40) & 0xff) / 255.0f; |
| return Half.toFloat((short) ((color >> 32) & 0xffff)); |
| } |
| |
| /** |
| * Returns the blue component encoded in the specified color long. |
| * The range of the returned value depends on the color space |
| * associated with the specified color. The color space can be |
| * queried by calling {@link #colorSpace(long)}. |
| * |
| * @param color The color long whose blue channel to extract |
| * @return A float value with a range defined by the specified color's |
| * color space |
| * |
| * @see #colorSpace(long) |
| * @see #red(long) |
| * @see #green(long) |
| * @see #alpha(long) |
| */ |
| public static float blue(@ColorLong long color) { |
| if ((color & 0x3fL) == 0L) return ((color >> 32) & 0xff) / 255.0f; |
| return Half.toFloat((short) ((color >> 16) & 0xffff)); |
| } |
| |
| /** |
| * Returns the alpha component encoded in the specified color long. |
| * The returned value is always in the range \([0..1]\). |
| * |
| * @param color The color long whose blue channel to extract |
| * @return A float value in the range \([0..1]\) |
| * |
| * @see #colorSpace(long) |
| * @see #red(long) |
| * @see #green(long) |
| * @see #blue(long) |
| */ |
| public static float alpha(@ColorLong long color) { |
| if ((color & 0x3fL) == 0L) return ((color >> 56) & 0xff) / 255.0f; |
| return ((color >> 6) & 0x3ff) / 1023.0f; |
| } |
| |
| /** |
| * Indicates whether the specified color is in the |
| * {@link ColorSpace.Named#SRGB sRGB} color space. |
| * |
| * @param color The color to test |
| * @return True if the color is in the sRGB color space, false otherwise |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| * |
| * @see #isInColorSpace(long, ColorSpace) |
| * @see #isWideGamut(long) |
| */ |
| public static boolean isSrgb(@ColorLong long color) { |
| return colorSpace(color).isSrgb(); |
| } |
| |
| /** |
| * Indicates whether the specified color is in a wide-gamut color space. |
| * See {@link ColorSpace#isWideGamut()} for a definition of a wide-gamut |
| * color space. |
| * |
| * @param color The color to test |
| * @return True if the color is in a wide-gamut color space, false otherwise |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| * |
| * @see #isInColorSpace(long, ColorSpace) |
| * @see #isSrgb(long) |
| * @see ColorSpace#isWideGamut() |
| */ |
| public static boolean isWideGamut(@ColorLong long color) { |
| return colorSpace(color).isWideGamut(); |
| } |
| |
| /** |
| * Indicates whether the specified color is in the specified color space. |
| * |
| * @param color The color to test |
| * @param colorSpace The color space to test against |
| * @return True if the color is in the specified color space, false otherwise |
| * |
| * @see #isSrgb(long) |
| * @see #isWideGamut(long) |
| */ |
| public static boolean isInColorSpace(@ColorLong long color, @NonNull ColorSpace colorSpace) { |
| return (int) (color & 0x3fL) == colorSpace.getId(); |
| } |
| |
| /** |
| * Converts the specified color long to an ARGB color int. A color int is |
| * always in the {@link ColorSpace.Named#SRGB sRGB} color space. This implies |
| * a color space conversion is applied if needed. |
| * |
| * @return An ARGB color in the sRGB color space |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| */ |
| @ColorInt |
| public static int toArgb(@ColorLong long color) { |
| if ((color & 0x3fL) == 0L) return (int) (color >> 32); |
| |
| float r = red(color); |
| float g = green(color); |
| float b = blue(color); |
| float a = alpha(color); |
| |
| // The transformation saturates the output |
| float[] c = ColorSpace.connect(colorSpace(color)).transform(r, g, b); |
| |
| return ((int) (a * 255.0f + 0.5f) << 24) | |
| ((int) (c[0] * 255.0f + 0.5f) << 16) | |
| ((int) (c[1] * 255.0f + 0.5f) << 8) | |
| (int) (c[2] * 255.0f + 0.5f); |
| } |
| |
| /** |
| * Creates a new <code>Color</code> instance from an ARGB color int. |
| * The resulting color is in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space. |
| * |
| * @param color The ARGB color int to create a <code>Color</code> from |
| * @return A non-null instance of {@link Color} |
| */ |
| @NonNull |
| public static Color valueOf(@ColorInt int color) { |
| float r = ((color >> 16) & 0xff) / 255.0f; |
| float g = ((color >> 8) & 0xff) / 255.0f; |
| float b = ((color ) & 0xff) / 255.0f; |
| float a = ((color >> 24) & 0xff) / 255.0f; |
| return new Color(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB)); |
| } |
| |
| /** |
| * Creates a new <code>Color</code> instance from a color long. |
| * The resulting color is in the same color space as the specified color long. |
| * |
| * @param color The color long to create a <code>Color</code> from |
| * @return A non-null instance of {@link Color} |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| */ |
| @NonNull |
| public static Color valueOf(@ColorLong long color) { |
| return new Color(red(color), green(color), blue(color), alpha(color), colorSpace(color)); |
| } |
| |
| /** |
| * Creates a new opaque <code>Color</code> in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space with the specified red, green and blue component values. The component |
| * values must be in the range \([0..1]\). |
| * |
| * @param r The red component of the opaque sRGB color to create, in \([0..1]\) |
| * @param g The green component of the opaque sRGB color to create, in \([0..1]\) |
| * @param b The blue component of the opaque sRGB color to create, in \([0..1]\) |
| * @return A non-null instance of {@link Color} |
| */ |
| @NonNull |
| public static Color valueOf(float r, float g, float b) { |
| return new Color(r, g, b, 1.0f); |
| } |
| |
| /** |
| * Creates a new <code>Color</code> in the {@link ColorSpace.Named#SRGB sRGB} |
| * color space with the specified red, green, blue and alpha component values. |
| * The component values must be in the range \([0..1]\). |
| * |
| * @param r The red component of the sRGB color to create, in \([0..1]\) |
| * @param g The green component of the sRGB color to create, in \([0..1]\) |
| * @param b The blue component of the sRGB color to create, in \([0..1]\) |
| * @param a The alpha component of the sRGB color to create, in \([0..1]\) |
| * @return A non-null instance of {@link Color} |
| */ |
| @NonNull |
| public static Color valueOf(float r, float g, float b, float a) { |
| return new Color(saturate(r), saturate(g), saturate(b), saturate(a)); |
| } |
| |
| /** |
| * Creates a new <code>Color</code> in the specified color space with the |
| * specified red, green, blue and alpha component values. The range of the |
| * components is defined by {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}. The values passed to this method |
| * must be in the proper range. |
| * |
| * @param r The red component of the color to create |
| * @param g The green component of the color to create |
| * @param b The blue component of the color to create |
| * @param a The alpha component of the color to create, in \([0..1]\) |
| * @param colorSpace The color space of the color to create |
| * @return A non-null instance of {@link Color} |
| * |
| * @throws IllegalArgumentException If the specified color space uses a |
| * color model with more than 3 components |
| */ |
| @NonNull |
| public static Color valueOf(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) { |
| if (colorSpace.getComponentCount() > 3) { |
| throw new IllegalArgumentException("The specified color space must use a color model " + |
| "with at most 3 color components"); |
| } |
| return new Color(r, g, b, a, colorSpace); |
| } |
| |
| /** |
| * <p>Creates a new <code>Color</code> in the specified color space with the |
| * specified component values. The range of the components is defined by |
| * {@link ColorSpace#getMinValue(int)} and {@link ColorSpace#getMaxValue(int)}. |
| * The values passed to this method must be in the proper range. The alpha |
| * component is always in the range \([0..1]\).</p> |
| * |
| * <p>The length of the array of components must be at least |
| * <code>{@link ColorSpace#getComponentCount()} + 1</code>. The component at index |
| * {@link ColorSpace#getComponentCount()} is always alpha.</p> |
| * |
| * @param components The components of the color to create, with alpha as the last component |
| * @param colorSpace The color space of the color to create |
| * @return A non-null instance of {@link Color} |
| * |
| * @throws IllegalArgumentException If the array of components is smaller than |
| * required by the color space |
| */ |
| @NonNull |
| public static Color valueOf(@NonNull @Size(min = 4, max = 5) float[] components, |
| @NonNull ColorSpace colorSpace) { |
| if (components.length < colorSpace.getComponentCount() + 1) { |
| throw new IllegalArgumentException("Received a component array of length " + |
| components.length + " but the color model requires " + |
| (colorSpace.getComponentCount() + 1) + " (including alpha)"); |
| } |
| return new Color(Arrays.copyOf(components, colorSpace.getComponentCount() + 1), colorSpace); |
| } |
| |
| /** |
| * Converts the specified ARGB color int to an RGBA color long in the sRGB |
| * color space. See the documentation of this class for a description of |
| * the color long format. |
| * |
| * @param color The ARGB color int to convert to an RGBA color long in sRGB |
| * |
| * @return A color long |
| */ |
| @ColorLong |
| public static long pack(@ColorInt int color) { |
| return (color & 0xffffffffL) << 32; |
| } |
| |
| /** |
| * Packs the sRGB color defined by the specified red, green and blue component |
| * values into an RGBA color long in the sRGB color space. The alpha component |
| * is set to 1.0. See the documentation of this class for a description of the |
| * color long format. |
| * |
| * @param red The red component of the sRGB color to create, in \([0..1]\) |
| * @param green The green component of the sRGB color to create, in \([0..1]\) |
| * @param blue The blue component of the sRGB color to create, in \([0..1]\) |
| * |
| * @return A color long |
| */ |
| @ColorLong |
| public static long pack(float red, float green, float blue) { |
| return pack(red, green, blue, 1.0f, ColorSpace.get(ColorSpace.Named.SRGB)); |
| } |
| |
| /** |
| * Packs the sRGB color defined by the specified red, green, blue and alpha |
| * component values into an RGBA color long in the sRGB color space. See the |
| * documentation of this class for a description of the color long format. |
| * |
| * @param red The red component of the sRGB color to create, in \([0..1]\) |
| * @param green The green component of the sRGB color to create, in \([0..1]\) |
| * @param blue The blue component of the sRGB color to create, in \([0..1]\) |
| * @param alpha The alpha component of the sRGB color to create, in \([0..1]\) |
| * |
| * @return A color long |
| */ |
| @ColorLong |
| public static long pack(float red, float green, float blue, float alpha) { |
| return pack(red, green, blue, alpha, ColorSpace.get(ColorSpace.Named.SRGB)); |
| } |
| |
| /** |
| * <p>Packs the 3 component color defined by the specified red, green, blue and |
| * alpha component values into a color long in the specified color space. See the |
| * documentation of this class for a description of the color long format.</p> |
| * |
| * <p>The red, green and blue components must be in the range defined by the |
| * specified color space. See {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}.</p> |
| * |
| * @param red The red component of the color to create |
| * @param green The green component of the color to create |
| * @param blue The blue component of the color to create |
| * @param alpha The alpha component of the color to create, in \([0..1]\) |
| * |
| * @return A color long |
| * |
| * @throws IllegalArgumentException If the color space's id is {@link ColorSpace#MIN_ID} |
| * or if the color space's color model has more than 3 components |
| */ |
| @ColorLong |
| public static long pack(float red, float green, float blue, float alpha, |
| @NonNull ColorSpace colorSpace) { |
| if (colorSpace.isSrgb()) { |
| int argb = |
| ((int) (alpha * 255.0f + 0.5f) << 24) | |
| ((int) (red * 255.0f + 0.5f) << 16) | |
| ((int) (green * 255.0f + 0.5f) << 8) | |
| (int) (blue * 255.0f + 0.5f); |
| return (argb & 0xffffffffL) << 32; |
| } |
| |
| int id = colorSpace.getId(); |
| if (id == ColorSpace.MIN_ID) { |
| throw new IllegalArgumentException( |
| "Unknown color space, please use a color space returned by ColorSpace.get()"); |
| } |
| if (colorSpace.getComponentCount() > 3) { |
| throw new IllegalArgumentException( |
| "The color space must use a color model with at most 3 components"); |
| } |
| |
| @HalfFloat short r = Half.toHalf(red); |
| @HalfFloat short g = Half.toHalf(green); |
| @HalfFloat short b = Half.toHalf(blue); |
| |
| int a = (int) (Math.max(0.0f, Math.min(alpha, 1.0f)) * 1023.0f + 0.5f); |
| |
| // Suppress sign extension |
| return (r & 0xffffL) << 48 | |
| (g & 0xffffL) << 32 | |
| (b & 0xffffL) << 16 | |
| (a & 0x3ffL ) << 6 | |
| id & 0x3fL; |
| } |
| |
| /** |
| * Converts the specified ARGB color int from the {@link ColorSpace.Named#SRGB sRGB} |
| * color space into the specified destination color space. The resulting color is |
| * returned as a color long. See the documentation of this class for a description |
| * of the color long format. |
| * |
| * @param color The sRGB color int to convert |
| * @param colorSpace The destination color space |
| * @return A color long in the destination color space |
| */ |
| @ColorLong |
| public static long convert(@ColorInt int color, @NonNull ColorSpace colorSpace) { |
| float r = ((color >> 16) & 0xff) / 255.0f; |
| float g = ((color >> 8) & 0xff) / 255.0f; |
| float b = ((color ) & 0xff) / 255.0f; |
| float a = ((color >> 24) & 0xff) / 255.0f; |
| ColorSpace source = ColorSpace.get(ColorSpace.Named.SRGB); |
| return convert(r, g, b, a, source, colorSpace); |
| } |
| |
| /** |
| * <p>Converts the specified color long from its color space into the specified |
| * destination color space. The resulting color is returned as a color long. See |
| * the documentation of this class for a description of the color long format.</p> |
| * |
| * <p>When converting several colors in a row, it is recommended to use |
| * {@link #convert(long, ColorSpace.Connector)} instead to |
| * avoid the creation of a {@link ColorSpace.Connector} on every invocation.</p> |
| * |
| * @param color The color long to convert |
| * @param colorSpace The destination color space |
| * @return A color long in the destination color space |
| * @throws IllegalArgumentException If the encoded color space is invalid or unknown |
| */ |
| @ColorLong |
| public static long convert(@ColorLong long color, @NonNull ColorSpace colorSpace) { |
| float r = red(color); |
| float g = green(color); |
| float b = blue(color); |
| float a = alpha(color); |
| ColorSpace source = colorSpace(color); |
| return convert(r, g, b, a, source, colorSpace); |
| } |
| |
| /** |
| * <p>Converts the specified 3 component color from the source color space to the |
| * destination color space. The resulting color is returned as a color long. See |
| * the documentation of this class for a description of the color long format.</p> |
| * |
| * <p>When converting multiple colors in a row, it is recommended to use |
| * {@link #convert(float, float, float, float, ColorSpace.Connector)} instead to |
| * avoid the creation of a {@link ColorSpace.Connector} on every invocation.</p> |
| * |
| * <p>The red, green and blue components must be in the range defined by the |
| * specified color space. See {@link ColorSpace#getMinValue(int)} and |
| * {@link ColorSpace#getMaxValue(int)}.</p> |
| * |
| * @param r The red component of the color to convert |
| * @param g The green component of the color to convert |
| * @param b The blue component of the color to convert |
| * @param a The alpha component of the color to convert, in \([0..1]\) |
| * @param source The source color space, cannot be null |
| * @param destination The destination color space, cannot be null |
| * @return A color long in the destination color space |
| * |
| * @see #convert(float, float, float, float, ColorSpace.Connector) |
| */ |
| @ColorLong |
| public static long convert(float r, float g, float b, float a, |
| @NonNull ColorSpace source, @NonNull ColorSpace destination) { |
| float[] c = ColorSpace.connect(source, destination).transform(r, g, b); |
| return pack(c[0], c[1], c[2], a, destination); |
| } |
| |
| /** |
| * <p>Converts the specified color long from a color space to another using the |
| * specified color space {@link ColorSpace.Connector connector}. The resulting |
| * color is returned as a color long. See the documentation of this class for a |
| * description of the color long format.</p> |
| * |
| * <p>When converting several colors in a row, this method is preferable to |
| * {@link #convert(long, ColorSpace)} as it prevents a new connector from being |
| * created on every invocation.</p> |
| * |
| * <p class="note">The connector's source color space should match the color long's |
| * color space.</p> |
| * |
| * @param color The color long to convert |
| * @param connector A color space connector, cannot be null |
| * @return A color long in the destination color space of the connector |
| */ |
| @ColorLong |
| public static long convert(@ColorLong long color, @NonNull ColorSpace.Connector connector) { |
| float r = red(color); |
| float g = green(color); |
| float b = blue(color); |
| float a = alpha(color); |
| return convert(r, g, b, a, connector); |
| } |
| |
| /** |
| * <p>Converts the specified 3 component color from a color space to another using |
| * the specified color space {@link ColorSpace.Connector connector}. The resulting |
| * color is returned as a color long. See the documentation of this class for a |
| * description of the color long format.</p> |
| * |
| * <p>When converting several colors in a row, this method is preferable to |
| * {@link #convert(float, float, float, float, ColorSpace, ColorSpace)} as |
| * it prevents a new connector from being created on every invocation.</p> |
| * |
| * <p>The red, green and blue components must be in the range defined by the |
| * source color space of the connector. See {@link ColorSpace#getMinValue(int)} |
| * and {@link ColorSpace#getMaxValue(int)}.</p> |
| * |
| * @param r The red component of the color to convert |
| * @param g The green component of the color to convert |
| * @param b The blue component of the color to convert |
| * @param a The alpha component of the color to convert, in \([0..1]\) |
| * @param connector A color space connector, cannot be null |
| * @return A color long in the destination color space of the connector |
| * |
| * @see #convert(float, float, float, float, ColorSpace, ColorSpace) |
| */ |
| @ColorLong |
| public static long convert(float r, float g, float b, float a, |
| @NonNull ColorSpace.Connector connector) { |
| float[] c = connector.transform(r, g, b); |
| return pack(c[0], c[1], c[2], a, connector.getDestination()); |
| } |
| |
| /** |
| * <p>Returns the relative luminance of a color.</p> |
| * |
| * <p>Based on the formula for relative luminance defined in WCAG 2.0, |
| * W3C Recommendation 11 December 2008.</p> |
| * |
| * @return A value between 0 (darkest black) and 1 (lightest white) |
| * |
| * @throws IllegalArgumentException If the specified color's color space |
| * is unknown or does not use the {@link ColorSpace.Model#RGB RGB} color model |
| */ |
| public static float luminance(@ColorLong long color) { |
| ColorSpace colorSpace = colorSpace(color); |
| if (colorSpace.getModel() != ColorSpace.Model.RGB) { |
| throw new IllegalArgumentException("The specified color must be encoded in an RGB " + |
| "color space. The supplied color space is " + colorSpace.getModel()); |
| } |
| |
| DoubleUnaryOperator eotf = ((ColorSpace.Rgb) colorSpace).getEotf(); |
| double r = eotf.applyAsDouble(red(color)); |
| double g = eotf.applyAsDouble(green(color)); |
| double b = eotf.applyAsDouble(blue(color)); |
| |
| return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b))); |
| } |
| |
| private static float saturate(float v) { |
| return v <= 0.0f ? 0.0f : (v >= 1.0f ? 1.0f : v); |
| } |
| |
| /** |
| * Return the alpha component of a color int. This is the same as saying |
| * color >>> 24 |
| */ |
| @IntRange(from = 0, to = 255) |
| public static int alpha(int color) { |
| return color >>> 24; |
| } |
| |
| /** |
| * Return the red component of a color int. This is the same as saying |
| * (color >> 16) & 0xFF |
| */ |
| @IntRange(from = 0, to = 255) |
| public static int red(int color) { |
| return (color >> 16) & 0xFF; |
| } |
| |
| /** |
| * Return the green component of a color int. This is the same as saying |
| * (color >> 8) & 0xFF |
| */ |
| @IntRange(from = 0, to = 255) |
| public static int green(int color) { |
| return (color >> 8) & 0xFF; |
| } |
| |
| /** |
| * Return the blue component of a color int. This is the same as saying |
| * color & 0xFF |
| */ |
| @IntRange(from = 0, to = 255) |
| public static int blue(int color) { |
| return color & 0xFF; |
| } |
| |
| /** |
| * Return a color-int from red, green, blue components. |
| * The alpha component is implicitly 255 (fully opaque). |
| * These component values should be \([0..255]\), but there is no |
| * range check performed, so if they are out of range, the |
| * returned color is undefined. |
| * |
| * @param red Red component \([0..255]\) of the color |
| * @param green Green component \([0..255]\) of the color |
| * @param blue Blue component \([0..255]\) of the color |
| */ |
| @ColorInt |
| public static int rgb( |
| @IntRange(from = 0, to = 255) int red, |
| @IntRange(from = 0, to = 255) int green, |
| @IntRange(from = 0, to = 255) int blue) { |
| return 0xff000000 | (red << 16) | (green << 8) | blue; |
| } |
| |
| /** |
| * Return a color-int from red, green, blue float components |
| * in the range \([0..1]\). The alpha component is implicitly |
| * 1.0 (fully opaque). If the components are out of range, the |
| * returned color is undefined. |
| * |
| * @param red Red component \([0..1]\) of the color |
| * @param green Green component \([0..1]\) of the color |
| * @param blue Blue component \([0..1]\) of the color |
| */ |
| @ColorInt |
| public static int rgb(float red, float green, float blue) { |
| return 0xff000000 | |
| ((int) (red * 255.0f + 0.5f) << 16) | |
| ((int) (green * 255.0f + 0.5f) << 8) | |
| (int) (blue * 255.0f + 0.5f); |
| } |
| |
| /** |
| * Return a color-int from alpha, red, green, blue components. |
| * These component values should be \([0..255]\), but there is no |
| * range check performed, so if they are out of range, the |
| * returned color is undefined. |
| * @param alpha Alpha component \([0..255]\) of the color |
| * @param red Red component \([0..255]\) of the color |
| * @param green Green component \([0..255]\) of the color |
| * @param blue Blue component \([0..255]\) of the color |
| */ |
| @ColorInt |
| public static int argb( |
| @IntRange(from = 0, to = 255) int alpha, |
| @IntRange(from = 0, to = 255) int red, |
| @IntRange(from = 0, to = 255) int green, |
| @IntRange(from = 0, to = 255) int blue) { |
| return (alpha << 24) | (red << 16) | (green << 8) | blue; |
| } |
| |
| /** |
| * Return a color-int from alpha, red, green, blue float components |
| * in the range \([0..1]\). If the components are out of range, the |
| * returned color is undefined. |
| * |
| * @param alpha Alpha component \([0..1]\) of the color |
| * @param red Red component \([0..1]\) of the color |
| * @param green Green component \([0..1]\) of the color |
| * @param blue Blue component \([0..1]\) of the color |
| */ |
| @ColorInt |
| public static int argb(float alpha, float red, float green, float blue) { |
| return ((int) (alpha * 255.0f + 0.5f) << 24) | |
| ((int) (red * 255.0f + 0.5f) << 16) | |
| ((int) (green * 255.0f + 0.5f) << 8) | |
| (int) (blue * 255.0f + 0.5f); |
| } |
| |
| /** |
| * Returns the relative luminance of a color. |
| * <p> |
| * Assumes sRGB encoding. Based on the formula for relative luminance |
| * defined in WCAG 2.0, W3C Recommendation 11 December 2008. |
| * |
| * @return a value between 0 (darkest black) and 1 (lightest white) |
| */ |
| public static float luminance(@ColorInt int color) { |
| ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB); |
| DoubleUnaryOperator eotf = cs.getEotf(); |
| |
| double r = eotf.applyAsDouble(red(color) / 255.0); |
| double g = eotf.applyAsDouble(green(color) / 255.0); |
| double b = eotf.applyAsDouble(blue(color) / 255.0); |
| |
| return (float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b)); |
| } |
| |
| /** |
| * </p>Parse the color string, and return the corresponding color-int. |
| * If the string cannot be parsed, throws an IllegalArgumentException |
| * exception. Supported formats are:</p> |
| * |
| * <ul> |
| * <li><code>#RRGGBB</code></li> |
| * <li><code>#AARRGGBB</code></li> |
| * </ul> |
| * |
| * <p>The following names are also accepted: <code>red</code>, <code>blue</code>, |
| * <code>green</code>, <code>black</code>, <code>white</code>, <code>gray</code>, |
| * <code>cyan</code>, <code>magenta</code>, <code>yellow</code>, <code>lightgray</code>, |
| * <code>darkgray</code>, <code>grey</code>, <code>lightgrey</code>, <code>darkgrey</code>, |
| * <code>aqua</code>, <code>fuchsia</code>, <code>lime</code>, <code>maroon</code>, |
| * <code>navy</code>, <code>olive</code>, <code>purple</code>, <code>silver</code>, |
| * and <code>teal</code>.</p> |
| */ |
| @ColorInt |
| public static int parseColor(@Size(min=1) String colorString) { |
| if (colorString.charAt(0) == '#') { |
| // Use a long to avoid rollovers on #ffXXXXXX |
| long color = Long.parseLong(colorString.substring(1), 16); |
| if (colorString.length() == 7) { |
| // Set the alpha value |
| color |= 0x00000000ff000000; |
| } else if (colorString.length() != 9) { |
| throw new IllegalArgumentException("Unknown color"); |
| } |
| return (int)color; |
| } else { |
| Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT)); |
| if (color != null) { |
| return color; |
| } |
| } |
| throw new IllegalArgumentException("Unknown color"); |
| } |
| |
| /** |
| * Convert RGB components to HSV. |
| * <ul> |
| * <li><code>hsv[0]</code> is Hue \([0..360[\)</li> |
| * <li><code>hsv[1]</code> is Saturation \([0...1]\)</li> |
| * <li><code>hsv[2]</code> is Value \([0...1]\)</li> |
| * </ul> |
| * @param red red component value \([0..255]\) |
| * @param green green component value \([0..255]\) |
| * @param blue blue component value \([0..255]\) |
| * @param hsv 3 element array which holds the resulting HSV components. |
| */ |
| public static void RGBToHSV( |
| @IntRange(from = 0, to = 255) int red, |
| @IntRange(from = 0, to = 255) int green, |
| @IntRange(from = 0, to = 255) int blue, @Size(3) float hsv[]) { |
| if (hsv.length < 3) { |
| throw new RuntimeException("3 components required for hsv"); |
| } |
| nativeRGBToHSV(red, green, blue, hsv); |
| } |
| |
| /** |
| * Convert the ARGB color to its HSV components. |
| * <ul> |
| * <li><code>hsv[0]</code> is Hue \([0..360[\)</li> |
| * <li><code>hsv[1]</code> is Saturation \([0...1]\)</li> |
| * <li><code>hsv[2]</code> is Value \([0...1]\)</li> |
| * </ul> |
| * @param color the argb color to convert. The alpha component is ignored. |
| * @param hsv 3 element array which holds the resulting HSV components. |
| */ |
| public static void colorToHSV(@ColorInt int color, @Size(3) float hsv[]) { |
| RGBToHSV((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsv); |
| } |
| |
| /** |
| * Convert HSV components to an ARGB color. Alpha set to 0xFF. |
| * <ul> |
| * <li><code>hsv[0]</code> is Hue \([0..360[\)</li> |
| * <li><code>hsv[1]</code> is Saturation \([0...1]\)</li> |
| * <li><code>hsv[2]</code> is Value \([0...1]\)</li> |
| * </ul> |
| * If hsv values are out of range, they are pinned. |
| * @param hsv 3 element array which holds the input HSV components. |
| * @return the resulting argb color |
| */ |
| @ColorInt |
| public static int HSVToColor(@Size(3) float hsv[]) { |
| return HSVToColor(0xFF, hsv); |
| } |
| |
| /** |
| * Convert HSV components to an ARGB color. The alpha component is passed |
| * through unchanged. |
| * <ul> |
| * <li><code>hsv[0]</code> is Hue \([0..360[\)</li> |
| * <li><code>hsv[1]</code> is Saturation \([0...1]\)</li> |
| * <li><code>hsv[2]</code> is Value \([0...1]\)</li> |
| * </ul> |
| * If hsv values are out of range, they are pinned. |
| * @param alpha the alpha component of the returned argb color. |
| * @param hsv 3 element array which holds the input HSV components. |
| * @return the resulting argb color |
| */ |
| @ColorInt |
| public static int HSVToColor(@IntRange(from = 0, to = 255) int alpha, @Size(3) float hsv[]) { |
| if (hsv.length < 3) { |
| throw new RuntimeException("3 components required for hsv"); |
| } |
| return nativeHSVToColor(alpha, hsv); |
| } |
| |
| private static native void nativeRGBToHSV(int red, int greed, int blue, float hsv[]); |
| private static native int nativeHSVToColor(int alpha, float hsv[]); |
| |
| /** |
| * Converts an HTML color (named or numeric) to an integer RGB value. |
| * |
| * @param color Non-null color string. |
| * |
| * @return A color value, or {@code -1} if the color string could not be interpreted. |
| * |
| * @hide |
| */ |
| @ColorInt |
| public static int getHtmlColor(@NonNull String color) { |
| Integer i = sColorNameMap.get(color.toLowerCase(Locale.ROOT)); |
| if (i != null) { |
| return i; |
| } else { |
| try { |
| return XmlUtils.convertValueToInt(color, -1); |
| } catch (NumberFormatException nfe) { |
| return -1; |
| } |
| } |
| } |
| |
| private static final HashMap<String, Integer> sColorNameMap; |
| static { |
| sColorNameMap = new HashMap<>(); |
| sColorNameMap.put("black", BLACK); |
| sColorNameMap.put("darkgray", DKGRAY); |
| sColorNameMap.put("gray", GRAY); |
| sColorNameMap.put("lightgray", LTGRAY); |
| sColorNameMap.put("white", WHITE); |
| sColorNameMap.put("red", RED); |
| sColorNameMap.put("green", GREEN); |
| sColorNameMap.put("blue", BLUE); |
| sColorNameMap.put("yellow", YELLOW); |
| sColorNameMap.put("cyan", CYAN); |
| sColorNameMap.put("magenta", MAGENTA); |
| sColorNameMap.put("aqua", 0xFF00FFFF); |
| sColorNameMap.put("fuchsia", 0xFFFF00FF); |
| sColorNameMap.put("darkgrey", DKGRAY); |
| sColorNameMap.put("grey", GRAY); |
| sColorNameMap.put("lightgrey", LTGRAY); |
| sColorNameMap.put("lime", 0xFF00FF00); |
| sColorNameMap.put("maroon", 0xFF800000); |
| sColorNameMap.put("navy", 0xFF000080); |
| sColorNameMap.put("olive", 0xFF808000); |
| sColorNameMap.put("purple", 0xFF800080); |
| sColorNameMap.put("silver", 0xFFC0C0C0); |
| sColorNameMap.put("teal", 0xFF008080); |
| |
| } |
| } |