/* * 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 java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; 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.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderedOp; import org.geotools.factory.Hints; import com.sun.media.jai.util.PropertyGeneratorImpl; /** * A property generator for the Affine operation that builds a ROI with a sane image layout even with large upscale factors * * @author Andrea Aime - GeoSolutions * @author Daniele Romagnoli - GeoSolutions */ public class GTAffinePropertyGenerator extends PropertyGeneratorImpl { private static final long serialVersionUID = 6622489670499745306L; /** Constructor. */ public GTAffinePropertyGenerator() { 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", "Affine", new GTAffinePropertyGenerator()); registered = true; } } /** * Returns the specified property in the rendered layer. * * @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)) { // Check on the parameterBlock if (pb.getNumParameters() >= 4 && pb.getObjectParameter(3) != null) { property = pb.getObjectParameter(3); } else { return java.awt.Image.UndefinedProperty; } } ROI srcROI = (ROI) property; // 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 AffineTransform object. AffineTransform transform = (AffineTransform) pb.getObjectParameter(0); // Create the transformed ROI. ROI dstROI; if(srcROI.getClass().equals(ROI.class)) { // we need to build an image with the same layout of the op, or we // risk of building a very large ROI with super-tiny tiles when // doing up-sampling of a single pixel image (high oversample case) ParameterBlock paramBlock = new ParameterBlock(); paramBlock.add(transform); paramBlock.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST)); Hints localHints = new Hints(op.getRenderingHints()); localHints.remove(JAI.KEY_IMAGE_LAYOUT); ImageLayout il = new ImageLayout(); Rectangle dstBounds = op.getBounds(); il.setMinX(dstBounds.x); il.setMinY(dstBounds.y); il.setWidth(dstBounds.width); il.setHeight(dstBounds.height); il.setTileWidth(op.getTileWidth()); il.setTileWidth(op.getTileHeight()); localHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il)); dstROI = srcROI.performImageOp("Affine", paramBlock, 0, localHints); } else { // let the geometry based ROIs do their work at the vector level dstROI = srcROI.transform((AffineTransform) transform); } // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the transformed ROI to the // destination bounds. if (!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the transformed and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } }