/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.scale; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import javax.media.jai.BorderExtender; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ScaleOpImage; import com.sun.media.jai.mlib.MlibScaleRIF; import com.sun.media.jai.opimage.CopyOpImage; import com.sun.media.jai.opimage.RIFUtil; import it.geosolutions.jaiext.interpolators.InterpolationBicubic; import it.geosolutions.jaiext.interpolators.InterpolationBilinear; import it.geosolutions.jaiext.interpolators.InterpolationNearest; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.range.RangeFactory; import it.geosolutions.jaiext.translate.TranslateIntOpImage; import it.geosolutions.jaiext.utilities.ImageUtilities; /** * @see ScaleOpImage */ public class ScaleCRIF extends CRIFImpl { private static final float TOLERANCE = 0.01F; /** Constructor. */ public ScaleCRIF() { super("scale"); } /** * Creates a new instance of ScaleOpImage in the rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factor, and the interpolation method for resampling. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); float xScale = paramBlock.getFloatParameter(0); float yScale = paramBlock.getFloatParameter(1); float xTrans = paramBlock.getFloatParameter(2); float yTrans = paramBlock.getFloatParameter(3); Interpolation interp = (Interpolation) paramBlock.getObjectParameter(4); // Get the Nodata Range Range nodata = (Range) paramBlock.getObjectParameter(7); nodata = RangeFactory.convert(nodata, source.getSampleModel().getDataType()); // Get the backgroundValues double[] backgroundValues = (double[]) paramBlock.getObjectParameter(8); // SG make sure we use the ROI Object property = paramBlock.getObjectParameter(5); ROI roi = null; boolean useRoiAccessor = false; if (property instanceof ROI) { roi = (ROI) property; PlanarImage temp = PlanarImage.wrapRenderedImage(source); temp.setProperty("ROI", roi); source = temp; useRoiAccessor = (Boolean) paramBlock.getObjectParameter(6); } Rectangle sourceBounds = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); // Check and see if we are scaling by 1.0 in both x and y and no // translations. If so call the copy operation. This operation can // be executed only if ROI data are not defined or contain all the // image or are empty if (xScale == 1.0F && yScale == 1.0F && xTrans == 0.0F && yTrans == 0.0F && (roi == null || (roi.getBounds().isEmpty() || roi.contains(sourceBounds)))) { return new CopyOpImage(source, renderHints, layout); } // Check to see whether the operation specified is a pure // integer translation. If so call translate // If the hints contain an ImageLayout hint, then we can't use // TranslateIntOpImage since that can't deal with the ImageLayout hint. // This operation can be executed only if ROI data are not defined or // contain all the image or are empty if (xScale == 1.0F && yScale == 1.0F && (Math.abs(xTrans - (int) xTrans) < TOLERANCE) && (Math.abs(yTrans - (int) yTrans) < TOLERANCE) && layout == null && (roi == null || (roi.getBounds().isEmpty() || roi.contains(sourceBounds)))) { // It's an integer translate. return new TranslateIntOpImage(source, renderHints, (int) xTrans, (int) yTrans); } try { // check if we can use the native operation instead // Rectangle sourceBounds = new Rectangle(source.getMinX(), // source.getMinY(), source.getWidth(), source.getHeight()); if ((roi == null || (ImageUtilities.isMediaLibAvailable() && (roi.getBounds().isEmpty() || roi .contains(sourceBounds)))) && (nodata == null)) { RenderedImage accelerated = new MlibScaleRIF().create(paramBlock, renderHints); if (accelerated != null) { return accelerated; } } } catch (Exception e) { // Eat exception and proceed with pure java approach } SampleModel sm = source.getSampleModel(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); // Check which kind of interpolation we are using boolean nearestInterp = interp instanceof InterpolationNearest || interp instanceof javax.media.jai.InterpolationNearest; boolean bilinearInterp = interp instanceof InterpolationBilinear || interp instanceof javax.media.jai.InterpolationBilinear; boolean bicubicInterp = interp instanceof InterpolationBicubic || interp instanceof javax.media.jai.InterpolationBicubic || interp instanceof javax.media.jai.InterpolationBicubic2; // Transformation of the interpolators JAI-->JAI-EXT int dataType = source.getSampleModel().getDataType(); double destinationNoData = (backgroundValues != null && backgroundValues.length > 0)? backgroundValues[0] : 0; if (interp instanceof javax.media.jai.InterpolationNearest) { interp = new InterpolationNearest(nodata, useRoiAccessor, destinationNoData, dataType); } else if (interp instanceof javax.media.jai.InterpolationBilinear) { interp = new InterpolationBilinear(interp.getSubsampleBitsH(), nodata, useRoiAccessor, destinationNoData, dataType); } else if (interp instanceof javax.media.jai.InterpolationBicubic ) { javax.media.jai.InterpolationBicubic bic = (javax.media.jai.InterpolationBicubic) interp; interp = new InterpolationBicubic(bic.getSubsampleBitsH(), nodata, useRoiAccessor, destinationNoData, dataType,true, bic.getPrecisionBits()); } else if (interp instanceof javax.media.jai.InterpolationBicubic2 ) { javax.media.jai.InterpolationBicubic2 bic = (javax.media.jai.InterpolationBicubic2) interp; interp = new InterpolationBicubic(bic.getSubsampleBitsH(), nodata, useRoiAccessor, destinationNoData, dataType,false, bic.getPrecisionBits()); } if (nearestInterp && isBinary) { return new ScaleGeneralOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else if (nearestInterp && !isBinary) { return new ScaleNearestOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else if (bilinearInterp && !isBinary) { return new ScaleBilinearOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else if (bilinearInterp && isBinary) { return new ScaleGeneralOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else if (bicubicInterp && !isBinary) { return new ScaleBicubicOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else if (bicubicInterp && isBinary) { return new ScaleGeneralOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } else { return new ScaleGeneralOpImage(source, layout, renderHints, extender, interp, xScale, yScale, xTrans, yTrans, useRoiAccessor, nodata, backgroundValues); } } /** * Creates a new instance of <code>AffineOpImage</code> in the renderable layer. This method satisfies the implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith source. This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources and the translation factors. * @param image The RenderableImageOp from which this method was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); float trans_x = paramBlock.getFloatParameter(2); float trans_y = paramBlock.getFloatParameter(3); AffineTransform scale = new AffineTransform(scale_x, 0.0, 0.0, scale_y, trans_x, trans_y); RenderContext RC = (RenderContext) renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(scale); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of <code>ScaleOpImage</code>. This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); float trans_x = paramBlock.getFloatParameter(2); float trans_y = paramBlock.getFloatParameter(3); // Get the source dimensions float x0 = source.getMinX(); float y0 = source.getMinY(); float w = source.getWidth(); float h = source.getHeight(); // Forward map the source using x0, y0, w and h float d_x0 = x0 * scale_x + trans_x; float d_y0 = y0 * scale_y + trans_y; float d_w = w * scale_x; float d_h = h * scale_y; return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h); } }