001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.coverage.processing.operation;
017:
018: import java.awt.image.ColorModel;
019: import java.awt.image.IndexColorModel;
020: import java.awt.image.RenderedImage;
021:
022: import javax.media.jai.ImageLayout;
023: import javax.media.jai.NullOpImage;
024: import javax.media.jai.OpImage;
025:
026: import org.opengis.coverage.Coverage;
027: import org.opengis.coverage.SampleDimension;
028: import org.opengis.coverage.grid.GridCoverage;
029: import org.opengis.parameter.ParameterValueGroup;
030:
031: import org.geotools.factory.Hints;
032: import org.geotools.coverage.FactoryFinder;
033: import org.geotools.coverage.GridSampleDimension;
034: import org.geotools.coverage.grid.GridCoverage2D;
035: import org.geotools.coverage.processing.Operation2D;
036: import org.geotools.parameter.DefaultParameterDescriptorGroup;
037: import org.geotools.resources.coverage.CoverageUtilities;
038: import org.geotools.resources.image.ColorUtilities;
039: import org.geotools.resources.Utilities;
040: import org.geotools.resources.i18n.ErrorKeys;
041: import org.geotools.resources.i18n.Errors;
042:
043: /**
044: * Operation applied only on image's colors. This operation work only for source
045: * image using an {@link IndexColorModel}.
046: *
047: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/processing/operation/IndexColorOperation.java $
048: * @version $Id: IndexColorOperation.java 26601 2007-08-19 23:16:35Z desruisseaux $
049: * @author Martin Desruisseaux
050: *
051: * @todo Consider moving this class to the {@link org.geotools.coverage.processing} package.
052: */
053: abstract class IndexColorOperation extends Operation2D {
054: /**
055: * Constructs an operation.
056: */
057: public IndexColorOperation(
058: final DefaultParameterDescriptorGroup descriptor) {
059: super (descriptor);
060: }
061:
062: /**
063: * Performs the color transformation. This method invokes the
064: * {@link #transformColormap transformColormap(...)} method with current RGB
065: * colormap, the source {@link SampleDimension} and the supplied parameters.
066: *
067: * @param parameters The parameters.
068: * @param hints Rendering hints (ignored in this implementation).
069: *
070: * @throws IllegalArgumentException if the candidate image do not use an
071: * {@link IndexColorModel}.
072: */
073: public Coverage doOperation(final ParameterValueGroup parameters,
074: final Hints hints) {
075: final GridCoverage2D source = (GridCoverage2D) parameters
076: .parameter("Source").getValue();
077: final GridCoverage2D visual = source.geophysics(false);
078: final RenderedImage image = visual.getRenderedImage();
079: final GridSampleDimension[] bands = visual
080: .getSampleDimensions();
081: final int visibleBand = CoverageUtilities.getVisibleBand(image);
082: ColorModel model = image.getColorModel();
083: boolean colorChanged = false;
084: for (int i = 0; i < bands.length; i++) {
085: /*
086: * Extracts the ARGB codes from the IndexColorModel and invokes the
087: * transformColormap(...) method, which needs to be defined by subclasses.
088: */
089: GridSampleDimension band = bands[i];
090: final ColorModel candidate = (i == visibleBand) ? image
091: .getColorModel() : band.getColorModel();
092: if (!(candidate instanceof IndexColorModel)) {
093: // Current implementation supports only sources that use an index color model.
094: throw new IllegalArgumentException(Errors.format(
095: ErrorKeys.ILLEGAL_CLASS_$2, Utilities
096: .getShortClassName(candidate),
097: Utilities.getShortName(IndexColorModel.class)));
098: }
099: final IndexColorModel colors = (IndexColorModel) candidate;
100: final int mapSize = colors.getMapSize();
101: final int[] ARGB = new int[mapSize];
102: colors.getRGBs(ARGB);
103: band = transformColormap(ARGB, i, band, parameters);
104: /*
105: * Checks if there is any change, either as a new GridSampleDimension instance or in
106: * the ARGB array. Note that if the new GridSampleDimension is equals to the old one,
107: * then the new one will be discarted since the old one is more likely to be a shared
108: * instance.
109: */
110: if (!bands[i].equals(band)) {
111: bands[i] = band;
112: colorChanged = true;
113: } else if (!colorChanged) {
114: for (int j = 0; j < mapSize; j++) {
115: if (ARGB[j] != colors.getRGB(j)) {
116: colorChanged = true;
117: break;
118: }
119: }
120: }
121: /*
122: * If we changed the color of the visible band, then create immediately a new
123: * color model for this band. The new color model will be given later to the
124: * image operator.
125: */
126: if (colorChanged && (i == visibleBand)) {
127: model = ColorUtilities.getIndexColorModel(ARGB,
128: bands.length, visibleBand);
129: }
130: }
131: if (!colorChanged) {
132: return source;
133: }
134: /*
135: * Gives the color model to the image layout and creates a new image using the Null
136: * operation, which merely propagates its first source along the operation chain
137: * unmodified (except for the ColorModel given in the layout in this case).
138: */
139: final ImageLayout layout = new ImageLayout()
140: .setColorModel(model);
141: final RenderedImage newImage = new NullOpImage(image, layout,
142: null, OpImage.OP_COMPUTE_BOUND);
143: final GridCoverage2D target = FactoryFinder
144: .getGridCoverageFactory(null).create(visual.getName(),
145: newImage,
146: visual.getCoordinateReferenceSystem2D(),
147: visual.getGridGeometry().getGridToCRS(), bands,
148: new GridCoverage[] { visual }, null);
149:
150: if (source != visual) {
151: return target.geophysics(true);
152: }
153: return target;
154: }
155:
156: /**
157: * Transforms the supplied RGB colors. This method is automatically invoked
158: * by {@link #doOperation(ParameterList)} for each band in the source
159: * {@link GridCoverage2D}. The {@code ARGB} array contains the ARGB values
160: * from the current source and should be overridden with new ARGB values
161: * for the destination image.
162: *
163: * @param ARGB
164: * Alpha, Red, Green and Blue components to transform.
165: * @param band
166: * The band number, from 0 to the number of bands in the image -1.
167: * @param sampleDimension
168: * The sample dimension of band {@code band}.
169: * @param parameters
170: * The user-supplied parameters.
171: * @return A sample dimension identical to {@code sampleDimension} except for the
172: * colors. Subclasses may conservatively returns {@code sampleDimension}.
173: */
174: protected abstract GridSampleDimension transformColormap(
175: final int[] ARGB, final int band,
176: final GridSampleDimension sampleDimension,
177: final ParameterValueGroup parameters);
178: }
|