Advertisement
Guest User

FloodFill.java

a guest
Feb 24th, 2011
463
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 17.93 KB | None | 0 0
  1. /*
  2.  * To change this template, choose Tools | Templates
  3.  * and open the template in the editor.
  4.  */
  5. package RingDetectTest;
  6.  
  7. import java.awt.Image;
  8. import java.awt.Graphics;
  9. import java.awt.image.BufferedImage;
  10. import java.awt.image.DataBufferInt;
  11. import java.awt.Color;
  12. import java.util.ArrayList;
  13.  
  14. /**
  15.  * Drawing class which provides flood fill functionality. Found
  16.  * on http://www.javagaming.org forum and adapted to be more generic. Antialiasing
  17.  * and texture filling capability added.
  18.  *
  19.  * @author kingaschi (Christph Aschwanden - king@kingx.com)
  20.  * @author moogie (Javagaming Forum)
  21.  * @author tom  (Javagaming Forum)
  22.  * @since April 26, 2005
  23.  */
  24. public final class FloodFill {
  25.  
  26.     /**
  27.      * Line info class for linear non-recursive fill.
  28.      *
  29.      * @author king
  30.      * @since April 27, 2005
  31.      */
  32.     class LineInfo {
  33.  
  34.         /** The left position. */
  35.         int left;
  36.         /** The right position. */
  37.         int right;
  38.         /** The y position. */
  39.         int y;
  40.  
  41.         /**
  42.          * Sets the line info.
  43.          *
  44.          * @param left  Previous left position.
  45.          * @param right  Previous right position.
  46.          * @param y  Y position.
  47.          */
  48.         void setInfo(int left, int right, int y) {
  49.             this.left = left;
  50.             this.right = right;
  51.             this.y = y;
  52.         }
  53.     }
  54.     /** The array used for fast flood fill. Instantiated only once to improve performance. */
  55.     private ArrayList<LineInfo> linearNRTodo = new ArrayList<LineInfo>();
  56.     /** The index into linear non-recursive fill. */
  57.     private int index;
  58.     /** True, if antialised fill should be used. */
  59.     private boolean antialiased = true;
  60.     /** The raw image data to fill. */
  61.     private int[] image;
  62.     /** The raw mask image data with the borders. */
  63.     private int[] maskImage;
  64.     /** The start x position for the fill. */
  65.     private int startX;
  66.     /** The start y position for the fill. */
  67.     private int startY;
  68.     /** The fill color to use for the original image. */
  69.     private int fillColor;
  70.     /** The fill color to use for the mask image. */
  71.     private int maskColor;
  72.     /** The pattern fill color to use. */
  73.     private int patternColor;
  74.     /** The width of the chessboard pattern. */
  75.     private int patternWidth;
  76.     /** The height of the chessboard pattern. */
  77.     private int patternHeight;
  78.     /** The color to replace with the fill color. */
  79.     private int startColor;
  80.     /** The width of the image to fill. */
  81.     private int width;
  82.     /** The height of the image to fill. */
  83.     private int height;
  84.     /** The result image. */
  85.     private BufferedImage bufferedImage;
  86.     /** The mask image used. */
  87.     private BufferedImage bufferedMaskImage;
  88.  
  89.     /**
  90.      * Constructor for flood fill, requires the image for filling operation.
  91.      *
  92.      * @param imageToFill  The image used for filling.
  93.      */
  94.     public FloodFill(Image imageToFill) {
  95.         this(imageToFill, null);
  96.     }
  97.  
  98.     /**
  99.      * Constructor for flood fill, requires the image and mask for filling operation.
  100.      *
  101.      * @param imageToFill  The image used for filling.
  102.      * @param maskImage    The image containing the border lines.
  103.      */
  104.     public FloodFill(Image imageToFill, Image maskImage) {
  105.         // sets image to fill
  106.         setImage(imageToFill);
  107.  
  108.         // sets the mask
  109.         setMask(maskImage);
  110.     }
  111.  
  112.     /**
  113.      * Returns true, if antialiased filling is used.
  114.      *
  115.      * @return  True, for antialiased filling.
  116.      */
  117.     public boolean isAntialiased() {
  118.         return this.antialiased;
  119.     }
  120.  
  121.     /**
  122.      * Sets if antialiased filling is used.
  123.      *
  124.      * @param antialiased  True, for antialiased filling.
  125.      */
  126.     public void setAntialiased(boolean antialiased) {
  127.         this.antialiased = antialiased;
  128.     }
  129.  
  130.     /**
  131.      * Returns the width of the fill area.
  132.      *
  133.      * @return  The width of the fill area.
  134.      */
  135.     public int getWidth() {
  136.         return this.width;
  137.     }
  138.  
  139.     /**
  140.      * Returns the height of the fill area.
  141.      *
  142.      * @return  The height of the fill area.
  143.      */
  144.     public int getHeight() {
  145.         return this.height;
  146.     }
  147.  
  148.     /**
  149.      * Returns the image that was filled.
  150.      *
  151.      * @return  The image that was filled.
  152.      */
  153.     public BufferedImage getImage() {
  154.         return bufferedImage;
  155.     }
  156.  
  157.     /**
  158.      * Sets the image to be filled.
  159.      *
  160.      * @param imageToFill  The image to be filled.
  161.      */
  162.     public void setImage(Image imageToFill) {
  163.         // copy image to fill into buffered image first
  164.         width = imageToFill.getWidth(null);
  165.         height = imageToFill.getHeight(null);
  166.         bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  167.         Graphics g = bufferedImage.getGraphics();
  168.         g.drawImage(imageToFill, 0, 0, null);
  169.  
  170.         // get fill image
  171.         this.image = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer()).getData();
  172.     }
  173.  
  174.     /**
  175.      * Returns the mask containing the fill borders.
  176.      *
  177.      * @return  The mask with the fill borders.
  178.      */
  179.     public BufferedImage getMask() {
  180.         return bufferedMaskImage;
  181.     }
  182.  
  183.     /**
  184.      * Sets the mask image which contains the borders.
  185.      *
  186.      * @param maskImage  The mask image to set. If null, the image to fill is used as
  187.      *                   the mask.
  188.      */
  189.     public void setMask(Image maskImage) {
  190.         this.bufferedMaskImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  191.         Graphics g = this.bufferedMaskImage.getGraphics();
  192.  
  193.         if (maskImage == null) {
  194.             // if no mask, use the original image to fill
  195.             g.drawImage(this.bufferedImage, 0, 0, null);
  196.         } else {
  197.             // if mask, use it
  198.             g.drawImage(maskImage, 0, 0, null);
  199.         }
  200.  
  201.         this.maskImage = bufferedMaskImage.getRGB(0, 0, width, height, null, 0, width);
  202.     }
  203.  
  204.     /**
  205.      * Flood fills parts of an image.
  206.      *
  207.      * @param x  The x coordinate to start filling.
  208.      * @param y  The y coordinate to start filling.
  209.      * @param color  The new fill color.
  210.      */
  211.     public void fill(int x, int y, Color color) {
  212.         this.startX = x;
  213.         this.startY = y;
  214.         this.fillColor = color.getRGB();
  215.         this.maskColor = color.getRGB();  // the fill color for the mask
  216.         this.startColor = this.maskImage[startY * width + startX];
  217.         if (fillColor != startColor) {
  218.             floodFill();
  219.         }
  220.     }
  221.  
  222.     /**
  223.      * Fills a bounded area of an image. Uses a chessboard pattern. The width in pixels
  224.      * of the chessboard pattern can be specified.
  225.      *
  226.      * @param x  The x coordinate to start filling.
  227.      * @param y  The y coordinate to start filling.
  228.      * @param color  The new fill color.
  229.      * @param patternColor  The color of the pattern to use.
  230.      * @param patternWidth  The width of the chessboard pattern.
  231.      * @param patternHeight  The height of the chessboard pattern.
  232.      */
  233.     public void fill(int x, int y, Color color, Color patternColor, int patternWidth, int patternHeight) {
  234.         this.startX = x;
  235.         this.startY = y;
  236.         this.fillColor = color.getRGB();
  237.         this.patternColor = patternColor.getRGB();
  238.         this.patternWidth = (patternWidth > 0) ? patternWidth : Integer.MAX_VALUE;
  239.         this.patternHeight = (patternHeight > 0) ? patternHeight : Integer.MAX_VALUE;
  240.         this.startColor = this.maskImage[startY * width + startX];
  241.  
  242.         // the fill color for the mask
  243.         this.maskColor = ((this.fillColor >> 2) & 0x3F3F3F3F)
  244.                 + ((this.patternColor >> 1) & 0x7F7F7F7F);
  245.  
  246.         if (this.maskColor != this.startColor) {
  247.             // mask color ok (=different)
  248.             floodFill2();
  249.         } else {
  250.             // create new mask color first
  251.             this.maskColor = ((this.fillColor >> 1) & 0x7F7F7F7F)
  252.                     + ((this.patternColor >> 2) & 0x3F3F3F3F);
  253.             floodFill2();
  254.         }
  255.     }
  256.  
  257.     /**
  258.      * Fills the line at (x, y). Then fills the line above and below the current line.
  259.      * The border is defined as any color except the start color. Non-recursive version,
  260.      * doesn't have JVM stack size limitations.
  261.      */
  262.     private void floodFill() {
  263.         // init stack
  264.         linearNRTodo.clear();
  265.         index = 0;
  266.         floodFill(startX, startY);
  267.  
  268.         // loop through todo list
  269.         while (index < linearNRTodo.size()) {
  270.             // get loop data
  271.             LineInfo lineInfo = linearNRTodo.get(index);
  272.             index++;
  273.             int y = lineInfo.y;
  274.             int left = lineInfo.left;
  275.             int right = lineInfo.right;
  276.  
  277.             // check top
  278.             if (y > 0) {
  279.                 int yOff = (y - 1) * width;
  280.                 int x = left;
  281.                 while (x <= right) {
  282.                     if (maskImage[yOff + x] == startColor) {
  283.                         x = floodFill(x, y - 1);
  284.                     } else {
  285.                         // fill antialised if allowed
  286.                         if (antialiased) {
  287.                             int antialiasedColor = maskImage[yOff + x];
  288.                             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  289.                             antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
  290.                             if (antialiasedColor != startColor) {
  291.                                 image[yOff + x] = antialiasedColor;
  292.                             }
  293.                         }
  294.                     }
  295.                     x++;
  296.                 }
  297.             }
  298.  
  299.             // check bottom
  300.             if (y < height - 1) {
  301.                 int yOff = (y + 1) * width;
  302.                 int x = left;
  303.                 while (x <= right) {
  304.                     if (maskImage[yOff + x] == startColor) {
  305.                         x = floodFill(x, y + 1);
  306.                     } else {
  307.                         // fill antialised if allowed
  308.                         if (antialiased) {
  309.                             int antialiasedColor = maskImage[yOff + x];
  310.                             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  311.                             antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
  312.                             if (antialiasedColor != startColor) {
  313.                                 image[yOff + x] = antialiasedColor;
  314.                             }
  315.                         }
  316.                     }
  317.                     x++;
  318.                 }
  319.             }
  320.         }
  321.     }
  322.  
  323.     /**
  324.      * Fills the line at (x, y). And adds to the stack.
  325.      *
  326.      * @param x  The x-coordinate of the start position.
  327.      * @param y  The y-coordinate of the start position.
  328.      * @return Right.
  329.      */
  330.     private int floodFill(int x, int y) {
  331.         int yOff = y * width;
  332.  
  333.         // fill left of (x,y) until border or edge of image
  334.         int left = x;
  335.         do {
  336.             image[yOff + left] = fillColor;
  337.             maskImage[yOff + left] = maskColor;
  338.             left--;
  339.         } while ((left >= 0) && (maskImage[yOff + left] == startColor));
  340.         // fill antialised if allowed
  341.         if ((antialiased) && (left >= 0)) {
  342.             int antialiasedColor = maskImage[yOff + left];
  343.             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  344.             antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
  345.             if (antialiasedColor != startColor) {
  346.                 image[yOff + left] = antialiasedColor;
  347.             }
  348.         }
  349.         left++;
  350.  
  351.         // fill right of (x, y) until border or edge of image
  352.         int right = x;
  353.         do {
  354.             image[yOff + right] = fillColor;
  355.             maskImage[yOff + right] = maskColor;
  356.             right++;
  357.         } while ((right < width) && (maskImage[yOff + right] == startColor));
  358.         // fill antialised if allowed
  359.         if ((antialiased) && (right < width)) {
  360.             int antialiasedColor = maskImage[yOff + right];
  361.             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  362.             antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
  363.             if (antialiasedColor != startColor) {
  364.                 image[yOff + right] = antialiasedColor;
  365.             }
  366.         }
  367.         right--;
  368.  
  369.         // add to stack
  370.         if (index == 0) {
  371.             LineInfo lineInfo = new LineInfo();
  372.             lineInfo.setInfo(left, right, y);
  373.             linearNRTodo.add(lineInfo);
  374.         } else {
  375.             index--;
  376.             linearNRTodo.get(index).setInfo(left, right, y);
  377.         }
  378.  
  379.         // return right position
  380.         return right;
  381.     }
  382.  
  383.     /**
  384.      * Fills the line at (x, y). Then fills the line above and below the current line.
  385.      * The border is defined as any color except the start color. Non-recursive version,
  386.      * doesn't have JVM stack size limitations.
  387.      */
  388.     private void floodFill2() {
  389.         // init stack
  390.         linearNRTodo.clear();
  391.         index = 0;
  392.         floodFill2(startX, startY);
  393.  
  394.         // loop through todo list
  395.         while (index < linearNRTodo.size()) {
  396.             // get loop data
  397.             LineInfo lineInfo = linearNRTodo.get(index);
  398.             index++;
  399.             int y = lineInfo.y;
  400.             int left = lineInfo.left;
  401.             int right = lineInfo.right;
  402.  
  403.             // check top
  404.             if (y > 0) {
  405.                 int yOff = (y - 1) * width;
  406.                 int x = left;
  407.                 while (x <= right) {
  408.                     if (maskImage[yOff + x] == startColor) {
  409.                         x = floodFill2(x, y - 1);
  410.                     } else {
  411.                         // fill antialised if allowed
  412.                         if (antialiased) {
  413.                             int antialiasedColor = maskImage[yOff + x];
  414.                             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  415.                             antialiasedColor = antialiasedColor + ((fillColor2(x, y - 1) >> 1) & 0x7F7F7F7F);
  416.                             if (antialiasedColor != startColor) {
  417.                                 image[yOff + x] = antialiasedColor;
  418.                             }
  419.                         }
  420.                     }
  421.                     x++;
  422.                 }
  423.             }
  424.  
  425.             // check bottom
  426.             if (y < height - 1) {
  427.                 int yOff = (y + 1) * width;
  428.                 int x = left;
  429.                 while (x <= right) {
  430.                     if (maskImage[yOff + x] == startColor) {
  431.                         x = floodFill2(x, y + 1);
  432.                     } else {
  433.                         // fill antialised if allowed
  434.                         if (antialiased) {
  435.                             int antialiasedColor = maskImage[yOff + x];
  436.                             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  437.                             antialiasedColor = antialiasedColor + ((fillColor2(x, y + 1) >> 1) & 0x7F7F7F7F);
  438.                             if (antialiasedColor != startColor) {
  439.                                 image[yOff + x] = antialiasedColor;
  440.                             }
  441.                         }
  442.                     }
  443.                     x++;
  444.                 }
  445.             }
  446.         }
  447.     }
  448.  
  449.     /**
  450.      * Fills the line at (x, y). And adds to the stack.
  451.      *
  452.      * @param x  The x-coordinate of the start position.
  453.      * @param y  The y-coordinate of the start position.
  454.      * @return Right.
  455.      */
  456.     private int floodFill2(int x, int y) {
  457.         int yOff = y * width;
  458.  
  459.         // fill left of (x,y) until border or edge of image
  460.         int left = x;
  461.         do {
  462.             image[yOff + left] = fillColor2(left, y);
  463.             maskImage[yOff + left] = maskColor;
  464.             left--;
  465.         } while ((left >= 0) && (maskImage[yOff + left] == startColor));
  466.         // fill antialised if allowed
  467.         if ((antialiased) && (left >= 0)) {
  468.             int antialiasedColor = maskImage[yOff + left];
  469.             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  470.             antialiasedColor = antialiasedColor + ((fillColor2(left, y) >> 1) & 0x7F7F7F7F);
  471.             if (antialiasedColor != startColor) {
  472.                 image[yOff + left] = antialiasedColor;
  473.             }
  474.         }
  475.         left++;
  476.  
  477.         // fill right of (x, y) until border or edge of image
  478.         int right = x;
  479.         do {
  480.             image[yOff + right] = fillColor2(right, y);
  481.             maskImage[yOff + right] = maskColor;
  482.             right++;
  483.         } while ((right < width) && (maskImage[yOff + right] == startColor));
  484.         // fill antialised if allowed
  485.         if ((antialiased) && (right < width)) {
  486.             int antialiasedColor = maskImage[yOff + right];
  487.             antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
  488.             antialiasedColor = antialiasedColor + ((fillColor2(right, y) >> 1) & 0x7F7F7F7F);
  489.             if (antialiasedColor != startColor) {
  490.                 image[yOff + right] = antialiasedColor;
  491.             }
  492.         }
  493.         right--;
  494.  
  495.         // add to stack
  496.         if (index == 0) {
  497.             LineInfo lineInfo = new LineInfo();
  498.             lineInfo.setInfo(left, right, y);
  499.             linearNRTodo.add(lineInfo);
  500.         } else {
  501.             index--;
  502.             linearNRTodo.get(index).setInfo(left, right, y);
  503.         }
  504.  
  505.         // return right position
  506.         return right;
  507.     }
  508.  
  509.     /**
  510.      * Returns the fill color for a given x and y value.
  511.      *
  512.      * @param x  The x position to return the color for.
  513.      * @param y  The y position to return the color for.
  514.      * @return  The color for the given position.
  515.      */
  516.     private int fillColor2(int x, int y) {
  517.         x /= this.patternWidth;
  518.         y /= this.patternHeight;
  519.         if ((x + y) % 2 == 0) {
  520.             return this.fillColor;
  521.         } else {
  522.             return this.patternColor;
  523.         }
  524.     }
  525. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement