/*
* 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.io.Closeable;
import org.apache.sis.util.ArgumentChecks;
import org.opengis.coverage.grid.SequenceType;
/**
* Define standard iterator for image pixel.
*
* Iteration order is define in sub-classes implementation.
* However iteration begging by Bands.
*
* Moreover comportment not specify if iterator exceed image limits.
*
* TODO : Move setSample* methods in a separate WritablePixelIterator interface.
*
* @author RĂ©mi Marechal (Geomatys).
* @author Martin Desruisseaux (Geomatys).
*/
public abstract class PixelIterator implements Closeable {
/**
* The X coordinate of the upper-left pixel of iteration area.
*/
protected final int areaIterateMinX;
/**
* The Y coordinate of the upper-left pixel of iteration area.
*/
protected final int areaIterateMinY;
/**
* The X coordinate of the lower-right pixel of iteration area.
*/
protected final int areaIterateMaxX;
/**
* The X coordinate of the lower-right pixel of iteration area.
*/
protected final int areaIterateMaxY;
/**
* Current raster which is followed by Iterator.
*/
protected Raster currentRaster;
/**
* RenderedImage which is followed by Iterator.
*/
protected final RenderedImage renderedImage;
/**
* Number of band.
*/
protected final int fixedNumBand;
/**
* Number of raster band.
* WARNING ! this is used a bit everywhere in iterator as a 'updateTileRaster' flag.
*/
protected int rasterNumBand;
/**
* The X coordinate of the bottom-right pixel of this current raster.
*/
protected int maxX;
/**
* The Y coordinate of the bottom-right pixel of this current raster.
*/
protected int maxY;
/**
* Current band position in this current raster.
*/
protected int band;
/**
* The X index coordinate of the upper-left tile of this rendered image.
*/
protected int tMinX;
/**
* The Y index coordinate of the upper-left tile of this rendered image.
*/
protected int tMinY;
/**
* The X index coordinate of the bottom-right tile of this rendered image.
*/
protected int tMaxX;
/**
* The Y index coordinate of the bottom-right tile of this rendered image.
*/
protected int tMaxY;
/**
* Current x tile position in rendered image tile array.
*/
protected int tX;
/**
* Current y tile position in rendered image tile array.
*/
protected int tY;
/**
* Tile width of object that iterate.
* @see RenderedImage#getTileWidth()
* @see Raster#getWidth()
*/
protected final int tileWidth;
/**
* Tile height of object that iterate.
* @see RenderedImage#getTileHeight()
* @see Raster#getHeight()
*/
protected final int tileHeight;
/**
* Define area travel by iterator in the current object that iterate.
* @see #getBoundary(boolean)
*/
protected Rectangle areaIterate;
/**
* Define area of the object that iterate.
* @see #getBoundary(boolean)
*/
protected Rectangle generalObjectArea;
/**
* {@link SampleModel} from the iterate object.
*/
protected final SampleModel currentSampleModel;
/**
* Create raster iterator to follow from minX, minY raster and rectangle intersection coordinate.
*
* @param raster will be followed by this iterator.
* @param subArea {@code Rectangle} which define read iterator area.
* @throws IllegalArgumentException if subArea don't intersect raster boundary.
*/
PixelIterator(final Raster raster, final Rectangle subArea) {
ArgumentChecks.ensureNonNull("raster", raster);
this.currentRaster = raster;
this.renderedImage = null;
final int minx = raster.getMinX();
final int miny = raster.getMinY();
tileWidth = raster.getWidth();
tileHeight = raster.getHeight();
final int maxx = minx + tileWidth;
final int maxy = miny + tileHeight;
currentSampleModel = raster.getSampleModel();
if (subArea != null) {
final int sAMX = subArea.x;
final int sAMY = subArea.y;
//intersection
this.areaIterateMinX = Math.max(sAMX, minx);
this.areaIterateMinY = Math.max(sAMY, miny);
this.maxX = this.areaIterateMaxX = Math.min(sAMX + Math.max(0, subArea.width), maxx);
this.maxY = this.areaIterateMaxY = Math.min(sAMY + Math.max(0, subArea.height), maxy);
} else {
//areaIterate
this.areaIterateMinX = minx;
this.areaIterateMinY = miny;
this.areaIterateMaxX = maxx;
this.areaIterateMaxY = maxy;
}
this.rasterNumBand = raster.getNumBands();
this.fixedNumBand = this.rasterNumBand;
if(areaIterateMinX >= areaIterateMaxX || areaIterateMinY >= areaIterateMaxY) {
throw new IllegalArgumentException("No intersection between subArea and raster.\n" + raster + '\n' + subArea);
}
this.band = -1;
tX = tY = 0;
tMaxX = tMaxY = 1;
}
/**
* Create default rendered image iterator.
*
* @param renderedImage image which will be follow by iterator.
* @param subArea {@code Rectangle} which represent image sub area iteration.
* @throws IllegalArgumentException if subArea don't intersect image boundary.
*/
PixelIterator(final RenderedImage renderedImage, final Rectangle subArea) {
ArgumentChecks.ensureNonNull("renderedImage", renderedImage);
this.renderedImage = renderedImage;
final int rIminX = renderedImage.getMinX();
final int rIminY = renderedImage.getMinY();
final int rImaxX = rIminX + renderedImage.getWidth();
final int rImaxY = rIminY + renderedImage.getHeight();
tileWidth = renderedImage.getTileWidth();
tileHeight = renderedImage.getTileHeight();
final int rIMinTileX = renderedImage.getMinTileX();
final int rIMinTileY = renderedImage.getMinTileY();
currentSampleModel = renderedImage.getSampleModel();
if (subArea != null) {
final int sAMX = subArea.x;
final int sAMY = subArea.y;
//intersection
this.areaIterateMinX = Math.max(sAMX, rIminX);
this.areaIterateMinY = Math.max(sAMY, rIminY);
this.areaIterateMaxX = Math.min(sAMX + subArea.width, rImaxX);
this.areaIterateMaxY = Math.min(sAMY + subArea.height, rImaxY);
} else {
//areaIterate
this.areaIterateMinX = rIminX;
this.areaIterateMinY = rIminY;
this.areaIterateMaxX = rImaxX;
this.areaIterateMaxY = rImaxY;
}
//intersection test
if (areaIterateMinX >= areaIterateMaxX || areaIterateMinY >= areaIterateMaxY) {
throw new IllegalArgumentException("No intersection between subArea and image.\n" + renderedImage + '\n' + subArea);
}
//tiles attributs
this.tMinX = (areaIterateMinX - rIminX) / tileWidth + rIMinTileX;
this.tMinY = (areaIterateMinY - rIminY) / tileHeight + rIMinTileY;
this.tMaxX = (areaIterateMaxX - rIminX + tileWidth - 1) / tileWidth + rIMinTileX;
this.tMaxY = (areaIterateMaxY - rIminY + tileHeight - 1) / tileHeight + rIMinTileY;
//initialize attributs to first iteration
this.rasterNumBand = this.maxY = this.maxX = 1;
this.fixedNumBand = currentSampleModel.getNumBands();
}
/**
* Returns true if the iteration has more pixel(in other words if {@linkplain #next()} is possible)
* and move forward iterator.
*
* @return true if next value exist else false.
* @throws IllegalStateException if you call again this method when you have
* already reach the end of the iteration.
*/
public abstract boolean next();
/**
* Returns next X iterator coordinate without move forward it.
* User must call next() method before getX() method.
*
* @return X iterator position.
*/
public abstract int getX();
/**
* Returns next Y iterator coordinate without move forward it.
* User must call next() method before getY() method.
*
* @return Y iterator position.
*/
public abstract int getY();
/**
* Returns the next integer value from iteration.
*
* @return the next integer value.
*/
public abstract int getSample();
/**
* Returns the next float value from iteration.
*
* @return the next float value.
*/
public abstract float getSampleFloat();
/**
* Returns the next double value from iteration.
*
* @return the next double value.
*/
public abstract double getSampleDouble();
/**
* Initializes iterator.
* Carry back iterator at its initial position like iterator is just build.
*/
public abstract void rewind();
/**
* Write integer value at current iterator position.
*
* @param value integer to write.
*/
public abstract void setSample(final int value);
/**
* Write float value at current iterator position.
*
* @param value float to write.
*/
public abstract void setSampleFloat(final float value);
/**
* Write double value at current iterator position.
*
* @param value double to write.
*/
public abstract void setSampleDouble(final double value);
/**
* To release last tiles iteration from writable rendered image tiles array.
* if this method is invoked in read-only iterator, method is idempotent (has no effect).
*/
public abstract void close();
/**
* Return type of sequence iteration direction.
*
* @return type of sequence.
*/
public abstract SequenceType getIterationDirection();
/**
* <p>Move forward iterator cursor at x, y coordinates. Cursor is automatically
* positioned at band index.<br/>
*
* Code example :<br/>
* {@code PixelIterator.moveTo(x, y, b);}<br/>
*
* {@code do} {<br/>
* {@code PixelIterator.getSample();//for example}<br/>
* } {@code while (PixelIterator.next());}<br/>
*
* MoveTo method is configure to use do...while() loop after moveTo call.</p>
*
* @param x the x coordinate cursor position.
* @param y the y coordinate cursor position.
* @param b the band index cursor position.
* @throws IllegalArgumentException if coordinates are out of iteration area boundary.
*/
public void moveTo(int x, int y, int b){
if (x < areaIterateMinX || x >= areaIterateMaxX
|| y < areaIterateMinY || y >= areaIterateMaxY)
throw new IllegalArgumentException("coordinate out of iteration area define by: "
+"("+areaIterateMinX+", "+areaIterateMinY+")"+" ; ("+areaIterateMaxX+", "+areaIterateMaxY+") given coord is "+x+" "+y);
if (b<0 || b>=fixedNumBand)
throw new IllegalArgumentException("band index out of numband border define by: [0;"+fixedNumBand+"]");
}
/**
* Returns the number of bands (samples per pixel) from Image or Raster within this Iterator.
*
* @return the number of bands (samples per pixel) from current raster or Image.
*/
public int getNumBands() {
return fixedNumBand;
}
/**
* Returns {@code Rectangle} which is Image or Raster boundary within this Iterator.
*
* @param areaIterate true to get area iterate boundary, false to get boundary of object that iterate.
* @return {@code Rectangle} which is Image or Raster boundary within this Iterator.
*/
public Rectangle getBoundary(final boolean areaIterate) {
if (areaIterate){
if (this.areaIterate == null) this.areaIterate = new Rectangle(areaIterateMinX, areaIterateMinY, areaIterateMaxX-areaIterateMinX, areaIterateMaxY-areaIterateMinY);
return this.areaIterate;
}
if (generalObjectArea == null) {
int x, y, w, h;
if (renderedImage == null) {
x = currentRaster.getMinX();
y = currentRaster.getMinY();
w = currentRaster.getWidth();
h = currentRaster.getHeight();
} else {
x = renderedImage.getMinX();
y = renderedImage.getMinY();
w = renderedImage.getWidth();
h = renderedImage.getHeight();
}
generalObjectArea = new Rectangle(x, y, w, h);
}
return generalObjectArea;
}
/**
* Return {@link RenderedImage} that this iterator travel.
*
* @return {@link RenderedImage} that this iterator travel.
*/
public RenderedImage getRenderedImage() {
return renderedImage;
}
/**
* Check that the two input rasters are compatible for coupling in a {@link WritablePixelIterator}
*/
public static void checkRasters(final Raster readableRaster, final WritableRaster writableRaster){
//raster dimension
if (readableRaster.getMinX() != writableRaster.getMinX()
|| readableRaster.getMinY() != writableRaster.getMinY()
|| readableRaster.getWidth() != writableRaster.getWidth()
|| readableRaster.getHeight() != writableRaster.getHeight()
|| readableRaster.getNumBands() != writableRaster.getNumBands())
throw new IllegalArgumentException("raster and writable raster are not in same dimension"+readableRaster+writableRaster);
//raster data type
if (readableRaster.getDataBuffer().getDataType() != writableRaster.getDataBuffer().getDataType())
throw new IllegalArgumentException("raster and writable raster haven't got same datas type");
}
/**
* Verify Rendered image conformity.
*/
public static void checkRenderedImage(final RenderedImage renderedImage, final WritableRenderedImage writableRI) {
//image dimensions
if (renderedImage.getMinX() != writableRI.getMinX()
|| renderedImage.getMinY() != writableRI.getMinY()
|| renderedImage.getWidth() != writableRI.getWidth()
|| renderedImage.getHeight() != writableRI.getHeight()
|| renderedImage.getSampleModel().getNumBands() != writableRI.getSampleModel().getNumBands())
throw new IllegalArgumentException("rendered image and writable rendered image dimensions are not conform.\n" +
"First : "+renderedImage+"\nSecond : "+writableRI);
final int wrimtx = writableRI.getMinTileX();
final int wrimty = writableRI.getMinTileY();
final int rimtx = writableRI.getMinTileX();
final int rimty = writableRI.getMinTileY();
//tiles dimensions
if (rimtx != wrimtx
|| rimty != wrimty
|| renderedImage.getNumXTiles() != writableRI.getNumXTiles()
|| renderedImage.getNumYTiles() != writableRI.getNumYTiles()
|| renderedImage.getTileGridXOffset() != writableRI.getTileGridXOffset()
|| renderedImage.getTileGridYOffset() != writableRI.getTileGridYOffset()
|| renderedImage.getTileHeight() != writableRI.getTileHeight()
|| renderedImage.getTileWidth() != writableRI.getTileWidth())
throw new IllegalArgumentException("rendered image and writable rendered image tiles configuration are not conform.\n" +
"First : "+renderedImage+"\nSecond : "+writableRI);
//data type
// TODO : Should be required only for Direct iterators (working directly with data buffers)
if (renderedImage.getTile(rimtx, rimty).getDataBuffer().getDataType() != writableRI.getTile(wrimtx, wrimty).getDataBuffer().getDataType())
throw new IllegalArgumentException("rendered image and writable rendered image haven't got same datas type");
}
/**
* Verify raster conformity.
*/
protected void checkRasters(final Raster readableRaster, final WritableRaster writableRaster, final Rectangle subArea) {
final int wRmx = writableRaster.getMinX();
final int wRmy = writableRaster.getMinY();
final int wRw = writableRaster.getWidth();
final int wRh = writableRaster.getHeight();
if ((wRmx != areaIterateMinX)
|| wRmy != areaIterateMinY
|| wRw != areaIterateMaxX - areaIterateMinX
|| wRh != areaIterateMaxY - areaIterateMinY)
//raster dimension
if ((readableRaster.getMinX() != wRmx)
|| readableRaster.getMinY() != wRmy
|| readableRaster.getWidth() != wRw
|| readableRaster.getHeight() != wRh)
throw new IllegalArgumentException("raster and writable raster are not in same dimension"+readableRaster+writableRaster);
if (readableRaster.getNumBands() != writableRaster.getNumBands())
throw new IllegalArgumentException("raster and writable raster haven't got same band number");
//raster data type
if (readableRaster.getDataBuffer().getDataType() != writableRaster.getDataBuffer().getDataType())
throw new IllegalArgumentException("raster and writable raster haven't got same datas type");
}
/**
* Verify Rendered image conformity.
*/
protected void checkRenderedImage(final RenderedImage renderedImage, final WritableRenderedImage writableRI, final Rectangle subArea) {
if (renderedImage.getSampleModel().getNumBands() != writableRI.getSampleModel().getNumBands())
throw new IllegalArgumentException("renderedImage and writableRenderedImage haven't got same band number");
final int riMinX = renderedImage.getMinX();
final int riMinY = renderedImage.getMinY();
final int riTileWidth = renderedImage.getTileWidth();
final int riTileHeight = renderedImage.getTileHeight();
final int rimtx = renderedImage.getMinTileX();
final int rimty = renderedImage.getMinTileY();
final int wrimtx = writableRI.getMinTileX();
final int wrimty = writableRI.getMinTileY();
//data type
if (renderedImage.getTile(rimtx, rimty).getDataBuffer().getDataType() != writableRI.getTile(wrimtx, wrimty).getDataBuffer().getDataType())
throw new IllegalArgumentException("rendered image and writable rendered image haven't got same datas type");
//tiles dimensions
if (renderedImage.getTileHeight() != writableRI.getTileHeight()
|| renderedImage.getTileWidth() != writableRI.getTileWidth()
|| renderedImage.getTileGridXOffset() != writableRI.getTileGridXOffset()
|| renderedImage.getTileGridYOffset() != writableRI.getTileGridYOffset())
throw new IllegalArgumentException("rendered image and writable rendered image tiles configuration are not conform"+renderedImage+writableRI);
//verifier les index de tuiles au depart
final boolean minTileX = (wrimtx == (areaIterateMinX-riMinX)/riTileWidth+rimtx);
final boolean minTileY = (wrimty == (areaIterateMinY-riMinY)/riTileHeight+rimty);
//writable image correspond with iteration area
if (writableRI.getMinX() != areaIterateMinX //areaiteration
|| writableRI.getMinY() != areaIterateMinY //areaiteration
|| writableRI.getWidth() != areaIterateMaxX-areaIterateMinX//longueuriteration
|| writableRI.getHeight()!= areaIterateMaxY-areaIterateMinY//largeuriteration
|| !minTileX || !minTileY )
//image dimensions
if (renderedImage.getMinX() != writableRI.getMinX()
|| renderedImage.getMinY() != writableRI.getMinY()
|| renderedImage.getWidth() != writableRI.getWidth()
|| renderedImage.getHeight() != writableRI.getHeight()
|| rimtx != wrimtx || rimty != wrimty
|| renderedImage.getNumXTiles() != writableRI.getNumXTiles()
|| renderedImage.getNumYTiles() != writableRI.getNumYTiles())
throw new IllegalArgumentException("rendered image and writable rendered image dimensions are not conform"+renderedImage+writableRI);
}
/**
* Fill given buffer with samples within the given area at the specified image band.
* Adapted for a {@link PixelInterleavedSampleModel} {@link SampleModel} type.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
* @param band the interest band.
*/
private void getAreaByInterleaved(final Rectangle area, final Object buffer, final int band) {
rewind();
final Rectangle generalArea = getBoundary(false);//-- compute one time
final int sourceDataType = getSourceDatatype();
final int minTX, minTY, maxTX, maxTY;
if (renderedImage != null) {
minTX = tMinX + (area.x - generalArea.x) / tileWidth;
minTY = tMinY + (area.y - generalArea.y) / tileHeight;
maxTX = tMinX + (area.x + area.width + tileWidth - 1) / tileWidth;
maxTY = tMinY + (area.y + area.height + tileHeight - 1) / tileHeight;
} else {
minTX = minTY = 0;
maxTX = maxTY = 1;
}
for (int ty = minTY; ty < maxTY; ty++) {
for (int tx = minTX; tx < maxTX; tx++) {
//-- intersection sur
final Raster rast = (renderedImage != null) ? renderedImage.getTile(tx, ty) : currentRaster;
final int minX = Math.max(rast.getMinX(), area.x);
final int minY = Math.max(rast.getMinY(), area.y);
final int maxX = Math.min(rast.getMinX() + tileWidth, area.x + area.width);
final int maxY = Math.min(rast.getMinY() + tileHeight, area.y + area.height);
if (minX > maxX || minY > maxY) throw new IllegalArgumentException("Expected area don't intersect internal data.");
final int readLength = (maxX - minX);
int destId = 0;
for (int y = minY; y < maxY; y++) {
moveTo(minX, y, band);
int s = 0;
int id = destId;
while (s < readLength) {
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
((byte[])buffer)[id++] = (byte) getSample();
break;
}
case DataBuffer.TYPE_USHORT :
case DataBuffer.TYPE_SHORT : {
((short[])buffer)[id++] = (short) getSample();
break;
}
case DataBuffer.TYPE_INT : {
((int[])buffer)[id++] = getSample();
break;
}
case DataBuffer.TYPE_FLOAT : {
((float[])buffer)[id++] = getSampleFloat();
break;
}
case DataBuffer.TYPE_DOUBLE : {
((double[])buffer)[id++] = getSampleDouble();
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
int b = 0;
while (next()) {
if (++b == getNumBands()) break;
}
s++;
}
destId += area.width;
}
}
}
}
/**
* Fill given buffer with samples within the given area and from all image band.
* Adapted for a {@link PixelInterleavedSampleModel} {@link SampleModel} type.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
*/
private void getAreaByInterleaved(final Rectangle area, final Object[] buffer) {
rewind();
final Rectangle generalArea = getBoundary(false);//-- compute one time
final int sourceDataType = getSourceDatatype();
final int minTX, minTY, maxTX, maxTY;
if (renderedImage != null) {
minTX = tMinX + (area.x - generalArea.x) / tileWidth;
minTY = tMinY + (area.y - generalArea.y) / tileHeight;
maxTX = tMinX + (area.x + area.width + tileWidth - 1) / tileWidth;
maxTY = tMinY + (area.y + area.height + tileHeight - 1) / tileHeight;
} else {
minTX = minTY = 0;
maxTX = maxTY = 1;
}
for (int ty = minTY; ty < maxTY; ty++) {
for (int tx = minTX; tx < maxTX; tx++) {
//-- intersection sur
final Raster rast = (renderedImage != null) ? renderedImage.getTile(tx, ty) : currentRaster;
final int minX = Math.max(rast.getMinX(), area.x);
final int minY = Math.max(rast.getMinY(), area.y);
final int maxX = Math.min(rast.getMinX() + tileWidth, area.x + area.width);
final int maxY = Math.min(rast.getMinY() + tileHeight, area.y + area.height);
if (minX > maxX || minY > maxY) throw new IllegalArgumentException("Expected area don't intersect internal data.");
final int readLength = (maxX - minX);
int destId = 0;
for (int y = minY; y < maxY; y++) {
moveTo(minX, y, 0);
int s = 0;
int id = destId;
while (s < readLength) {
int b = 0;
while (b < getNumBands()) {
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
((byte[])buffer[b])[id++] = (byte) getSample();
break;
}
case DataBuffer.TYPE_USHORT :
case DataBuffer.TYPE_SHORT : {
((short[])buffer[b])[id++] = (short) getSample();
break;
}
case DataBuffer.TYPE_INT : {
((int[])buffer[b])[id++] = getSample();
break;
}
case DataBuffer.TYPE_FLOAT : {
((float[])buffer[b])[id++] = getSampleFloat();
break;
}
case DataBuffer.TYPE_DOUBLE : {
((double[])buffer[b])[id++] = getSampleDouble();
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
b++;
next();
}
s++;
}
destId += area.width;
}
}
}
}
/**
* Fill given buffer with samples within the given area and from all image band.
* Adapted for a {@link BandedSampleModel} {@link SampleModel} type.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
*/
private void getAreaByBanded (final Rectangle area, final Object[] buffer) {
final ComponentSampleModel compSM = (ComponentSampleModel) currentSampleModel;
final int[] bankIndices = compSM.getBankIndices();
assert bankIndices.length == getNumBands();
final int[] bandOffsets = compSM.getBandOffsets();
assert bandOffsets.length == getNumBands();
final Rectangle generalArea = getBoundary(false);//-- compute one time
final int sourceDataType = getSourceDatatype();
final int minTX, minTY, maxTX, maxTY;
if (renderedImage != null) {
minTX = tMinX + (area.x - generalArea.x) / tileWidth;
minTY = tMinY + (area.y - generalArea.y) / tileHeight;
maxTX = tMinX + (area.x + area.width + tileWidth - 1) / tileWidth;
maxTY = tMinY + (area.y + area.height + tileHeight - 1) / tileHeight;
} else {
minTX = minTY = 0;
maxTX = maxTY = 1;
}
for (int b = 0; b < getNumBands(); b++) {
for (int ty = minTY; ty < maxTY; ty++) {
for (int tx = minTX; tx < maxTX; tx++) {
//-- intersection sur
final Raster rast = (renderedImage != null) ? renderedImage.getTile(tx, ty) : currentRaster;
final int minX = Math.max(rast.getMinX(), area.x);
final int minY = Math.max(rast.getMinY(), area.y);
final int maxX = Math.min(rast.getMinX() + tileWidth, area.x + area.width);
final int maxY = Math.min(rast.getMinY() + tileHeight, area.y + area.height);
if (minX > maxX || minY > maxY) throw new IllegalArgumentException("Expected area don't intersect internal data.");
final DataBuffer databuff = rast.getDataBuffer();
int srcRastId = bandOffsets[b] + ((minY - rast.getMinY()) * tileWidth + minX - rast.getMinX());
final int readLength = (maxX - minX);
int destId = 0;
for (int y = minY; y < maxY; y++) {
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
final byte[] src = ((DataBufferByte) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (byte[]) buffer[b], destId, readLength);
break;
}
case DataBuffer.TYPE_USHORT : {
final short[] src = ((DataBufferUShort) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (short[]) buffer[b], destId, readLength);
break;
}
case DataBuffer.TYPE_SHORT : {
final short[] src = ((DataBufferShort) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (short[]) buffer[b], destId, readLength);
break;
}
case DataBuffer.TYPE_INT : {
final int[] src = ((DataBufferInt) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (int[]) buffer[b], destId, readLength);
break;
}
case DataBuffer.TYPE_FLOAT : {
final float[] src = ((DataBufferFloat) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (float[]) buffer[b], destId, readLength);
break;
}
case DataBuffer.TYPE_DOUBLE : {
final double[] src = ((DataBufferDouble) databuff).getData(bankIndices[b]);
System.arraycopy(src, srcRastId, (double[]) buffer[b], destId, readLength);
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
srcRastId += tileWidth;
destId += area.width;
}
}
}
}
}
/**
* Fill given buffer with samples within the given area at the specified image band.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
* @param band the interest band.
*/
public void getArea(final Rectangle area, final Object buffer, int band) {
ArgumentChecks.ensureNonNull("area", area);
ArgumentChecks.ensureNonNull("buffer", buffer);
final int sourceDataType = getSourceDatatype();
final int areaLength = area.width * area.height;
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
if (!(buffer instanceof byte[])) throw new IllegalArgumentException("Buffer argument must be instance of byte[][] array");
if (((byte[]) buffer).length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_USHORT :
case DataBuffer.TYPE_SHORT : {
if (!(buffer instanceof short[])) throw new IllegalArgumentException("Buffer argument must be instance of short[][] array");
if (((short[]) buffer).length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_INT : {
if (!(buffer instanceof int[])) throw new IllegalArgumentException("Buffer argument must be instance of int[][] array");
if (((int[])buffer).length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_FLOAT : {
if (!(buffer instanceof float[])) throw new IllegalArgumentException("Buffer argument must be instance of float[][] array");
if (((float[])buffer).length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_DOUBLE : {
if (!(buffer instanceof double[])) throw new IllegalArgumentException("Buffer argument must be instance of double[][] array");
if (((double[])buffer).length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
if (currentSampleModel instanceof ComponentSampleModel) {
if (((ComponentSampleModel)currentSampleModel).getPixelStride() == 1) {
getAreaByBanded(area, buffer, band);
return;
}
}
getAreaByInterleaved(area, buffer, band);
}
/**
* Fill given buffer with samples within the given area at the specified image band.
* Adapted for a {@link BandedSampleModel} {@link SampleModel} type.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
* @param band the interest band.
*/
public void getAreaByBanded(final Rectangle area, final Object buffer, final int band) {
final ComponentSampleModel compSM = (ComponentSampleModel) currentSampleModel;
final int bankIndices = compSM.getBankIndices()[band];
final int bandOffsets = compSM.getBandOffsets()[band];
final Rectangle generalArea = getBoundary(false);//-- compute one time
final int sourceDataType = getSourceDatatype();
final int minTX, minTY, maxTX, maxTY;
if (renderedImage != null) {
minTX = tMinX + (area.x - generalArea.x) / tileWidth;
minTY = tMinY + (area.y - generalArea.y) / tileHeight;
maxTX = tMinX + (area.x + area.width + tileWidth - 1) / tileWidth;
maxTY = tMinY + (area.y + area.height + tileHeight - 1) / tileHeight;
} else {
minTX = minTY = 0;
maxTX = maxTY = 1;
}
for (int ty = minTY; ty < maxTY; ty++) {
for (int tx = minTX; tx < maxTX; tx++) {
//-- intersection sur
final Raster rast = (renderedImage != null) ? renderedImage.getTile(tx, ty) : currentRaster;
final int minX = Math.max(rast.getMinX(), area.x);
final int minY = Math.max(rast.getMinY(), area.y);
final int maxX = Math.min(rast.getMinX() + tileWidth, area.x + area.width);
final int maxY = Math.min(rast.getMinY() + tileHeight, area.y + area.height);
if (minX > maxX || minY > maxY) throw new IllegalArgumentException("Expected area don't intersect internal data.");
final DataBuffer databuff = rast.getDataBuffer();
int srcRastId = bandOffsets + ((minY - rast.getMinY()) * tileWidth + minX - rast.getMinX());
final int readLength = (maxX - minX);
int destId = 0;
for (int y = minY; y < maxY; y++) {
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
final byte[] src = ((DataBufferByte) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (byte[]) buffer, destId, readLength);
break;
}
case DataBuffer.TYPE_USHORT : {
final short[] src = ((DataBufferUShort) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (short[]) buffer, destId, readLength);
break;
}
case DataBuffer.TYPE_SHORT : {
final short[] src = ((DataBufferShort) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (short[]) buffer, destId, readLength);
break;
}
case DataBuffer.TYPE_INT : {
final int[] src = ((DataBufferInt) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (int[]) buffer, destId, readLength);
break;
}
case DataBuffer.TYPE_FLOAT : {
final float[] src = ((DataBufferFloat) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (float[]) buffer, destId, readLength);
break;
}
case DataBuffer.TYPE_DOUBLE : {
final double[] src = ((DataBufferDouble) databuff).getData(bankIndices);
System.arraycopy(src, srcRastId, (double[]) buffer, destId, readLength);
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
srcRastId += tileWidth;
destId += area.width;
}
}
}
}
/**
* Fill given buffer with samples within the given area and from all the source image band.
*
* @param area define needed samples area.
* @param buffer array which will be filled by samples.
* @param band the interest band.
*/
public void getArea(final Rectangle area, final Object[] buffer) {
ArgumentChecks.ensureNonNull("area", area);
ArgumentChecks.ensureNonNull("buffer", buffer);
if (buffer.length < getNumBands()) throw new IllegalArgumentException("buffer must have length equals to numbands. Found : "+buffer.length+". Expected : "+getNumBands());
final int sourceDataType = getSourceDatatype();
final int areaLength = area.width * area.height * getNumBands();
switch (sourceDataType) {
case DataBuffer.TYPE_BYTE : {
if (!(buffer instanceof byte[][])) throw new IllegalArgumentException("Buffer argument must be instance of byte[][] array");
if (((byte[][]) buffer)[0].length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_USHORT :
case DataBuffer.TYPE_SHORT : {
if (!(buffer instanceof short[][])) throw new IllegalArgumentException("Buffer argument must be instance of short[][] array");
if (((short[][])buffer)[0].length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_INT : {
if (!(buffer instanceof int[][])) throw new IllegalArgumentException("Buffer argument must be instance of int[][] array");
if (((int[][])buffer)[0].length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_FLOAT : {
if (!(buffer instanceof float[][])) throw new IllegalArgumentException("Buffer argument must be instance of float[][] array");
if (((float[][])buffer)[0].length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
case DataBuffer.TYPE_DOUBLE : {
if (!(buffer instanceof double[][])) throw new IllegalArgumentException("Buffer argument must be instance of double[][] array");
if (((double[][])buffer)[0].length < areaLength) throw new IllegalArgumentException("Buffer must have a length equal or upper than area sample number. Expected : "+areaLength);
break;
}
default : {
throw new IllegalStateException("Unknow datatype.");
}
}
if (currentSampleModel instanceof ComponentSampleModel) {
if (((ComponentSampleModel)currentSampleModel).getPixelStride() == 1) {
getAreaByBanded(area, buffer);
return;
}
}
getAreaByInterleaved(area, buffer);
}
/**
* Return type data from iterate source.
* @return type data from iterate source.
*/
public int getSourceDatatype() {
return (renderedImage == null) ? currentRaster.getSampleModel().getDataType() : renderedImage.getSampleModel().getDataType();
}
/**
* Compute an array which give the number of data elements until the next sample in the pixel. Note that the first
* element gives number of elements between the last sample of the previous pixel and the first sample of current one.
* @param bandOffsets The bandOffsets table given by {@link java.awt.image.ComponentSampleModel#getBandOffsets()}.
* @param pixelStride The pixel stride value given by {@link java.awt.image.ComponentSampleModel#getPixelStride()}
* @return An array whose components are the number of elements to skip until the next sample.
*/
public static int[] getBandSteps(final int[] bandOffsets, final int pixelStride) {
final int[] bandSteps = new int[bandOffsets.length];
bandSteps[0] = bandOffsets[0] + pixelStride - bandOffsets[bandOffsets.length-1];
for (int i = 1 ; i < bandSteps.length ; i++) {
bandSteps[i] = bandOffsets[i] - bandOffsets[i-1];
}
return bandSteps;
}
}