Package org.geotools.processing.jai

Source Code of org.geotools.processing.jai.ExtendedBandMergeOpImage

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2014, Open Source Geospatial Foundation (OSGeo)
*    (C) 2014 TOPP - www.openplans.org.
*
*    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.processing.jai;


import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.media.jai.GeometricOpImage;
import javax.media.jai.ImageLayout;
import javax.media.jai.PixelAccessor;
import javax.media.jai.PlanarImage;
import javax.media.jai.ROI;
import javax.media.jai.ROIShape;
import javax.media.jai.RasterFactory;
import javax.media.jai.UnpackedImageData;
import javax.media.jai.iterator.RandomIter;
import javax.media.jai.iterator.RandomIterFactory;

import org.geotools.processing.jai.nodata.Range;

import com.sun.media.jai.codecimpl.util.FloatDoubleColorModel;
import com.sun.media.jai.util.ImageUtil;
import com.sun.media.jai.util.JDKWorkarounds;

/**
* An <code>OpImage</code> implementing the "BandMerge" operation as described in {@link BandMergeDescriptor}. This version of the BandMergeOpImage
* also supports a backward mapping from the destination image to each source image. The mapping is achieved by setting a List object containing the
* affine transformations, each of them is associated to the related source image.
*
* <p>
* This <code>OpImage</code> merges the pixel values of two or more source images.
*
* The data type <code>byte</code> is treated as unsigned, with maximum value as 255 and minimum value as 0.
*
* There is no attempt to rescale binary images to the appropriate gray levels, such as 255 or 0. A lookup should be performed first if so desired.
*
* If No Data are present, they can be handled if the user provides an array of No Data Range objects and a double value for the destination No Data.
*
* If a ROI is present, then it is taken into account during calculations.
*
@author Nicola Lagomarsini, GeoSolutions S.A.S.
*
*/
class ExtendedBandMergeOpImage extends GeometricOpImage {

    /** Quantity used for extending the input tile dimensions */
    public static final int TILE_EXTENDER = 1;

    /** List of ColorModels required for IndexColorModel support */
    ColorModel[] colorModels;

    /** Array containing all the No Data Ranges */
    private final Range[] noData;

    /** Boolean indicating if No Data are present */
    private final boolean hasNoData;

    /** Boolean indicating if ROI is present */
    private final boolean hasROI;

    /** Destination No Data value used for Byte images */
    private byte destNoDataByte;

    /** Destination No Data value used for Short/Unsigned Short images */
    private short destNoDataShort;

    /** Destination No Data value used for Integer images */
    private int destNoDataInt;

    /** Destination No Data value used for Float images */
    private float destNoDataFloat;

    /** Destination No Data value used for Double images */
    private double destNoDataDouble;

    private List<AffineTransform> transforms;

    private List<TRANSFORM> transformObj;

    /** Boolean indicating if No Data and ROI are not used */
    protected boolean caseA;

    /** Boolean indicating if only the ROI is used */
    protected boolean caseB;

    /** Boolean indicating if only the No Data are used */
    protected boolean caseC;

    /** ROI object to use*/
    private ROI roi;

    /**
     * Enum used for implementing the various optional transformations on the input points
     *
     * @author Nicola Lagomarsini GeoSolutions S.A.S.
     *
     */
    public enum TRANSFORM {
        AFFINE {
            @Override
            public void transform(AffineTransform tr, Point2D src, Point2D dst) {
                // Implement the Affine Transformation
                tr.transform(src, dst);
            }
        },
        IDENTITY {
            @Override
            public void transform(AffineTransform tr, Point2D src, Point2D dst) {
                dst.setLocation(src);
            }
        },
        TRANSLATION {
            @Override
            public void transform(AffineTransform tr, Point2D src, Point2D dst) {
                dst.setLocation(src.getX() + tr.getTranslateX(), src.getY() + tr.getTranslateY());
            }
        };

        /**
         * Transforms the input point coordinates and stores them inside the destination point
         *
         * @param tr
         * @param src
         * @param dst
         */
        public abstract void transform(AffineTransform tr, Point2D src, Point2D dst);

