/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-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.io;
import java.awt.Transparency;
import java.awt.image.DataBuffer;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import javax.media.jai.ComponentSampleModelJAI;
import javax.imageio.ImageTypeSpecifier;
import java.io.IOException;
import org.geotools.resources.image.ComponentColorModelJAI;
/**
* A factory for building {@linkplain ColorModel color model} suitable for
* {@link DataBuffer#TYPE_FLOAT}.
*
* @since 2.4
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*/
final class ContinuousPalette extends Palette {
/**
* Tells if we should use {@link ComponentSampleModelJAI} instead of the more standard
* {@link java.awt.image.ComponentSampleModel}. There is two problems with models provided
* with J2SE 1.4:
* <p>
* <ul>
* <li>As of J2SE 1.4.0, {@link ImageTypeSpecifier#createBanded} doesn't accept
* {@link DataBuffer#TYPE_FLOAT} and {@link DataBuffer#TYPE_DOUBLE} argument.
*
* UPDATE: it seems to be fixed with Java 5.</li>
*
* <li>As of JAI 1.1, operators don't accept Java2D's {@link java.awt.image.DataBufferFloat}
* and {@link java.awt.image.DataBufferDouble}. They require JAI's DataBuffer instead.
*
* We have not yet checked if the problem still presents with JAI 1.1.3.</li>
* </ul>
*/
static final boolean USE_JAI_MODEL = ComponentColorModelJAI.ENABLED;
/**
* The minimal value, inclusive.
*/
protected final float minimum;
/**
* The maximal value, inclusive.
*/
protected final float maximum;
/**
* The data type, as a {@link DataBuffer#TYPE_FLOAT} or {@link DataBuffer#TYPE_DOUBLE}
* constant.
*/
private final int dataType;
/**
* Creates a palette with the specified name.
*
* @param factory The originating factory.
* @param name The palette name.
* @param minimum The minimal sample value expected.
* @param maximum The maximal sample value expected.
* @param dataType The data type as a {@link DataBuffer#TYPE_FLOAT}
* or {@link DataBuffer#TYPE_DOUBLE} constant.
* @param numBands The number of bands (usually 1).
* @param visibleBand The band to use for color computations (usually 0).
*/
protected ContinuousPalette(final PaletteFactory factory, final String name, final float minimum,
final float maximum, final int dataType, final int numBands, final int visibleBand)
{
super(factory, name, numBands, visibleBand);
this.minimum = minimum;
this.maximum = maximum;
this.dataType = dataType;
}
/**
* Returns the scale from <cite>normalized values</cite> (values in the range [0..1])
* to values in the range of this palette.
*/
@Override
double getScale() {
return maximum - minimum;
}
/**
* Returns the offset from <cite>normalized values</cite> (values in the range [0..1])
* to values in the range of this palette.
*/
@Override
double getOffset() {
return minimum;
}
/**
* Creates a grayscale image type for this palette.
* The image type is suitable for floating point values.
*
* @return A default color space scaled to fit data.
* @throws IOException If an I/O operation was needed and failed.
*/
public synchronized ImageTypeSpecifier getImageTypeSpecifier() throws IOException {
ImageTypeSpecifier its = queryCache();
if (its != null) {
return its;
}
final ColorSpace colorSpace;
if (minimum < maximum && !Float.isInfinite(minimum) && !Float.isInfinite(maximum)) {
colorSpace = new ScaledColorSpace(numBands, visibleBand, minimum, maximum);
} else {
colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
}
final int[] bankIndices = new int[numBands];
final int[] bandOffsets = new int[numBands];
for (int i=numBands; --i>=0;) {
bankIndices[i] = i;
}
if (USE_JAI_MODEL) {
final ColorModel cm = new ComponentColorModelJAI(
colorSpace, null, false, false, Transparency.OPAQUE, dataType);
its = new ImageTypeSpecifier(cm, new ComponentSampleModelJAI(
dataType, 1, 1, 1, 1, bankIndices, bandOffsets));
} else {
its = ImageTypeSpecifier.createBanded(
colorSpace, bankIndices, bandOffsets, dataType, false, false);
}
cache(its);
return its;
}
/**
* Returns a hash value for this palette.
*/
@Override
public int hashCode() {
return 37 * (37 * (37 * super.hashCode() + Float.floatToIntBits(minimum)) +
Float.floatToIntBits(maximum)) + dataType;
}
/**
* Compares this palette with the specified object for equality.
*/
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (super.equals(object)) {
final ContinuousPalette that = (ContinuousPalette) object;
return Float.floatToIntBits(this.minimum) == Float.floatToIntBits(that.minimum) &&
Float.floatToIntBits(this.maximum) == Float.floatToIntBits(that.maximum) &&
this.dataType == that.dataType;
}
return false;
}
/**
* Returns a string representation of this palette. Used for debugging purpose only.
*/
@Override
public String toString() {
return name + " [" + minimum + " ... " + maximum + ']';
}
}