/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, 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.display2d.ext.cellular; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import org.apache.sis.math.Statistics; /** * Iterator over pixels, skipping values if decimation is set. * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) */ public class CellIterator { /** * Image to iterate over. */ private final RenderedImage image; private final SampleModel sm; private final boolean decimate; private final int decimateX; private final int decimateY; private final Statistics[] cellStats; private final double[] pixel; /** * The upper limit (exclusive) for {@link #index}. */ private final int count; /** * The index of current position. * The <var>i</var>,<var>j</var> index in the underlying grid coverage can be deduced with: * <blockquote><pre> * i = index % width; * j = index / width; * </pre></blockquote> */ private int index = -1; /** * The <var>i</var>,<var>j</var> indices for the current position. * Will be computed only when first required. */ private double i, j; /** * <code>true</code> if {@link #i}, {@link #j}, {@link #cellStats} are valids. */ private boolean valid; /** * Create iterator over image without decimation. * * @param image to iterate over */ public CellIterator(RenderedImage image) { this(image,1,1); } /** * Create iterator over image with decimation. * * @param image to iterate over * @param decimateX x decimation * @param decimateY y decimation */ public CellIterator(RenderedImage image, int decimateX, int decimateY) { this.image = image; this.sm = image.getSampleModel(); this.decimateX = decimateX; this.decimateY = decimateY; this.decimate = (decimateX!=1 || decimateY!=1); this.count = (image.getWidth()/decimateX) * (image.getHeight()/decimateY); this.cellStats = new Statistics[sm.getNumBands()]; for(int i=0;i<this.cellStats.length;i++){ this.cellStats[i] = new Statistics(null); } this.pixel = new double[this.cellStats.length]; } /** * Moves the iterator to the next position. */ public boolean next() { valid = false; return ++index < count; } /** * Calcule les composantes <var>x</var> et <var>y</var> du vecteur à l'index spécifié. */ private void compute() { //reset stats for(int b=0;b<this.cellStats.length;b++){ this.cellStats[b].reset(); } int count = 0; int sumI = 0; int sumJ = 0; final int decWidth = image.getWidth()/decimateX; final int imin = (index % decWidth)*decimateX + image.getMinX(); final int jmin = (index / decWidth)*decimateY + image.getMinY(); for (int i=imin+decimateX; --i>=imin;) { for (int j=jmin+decimateY; --j>=jmin;) { final Raster tile = image.getTile(XToTileX(image,i), YToTileY(image,j)); tile.getPixel(i, j, pixel); for(int b=0;b<pixel.length;b++){ if(!Double.isNaN(pixel[b])){ cellStats[b].accept(pixel[b]); } } sumI += i; sumJ += j; count++; } } this.i = imin + (double)decimateX/2d; this.j = jmin + (double)decimateX/2d; valid = true; } /** * Retourne les coordonnées (<var>x</var>,<var>y</var>) d'un point de la grille. * * Si une décimation a été spécifiée alors la position retournée * sera située au milieu des points à moyenner. */ public Point2D position() { if (!decimate) { final int width = image.getWidth(); return new Point2D.Double(index % width, index / width); } else { if (!valid) { compute(); } return new Point2D.Double(i, j); } } /** * Get the cell statistics. */ public Statistics[] statistics() { if (!valid) { compute(); } return cellStats; } /** * Returns <code>true</code> if the current mark is visible in the specified clip. */ boolean visible(final Shape clip) { if (clip == null) { return true; } final int decWidth = image.getWidth()/decimateX; return clip.contains(index%decWidth, index/decWidth); } public static int XToTileX(final RenderedImage image, int x){ final int tileWidth = image.getTileWidth(); x -= image.getTileGridXOffset(); if (x < 0) { x += 1 - tileWidth; } return x/tileWidth; } public static int YToTileY(final RenderedImage image, int y){ final int tileHeight = image.getTileHeight(); y -= image.getTileGridYOffset(); if (y < 0) { y += 1 - tileHeight; } return y/tileHeight; } }