/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.image.palette;
/**
* This class is responsible for computing efficiently an inverse color map for
* a given color map.
*
* <p>
* This algorithm is adapted from the algorithm found in Graphics Gems volume 2
* by Spencer W. Thomas "Efficient Inverse Color Map Computation".
*
* @author Simone Giannecchini, GeoSolutions SAS
*
*/
public final class EfficientInverseColorMapComputation {
/**
* Number of most significant bits we are going to use from the input color
* in order to quantize them.
*/
protected final int bits;
protected final int truncationBits;
protected final int blueQuantizationMask;
protected final int greenQuantizationMask;
protected final int redQuantizationMask;
/**
* Forward color map. Is a 3*numcolors array.
*/
protected final byte[][] colorMap;
/**
* inverse rgb color map
*/
protected final byte[] mapBuf;
/**
* {@link EfficientInverseColorMapComputation} that allows us to specify the
* number of bits we are going to save from the quantization.
*
* @param rgbColorMap
* @param quantizationBits
*/
public EfficientInverseColorMapComputation(byte[][] rgbColorMap,
final int quantizationBits) {
colorMap = rgbColorMap;
bits = quantizationBits;
truncationBits = 8 - bits;
blueQuantizationMask = (1 << bits) - 1;
greenQuantizationMask = (blueQuantizationMask << bits);
redQuantizationMask = (greenQuantizationMask << bits);
final int maximumQuantizationValue = 1 << bits;
final int numberOfColors = colorMap[0].length;
mapBuf = new byte[maximumQuantizationValue * maximumQuantizationValue* maximumQuantizationValue];
if(mapBuf.length<=0)
throw new IllegalArgumentException("Illegal number of quantization colors");
final int[] distBuf = new int[maximumQuantizationValue* maximumQuantizationValue * maximumQuantizationValue];
final int x = (1 << truncationBits);
final int xsqr = x * x;
final int txsqr = xsqr + xsqr;
// /////////////////////////////////////////////////////////////////////
//
// This code visits every cell in the inverse color map for each
// representative color.
//
// /////////////////////////////////////////////////////////////////////
for (int i = 0; i < numberOfColors; ++i) {
// //
//
// Get the representative color components
//
// //
final int red = colorMap[0][i] & 0xFF;
final int green = colorMap[1][i] & 0xFF;
final int blue = colorMap[2][i] & 0xFF;
// //
//
// Distances are measured to the center of each quantized cell, so
// x/2 is used as the starting point. This is due to the fact that
// we quantize by performing right shift.
//
// //
final int x_ = x / 2;
int rdist = red - x_;
int gdist = green - x_;
int bdist = blue - x_;
// distance
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
final int rinc = 2 * (xsqr - (red << truncationBits));
final int ginc = 2 * (xsqr - (green << truncationBits));
final int binc = 2 * (xsqr - (blue << truncationBits));
// //
//
// Going to check for all the quantized space
//
// //
for (int r = 0, rxx = rinc, rgbI = 0; r < maximumQuantizationValue; rdist += rxx, ++r, rxx += txsqr) {
gdist = rdist;
for (int g = 0, gxx = ginc; g < maximumQuantizationValue; gdist += gxx, ++g, gxx += txsqr) {
bdist = gdist;
for (int b = 0, bxx = binc; b < maximumQuantizationValue; bdist += bxx, ++b, ++rgbI, bxx += txsqr) {
if (i == 0 || distBuf[rgbI] > bdist) {
distBuf[rgbI] = bdist;
mapBuf[rgbI] = (byte) i;
}
}
}
}
}
}
/**
* This method is responsible for doing the actual lookup that given an rgb
* triple returns the best, taking into account quantization, index in the
* forward color map.
*
* @param red
* component.
* @param green
* component.
* @param blue
* component.
* @return the best, taking into account quantization, index in the forward
* color map for the provided triple.
*/
public int getIndexNearest(int red, int green, int blue) {
return mapBuf[((red << (2 * bits - truncationBits)) & redQuantizationMask)
+ ((green << (1 * bits - truncationBits)) & greenQuantizationMask)
+ ((blue >> (truncationBits)) & blueQuantizationMask)] & 0xFF;
}
}