        public static TRANSFORM getTransform(AffineTransform tr) {
            if (tr.isIdentity() || Math.abs(tr.getScaleX() - 1) == 0
                    && Math.abs(tr.getScaleY() - 1) == 0 && Math.abs(tr.getShearX()) == 0
                    && Math.abs(tr.getShearY()) == 0 && Math.abs(tr.getTranslateX()) <= 1E-3
                    && Math.abs(tr.getTranslateY()) <= 1E-3) {
                return IDENTITY;
            } else if (Math.abs(tr.getScaleX() - 1) == 0 && Math.abs(tr.getScaleY() - 1) == 0
                    && Math.abs(tr.getShearX()) == 0 && Math.abs(tr.getShearY()) == 0) {
                return TRANSLATION;
            } else {
                return AFFINE;
            }
        }
    }

    /**
     * Constructs a <code>BandMergeOpImage</code>.
     *
     * <p>
     * The <code>layout</code> parameter may optionally contain the tile grid layout, sample model, and/or color model. The image dimension is
     * determined by the intersection of the bounding boxes of the source images.
     *
     * <p>
     * The image layout of the first source image, <code>source1</code>, is used as the fallback for the image layout of the destination image. The
     * destination number of bands is the sum of all source image bands.
     *
     * @param sources <code>List</code> of sources.
     * @param transforms List of Affine transformations to use.
     * @param config Configurable attributes of the image including configuration variables indexed by <code>RenderingHints.Key</code>s and image
     *        properties indexed by <code>String</code>s or <code>CaselessStringKey</code>s. This is simply forwarded to the superclass constructor.
     * @param noData Array of No Data Range.
     * @param roi Input ROI to use for the calculations.
     * @param destinationNoData output value for No Data.
     * @param layout The destination image layout.
     */
    public ExtendedBandMergeOpImage(List sources, List<AffineTransform> transforms, Map config,
            Range[] noData, ROI roi, double destinationNoData, ImageLayout layout) {
        super(vectorize(sources), layoutHelper(sources, layout), config, false, null, null,
                new double[] { destinationNoData });

        // Initial Check on the source number and the related transformations
        if (transforms != null) {
            if (transforms.size() != sources.size()) {
                throw new IllegalArgumentException("Wrong Transformations number");
            }
            // Setting of the variable for the transformations
            this.transforms = transforms;
            this.transformObj = optimize(transforms); // optimize transformations

        } else {
            throw new IllegalArgumentException("No Transformation has been set");
        }

        // get ColorModels for IndexColorModel support
        int numSrcs = sources.size();
        colorModels = new ColorModel[numSrcs];

        for (int i = 0; i < numSrcs; i++) {
            colorModels[i] = ((RenderedImage) sources.get(i)).getColorModel();
        }
        // Destination Image data Type
        int dataType = getSampleModel().getDataType();

        // If No Data are present
        if (noData != null) {
            // If the length of the array is different from that of the sources
            // the first Range is used for all the images
            if (noData.length != numSrcs) {
                Range firstNoData = noData[0];

                this.noData = new Range[numSrcs];

                for (int i = 0; i < numSrcs; i++) {
                    this.noData[i] = firstNoData;
                }
            } else {
                // Else the whole array is used
                this.noData = noData;
            }
            // No Data are present, so associated flaw is set to true
            this.hasNoData = true;

        } else {
            this.noData = null;
            this.hasNoData = false;
        }

        // ROI settings
        this.roi = roi;
        hasROI = roi != null;

        // Definition of the possible cases that can be found
        // caseA = no ROI nor No Data
        // caseB = ROI present but No Data not present
        // caseC = No Data present but ROI not present
        // Last case not defined = both ROI and No Data are present
        caseA = !hasROI && !hasNoData;
        caseB = hasROI && !hasNoData;
        caseC = !hasROI && hasNoData;

        // Destination No Data value is clamped to the image data type
        switch (dataType) {
        case DataBuffer.TYPE_BYTE:
            this.destNoDataByte = ImageUtil.clampRoundByte(destinationNoData);
            break;
        case DataBuffer.TYPE_USHORT:
            this.destNoDataShort = ImageUtil.clampRoundUShort(destinationNoData);
            break;
        case DataBuffer.TYPE_SHORT:
            this.destNoDataShort = ImageUtil.clampRoundShort(destinationNoData);
            break;
        case DataBuffer.TYPE_INT:
            this.destNoDataInt = ImageUtil.clampRoundInt(destinationNoData);
            break;
        case DataBuffer.TYPE_FLOAT:
            this.destNoDataFloat = ImageUtil.clampFloat(destinationNoData);
            break;
        case DataBuffer.TYPE_DOUBLE:
            this.destNoDataDouble = destinationNoData;
            break;
        default:
            throw new IllegalArgumentException("Wrong image data type");
        }
    }

