From 80633478153b1f847c493af6eb3c44bcfcabd0a7 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Mon, 6 May 2002 23:23:08 +0000 Subject: [PATCH] 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 --- .../threerings/media/image/Colorization.java | 100 ++++++++++++++- .../com/threerings/media/image/ImageUtil.java | 115 ++++++------------ 2 files changed, 130 insertions(+), 85 deletions(-) diff --git a/src/java/com/threerings/media/image/Colorization.java b/src/java/com/threerings/media/image/Colorization.java index 406a3efe9..5d4492e61 100644 --- a/src/java/com/threerings/media/image/Colorization.java +++ b/src/java/com/threerings/media/image/Colorization.java @@ -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 fhsv 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; } diff --git a/src/java/com/threerings/media/image/ImageUtil.java b/src/java/com/threerings/media/image/ImageUtil.java index 04e936e73..3fdcba3fe 100644 --- a/src/java/com/threerings/media/image/ImageUtil.java +++ b/src/java/com/threerings/media/image/ImageUtil.java @@ -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 {