Modified image recoloring to use Colorization objects since they now live
in the same package. Also added support for applying a set of recolorizations all at once so that previous recolorings aren't borked by subsequent recolorings (plus it's more efficient). git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1340 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Colorization.java,v 1.4 2002/05/06 18:08:32 mdb Exp $
|
||||
// $Id: Colorization.java,v 1.5 2002/05/06 23:23:08 mdb Exp $
|
||||
|
||||
package com.threerings.media.util;
|
||||
|
||||
@@ -36,6 +36,11 @@ public class Colorization
|
||||
this.rootColor = rootColor;
|
||||
this.range = range;
|
||||
this.offsets = offsets;
|
||||
|
||||
// compute our HSV and fixed HSV
|
||||
_hsv = Color.RGBtoHSB(rootColor.getRed(), rootColor.getGreen(),
|
||||
rootColor.getBlue(), null);
|
||||
_fhsv = toFixedHSV(_hsv, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,9 +48,56 @@ public class Colorization
|
||||
*/
|
||||
public Color getColorizedRoot ()
|
||||
{
|
||||
float[] hsv = Color.RGBtoHSB(rootColor.getRed(), rootColor.getGreen(),
|
||||
rootColor.getBlue(), null);
|
||||
return new Color(ImageUtil.recolorColor(hsv, offsets));
|
||||
return new Color(recolorColor(_hsv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the supplied color by the offests in this colorization,
|
||||
* taking the appropriate measures for hue (wrapping it around) and
|
||||
* saturation and value (clipping).
|
||||
*
|
||||
* @return the RGB value of the recolored color.
|
||||
*/
|
||||
public int recolorColor (float[] hsv)
|
||||
{
|
||||
// for hue, we wrap around
|
||||
hsv[0] += offsets[0];
|
||||
if (hsv[0] > 1.0) {
|
||||
hsv[0] -= 1.0;
|
||||
}
|
||||
|
||||
// otherwise we clip
|
||||
hsv[1] = Math.min(Math.max(hsv[1] + offsets[1], 0), 1);
|
||||
hsv[2] = Math.min(Math.max(hsv[2] + offsets[2], 0), 1);
|
||||
|
||||
// convert back to RGB space
|
||||
return Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this colorization matches the supplied color, false
|
||||
* otherwise.
|
||||
*
|
||||
* @param hsv the HSV values for the color in question.
|
||||
* @param fhsv the HSV values converted to fixed point via {@link
|
||||
* #toFixedHSV} for the color in question.
|
||||
*/
|
||||
public boolean matches (float[] hsv, int[] fhsv)
|
||||
{
|
||||
// check to see that this color is sufficiently "close" to the
|
||||
// root color based on the supplied distance parameters
|
||||
if (distance(fhsv[0], _fhsv[0], Short.MAX_VALUE) >=
|
||||
range[0] * Short.MAX_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// saturation and value don't wrap around like hue
|
||||
if (Math.abs(_hsv[1] - hsv[1]) >= range[1] ||
|
||||
Math.abs(_hsv[2] - hsv[2]) >= range[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,4 +127,44 @@ public class Colorization
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts floating point HSV values to a fixed point integer
|
||||
* representation.
|
||||
*
|
||||
* @param hsv the HSV values to be converted.
|
||||
* @param fhsv the destination array into which the fixed values will
|
||||
* be stored. If this is null, a new array will be created of the
|
||||
* appropriate length.
|
||||
*
|
||||
* @return the <code>fhsv</code> parameter if it was non-null or the
|
||||
* newly created target array.
|
||||
*/
|
||||
public static int[] toFixedHSV (float[] hsv, int[] fhsv)
|
||||
{
|
||||
if (fhsv == null) {
|
||||
fhsv = new int[hsv.length];
|
||||
}
|
||||
for (int i = 0; i < hsv.length; i++) {
|
||||
// fhsv[i] = (int)(hsv[i]*Integer.MAX_VALUE);
|
||||
fhsv[i] = (int)(hsv[i]*Short.MAX_VALUE);
|
||||
}
|
||||
return fhsv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the supplied to numbers modulo N.
|
||||
*/
|
||||
public static int distance (int a, int b, int N)
|
||||
{
|
||||
return (a > b) ? Math.min(a-b, b+N-a) : Math.min(b-a, a+N-b);
|
||||
}
|
||||
|
||||
/** Fixed HSV values for our root color; used when calculating
|
||||
* recolorizations using this colorization. */
|
||||
protected int[] _fhsv;
|
||||
|
||||
/** HSV values for our root color; used when calculating
|
||||
* recolorizations using this colorization. */
|
||||
protected float[] _hsv;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: ImageUtil.java,v 1.13 2002/05/06 18:08:32 mdb Exp $
|
||||
// $Id: ImageUtil.java,v 1.14 2002/05/06 23:23:08 mdb Exp $
|
||||
|
||||
package com.threerings.media.util;
|
||||
|
||||
@@ -103,6 +103,28 @@ public class ImageUtil
|
||||
*/
|
||||
public static BufferedImage recolorImage (
|
||||
BufferedImage image, Color rootColor, float[] dists, float[] offsets)
|
||||
{
|
||||
return recolorImage(image, new Colorization[] {
|
||||
new Colorization(-1, rootColor, dists, offsets) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Recolors the supplied image as in {@link
|
||||
* #recolorImage(BufferedImage,Color,float[],float[])} obtaining the
|
||||
* recoloring parameters from the supplied {@link Colorization}
|
||||
* instance.
|
||||
*/
|
||||
public static BufferedImage recolorImage (
|
||||
BufferedImage image, Colorization cz)
|
||||
{
|
||||
return recolorImage(image, new Colorization[] { cz });
|
||||
}
|
||||
|
||||
/**
|
||||
* Recolors the supplied image using the supplied colorizations.
|
||||
*/
|
||||
public static BufferedImage recolorImage (
|
||||
BufferedImage image, Colorization[] zations)
|
||||
{
|
||||
ColorModel cm = image.getColorModel();
|
||||
if (!(cm instanceof IndexColorModel)) {
|
||||
@@ -111,15 +133,10 @@ public class ImageUtil
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
|
||||
// first convert the root color to HSV for later comparison
|
||||
float[] rHSV = Color.RGBtoHSB(rootColor.getRed(), rootColor.getGreen(),
|
||||
rootColor.getBlue(), null);
|
||||
int[] frHSV = toFixedHSV(rHSV, null);
|
||||
int[] rgb = new int[3];
|
||||
|
||||
// now process the image
|
||||
IndexColorModel icm = (IndexColorModel)cm;
|
||||
int size = icm.getMapSize();
|
||||
int zcount = zations.length;
|
||||
int[] rgbs = new int[size];
|
||||
|
||||
// fetch the color data
|
||||
@@ -143,23 +160,18 @@ public class ImageUtil
|
||||
int green = (value >> 8) & 0xFF;
|
||||
int blue = (value >> 0) & 0xFF;
|
||||
Color.RGBtoHSB(red, green, blue, hsv);
|
||||
Colorization.toFixedHSV(hsv, fhsv);
|
||||
|
||||
// check to see that this color is sufficiently "close" to the
|
||||
// root color based on the supplied distance parameters
|
||||
toFixedHSV(hsv, fhsv);
|
||||
if (distance(fhsv[0], frHSV[0], Short.MAX_VALUE) >=
|
||||
dists[0] * Short.MAX_VALUE) {
|
||||
continue;
|
||||
// see if this color matches and of our colorizations and
|
||||
// recolor it if it does
|
||||
for (int z = 0; z < zcount; z++) {
|
||||
Colorization cz = zations[z];
|
||||
if (cz != null && cz.matches(hsv, fhsv)) {
|
||||
// massage the HSV bands and update the RGBs array
|
||||
rgbs[i] = cz.recolorColor(hsv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// saturation and value don't wrap around like hue
|
||||
if (Math.abs(rHSV[1] - hsv[1]) >= dists[1] ||
|
||||
Math.abs(rHSV[2] - hsv[2]) >= dists[2]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// massage the HSV bands and update the RGBs array
|
||||
rgbs[i] = recolorColor(hsv, offsets);
|
||||
}
|
||||
|
||||
// create a new image with the adjusted color palette
|
||||
@@ -169,41 +181,6 @@ public class ImageUtil
|
||||
return new BufferedImage(nicm, image.getRaster(), false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recolors the supplied image as in {@link
|
||||
* #recolorImage(BufferedImage,Color,float[],float[])} obtaining the
|
||||
* recoloring parameters from the supplied {@link Colorization}
|
||||
* instance.
|
||||
*/
|
||||
public static BufferedImage recolorImage (
|
||||
BufferedImage image, Colorization cz)
|
||||
{
|
||||
return recolorImage(image, cz.rootColor, cz.range, cz.offsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the supplied color by the specified offests, taking the
|
||||
* appropriate measures for hue (wrapping it around) and saturation
|
||||
* and value (clipping).
|
||||
*
|
||||
* @return the RGB value of the recolored color.
|
||||
*/
|
||||
public static int recolorColor (float[] hsv, float[] offsets)
|
||||
{
|
||||
// for hue, we wrap around
|
||||
hsv[0] += offsets[0];
|
||||
if (hsv[0] > 1.0) {
|
||||
hsv[0] -= 1.0;
|
||||
}
|
||||
|
||||
// otherwise we clip
|
||||
hsv[1] = Math.min(Math.max(hsv[1] + offsets[1], 0), 1);
|
||||
hsv[2] = Math.min(Math.max(hsv[2] + offsets[2], 0), 1);
|
||||
|
||||
// convert back to RGB space
|
||||
return Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints multiple copies of the supplied image using the supplied
|
||||
* graphics context such that the requested width is filled with the
|
||||
@@ -307,30 +284,6 @@ public class ImageUtil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts floating point HSV values to a fixed point integer
|
||||
* representation.
|
||||
*/
|
||||
protected static int[] toFixedHSV (float[] hsv, int[] fhsv)
|
||||
{
|
||||
if (fhsv == null) {
|
||||
fhsv = new int[hsv.length];
|
||||
}
|
||||
for (int i = 0; i < hsv.length; i++) {
|
||||
// fhsv[i] = (int)(hsv[i]*Integer.MAX_VALUE);
|
||||
fhsv[i] = (int)(hsv[i]*Short.MAX_VALUE);
|
||||
}
|
||||
return fhsv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the supplied to numbers modulo N.
|
||||
*/
|
||||
protected static int distance (int a, int b, int N)
|
||||
{
|
||||
return (a > b) ? Math.min(a-b, b+N-a) : Math.min(b-a, a+N-b);
|
||||
}
|
||||
|
||||
/** The graphics configuration for the default screen device. */
|
||||
protected static GraphicsConfiguration _gc;
|
||||
static {
|
||||
|
||||
Reference in New Issue
Block a user