/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2016, 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;
import it.geosolutions.jaiext.warp.WarpDescriptor;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import javax.media.jai.GeometricOpImage;
import javax.media.jai.ImageLayout;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.OperationRegistry;
import javax.media.jai.PlanarImage;
import javax.media.jai.PropertyGenerator;
import javax.media.jai.ROI;
import javax.media.jai.ROIShape;
import javax.media.jai.RenderedOp;
import javax.media.jai.Warp;
import javax.media.jai.operator.ConstantDescriptor;
import org.geotools.factory.Hints;
import it.geosolutions.jaiext.utilities.ImageLayout2;
import com.sun.media.jai.util.PropertyGeneratorImpl;
/**
* A property generator for the Warp operation that builds the expected ROI bounds even when the source and target image bounds are not superimposed
*
* @author Andrea Aime - GeoSolutions
* @author Daniele Romagnoli - GeoSolutions
*/
public class GTWarpPropertyGenerator extends PropertyGeneratorImpl {
private static final long serialVersionUID = 6622489670499745306L;
/** Constructor. */
public GTWarpPropertyGenerator() {
super(new String[] { "ROI" }, new Class[] { ROI.class }, new Class[] { RenderedOp.class });
}
static boolean registered = false;
public synchronized static void register(boolean force) {
if (!registered || force) {
OperationRegistry registry = JAI.getDefaultInstance().getOperationRegistry();
registry.addPropertyGenerator("rendered", "Warp", new GTWarpPropertyGenerator());
registered = true;
}
}
/**
* Returns the specified property.
*
* @param name Property name.
* @param opNode Operation node.
*/
public Object getProperty(String name, Object opNode) {
validate(name, opNode);
if (opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) {
RenderedOp op = (RenderedOp) opNode;
ParameterBlock pb = op.getParameterBlock();
// Retrieve the rendered source image and its ROI.
RenderedImage src = pb.getRenderedSource(0);
Object property = src.getProperty("ROI");
if (property == null || property.equals(java.awt.Image.UndefinedProperty)
|| !(property instanceof ROI)) {
return java.awt.Image.UndefinedProperty;
}
// Return undefined also if source ROI is empty.
ROI srcROI = (ROI) property;
if (srcROI.getBounds().isEmpty()) {
return java.awt.Image.UndefinedProperty;
}
// Retrieve the Interpolation object.
Interpolation interp = (Interpolation) pb.getObjectParameter(1);
// Determine the effective source bounds.
Rectangle srcBounds = null;
PlanarImage dst = op.getRendering();
if (dst instanceof GeometricOpImage
&& ((GeometricOpImage) dst).getBorderExtender() == null) {
srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY()
+ interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1,
src.getHeight() - interp.getHeight() + 1);
} else {
srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(),
src.getHeight());
}
// If necessary, clip the ROI to the effective source bounds.
if (!srcBounds.contains(srcROI.getBounds())) {
srcROI = srcROI.intersect(new ROIShape(srcBounds));
}
// Retrieve the Warp object.
Warp warp = (Warp) pb.getObjectParameter(0);
// Setting constant image to be warped as a ROI
Rectangle dstBounds = op.getBounds();
// Setting layout of the constant image
ImageLayout2 layout = new ImageLayout2();
int minx = (int) srcBounds.getMinX();
int miny = (int) srcBounds.getMinY();
int w = (int) srcBounds.getWidth();
int h = (int) srcBounds.getHeight();
layout.setMinX(minx);
layout.setMinY(miny);
layout.setWidth(w);
layout.setHeight(h);
layout.setTileWidth(op.getTileWidth());
layout.setTileHeight(op.getTileHeight());
RenderingHints hints = op.getRenderingHints();
hints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
final PlanarImage constantImage = ConstantDescriptor.create(new Float(w), new Float(h),
new Byte[] { (byte) 255 }, hints);
PlanarImage roiImage = null;
// Make sure to specify tileCache, tileScheduler, tileRecyclier, by cloning hints.
RenderingHints warpingHints = op.getRenderingHints();
warpingHints.remove(JAI.KEY_IMAGE_LAYOUT);
// Creating warped roi by the same way (Warp, Interpolation, source ROI) we warped the
// input image.
final ParameterBlock paramBlk = new ParameterBlock();
paramBlk.addSource(constantImage);
paramBlk.add(warp);
paramBlk.add(interp);
paramBlk.add(null);
paramBlk.add(srcROI);
// force in the image layout, this way we get exactly the same
// as the affine we're eliminating
Hints localHints = new Hints(op.getRenderingHints());
localHints.remove(JAI.KEY_IMAGE_LAYOUT);
ImageLayout il = new ImageLayout();
il.setMinX(dstBounds.x);
il.setMinY(dstBounds.y);
il.setWidth(dstBounds.width);
il.setHeight(dstBounds.height);
il.setTileWidth(op.getTileWidth());
il.setTileHeight(op.getTileHeight());
localHints.put(JAI.KEY_IMAGE_LAYOUT, il);
roiImage = JAI.create("Warp", paramBlk, localHints);
ROI dstROI = new ROI(roiImage, 1);
// If necessary, clip the warped ROI to the destination bounds.
if (!dstBounds.contains(dstROI.getBounds())) {
dstROI = dstROI.intersect(new ROIShape(dstBounds));
}
// Return the warped and possibly clipped ROI.
return dstROI;
}
return java.awt.Image.UndefinedProperty;
}
}