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;
|
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user