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;
@@ -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 {