    private List<TRANSFORM> optimize(List<AffineTransform> transforms) {
        final List<TRANSFORM> result = new ArrayList<TRANSFORM>();
        for (AffineTransform tr : transforms) {
            result.add(TRANSFORM.getTransform(tr));
        }
        return result;
    }

    /**
     * This method takes in input the list of all the sources and calculates the total number of bands of the destination image.
     *
     * @param sources List of the source images
     * @return the total number of the destination bands
     */
    private static int totalNumBands(List sources) {
        // Initialization
        int total = 0;

        // Cycle on all the sources
        for (int i = 0; i < sources.size(); i++) {
            RenderedImage image = (RenderedImage) sources.get(i);

            // If the source ColorModel is IndexColorModel, then the bands are defined by its components
            if (image.getColorModel() instanceof IndexColorModel) {
                total += image.getColorModel().getNumComponents();
                // Else the bands are defined from the SampleModel
            } else {
                total += image.getSampleModel().getNumBands();
            }
        }
        // Total bands number
        return total;
    }

    private static ImageLayout layoutHelper(List sources, ImageLayout il) {

        // If the layout is not defined, a new one is created, else is cloned
        boolean newLayout = il == null;
        ImageLayout layout = newLayout ? new ImageLayout() : (ImageLayout) il.clone();
        // Number of input sources
        int numSources = sources.size();

        // dest data type is the maximum of transfertype of source image
        // utilizing the monotonicity of data types.

        // dest number of bands = sum of source bands
        int destNumBands = totalNumBands(sources);

        int destDataType = DataBuffer.TYPE_BYTE; // initialize
        RenderedImage srci = (RenderedImage) sources.get(0);
        // Boolean indicating that the rectangle intersection must be calculated
        boolean intersect = true;

        Rectangle destBounds = null;
        // If the layout is already present and contains the final image dimensions, then these dimensions are used for the layout
        if (layout.isValid(ImageLayout.MIN_X_MASK) && layout.isValid(ImageLayout.MIN_Y_MASK)
                && layout.isValid(ImageLayout.WIDTH_MASK)
                && layout.isValid(ImageLayout.HEIGHT_MASK)) {
            destBounds = new Rectangle(layout.getMinX(null), layout.getMinY(null),
                    layout.getWidth(null), layout.getHeight(null));
            intersect = false;
            if (destBounds.isEmpty()) {
                destBounds = null;
                intersect = true;
            }
        }

        // Destination Bounds are taken from the first image(if not present in the layout)
        if (intersect) {
            destBounds = new Rectangle(srci.getMinX(), srci.getMinY(), srci.getWidth(),
                    srci.getHeight());
        }

        // Cycle on all the images
        for (int i = 0; i < numSources; i++) {
            // Selection of a source
            srci = (RenderedImage) sources.get(i);
            // Intersection of the initial bounds with the source bounds, if not already defined
            if (intersect) {
                destBounds = destBounds.intersection(new Rectangle(srci.getMinX(), srci.getMinY(),
                        srci.getWidth(), srci.getHeight()));
            }

            // Selection of the source TransferType
            int typei = srci.getSampleModel().getTransferType();

            // NOTE: this depends on JDK ordering
            destDataType = typei > destDataType ? typei : destDataType;
        }

        if (intersect) {
            // Definition of the Layout
            layout.setMinX(destBounds.x);
            layout.setMinY(destBounds.y);
            layout.setWidth(destBounds.width);
            layout.setHeight(destBounds.height);
        }

        // First image sampleModel
        SampleModel sm = layout.getSampleModel((RenderedImage) sources.get(0));

        // Creation of a new SampleModel with the new settings
        if (sm.getNumBands() < destNumBands) {
            int[] destOffsets = new int[destNumBands];

            for (int i = 0; i < destNumBands; i++) {
                destOffsets[i] = i;
            }

            // determine the proper width and height to use
            int destTileWidth = sm.getWidth();
            int destTileHeight = sm.getHeight();
            if (layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
                destTileWidth = layout.getTileWidth((RenderedImage) sources.get(0));
            }
            if (layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
                destTileHeight = layout.getTileHeight((RenderedImage) sources.get(0));
            }

            sm = RasterFactory.createComponentSampleModel(sm, destDataType, destTileWidth,
                    destTileHeight, destNumBands);

            layout.setSampleModel(sm);
        }

        // Selection of a colormodel associated with the layout
        ColorModel cm = layout.getColorModel(null);

        if (cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
            // Clear the mask bit if incompatible.
            layout.unsetValid(ImageLayout.COLOR_MODEL_MASK);
        }

        if ((cm == null || !cm.hasAlpha()) && sm instanceof ComponentSampleModel) {
            cm = getDefaultColorModel(sm);
            layout.setColorModel(cm);
        }

        return layout;
    }

