/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2015, 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;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.TileObserver;
import java.awt.image.WritableRaster;
import java.awt.image.WritableRenderedImage;
import java.util.Vector;
import org.geotoolkit.image.iterator.PixelIterator;
import org.geotoolkit.image.iterator.PixelIteratorFactory;
/**
* A l'arrache.com
* @author rmarechal
*/
public class WritableMemoryRenderedImage implements WritableRenderedImage {
/**
* Default tile size.
*/
private static final int DEFAULT_TILE_SIZE = 256;
/**
* Minimum required tile size.
*/
private static final int MIN_TILE_SIZE = 64;
/**
* Upper left corner of all image tiles.
*/
private Point[] tileIndices = null;
/**
* Image attributes.
*/
private final Raster[][] tiles;
/**
* Image attributs.
*/
private final int minX;
private final int minY;
private final int width;
private final int height;
private final int tileWidth;
private final int tileHeight;
private final int tileGridXOffset;
private final int tileGridYOffset;
private int minTileGridX;
private int minTileGridY;
private final int nbrTileX;
private final int nbrTileY;
private ColorModel cm;
private SampleModel sm;
/**
* Create {@link WritableLargeRenderedImage} with default upper corner at position (0, 0),
* a default tile size of 256 x 256 pixels and a default tile grid offset at position (0, 0).
*
* @param width
* @param height
* @param sampleModel
* @param colorModel
*/
public WritableMemoryRenderedImage(final int width, final int height, final SampleModel sampleModel, final ColorModel colorModel) {
this.minX = 0;
this.minY = 0;
this.width = width;
this.height = height;
this.tileWidth = DEFAULT_TILE_SIZE;
this.tileHeight = DEFAULT_TILE_SIZE;
this.tileGridXOffset = 0;
this.tileGridYOffset = 0;
this.nbrTileX = (int) StrictMath.ceil(width / tileWidth);
this.nbrTileY = (int) StrictMath.ceil(height / tileHeight);
tiles = new WritableRaster[nbrTileY][nbrTileX];
this.cm = colorModel;
this.sm = sampleModel;
}
public WritableMemoryRenderedImage(int minX, int minY, int width, int height,
int tileWidth, int tileHeight, int tileGridXOffset, int tileGridYOffset,
int nbrTileX, int nbrTileY, ColorModel cm, SampleModel sm) {
this.minX = minX;
this.minY = minY;
this.width = width;
this.height = height;
this.tileWidth = tileWidth;
this.tileHeight = tileHeight;
this.tileGridXOffset = tileGridXOffset;
this.tileGridYOffset = tileGridYOffset;
this.nbrTileX = nbrTileX;
this.nbrTileY = nbrTileY;
tiles = new WritableRaster[nbrTileY][nbrTileX];
this.cm = cm;
this.sm = sm;
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public void addTileObserver(TileObserver to) {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public void removeTileObserver(TileObserver to) {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* {@inheritDoc }.
*/
@Override
public WritableRaster getWritableTile(int tileX, int tileY) {
final int arrayIndexX = tileX - minTileGridX;
final int arrayIndexY = tileY - minTileGridY;
if (tiles[arrayIndexY][arrayIndexX] == null) {
tiles[arrayIndexY][arrayIndexX] = Raster.createWritableRaster(sm, new Point(minX + arrayIndexX * tileWidth, minY + arrayIndexY * tileHeight));
}
if (!(tiles[arrayIndexY][arrayIndexX] instanceof WritableRaster)) {
final Raster r = tiles[arrayIndexY][arrayIndexX];
//-- re-copy
tiles[arrayIndexY][arrayIndexX] = Raster.createWritableRaster(r.getSampleModel(), r.getDataBuffer(), new Point(r.getMinX(), r.getMinY()));
}
return (WritableRaster) tiles[arrayIndexY][arrayIndexX];
}
/**
* {@inheritDoc }.
*/
@Override
public void releaseWritableTile(int tileX, int tileY) {
//-- all tiles in memory do nothing
}
/**
* {@inheritDoc }.
*/
@Override
public boolean isTileWritable(int tileX, int tileY) {
return true;
}
@Override
public Point[] getWritableTileIndices() {
if (tileIndices == null) {
tileIndices = new Point[nbrTileX*nbrTileY];
int idTab = 0;
for (int ty = minTileGridY, tmy = minTileGridY+nbrTileY; ty < tmy; ty++) {
for (int tx = minTileGridX, tmx = minTileGridX+nbrTileX; tx < tmx; tx++) {
tileIndices[idTab++] = new Point(tx, ty);
}
}
}
return tileIndices;
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public boolean hasTileWriters() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* {@inheritDoc }.
*/
@Override
public void setData(Raster r) {
final int rminX = r.getMinX();
final int rminY = r.getMinY();
final int rw = r.getWidth();
final int rh = r.getHeight();
final int rmaxX = rminX+rw;
final int rmaxY = rminY+rh;
if (Math.abs(minX-rminX) % tileWidth != 0)
throw new IllegalArgumentException("raster minX value don't tie in tile coordinate");
if (Math.abs(minY-rminY) % tileHeight != 0)
throw new IllegalArgumentException("raster minY value don't tie in tile coordinate");
final int ix = Math.max(rminX, minX);
final int imx = Math.min(rmaxX, minX+width);
final int iy = Math.max(rminY, minY);
final int imy = Math.min(rmaxY, minY+height);
if (imx <= ix || imy <= iy) {
throw new IllegalArgumentException("raster is not within image boundary");
}
if ((imx-ix) != rw || (imy-iy) != rh) {
throw new IllegalArgumentException("raster boundary don't tie in tile coordinate");
}
if (r.getSampleModel().getDataType() != sm.getDataType()) {
throw new IllegalArgumentException("raster datatype don't tie with image datatype");
}
final int tx = (rminX-minX) / tileWidth;
final int ty = (rminY-minY) / tileHeight;
tiles[ty][tx] = (WritableRaster) r;
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public Vector<RenderedImage> getSources() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public Object getProperty(String name) {
return Image.UndefinedProperty;
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public String[] getPropertyNames() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* {@inheritDoc }.
*/
@Override
public ColorModel getColorModel() {
return cm;
}
/**
* Set a new {@link ColorModel} in this {@linkplain WritableMemoryRenderedImage WritableRenderedImage} implementation.
*
* @param colorModel the new setted {@link ColorModel}.
*/
public void setColorModel(final ColorModel colorModel) {
cm = colorModel;
}
/**
* {@inheritDoc }.
*/
@Override
public SampleModel getSampleModel() {
return sm;
}
/**
* Set a new {@link SampleModel} in this {@linkplain WritableMemoryRenderedImage WritableRenderedImage} implementation.
*
* @param sampleModel the new setted {@link SampleModel}.
*/
public void setSampleModel(final SampleModel sampleModel) {
sm = sampleModel;
}
/**
* {@inheritDoc }.
*/
@Override
public int getWidth() {
return width;
}
/**
* {@inheritDoc }.
*/
@Override
public int getHeight() {
return height;
}
/**
* {@inheritDoc }.
*/
@Override
public int getMinX() {
return minX;
}
/**
* {@inheritDoc }.
*/
@Override
public int getMinY() {
return minY;
}
/**
* {@inheritDoc }.
*/
@Override
public int getNumXTiles() {
return nbrTileX;
}
/**
* {@inheritDoc }.
*/
@Override
public int getNumYTiles() {
return nbrTileY;
}
/**
* {@inheritDoc }.
*/
@Override
public int getMinTileX() {
return minTileGridX;
}
/**
* {@inheritDoc }.
*/
@Override
public int getMinTileY() {
return minTileGridY;
}
/**
* {@inheritDoc }.
*/
@Override
public int getTileWidth() {
return tileWidth;
}
/**
* {@inheritDoc }.
*/
@Override
public int getTileHeight() {
return tileHeight;
}
/**
* {@inheritDoc }.
*/
@Override
public int getTileGridXOffset() {
return tileGridXOffset;
}
/**
* {@inheritDoc }.
*/
@Override
public int getTileGridYOffset() {
return tileGridYOffset;
}
/**
* {@inheritDoc }.
*/
@Override
public Raster getTile(int tileX, int tileY) {
return tiles[tileY - tileGridYOffset][tileX - tileGridXOffset];
}
/**
* {@inheritDoc }.
*/
@Override
public Raster getData() {
final WritableRaster wr = Raster.createWritableRaster(cm.createCompatibleSampleModel(width, height), new Point(minX, minY));
final Rectangle rect = new Rectangle();
int my = minY;
for (int ty = 0, tmy = nbrTileY; ty < tmy; ty++) {
int mx = minX;
for (int tx = 0, tmx = nbrTileX; tx < tmx; tx++) {
final Raster r = tiles[ty][tx];
rect.setBounds(mx, my, tileWidth, tileHeight);
//recopie
final PixelIterator copix = PixelIteratorFactory.createDefaultWriteableIterator(wr, wr, rect);
final PixelIterator pix = PixelIteratorFactory.createDefaultIterator(r, rect);
while (copix.next()) {
pix.next();
copix.setSampleDouble(pix.getSampleDouble());
}
mx += tileWidth;
}
my += tileHeight;
}
return wr;
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public Raster getData(Rectangle rect) {
throw new UnsupportedOperationException("WritableMemoryRenderedImage.getData(Rectangle) : Not supported yet.");
}
/**
* {@inheritDoc }.<br>
* note : not implemented yed.
*/
@Override
public WritableRaster copyData(WritableRaster raster) {
throw new UnsupportedOperationException("WritableMemoryRenderedImage.copyData : Not supported yet.");
}
}