/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2012, Geomatys * * 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.geotoolkit.image.iterator; import java.awt.Rectangle; import java.awt.image.*; import java.util.Arrays; /** * Create an appropriate iterator. * * @author RĂ©mi Marechal (Geomatys). */ public final class PixelIteratorFactory { private PixelIteratorFactory() { } /** * Create and return an adapted default raster iterator. * * @param raster {@link Raster} will be traveled by iterator. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultIterator(final Raster raster) { return createDefaultIterator(raster, null); } /** * Create and return an adapted default read-only raster iterator to read on raster sub-area. * * @param raster {@link Raster} will be traveled by iterator from it's sub-area. * @param subReadArea {@link Rectangle} which define raster read area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultIterator(final Raster raster, final Rectangle subReadArea) { final SampleModel sampleM = raster.getSampleModel(); if (sampleM instanceof ComponentSampleModel) { if (checkBankIndices(((ComponentSampleModel)sampleM).getBankIndices())) { switch (sampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new DefaultDirectByteIterator(raster, subReadArea); case DataBuffer.TYPE_FLOAT : return new DefaultDirectFloatIterator(raster, subReadArea); default : return new DefaultIterator(raster, subReadArea); } } } return new DefaultIterator(raster, subReadArea); } /** * Create and return an adapted default read-only rendered image iterator. * * @param renderedImage {@link RenderedImage} will be traveled by iterator. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultIterator(final RenderedImage renderedImage) { return createDefaultIterator(renderedImage, null); } /** * Create and return an adapted default read-only raster iterator to read on raster sub-area. * * @param renderedImage {@link RenderedImage} will be traveled by iterator from it's sub-area. * @param subReadArea {@link Rectangle} which define rendered image read area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultIterator(final RenderedImage renderedImage, final Rectangle subReadArea) { if(isSingleRaster(renderedImage)){ return createDefaultIterator(renderedImage.getTile(renderedImage.getMinTileX(), renderedImage.getMinTileY()), subReadArea); } final SampleModel sampleM = renderedImage.getSampleModel(); if (sampleM instanceof ComponentSampleModel ) { if (checkBankIndices(((ComponentSampleModel)sampleM).getBankIndices())) { switch (sampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new DefaultDirectByteIterator(renderedImage, subReadArea); case DataBuffer.TYPE_FLOAT : return new DefaultDirectFloatIterator(renderedImage, subReadArea); default : return new DefaultIterator(renderedImage, subReadArea); } } } return new DefaultIterator(renderedImage, subReadArea); } /** * Create and return an adapted default read and write raster iterator. * * @param raster {@link Raster} will be traveled by read-only iterator. * @param writeableRaster {@link WritableRaster} raster wherein value is set (write). * @return adapted {@link PixelIterator} . */ public static PixelIterator createDefaultWriteableIterator(final Raster raster, final WritableRaster writeableRaster) { return createDefaultWriteableIterator(raster, writeableRaster, null); } /** * Create and return an adapted default read and write raster iterator. * Iterator move in a raster sub-area. * * @param raster {@link Raster} will be traveled by read-only iterator. * @param writeableRaster {@link WritableRaster} raster wherein value is set (write). * @param subArea {@link Rectangle} which define raster read and write area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultWriteableIterator(final Raster raster, final WritableRaster writeableRaster, final Rectangle subArea) { final SampleModel srcSampleM = raster.getSampleModel(); final SampleModel destSampleM = raster.getSampleModel(); PixelIterator.checkRasters(raster, writeableRaster); if (srcSampleM instanceof ComponentSampleModel && destSampleM instanceof ComponentSampleModel) { ComponentSampleModel srcCSModel = (ComponentSampleModel) srcSampleM; ComponentSampleModel destCSModel = (ComponentSampleModel) destSampleM; // Source and destination image must have identical structure in order to allow a single iterator to move through them. if (checkBankIndices(srcCSModel.getBankIndices()) && checkBankIndices(destCSModel.getBankIndices()) && Arrays.equals(srcCSModel.getBandOffsets(), destCSModel.getBandOffsets()) && srcCSModel.getPixelStride() == destCSModel.getPixelStride() && srcCSModel.getScanlineStride() == destCSModel.getScanlineStride()) { switch (srcSampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new DefaultWritableDirectByteIterator(raster, writeableRaster, subArea); case DataBuffer.TYPE_FLOAT : return new DefaultWritableDirectFloatIterator(raster, writeableRaster, subArea); default : return new DefaultWritableIterator(raster, writeableRaster, subArea); } } } return new DefaultWritableIterator(raster, writeableRaster, subArea); } /** * Create and return an adapted default read and write rendered image iterator. * * @param renderedImage {@link RenderedImage} will be traveled by iterator. * @param writableRenderedImage {@link WritableRenderedImage} rendered image wherein value is set (write). * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultWriteableIterator(final RenderedImage renderedImage, final WritableRenderedImage writableRenderedImage) { return createDefaultWriteableIterator(renderedImage, writableRenderedImage, null); } /** * Create and return an adapted default read and write rendered image iterator from it's sub-area. * * @param renderedImage {@link RenderedImage} will be traveled by iterator from it's sub-area. * @param writableRenderedImage {@link WritableRenderedImage} rendered image wherein value is set (write). * @param subArea {@link Rectangle} which define rendered image read and write area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createDefaultWriteableIterator(final RenderedImage renderedImage, final WritableRenderedImage writableRenderedImage, final Rectangle subArea) { final SampleModel srcSampleM = renderedImage.getSampleModel(); final SampleModel destSampleM = renderedImage.getSampleModel(); if (srcSampleM instanceof ComponentSampleModel && destSampleM instanceof ComponentSampleModel) { ComponentSampleModel srcCSModel = (ComponentSampleModel) srcSampleM; ComponentSampleModel destCSModel = (ComponentSampleModel) destSampleM; // Source and destination image must have identical structure in order to allow a single iterator to move through them. if (checkBankIndices(srcCSModel.getBankIndices()) && checkBankIndices(destCSModel.getBankIndices()) && Arrays.equals(srcCSModel.getBandOffsets(), destCSModel.getBandOffsets()) && srcCSModel.getPixelStride() == destCSModel.getPixelStride() && srcCSModel.getScanlineStride() == destCSModel.getScanlineStride()) { switch (srcSampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new DefaultWritableDirectByteIterator(renderedImage, writableRenderedImage, subArea); case DataBuffer.TYPE_FLOAT : return new DefaultWritableDirectFloatIterator(renderedImage, writableRenderedImage, subArea); default : return new DefaultWritableIterator(renderedImage, writableRenderedImage, subArea); } } } return new DefaultWritableIterator(renderedImage, writableRenderedImage, subArea); } ////////////////////////////// Row Major Iterator //////////////////////////// /** * Create and return an adapted Row Major read-only rendered image iterator. * RowMajor : iterator move forward line per line one by one in downward order. * * @param renderedImage {@link RenderedImage} will be traveled by iterator. * @return adapted {@link PixelIterator}. */ public static PixelIterator createRowMajorIterator(final RenderedImage renderedImage) { return createRowMajorIterator(renderedImage, null); } /** * Create and return an adapted Row Major read-only rendered image iterator from it's sub-area. * RowMajor : iterator move forward line per line one by one in downward order. * * @param renderedImage {@link RenderedImage} will be traveled by iterator from it's sub-area. * @param subReadArea {@link Rectangle} which define rendered image read-only area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createRowMajorIterator(final RenderedImage renderedImage, final Rectangle subReadArea) { final SampleModel sampleM = renderedImage.getSampleModel(); if (sampleM instanceof ComponentSampleModel) { if (checkBankIndices(((ComponentSampleModel)sampleM).getBankIndices())) { switch (sampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new RowMajorDirectByteIterator(renderedImage, subReadArea); case DataBuffer.TYPE_FLOAT : return new RowMajorDirectFloatIterator(renderedImage, subReadArea); default : return new RowMajorIterator(renderedImage, subReadArea); } } } return new RowMajorIterator(renderedImage, subReadArea); } /** * Create and return an adapted Row Major read and write rendered image iterator. * RowMajor : iterator move forward line per line one by one in downward order. * * No build is allowed for single {@link java.awt.image.Raster} browsing, because {@link org.geotoolkit.image.iterator.DefaultDirectIterator} * will do the job as fast as it, and with the same behavior. * * @param renderedImage {@link RenderedImage} will be traveled by iterator. * @param writableRenderedImage {@link WritableRenderedImage} rendered image wherein value is set (write). * @return adapted {@link PixelIterator}. */ public static PixelIterator createRowMajorWriteableIterator(final RenderedImage renderedImage, final WritableRenderedImage writableRenderedImage) { return createRowMajorWriteableIterator(renderedImage, writableRenderedImage, null); } /** * Create and return an adapted Row Major read and write rendered image iterator from it's sub-area. * RowMajor : iterator move forward line per line one by one in downward order. * * No build is allowed for single {@link java.awt.image.Raster} browsing, because {@link org.geotoolkit.image.iterator.DefaultDirectIterator} * will do the job as fast as it, and with the same behavior. * * @param renderedImage {@link RenderedImage} will be traveled by iterator from it's sub-area. * @param writableRenderedImage {@link WritableRenderedImage} rendered image wherein value is set (write). * @param subArea {@link Rectangle} which define rendered image read and write area. * @return adapted {@link PixelIterator}. */ public static PixelIterator createRowMajorWriteableIterator(final RenderedImage renderedImage, final WritableRenderedImage writableRenderedImage, final Rectangle subArea) { final SampleModel srcSampleM = renderedImage.getSampleModel(); final SampleModel destSampleM = renderedImage.getSampleModel(); if (srcSampleM instanceof ComponentSampleModel && destSampleM instanceof ComponentSampleModel) { ComponentSampleModel srcCSModel = (ComponentSampleModel) srcSampleM; ComponentSampleModel destCSModel = (ComponentSampleModel) destSampleM; // Source and destination image must have identical structure in order to allow a single iterator to move through them. if (checkBankIndices(srcCSModel.getBankIndices()) && checkBankIndices(destCSModel.getBankIndices()) && Arrays.equals(srcCSModel.getBandOffsets(), destCSModel.getBandOffsets()) && srcCSModel.getPixelStride() == destCSModel.getPixelStride() && srcCSModel.getScanlineStride() == destCSModel.getScanlineStride()) { switch (srcSampleM.getDataType()) { case DataBuffer.TYPE_BYTE : return new RowMajorWritableDirectByteIterator(renderedImage, writableRenderedImage, subArea); case DataBuffer.TYPE_FLOAT : return new RowMajorWritableDirectFloatIterator(renderedImage, writableRenderedImage, subArea); default : return new RowMajorWritableIterator(renderedImage, writableRenderedImage, subArea); } } } return new RowMajorWritableIterator(renderedImage, writableRenderedImage, subArea); } /** * Verify bandOffset table conformity. * * @param bandOffset band offset table. * @return true if bandOffset table is conform else false. */ private static boolean checkBandOffset(int[] bandOffset) { for (int i = 0, l = bandOffset.length; i<l; i++) if (bandOffset[i] != i) return false; return true; } /** * Check image samples are stored in a single bank. It's a needed condition for {@link org.geotoolkit.image.iterator.DefaultDirectIterator} * * @param bankIndices bank indice table retrieved from input image (see {@link java.awt.image.ComponentSampleModel#getBankIndices()}. * @return true if input image use a single bank. false otherwise. */ private static boolean checkBankIndices(int[] bankIndices) { if (bankIndices.length == 1) return true; for (int i = 1, l = bankIndices.length; i<l; i++) if (bankIndices[i] != bankIndices[i-1]) return false; return true; } private static boolean isSingleRaster(final RenderedImage renderedImage){ return renderedImage.getNumXTiles()==1 && renderedImage.getNumYTiles()==1; } }