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.RenderingHints;
019: import java.awt.geom.NoninvertibleTransformException;
020: import java.awt.image.DataBuffer;
021: import java.awt.image.RenderedImage;
022: import java.awt.image.renderable.ParameterBlock;
023: import java.util.MissingResourceException;
024:
025: import javax.media.jai.ImageLayout;
026: import javax.media.jai.Interpolation;
027: import javax.media.jai.InterpolationNearest;
028: import javax.media.jai.JAI;
029: import javax.media.jai.PlanarImage;
030:
031: import org.geotools.coverage.GridSampleDimension;
032: import org.geotools.coverage.grid.GeneralGridRange;
033: import org.geotools.coverage.grid.GridCoverage2D;
034: import org.geotools.coverage.grid.GridGeometry2D;
035: import org.geotools.coverage.processing.CannotScaleException;
036: import org.geotools.coverage.processing.OperationJAI;
037: import org.geotools.factory.Hints;
038: import org.geotools.image.jai.Registry;
039: import org.geotools.resources.coverage.CoverageUtilities;
040: import org.geotools.resources.image.ImageUtilities;
041: import org.opengis.coverage.Coverage;
042: import org.opengis.coverage.grid.GridCoverage;
043: import org.opengis.parameter.ParameterValueGroup;
044:
045: /**
046: * GridCoverage2D specialization for creation of a scaledd version of a source
047: * coverage.
048: *
049: * @author Simone Giannecchini
050: */
051: final class ScaledGridCoverage2D extends GridCoverage2D {
052:
053: /**
054: *
055: */
056: private static final long serialVersionUID = 2521916272257997635L;
057:
058: /** Lock for unsetting native acceleration. */
059: private final static int[] lock = new int[1];
060:
061: /**
062: * Creates a scaled version of a coverage with the needed scale factors,
063: * interpolation and border extender.
064: *
065: * @param parameters
066: * Input parameters for the creation of this coverage.
067: * @param hints
068: * Hints for the creation of this coverage.
069: * @return Scaled version of the source coverage.
070: * @throws CannotScaleException
071: * @throws MissingResourceException
072: * @throws NoninvertibleTransformException
073: */
074: static Coverage create(ParameterValueGroup parameters, Hints hints)
075: throws NoninvertibleTransformException {
076:
077: // /////////////////////////////////////////////////////////////////////
078: //
079: // Getting the input parameters
080: //
081: // /////////////////////////////////////////////////////////////////////
082: final Float xScale = (Float) parameters.parameter("xScale")
083: .getValue();
084: final Float yScale = (Float) parameters.parameter("yScale")
085: .getValue();
086: final Float xTrans = (Float) parameters.parameter("xTrans")
087: .getValue();
088: final Float yTrans = (Float) parameters.parameter("yTrans")
089: .getValue();
090: final Interpolation interpolation = (Interpolation) parameters
091: .parameter("Interpolation").getValue();
092:
093: // /////////////////////////////////////////////////////////////////////
094: //
095: // Getting the source coverage
096: //
097: // /////////////////////////////////////////////////////////////////////
098: GridCoverage2D sourceCoverage = (GridCoverage2D) parameters
099: .parameter("Source").getValue();
100: RenderedImage sourceImage = sourceCoverage.getRenderedImage();
101:
102: // /////////////////////////////////////////////////////////////////////
103: //
104: // Do we need to explode the Palette to RGB(A)?
105: //
106: // /////////////////////////////////////////////////////////////////////
107: int actionTaken = CoverageUtilities
108: .prepareSourcesForGCOperation(sourceCoverage,
109: interpolation, false, hints);
110: switch (actionTaken) {
111: case 1:
112: // //
113: //
114: // In this case I do not require an explicit color expansion since I
115: // can leverage on te fact that the scale operation with latest
116: // versions of JAI is one of the opeations that perform automatic
117: // color expansion.
118: //
119: // //
120: break;
121: case 2:
122: // in this case we need to go back the geophysics view of the
123: // source coverage
124: sourceCoverage = sourceCoverage.geophysics(true);
125: sourceImage = sourceCoverage.getRenderedImage();
126: break;
127: case 3:
128: // in this case we work on the non gephysics version because it
129: // should be faster than working on the geophysics one. We are going
130: // to work on a single band indexed image.
131: sourceCoverage = sourceCoverage.geophysics(false);
132: sourceImage = sourceCoverage.getRenderedImage();
133: break;
134: }
135:
136: // /////////////////////////////////////////////////////////////////////
137: //
138: // Managing Hints, especially for output coverage's layout purposes.
139: //
140: // It is worthwhile to point out that layout hints for minx, miny, width
141: // and height are NOT honored by the scale operation. The other
142: // ImageLayout hints, like tileWidth and tileHeight, however are
143: // honored.
144: // /////////////////////////////////////////////////////////////////////
145: RenderingHints targetHints = ImageUtilities
146: .getRenderingHints(sourceImage);
147: if (targetHints == null) {
148: targetHints = new RenderingHints(hints);
149: } else if (hints != null) {
150: targetHints.add(hints);
151: }
152: ImageLayout layout = (ImageLayout) targetHints
153: .get(JAI.KEY_IMAGE_LAYOUT);
154: if (layout != null) {
155: layout = (ImageLayout) layout.clone();
156: } else {
157: layout = new ImageLayout(sourceImage);
158: layout.unsetTileLayout();
159: // At this point, only the color model and sample model are left
160: // valids.
161: }
162: if ((layout.getValidMask() & (ImageLayout.TILE_WIDTH_MASK
163: | ImageLayout.TILE_HEIGHT_MASK
164: | ImageLayout.TILE_GRID_X_OFFSET_MASK | ImageLayout.TILE_GRID_Y_OFFSET_MASK)) == 0) {
165: layout.setTileGridXOffset(layout.getMinX(sourceImage));
166: layout.setTileGridYOffset(layout.getMinY(sourceImage));
167: final int width = layout.getWidth(sourceImage);
168: final int height = layout.getHeight(sourceImage);
169: if (layout.getTileWidth(sourceImage) > width)
170: layout.setTileWidth(width);
171: if (layout.getTileHeight(sourceImage) > height)
172: layout.setTileHeight(height);
173: }
174: targetHints.put(JAI.KEY_IMAGE_LAYOUT, layout);
175: targetHints.put(JAI.KEY_BORDER_EXTENDER, parameters.parameter(
176: "BorderExtender").getValue());
177:
178: // it is crucial to correctly manage the Hints to control the
179: // replacement of IndexColorModel. It is worth to point out that setting
180: // the JAI.KEY_REPLACE_INDEX_COLOR_MODEL hint to true is not enough to
181: // force the operators to do an expansion.
182: // If we explicitly provide an ImageLayout built with the source image
183: // where the CM and the SM are valid. those will be employed overriding
184: // a the possibility to expand the color model.
185: if (actionTaken != 1)
186: targetHints
187: .add(ImageUtilities.DONT_REPLACE_INDEX_COLOR_MODEL);
188: else {
189: targetHints.add(ImageUtilities.REPLACE_INDEX_COLOR_MODEL);
190: layout.unsetValid(ImageLayout.COLOR_MODEL_MASK);
191: layout.unsetValid(ImageLayout.SAMPLE_MODEL_MASK);
192: }
193:
194: // /////////////////////////////////////////////////////////////////////
195: //
196: // Preparing the parameters for the scale operation
197: //
198: // /////////////////////////////////////////////////////////////////////
199: final int transferType = sourceImage.getSampleModel()
200: .getDataType();
201: final ParameterBlock pbjScale = new ParameterBlock();
202: pbjScale.add(xScale);
203: pbjScale.add(yScale);
204: pbjScale.add(xTrans);
205: pbjScale.add(yTrans);
206: pbjScale.add(interpolation);
207: pbjScale.addSource(sourceImage);
208:
209: // /////////////////////////////////////////////////////////////////////
210: //
211: // Creating final grid coverage.
212: //
213: // /////////////////////////////////////////////////////////////////////
214: final JAI processor = OperationJAI.getJAI(targetHints);
215: PlanarImage image;
216: if (!(interpolation instanceof InterpolationNearest)
217: && (transferType == DataBuffer.TYPE_FLOAT || transferType == DataBuffer.TYPE_DOUBLE)) {
218:
219: synchronized (lock) {
220:
221: /**
222: * Disables the native acceleration for the "Scale" operation.
223: * In JAI 1.1.2, the "Scale" operation on TYPE_FLOAT datatype
224: * with INTERP_BILINEAR interpolation cause an exception in the
225: * native code of medialib, which halt the Java Virtual Machine.
226: * Using the pure Java implementation instead resolve the
227: * problem.
228: *
229: * @todo Remove this hack when Sun will fix the medialib bug.
230: * See
231: * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4906854
232: */
233:
234: Registry.setNativeAccelerationAllowed("Scale", false);
235:
236: if (!processor.equals(JAI.getDefaultInstance()))
237: image = OperationJAI.getJAI(targetHints).createNS(
238: "Scale", pbjScale, targetHints)
239: .getRendering();
240: else
241: image = JAI.create("Scale", pbjScale, targetHints)
242: .getRendering();
243:
244: /**
245: * see above
246: */
247: Registry.setNativeAccelerationAllowed("Scale", true);
248: }
249:
250: } else
251:
252: if (!processor.equals(JAI.getDefaultInstance()))
253: image = OperationJAI.getJAI(targetHints).createNS("Scale",
254: pbjScale, targetHints);
255: else
256: image = JAI.create("Scale", pbjScale, targetHints);
257:
258: // /////////////////////////////////////////////////////////////////////
259: //
260: // Preparing the resulting coverage
261: //
262: // /////////////////////////////////////////////////////////////////////
263: GridCoverage2D result;
264: if (!processor.equals(JAI.getDefaultInstance()))
265: result = new ScaledGridCoverage2D(image, sourceCoverage,
266: actionTaken);
267: // no supplied processor
268: else
269: result = new ScaledGridCoverage2D(image, sourceCoverage,
270: actionTaken);
271: // now let's see what we need to do in order to clean things up
272: if (actionTaken == 2)
273: return result.geophysics(false);
274: if (actionTaken == 3)
275: return result.geophysics(true);
276: return result;
277:
278: }
279:
280: /**
281: * Creates a scaled coverage as requested.
282: *
283: * @param image
284: * is the source image to use for building this
285: * {@link GridCoverage2D}.
286: * @param sourceCoverage
287: * is the coverage that was used as the source for this
288: * operation.
289: * @param actionTaken
290: * is the action we took when preparing ths source image for the
291: * scale operation.
292: */
293: private ScaledGridCoverage2D(PlanarImage image,
294: GridCoverage2D sourceCoverage, int actionTaken) {
295: super (
296: sourceCoverage.getName(),
297: image,
298: new GridGeometry2D(new GeneralGridRange(image),
299: sourceCoverage.getEnvelope()),
300: (GridSampleDimension[]) (actionTaken == 1 ? null
301: : sourceCoverage.getSampleDimensions().clone()),
302: new GridCoverage[] { sourceCoverage }, sourceCoverage
303: .getProperties());
304: }
305: }
|