    /**
     * Create a colormodel without an alpha band in the case that no alpha band is present. Otherwise JAI set an alpha band by default for an image
     * with 2 or 4 bands.
     *
     * @param sm
     * @return
     */
    public static ColorModel getDefaultColorModel(SampleModel sm) {

        // Check on the data type
        int dataType = sm.getDataType();
        int numBands = sm.getNumBands();
        if (dataType < DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_SHORT
                || dataType > DataBuffer.TYPE_DOUBLE || numBands < 1 || numBands > 4) {
            return null;
        }

        // Creation of the colorspace
        ColorSpace cs = null;

        switch (numBands) {
        case 0:
            throw new IllegalArgumentException("No input bands defined");
        case 1:
            cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            break;
        case 2:
        case 4:
            // For 2 and 4 bands a custom colorspace is created
            cs = new ColorSpace(dataType, numBands) {

                @Override
                public float[] toRGB(float[] colorvalue) {
                    // TODO Auto-generated method stub
                    return null;
                }

                @Override
                public float[] toCIEXYZ(float[] colorvalue) {
                    // TODO Auto-generated method stub
                    return null;
                }

                @Override
                public float[] fromRGB(float[] rgbvalue) {
                    // TODO Auto-generated method stub
                    return null;
                }

                @Override
                public float[] fromCIEXYZ(float[] colorvalue) {
                    // TODO Auto-generated method stub
                    return null;
                }
            };
            break;
        case 3:
            cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            break;
        default:
            return null;
        }

        // Definition of the colormodel
        int dataTypeSize = DataBuffer.getDataTypeSize(dataType);
        int[] bits = new int[numBands];
        for (int i = 0; i < numBands; i++) {
            bits[i] = dataTypeSize;
        }

        boolean useAlpha = false, premultiplied = false;
        int transparency = Transparency.OPAQUE;
        switch (dataType) {
        case DataBuffer.TYPE_BYTE:
            return new ComponentColorModel(cs, bits, useAlpha, premultiplied, transparency,
                    dataType);
        case DataBuffer.TYPE_USHORT:
            return new ComponentColorModel(cs, bits, useAlpha, premultiplied, transparency,
                    dataType);
        case DataBuffer.TYPE_INT:
            return new ComponentColorModel(cs, bits, useAlpha, premultiplied, transparency,
                    dataType);
        case DataBuffer.TYPE_FLOAT:
            return new FloatDoubleColorModel(cs, useAlpha, premultiplied, transparency, dataType);
        case DataBuffer.TYPE_DOUBLE:
            return new FloatDoubleColorModel(cs, useAlpha, premultiplied, transparency, dataType);
        default:
            throw new IllegalArgumentException("Wrong data type used");
        }
    }

