// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.cm
//
//    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; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    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.
//
//    You should have received a copy of the GNU Lesser General Public
//    License along with this library; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

package Catalano.Imaging.Filters;

import Catalano.Core.IntPoint;
import Catalano.Imaging.Color;
import Catalano.Imaging.FastBitmap;
import Catalano.Imaging.IApplyInPlace;
import java.util.LinkedList;

/**
 * Flood Fill filter.
 * The purpose of Flood Fill is to color an entire area of connected pixels with the same color.
 * @author Diego Catalano
 */
public class FloodFill implements IApplyInPlace{
    
    /**
     * Specifies different floodfill algorithm.
     */
    public static enum Algorithm {

        /**
         * 
4 neighbors. *
0 X 0 *
X X X *
0 X 0 */ FourWay, /** *
8 neighbors. *
X X X *
X X X *
X X X */ EightWay }; private Algorithm algorithm = Algorithm.FourWay; IntPoint startPoint; private Color replace; private int gray; /** * Initialize a new instance of the FloodFill class. * @param x X axis coordinate. * @param y Y axis coordinate. * @param color Color. */ public FloodFill(int x, int y, Color color){ this.startPoint = new IntPoint(x, y); this.replace = color; } /** * Initialize a new instance of the FloodFill class. * @param x X-axis. * @param y Y-axis. * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. */ public FloodFill(int x, int y, int r, int g, int b) { this.startPoint = new IntPoint(x, y); this.replace = new Color(r, g, b); } /** * Initialize a new instance of the FloodFill class. * @param x X-axis. * @param y Y-axis. * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. * @param algorithm Floodfill algorithm. */ public FloodFill(int x, int y, int r, int g, int b, Algorithm algorithm) { this.startPoint = new IntPoint(x, y); this.replace = new Color(r, g, b); this.algorithm = algorithm; } /** * Initialize a new instance of the FloodFill class. * @param p Point (x,y); * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. */ public FloodFill(IntPoint p, int r, int g, int b){ this.startPoint = p; this.replace = new Color(r, g, b); } /** * Initialize a new instance of the FloodFill class. * @param p Point(x,y). * @param color Color. */ public FloodFill(IntPoint p, Color color){ this.startPoint = p; this.replace = color; } /** * Initialize a new instance of the FloodFill class. * @param p Point (x,y); * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. * @param algorithm Floodfill algorithm. */ public FloodFill(IntPoint p, int r, int g, int b, Algorithm algorithm) { this.startPoint = p; this.replace = new Color(r,g,b); this.algorithm = algorithm; } /** * Initialize a new instance of the FloodFill class. * @param x X-axis. * @param y Y-axis. * @param gray Gray channel value. */ public FloodFill(int x, int y, int gray) { this.startPoint = new IntPoint(x, y); this.gray = gray; } /** * Initialize a new instance of the FloodFill class. * @param x X-axis. * @param y Y-axis. * @param gray Gray channel value. * @param algorithm Floodfill algorithm. */ public FloodFill(int x, int y, int gray, Algorithm algorithm){ this.startPoint = new IntPoint(x, y); this.algorithm = algorithm; } /** * Initialize a new instance of the FloodFill class. * @param p Point (x,y). * @param gray Gray channel value. */ public FloodFill(IntPoint p, int gray){ this.startPoint = p; this.gray = gray; } /** * Initialize a new instance of the FloodFill class. * @param p Point (x,y). * @param gray Gray channel value. * @param algorithm Floodfill algorithm. */ public FloodFill(IntPoint p, int gray, Algorithm algorithm){ this.startPoint = p; this.gray = gray; this.algorithm = algorithm; } /** * Floodfill algorithm. * @return Floodfill algorithm. */ public Algorithm getAlgorithm() { return algorithm; } /** * Floodfill algorithm. * @param algorithm Floodfill algorithm. */ public void setAlgorithm(Algorithm algorithm) { this.algorithm = algorithm; } /** * Sets RGB. * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. */ public void setRGB(int r, int g, int b){ this.replace = new Color(r, g, b); } /** * Get point. * @return IntPoint. */ public IntPoint getPoint(){ return startPoint; } /** * Sets point. * @param x X-axis. * @param y Y-axis. */ public void setPoint(int x, int y){ this.startPoint = new IntPoint(x, y); } @Override public void applyInPlace(FastBitmap fastBitmap){ if(fastBitmap.isRGB()){ int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); LinkedList examList = new LinkedList(); Color old = new Color(fastBitmap.getRGB(startPoint)); switch(algorithm){ case FourWay: if (!Color.isEqual(old, replace)) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeLast(); Color temp = new Color(fastBitmap.getRGB(p)); if (Color.isEqual(old, temp)) { int x = p.x; int y = p.y; fastBitmap.setRGB(x, y, replace); if (y-1 >= 0) { examList.addFirst(new IntPoint(x,y-1)); // check west neighbor } if (y+1 < width) { examList.addFirst(new IntPoint(x,y+1)); // check east neighbor } if (x+1 < height) { examList.addFirst(new IntPoint(x+1,y)); // check south neighbor } if (x-1 >= 0) { examList.addFirst(new IntPoint(x-1,y)); // check north neighbor } } } } break; case EightWay: if (!Color.isEqual(old, replace)) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeFirst(); Color temp = new Color(fastBitmap.getRGB(p)); if (Color.isEqual(old, temp)) { int x = p.x; int y = p.y; fastBitmap.setRGB(x, y, replace); if ((x-1 >= 0) && (y-1 >= 0)) { examList.addFirst(new IntPoint(x-1,y-1)); // check north-west neighbor } if (x-1 >= 0) { examList.addFirst(new IntPoint(x-1,y)); // check north neighbor } if ((x+1 < height) && (y+1 < width)) { examList.addFirst(new IntPoint(x+1,y+1)); // check north-east neighbor } if (y-1 >= 0) { examList.addFirst(new IntPoint(x,y-1)); // check west neighbor } if (y+1 < width) { examList.addFirst(new IntPoint(x,y+1)); // check east neighbor } if ((x+1 < height) && (y-1 >= 0)) { examList.addFirst(new IntPoint(x+1,y-1)); // check south-west neighbor } if (x+1 < height) { examList.addFirst(new IntPoint(x+1,y)); // check south neighbor } if ((x+1 < height) && (y+1 < width)) { examList.addFirst(new IntPoint(x+1,y+1)); // check south-east neighbor } } } } break; } } else if (fastBitmap.isGrayscale()){ int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); LinkedList examList = new LinkedList(); int iGray = fastBitmap.getGray(startPoint); int _gray = gray; int _Gray = _gray; switch(algorithm){ case FourWay: if (iGray != _Gray) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeLast(); _gray = fastBitmap.getGray(p.x, p.y); _Gray = _gray; if (_Gray == iGray) { int x = p.x; int y = p.y; fastBitmap.setGray(x, y, gray); if (y-1 > 0) { examList.addFirst(new IntPoint(x,y-1)); // check west neighbor } if (y+1 < width) { examList.addFirst(new IntPoint(x,y+1)); // check east neighbor } if (x+1 < height) { examList.addFirst(new IntPoint(x+1,y)); // check south neighbor } if (x-1 > 0) { examList.addFirst(new IntPoint(x-1,y)); // check north neighbor } } } } break; case EightWay: if (iGray != _Gray) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.pop(); _gray = fastBitmap.getGray(p.x, p.y); _Gray = _gray; if (_Gray == iGray) { int x = p.x; int y = p.y; fastBitmap.setGray(x, y, gray); if ((x-1 > 0) && (y-1 > 0)) { examList.addFirst(new IntPoint(x-1,y-1)); // check north-west neighbor } if (x-1 > 0) { examList.addFirst(new IntPoint(x-1,y)); // check north neighbor } if ((x+1 < height) && (y+1 < width)) { examList.addFirst(new IntPoint(x+1,y+1)); // check north-east neighbor } if (y-1 > 0) { examList.addFirst(new IntPoint(x,y-1)); // check west neighbor } if (y+1 < width) { examList.addFirst(new IntPoint(x,y+1)); // check east neighbor } if ((x+1 < height) && (y-1 > 0)) { examList.addFirst(new IntPoint(x+1,y-1)); // check south-west neighbor } if (x+1 < height) { examList.addFirst(new IntPoint(x+1,y)); // check south neighbor } if ((x+1 < height) && (y+1 < width)) { examList.addFirst(new IntPoint(x+1,y+1)); // check south-east neighbor } } } } break; } } else{ throw new IllegalArgumentException("Flood fill only works in RGB and grayscale images."); } } }

Ads help maintain this website.