/*
* This file is part of the Illarion project.
*
* Copyright © 2015 - Illarion e.V.
*
* Illarion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Illarion is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package org.illarion.engine.graphic;
import illarion.common.net.NetCommReader;
import illarion.common.util.FastMath;
import org.jetbrains.annotations.Contract;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
/**
* This class is in general used to define a color value. It consists of four color components.
*
* @author Martin Karing <nitram@illarion.org>
*/
public class Color {
/**
* A fully opaque black color. This color is immutable.
*/
public static final ImmutableColor BLACK = new ImmutableColor(0.f, 0.f, 0.f);
/**
* The maximal valid integer value of a color component.
*/
public static final int MAX_INT_VALUE = 255;
/**
* A fully opaque white color. This color is immutable.
*/
public static final ImmutableColor WHITE = new ImmutableColor(1.f, 1.f, 1.f);
/**
* A fully opaque red color. This color is immutable.
*/
public static final ImmutableColor RED = new ImmutableColor(1.f, 0.f, 0.f);
/**
* A fully opaque yellow color. This color is immutable.
*/
public static final ImmutableColor YELLOW = new ImmutableColor(1.f, 1.f, 0.f);
/**
* A fully opaque gray color. This color is immutable.
*/
public static final ImmutableColor GRAY = new ImmutableColor(0.5f, 0.5f, 0.5f);
/**
* The alpha component of the color.
*/
private int alpha;
/**
* The blue component of the color.
*/
private int blue;
/**
* The green component of the color.
*/
private int green;
/**
* The red component of the color.
*/
private int red;
/**
* Create a new color instance with the specified values.
*
* @param red the red color component
* @param green the green color component
* @param blue the blue color component
* @param alpha the alpha color component
*/
public Color(int red, int green, int blue, int alpha) {
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
}
/**
* Create a new color instance with the specified values.
*
* @param red the red color component
* @param green the green color component
* @param blue the blue color component
* @param alpha the alpha color component
*/
public Color(float red, float green, float blue, float alpha) {
this(Math.round(red * MAX_INT_VALUE), Math.round(green * MAX_INT_VALUE), Math.round(blue * MAX_INT_VALUE),
Math.round(alpha * MAX_INT_VALUE));
}
/**
* Create a new opaque color instance with the specified values.
*
* @param red the red color component
* @param green the green color component
* @param blue the blue color component
*/
public Color(int red, int green, int blue) {
this(red, green, blue, MAX_INT_VALUE);
}
/**
* Create a new opaque color instance with the specified values.
*
* @param red the red color component
* @param green the green color component
* @param blue the blue color component
*/
public Color(float red, float green, float blue) {
this(red, green, blue, 1.f);
}
/**
* Copy the values of another instance of the color into a new color instance.
*
* @param org the original color value that is the data provider
*/
public Color(@Nonnull Color org) {
this(org.red, org.green, org.blue, org.alpha);
}
/**
* Create a color instance from the communication interface.
*
* @param reader the reader used to fetch the color values
* @throws IOException in case reading fails
*/
public Color(@Nonnull NetCommReader reader) throws IOException {
this(reader.readUByte(), reader.readUByte(), reader.readUByte(), reader.readUByte());
}
/**
* Add the values of a color to this one.
*
* @param color the color that supplies the new values
*/
public void add(@Nonnull Color color) {
red += color.red;
green += color.green;
blue += color.blue;
alpha += color.alpha;
}
/**
* Get the alpha color component.
*
* @return the alpha color component
*/
public int getAlpha() {
return alpha;
}
/**
* Set the alpha component of the color.
*
* @param alpha the alpha component of the color
*/
public void setAlpha(int alpha) {
this.alpha = alpha;
}
/**
* Get the alpha color component.
*
* @return the alpha color component
*/
public float getAlphaf() {
return (float) alpha / MAX_INT_VALUE;
}
/**
* Set the alpha component of the color.
*
* @param fAlpha the alpha component of the color
*/
public void setAlphaf(float fAlpha) {
setAlpha(Math.round(fAlpha * MAX_INT_VALUE));
}
/**
* Get the blue color component.
*
* @return the blue color component
*/
public int getBlue() {
return blue;
}
/**
* Set the blue component of the color.
*
* @param blue the blue component of the color
*/
public void setBlue(int blue) {
this.blue = blue;
}
/**
* Get the blue color component.
*
* @return the blue color component
*/
public float getBluef() {
return (float) blue / MAX_INT_VALUE;
}
/**
* Set the blue component of the color.
*
* @param fBlue the blue component of the color
*/
public void setBluef(float fBlue) {
setBlue(Math.round(fBlue * MAX_INT_VALUE));
}
/**
* Get the green color component.
*
* @return the green color component
*/
public int getGreen() {
return green;
}
/**
* Set the green component of the color.
*
* @param green the green component of the color
*/
public void setGreen(int green) {
this.green = green;
}
/**
* Get the green color component.
*
* @return the green color component
*/
public float getGreenf() {
return (float) green / MAX_INT_VALUE;
}
/**
* Set the green component of the color.
*
* @param fGreen the green component of the color
*/
public void setGreenf(float fGreen) {
setGreen(Math.round(fGreen * MAX_INT_VALUE));
}
/**
* Get the red color component.
*
* @return the red color component
*/
public int getRed() {
return red;
}
/**
* Set the red component of the color.
*
* @param red the red component of the color
*/
public void setRed(int red) {
this.red = red;
}
/**
* Get the red color component.
*
* @return the red color component
*/
public float getRedf() {
return (float) red / MAX_INT_VALUE;
}
/**
* Set the red component of the color.
*
* @param fRed the red component of the color
*/
public void setRedf(float fRed) {
setRed(Math.round(fRed * MAX_INT_VALUE));
}
/**
* Set this color to the same values as another color.
*
* @param org the color instance that supplies the new color values
*/
public void setColor(@Nonnull Color org) {
red = org.red;
green = org.green;
blue = org.blue;
alpha = org.alpha;
}
/**
* Multiply the values of this colors with the values of another and store the result in this color instance.
*
* @param mul the color that supplies the values multiplied to this color
*/
public void multiply(@Nonnull Color mul) {
red = (red * mul.red) / MAX_INT_VALUE;
green = (green * mul.green) / MAX_INT_VALUE;
blue = (blue * mul.blue) / MAX_INT_VALUE;
alpha = (alpha * mul.alpha) / MAX_INT_VALUE;
}
/**
* Scale all components of the color with a single value.
*
* @param value the value multiplied to each color component
*/
public void multiply(float value) {
red *= value;
green *= value;
blue *= value;
alpha *= value;
}
/**
* Get the luminance level of the color.
*
* @return the luminance of the color
*/
public int getLuminance() {
return (red + green + blue) / 3;
}
/**
* Get the luminance level of the color.
*
* @return the luminance level of the color
*/
public float getLuminancef() {
return (red + green + blue) / MAX_INT_VALUE / 3.f;
}
/**
* Limit the color components to its legal values.
*/
public void clamp() {
red = FastMath.clamp(red, 0, MAX_INT_VALUE);
green = FastMath.clamp(green, 0, MAX_INT_VALUE);
blue = FastMath.clamp(blue, 0, MAX_INT_VALUE);
alpha = FastMath.clamp(alpha, 0, MAX_INT_VALUE);
}
@Override
@Contract(value = "null->false", pure = true)
public boolean equals(@Nullable Object obj) {
return (obj instanceof Color) && equals((Color) obj);
}
@Contract(value = "null->false", pure = true)
public boolean equals(@Nullable Color color) {
return (color != null) && (red == color.red) && (green == color.green) && (blue == color.blue) &&
(alpha == color.alpha);
}
@Override
@Contract(pure = true)
public int hashCode() {
int result = alpha;
result = (31 * result) + blue;
result = (31 * result) + green;
result = (31 * result) + red;
return result;
}
@Override
@Nonnull
@Contract(pure = true)
public String toString() {
return "Color(r:" + red + " g:" + green + " b:" + blue + " a:" + alpha + ')';
}
}