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:
Michael Bayne
2002-05-06 23:23:08 +00:00
parent 99c4330646
commit 8063347815
2 changed files with 130 additions and 85 deletions
@@ -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; package com.threerings.media.util;
@@ -36,6 +36,11 @@ public class Colorization
this.rootColor = rootColor; this.rootColor = rootColor;
this.range = range; this.range = range;
this.offsets = offsets; 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 () public Color getColorizedRoot ()
{ {
float[] hsv = Color.RGBtoHSB(rootColor.getRed(), rootColor.getGreen(), return new Color(recolorColor(_hsv));
rootColor.getBlue(), null); }
return new Color(ImageUtil.recolorColor(hsv, offsets));
/**
* 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); 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; package com.threerings.media.util;
@@ -103,6 +103,28 @@ public class ImageUtil
*/ */
public static BufferedImage recolorImage ( public static BufferedImage recolorImage (
BufferedImage image, Color rootColor, float[] dists, float[] offsets) 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(); ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel)) { if (!(cm instanceof IndexColorModel)) {
@@ -111,15 +133,10 @@ public class ImageUtil
throw new RuntimeException(errmsg); 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 // now process the image
IndexColorModel icm = (IndexColorModel)cm; IndexColorModel icm = (IndexColorModel)cm;
int size = icm.getMapSize(); int size = icm.getMapSize();
int zcount = zations.length;
int[] rgbs = new int[size]; int[] rgbs = new int[size];
// fetch the color data // fetch the color data
@@ -143,23 +160,18 @@ public class ImageUtil
int green = (value >> 8) & 0xFF; int green = (value >> 8) & 0xFF;
int blue = (value >> 0) & 0xFF; int blue = (value >> 0) & 0xFF;
Color.RGBtoHSB(red, green, blue, hsv); Color.RGBtoHSB(red, green, blue, hsv);
Colorization.toFixedHSV(hsv, fhsv);
// check to see that this color is sufficiently "close" to the // see if this color matches and of our colorizations and
// root color based on the supplied distance parameters // recolor it if it does
toFixedHSV(hsv, fhsv); for (int z = 0; z < zcount; z++) {
if (distance(fhsv[0], frHSV[0], Short.MAX_VALUE) >= Colorization cz = zations[z];
dists[0] * Short.MAX_VALUE) { if (cz != null && cz.matches(hsv, fhsv)) {
continue; // 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 // create a new image with the adjusted color palette
@@ -169,41 +181,6 @@ public class ImageUtil
return new BufferedImage(nicm, image.getRaster(), false, null); 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 * Paints multiple copies of the supplied image using the supplied
* graphics context such that the requested width is filled with the * 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. */ /** The graphics configuration for the default screen device. */
protected static GraphicsConfiguration _gc; protected static GraphicsConfiguration _gc;
static { static {