/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME 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.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.contextfree.renderer.support;
import java.awt.Color;
import java.util.Arrays;
public class CFColor {
public static final int HUE_TARGET = 1 << 0;
public static final int SATURATION_TARGET = 1 << 1;
public static final int BRIGHTNESS_TARGET = 1 << 2;
public static final int ALPHA_TARGET = 1 << 3;
private static final float EQUALITY_THRESHOLD = 0.00001f;
private float[] hsba;
private int useTarget;
public CFColor() {
this.hsba = new float[] { 0, 0, 0, 0 };
}
public CFColor(float h, float s, float b, float a) {
this.hsba = new float[] { h, s, b, a };
}
public CFColor(int argb) {
this.hsba = new float[4];
Color.RGBtoHSB((argb >> 16) & 0xFF, (argb >> 8) & 0xFF, (argb >> 0) & 0xFF, hsba);
hsba[0] = hsba[0] * 360;
hsba[3] = ((argb >> 24) & 0xFF) / 255;
}
public CFColor(float[] hsba) {
this.hsba = hsba;
}
public CFColor(float[] hsba, int useTarget) {
this.hsba = hsba;
this.useTarget = useTarget;
}
public float getAlpha() {
return hsba[3];
}
public float getHue() {
return hsba[0];
}
public float getSaturation() {
return hsba[1];
}
public float getBrightness() {
return hsba[2];
}
public void setAlpha(float a) {
hsba[3] = a;
}
public void setHue(float h) {
hsba[0] = h;
}
public void setSaturation(float s) {
hsba[1] = s;
}
public void setBrightness(float b) {
hsba[2] = b;
}
private float adjust(float base, float adjustment, boolean useTarget, float target) {
if (adjustment == 0.0)
return base;
float v;
if (useTarget) {
// If we are really close to the target then don't change, even if
// the adjustment is negative (which way would we go?)
if (adjustment > 0 && Math.abs(base - target) < EQUALITY_THRESHOLD)
return base;
// Otherwise move away from or toward the target
float edge = base < target ? 0 : 1;
if (adjustment < 0)
v = base + (base - edge) * adjustment;
else
v = base + (target - base) * adjustment;
} else {
// Move toward 0 or toward 1
if (adjustment < 0)
v = base + base * adjustment;
else
v = base + (1 - base) * adjustment;
}
return v;
}
private float adjustHue(float base, float adjustment, boolean useTarget, float target) {
if (adjustment == 0.0)
return base;
float h;
if (useTarget) {
// decrease or increase toward target. If the target hue does not
// cooperate by being smaller (or larger) than the current hue then
// add/subtract 360 to make it so. This only works if all hues are
// within the interval [0,360).
if (adjustment < 0) {
if (target > base)
target -= 360;
h = base + (base - target) * adjustment;
} else {
if (target < base)
target += 360;
h = base + (target - base) * adjustment;
}
} else {
h = base + adjustment;
}
// Normalize result to the interval [0,360)
int H = (int) Math.rint(h);
return H < 0.0 ? (H + 360) % 360 : H % 360;
}
public void adjustWith(CFColor adj, CFColor target) {
// Adjust parent color w/shape color
hsba[0] = adjustHue(hsba[0], adj.hsba[0], (adj.getUseTarget() & HUE_TARGET) != 0, target.hsba[0]);
hsba[1] = adjust(hsba[1], adj.hsba[1], (adj.getUseTarget() & SATURATION_TARGET) != 0, target.hsba[1]);
hsba[2] = adjust(hsba[2], adj.hsba[2], (adj.getUseTarget() & BRIGHTNESS_TARGET) != 0, target.hsba[2]);
hsba[3] = adjust(hsba[3], adj.hsba[3], (adj.getUseTarget() & ALPHA_TARGET) != 0, target.hsba[3]);
}
public float delta(float to, float from, int steps) {
if (Math.abs(to - from) < EQUALITY_THRESHOLD)
return 0.0f;
if (to > from)
return -delta(1.0f - to, 1.0f - from, steps);
// from >= EQUALITY_THRESHOLD
if (steps == 1)
return to / from - 1.0f;
return (float) (Math.pow(to / from, 1.0f / steps) - 1.0f);
}
public float deltaHue(float to, float from, int steps) {
float diff = (int) Math.rint((to - from) / steps) % 360;
// Normalize result to the interval (-180,180]
if (diff <= -180.0) return diff + 360;
if (diff > 180.0) return diff - 360;
return diff;
}
public int getUseTarget() {
return useTarget;
}
public void setUseTarget(int useTarget) {
this.useTarget |= useTarget;
}
public void unsetUseTarget(int useTarget) {
this.useTarget ^= useTarget;
}
@Override
public CFColor clone() {
return new CFColor(hsba.clone(), useTarget);
}
public float[] getHSBA() {
return hsba;
}
public int getARGB() {
int argb = Color.HSBtoRGB(hsba[0] / 360, hsba[1], hsba[2]);
argb |= ((int) (hsba[3] * 255)) << 24;
return argb;
}
@Override
public String toString() {
return "CFColor [hsba=" + Arrays.toString(hsba) + ", useTarget=" + useTarget + "]";
}
}