/* * Copyright 2009 Hao Nguyen * * 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 gwt.g2d.shared; import gwt.g2d.client.math.MathHelper; import java.io.Serializable; import java.util.Arrays; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; /** * Stores a color. Color is immutable. * * @author hao1300@gmail.com */ public class Color implements Serializable { protected static final double DEFAULT_ALPHA = 1.0; private static final long serialVersionUID = 5370658935618812361L; private final String colorCode; public final int red, green, blue; public final double alpha; /** * Constructor. * * @param colorCode the string representing this color. * @param red red channel [0-255] * @param green green channel [0-255] * @param blue blue channel [0-255] * @param alpha alpha channel [0.0, 1.0] */ protected Color(String colorCode, int red, int green, int blue, double alpha) { this.colorCode = colorCode; this.red = red; this.green = green; this.blue = blue; this.alpha = alpha; } /** * Represents a color that contains transparency. * The color code is represented in the form "rgba(r,g,b,a)". * * @param red red channel [0-255] * @param green green channel [0-255] * @param blue blue channel [0-255] * @param alpha alpha channel [0.0, 1.0] */ public Color(int red, int green, int blue, double alpha) { this(new StringBuilder(21) .append("rgba(") .append(red).append(',') .append(green).append(',') .append(blue).append(',') .append(alpha).append(')') .toString(), red, green, blue, alpha); } /** * Construct a web-safe color. The color code is represented in the form * "#RRGGBB" which represents an RGB color using hexadecimal numbers. * * @param red red channel [0-255] * @param green green channel [0-255] * @param blue blue channel [0-255] */ public Color(int red, int green, int blue) { StringBuilder stringBuilder = new StringBuilder("#000000"); String hexString = Integer.toHexString(getHexValue(red, green, blue)); colorCode = stringBuilder.replace( stringBuilder.length() - hexString.length(), stringBuilder.length(), hexString).toString(); this.red = red; this.green = green; this.blue = blue; this.alpha = DEFAULT_ALPHA; } /** * Linear interpolation between two colors. * * @param value1 * @param value2 * @param amount the amount to interpolate [0.0, 1.0]. * @return a new interpolated color. */ public static final Color lerp(Color value1, Color value2, double amount) { return new Color( (int) MathHelper.lerp(value1.getR(), value2.getR(), amount), (int) MathHelper.lerp(value1.getG(), value2.getG(), amount), (int) MathHelper.lerp(value1.getB(), value2.getB(), amount), MathHelper.lerp(value1.getAlpha(), value2.getAlpha(), amount)); } /** * Linear interpolation between two colors with alpha premultiplication for better * graphical effects. * * @param value1 * @param value2 * @param amount the amount to interpolate [0.0, 1.0]. * @return a new interpolated color. */ public static final Color lerpPremultiplied(Color c1, Color c2, double amount) { double alpha = MathHelper.lerp(c1.getAlpha(), c2.getAlpha(), amount); return new Color( (int) (MathHelper.lerp(c1.red * c1.alpha, c2.red * c2.alpha, amount)/alpha), (int) (MathHelper.lerp(c1.green * c1.alpha, c2.green * c2.alpha, amount)/alpha), (int) (MathHelper.lerp(c1.blue * c1.alpha, c2.blue * c2.alpha, amount)/alpha), alpha); } /** * Smooth interpolation between two colors. * * @param value1 * @param value2 * @param amount the amount to interpolate [0.0, 1.0]. * @return a new interpolated color. */ public static final Color smoothStep(Color value1, Color value2, double amount) { return new Color( (int) MathHelper.smoothStep(value1.getR(), value2.getR(), amount), (int) MathHelper.smoothStep(value1.getG(), value2.getG(), amount), (int) MathHelper.smoothStep(value1.getB(), value2.getB(), amount), MathHelper.smoothStep(value1.getAlpha(), value2.getAlpha(), amount)); } /** * Gets the string representation of the color. */ public final String getColorCode() { return colorCode; } /** * Gets the value of the red channel. * * @return a value between 0 to 255, inclusive. */ public final int getR() { return red; } /** * Gets the value of the green channel. * * @return a value between 0 to 255, inclusive. */ public final int getG() { return green; } /** * Gets the value of the blue channel. * * @return a value between 0 to 255, inclusive. */ public final int getB() { return blue; } /** * Gets the value of the alpha channel. * * @return a value between 0.0 to 1.0, inclusive. */ public final double getAlpha() { return alpha; } /** * Return the color code (identical to getColorCode). */ @Override public final String toString() { return getColorCode(); } // regular expressions for parsing a color private static RegExp PatternRGBA = RegExp.compile("rgba\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]*\\.?[0-9]+)\\s*\\)"); // rgba private static RegExp PatternRGB = RegExp.compile("rgb\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)"); // rgb private static RegExp PatternHex = RegExp.compile("\\#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])"); // html color code #XXXXXX /** * Parse a color from a color code. */ public static final Color parseColor(String s) { // rgba pattern MatchResult res = PatternRGBA.exec(s); if (res != null && res.getGroupCount() == 5) return new Color(Integer.parseInt(res.getGroup(1)), Integer.parseInt(res.getGroup(2)), Integer.parseInt(res.getGroup(3)), Double.parseDouble(res.getGroup(4))); // rgb pattern res = PatternRGB.exec(s); if (res != null && res.getGroupCount() == 4) return new Color(Integer.parseInt(res.getGroup(1)), Integer.parseInt(res.getGroup(2)), Integer.parseInt(res.getGroup(3))); // hex pattern res = PatternHex.exec(s); if (res != null && res.getGroupCount() == 4) return new Color(Integer.parseInt(res.getGroup(1), 16), Integer.parseInt(res.getGroup(2), 16), Integer.parseInt(res.getGroup(3), 16)); // no match return null; } @Override public final boolean equals(Object obj) { return (obj instanceof Color) ? equals((Color) obj) : false; } public final boolean equals(Color rhs) { return getR() == rhs.getR() && getG() == rhs.getG() && getB() == rhs.getB() && getAlpha() == rhs.getAlpha(); } @Override public final int hashCode() { return Arrays.hashCode(new double[]{getHexValue(red, green, blue), alpha}); } /** * Gets the integer value of the given rgb value. */ private final int getHexValue(int r, int g, int b) { return ((r << 16) & 0xFF0000) | ((g << 8) & 0xFF00) | (b & 0xFF); } }