    /**
     * BandMerges the pixel values of multiple source images within a specified rectangle.
     *
     * @param sources Source images.
     * @param dest The tile containing the rectangle to be computed.
     * @param destRect The rectangle within the tile to be computed.
     */
    protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) {
        // Destination data type
        int destType = dest.getTransferType();

        ROI roiTile = null;

        // If a ROI is present, then only the part contained inside the current tile bounds is taken.
        if (hasROI) {
            Rectangle rect = new Rectangle(destRect);
            // The tile dimension is extended for avoiding border errors
            rect.grow(TILE_EXTENDER, TILE_EXTENDER);
            roiTile = roi.intersect(new ROIShape(rect));
        }

        if (!hasROI || !roiTile.getBounds().isEmpty()) {
            // Loop on the image raster
            switch (destType) {
            case DataBuffer.TYPE_BYTE:
                byteLoop(sources, dest, destRect, roiTile);
                break;
            case DataBuffer.TYPE_SHORT:
                ushortLoop(sources, dest, destRect, roiTile);
            case DataBuffer.TYPE_USHORT:
                shortLoop(sources, dest, destRect, roiTile);
                break;
            case DataBuffer.TYPE_INT:
                intLoop(sources, dest, destRect, roiTile);
                break;
            case DataBuffer.TYPE_FLOAT:
                floatLoop(sources, dest, destRect, roiTile);
                break;
            case DataBuffer.TYPE_DOUBLE:
                doubleLoop(sources, dest, destRect, roiTile);
                break;
            default:
                throw new RuntimeException("Wrong image data type");
            }
        } else {
            ImageUtil.fillBackground(dest, destRect, backgroundValues);
        }
    }

    private void byteLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect,
            ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        byte[][] dstdata = (byte[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (byte) (iter
                                        .getSample(srcX, srcY, sb) & 0xFF);
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (byte) (iter
                                            .getSample(srcX, srcY, sb) & 0xFF);
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                byte pixelValue = (byte) (iter.getSample(srcX, srcY, sb) & 0xFF);
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    byte pixelValue = (byte) (iter.getSample(srcX, srcY, sb) & 0xFF);
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataByte;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    private void ushortLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect,
            ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        short[][] dstdata = (short[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (short) (iter
                                        .getSample(srcX, srcY, sb));
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (short) (iter
                                            .getSample(srcX, srcY, sb));
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                short pixelValue = (short) (iter.getSample(srcX, srcY, sb));
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    short pixelValue = (short) (iter.getSample(srcX, srcY, sb));
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    private void shortLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect,
            ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        short[][] dstdata = (short[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (short) (iter
                                        .getSample(srcX, srcY, sb));
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (short) (iter
                                            .getSample(srcX, srcY, sb));
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                short pixelValue = (short) (iter.getSample(srcX, srcY, sb));
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    short pixelValue = (short) (iter.getSample(srcX, srcY, sb));
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataShort;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    private void intLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect, ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        int[][] dstdata = (int[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                        .getSample(srcX, srcY, sb));
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                            .getSample(srcX, srcY, sb));
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                int pixelValue = (iter.getSample(srcX, srcY, sb));
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    int pixelValue = (iter.getSample(srcX, srcY, sb));
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataInt;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    private void floatLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect,
            ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        float[][] dstdata = (float[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                        .getSampleFloat(srcX, srcY, sb));
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                            .getSampleFloat(srcX, srcY, sb));
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                float pixelValue = (iter.getSampleFloat(srcX, srcY, sb));
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    float pixelValue = (iter.getSampleFloat(srcX, srcY, sb));
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataFloat;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    private void doubleLoop(PlanarImage[] sources, WritableRaster dest, Rectangle destRect,
            ROI roiTile) {
        // Source number
        int nSrcs = sources.length;
        // Bands associated with each sources
        int[] snbands = new int[nSrcs];
        for (int i = 0; i < nSrcs; i++) {

            if (colorModels[i] instanceof IndexColorModel) {
                snbands[i] = colorModels[i].getNumComponents();
            } else {
                snbands[i] = sources[i].getNumBands();
            }
        }

        // Destination bands
        int dnbands = dest.getNumBands();
        // Destination data type
        int destType = dest.getTransferType();
        // PixelAccessor associated with the destination raster
        PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);

        UnpackedImageData dimd = d.getPixels(dest, destRect, destType, true);

        // Destination data values
        double[][] dstdata = (double[][]) dimd.data;

        int dstPixelStride = dimd.pixelStride;
        int dstLineStride = dimd.lineStride;

        RandomIter iter;
        // Source and Destination Point2D objects
        Point2D ptSrc = new Point2D.Double(0, 0);
        Point2D ptDst = new Point2D.Double(0, 0);
        // Destination object initial position
        final int minX = destRect.x;
        final int minY = destRect.y;

        int db = 0;

        // Only valid data
        if (caseA) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                        .getSampleDouble(srcX, srcY, sb));
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only ROI
        } else if (caseB) {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = (iter
                                            .getSampleDouble(srcX, srcY, sb));
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // Only NoData
        } else if (caseC) {

            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {

                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // Set the x,y destination pixel location
                        ptDst.setLocation(x + minX, y + minY);
                        // Map destination pixel to source pixel
                        transObj.transform(trans, ptDst, ptSrc);
                        // Source pixel indexes
                        int srcX = round(ptSrc.getX());
                        int srcY = round(ptSrc.getY());
                        // Check if the pixel is inside the source dimension
                        if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY) {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                if (db >= dnbands) {
                                    // exceeding destNumBands; should not have happened
                                    break;
                                }
                                // No Data control
                                double pixelValue = (iter.getSampleDouble(srcX, srcY, sb));
                                if (noData[sindex].contains(pixelValue)) {
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                                } else {
                                    // Setting the value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                }
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
            // NoData and ROI
        } else {
            // Cycle on all the sources
            for (int sindex = 0; sindex < nSrcs; sindex++) {
                // Random Iterator for cycling on the sources
                iter = RandomIterFactory.create(sources[sindex], sources[sindex].getBounds());
                // Affine transformation for the selected source
                AffineTransform trans = transforms.get(sindex);
                TRANSFORM transObj = transformObj.get(sindex);
                // Source corners
                final int srcMinX = sources[sindex].getMinX();
                final int srcMinY = sources[sindex].getMinY();
                final int srcMaxX = sources[sindex].getMaxX();
                final int srcMaxY = sources[sindex].getMaxY();
                // Destination Line and Pixel offset initialization
                int dstLineOffset = 0;
                int dstPixelOffset = 0;

                // Cycle on the y-axis
                for (int y = 0; y < destRect.height; y++) {
                    dstPixelOffset = dstLineOffset;
                    // Cycle on the x-axis
                    for (int x = 0; x < destRect.width; x++) {
                        // ROI check
                        int dstX = x + minX;
                        int dstY = y + minY;
                        if (roiTile.contains(dstX, dstY)) {
                            // Set the x,y destination pixel location
                            ptDst.setLocation(dstX, dstY);
                            // Map destination pixel to source pixel
                            transObj.transform(trans, ptDst, ptSrc);
                            // Source pixel indexes
                            int srcX = round(ptSrc.getX());
                            int srcY = round(ptSrc.getY());
                            // Check if the pixel is inside the source dimension
                            if (srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY
                                    || srcY >= srcMaxY) {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // Setting the no data value
                                    dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                                }
                            } else {
                                // Cycle on the bands
                                for (int sb = 0; sb < snbands[sindex]; sb++) {
                                    if (db >= dnbands) {
                                        // exceeding destNumBands; should not have happened
                                        break;
                                    }
                                    // No Data control
                                    double pixelValue = (iter.getSampleDouble(srcX, srcY, sb));
                                    if (noData[sindex].contains(pixelValue)) {
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                                    } else {
                                        // Setting the value
                                        dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = pixelValue;
                                    }
                                }
                            }
                        } else {
                            // Cycle on the bands
                            for (int sb = 0; sb < snbands[sindex]; sb++) {
                                // Setting the no data value
                                dstdata[db + sb][dstPixelOffset + dimd.getOffset(db + sb)] = destNoDataDouble;
                            }
                        }
                        dstPixelOffset += dstPixelStride;
                    }
                    dstLineOffset += dstLineStride;
                }
                db += snbands[sindex];
            }
        }
        d.setPixels(dimd);
    }

    /**
     * This method takes in input a List of Objects and creates a vector from its elements
     *
     * @param sources list of the input sources
     * @return a vector of all the input list
     */
    private static Vector vectorize(List sources) {
        if (sources instanceof Vector) {
            return (Vector) sources;
        } else {
            Vector vector = new Vector(sources.size());
            for (Object element : sources) {
                vector.add(element);
            }
            return vector;
        }
    }

    /** Returns the "round" value of a double. */
    private static int round(double f) {
        return f >= 0 ? (int) (f + 0.5F) : (int) (f - 0.5F);
    }

    @Override
    protected Rectangle backwardMapRect(Rectangle arg0, int arg1) {
        return arg0;
    }

    @Override
    protected Rectangle forwardMapRect(Rectangle arg0, int arg1) {
        return arg0;
    }
}
TOP

Related Classes of org.geotools.processing.jai.ExtendedBandMergeOpImage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.