001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.image;
020
021import java.awt.Rectangle;
022import java.util.NoSuchElementException;
023
024import icy.roi.BooleanMask2D;
025import icy.roi.ROI;
026import icy.type.DataIterator;
027import icy.type.DataType;
028import icy.type.collection.array.Array1DUtil;
029
030/**
031 * Image data iterator.<br>
032 * This class permit to use simple iterator to read / write <code>IcyBufferedImage</code> data<br>
033 * as double in XYC <i>([C[Y[X]]])</i> dimension order .<br>
034 * Whatever is the internal {@link DataType} data is returned and set as double.<br>
035 * <b>If the image size or type is modified during iteration the iterator
036 * becomes invalid and can causes exception to happen.</b>
037 * 
038 * @author Stephane
039 */
040public class ImageDataIterator implements DataIterator
041{
042    protected final IcyBufferedImage image;
043    protected final DataType dataType;
044
045    /**
046     * internals
047     */
048    protected final BooleanMask2D mask;
049    protected final Rectangle regionBounds;
050    protected final Rectangle imageBounds;
051    protected final Rectangle finalBounds;
052    protected final int c;
053    protected final int w, h;
054    protected int x, y;
055    protected int imgOff;
056    protected int maskOff;
057    protected boolean changed;
058    protected boolean done;
059    protected Object data;
060
061    /**
062     * Create a new ImageData iterator to iterate data through the specified XY region and channel.
063     * 
064     * @param image
065     *        Image we want to iterate data from
066     * @param XYBounds
067     *        XY region to iterate (inclusive).
068     * @param channel
069     *        channel (C position) we want to iterate data
070     */
071    protected ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, BooleanMask2D maskXY, int channel)
072    {
073        super();
074
075        if (maskXY != null)
076            regionBounds = maskXY.bounds;
077        else
078            regionBounds = boundsXY;
079
080        this.image = image;
081        this.mask = maskXY;
082
083        if (image != null)
084        {
085            imageBounds = image.getBounds();
086            dataType = image.getDataType_();
087            c = channel;
088            // retain data while we are iterating over image data
089            image.lockRaster();
090        }
091        else
092        {
093            imageBounds = new Rectangle();
094            dataType = DataType.UBYTE;
095            c = 0;
096        }
097
098        finalBounds = regionBounds.intersection(imageBounds);
099
100        // cached
101        w = finalBounds.width;
102        h = finalBounds.height;
103
104        changed = false;
105
106        // start iterator
107        reset();
108    }
109
110    /**
111     * Create a new ImageData iterator to iterate data through the specified XY region and channel.
112     * 
113     * @param image
114     *        Image we want to iterate data from
115     * @param boundsXY
116     *        XY region to iterate (inclusive).
117     * @param channel
118     *        channel (C position) we want to iterate data
119     */
120    public ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, int channel)
121    {
122        this(image, boundsXY, null, channel);
123    }
124
125    /**
126     * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead.
127     */
128    @Deprecated
129    public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int startC, int endC)
130    {
131        this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), startC);
132    }
133
134    /**
135     * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead.
136     */
137    @Deprecated
138    public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int c)
139    {
140        this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), c);
141    }
142
143    /**
144     * Create a new ImageData iterator to iterate data of specified channel.
145     * 
146     * @param image
147     *        Image we want to iterate data from
148     * @param c
149     *        C position (channel) we want to iterate data
150     */
151    public ImageDataIterator(IcyBufferedImage image, int c)
152    {
153        this(image, image.getBounds(), c);
154    }
155
156    /**
157     * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, int)} instead.<br>
158     *             The <code>ImageDataIterator</code> iterate only on single channel data.
159     */
160    @Deprecated
161    public ImageDataIterator(IcyBufferedImage image)
162    {
163        this(image, image.getBounds(), 0);
164    }
165
166    /**
167     * Create a new ImageData iterator to iterate data through the specified <code>BooleanMask2D</code> and C dimension.
168     * 
169     * @param image
170     *        Image we want to iterate data from
171     * @param maskXY
172     *        BooleanMask2D defining the XY region to iterate
173     * @param channel
174     *        channel (C position) we want to iterate data
175     */
176    public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY, int channel)
177    {
178        this(image, null, maskXY, channel);
179    }
180
181    /**
182     * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead
183     */
184    @Deprecated
185    public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY)
186    {
187        this(image, maskXY, 0);
188    }
189
190    /**
191     * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead.
192     *             You can use the {@link ROI#getBooleanMask2D(int, int, int, boolean)} method to
193     *             retrieve the boolean mask from the ROI.
194     */
195    @Deprecated
196    public ImageDataIterator(IcyBufferedImage image, ROI roi)
197    {
198        this(image, roi.getBooleanMask2D(0, 0, 0, false));
199    }
200
201    @Override
202    protected void finalize() throws Throwable
203    {
204        flush();
205        
206        super.finalize();
207    }
208
209    public int getMinX()
210    {
211        return finalBounds.x;
212    }
213
214    public int getMaxX()
215    {
216        return (finalBounds.x + w) - 1;
217    }
218
219    public int getMinY()
220    {
221        return finalBounds.y;
222    }
223
224    public int getMaxY()
225    {
226        return (finalBounds.y + h) - 1;
227    }
228
229    @Override
230    public void reset()
231    {
232        done = (image == null) || (c < 0) || (c >= image.getSizeC()) || finalBounds.isEmpty();
233
234        if (!done)
235        {
236            // get data
237            data = image.getDataXY(c);
238
239            // reset position
240            y = 0;
241            x = -1;
242            imgOff = (finalBounds.x - imageBounds.x) + ((finalBounds.y - imageBounds.y) * imageBounds.width);
243            imgOff--;
244            if (mask != null)
245            {
246                maskOff = (finalBounds.x - regionBounds.x) + ((finalBounds.y - regionBounds.y) * regionBounds.width);
247                maskOff--;
248            }
249            // allow to correctly set initial position in boolean mask
250            next();
251        }
252    }
253
254    @Override
255    public void next()
256    {
257        nextPosition();
258
259        if (mask != null)
260        {
261            // advance while mask do not contains current point
262            while (!done && !mask.mask[maskOff])
263                nextPosition();
264        }
265    }
266
267    /**
268     * Advance one position
269     */
270    protected void nextPosition()
271    {
272        if (mask != null)
273        {
274            imgOff++;
275            maskOff++;
276            if (++x >= w)
277            {
278                x = 0;
279                imgOff += imageBounds.width - finalBounds.width;
280                maskOff += regionBounds.width - finalBounds.width;
281
282                if (++y >= h)
283                    done = true;
284            }
285        }
286        else
287        {
288            imgOff++;
289            if (++x >= w)
290            {
291                x = 0;
292                imgOff += imageBounds.width - finalBounds.width;
293
294                if (++y >= h)
295                    done = true;
296            }
297        }
298    }
299
300    @Override
301    public boolean done()
302    {
303        return done;
304    }
305
306    @Override
307    public double get()
308    {
309        if (done)
310            throw new NoSuchElementException(null);
311
312        return Array1DUtil.getValue(data, imgOff, dataType);
313    }
314
315    @Override
316    public void set(double value)
317    {
318        if (done)
319            throw new NoSuchElementException(null);
320
321        Array1DUtil.setValue(data, imgOff, dataType, value);
322        changed = true;
323    }
324
325    /**
326     * Returns current X position.
327     */
328    public int getX()
329    {
330        return finalBounds.x + x;
331    }
332
333    /**
334     * Returns current Y position.
335     */
336    public int getY()
337    {
338        return finalBounds.y + y;
339    }
340
341    /**
342     * Returns C position (fixed)
343     */
344    public int getC()
345    {
346        return c;
347    }
348
349    /**
350     * @deprecated Use {@link #getX()} instead
351     */
352    @Deprecated
353    public int getPositionX()
354    {
355        return getX();
356    }
357
358    /**
359     * @deprecated Use {@link #getY()} instead
360     */
361    @Deprecated
362    public int getPositionY()
363    {
364        return getY();
365    }
366
367    /**
368     * @deprecated Use {@link #getC()} instead
369     */
370    @Deprecated
371    public int getPositionC()
372    {
373        return getC();
374    }
375
376    public void flush()
377    {
378        // release image raster and save changes to cache
379        if (image != null)
380            image.releaseRaster(changed);
381        changed = false;
382    }
383}