边界填充java导致stackOverflow

时间:2015-02-04 20:23:42

标签: java stack-overflow fill boundary

我正在尝试使用java中的边界填充算法实现一个简单的应用程序,每次我收到stackoverflow错误,我不知道为什么。

从我看到的帖子,我相信这是因为机器人。

这是代码

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

@SuppressWarnings("serial")
public class drawfill extends JPanel implements MouseListener,MouseMotionListener  {

    public static JFrame shell;
    public static Dimension shellSize = new Dimension(500, 500);
    public Graphics2D G;
    public Color boundaryColor = Color.black;
    public Color fillColor = Color.yellow;
    public int xInit;
    public int yInit;
    public int xFinal;
    public int yFinal;
    public boolean fill  = false;
    public Robot rb;
    BufferedImage img;
    Graphics2D gimg;

    public static void main(String[] args) throws AWTException {
        shell = new JFrame("Draw");
        shell.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent we){
                System.exit(0);
            }
        });
        shell.setLayout(new BorderLayout());
        shell.setMinimumSize(shellSize);
        shell.setResizable(false);
        drawfill dpanel = new drawfill();
        RadioPanelClass radio = dpanel.new RadioPanelClass();

        shell.add(radio,BorderLayout.NORTH);
        shell.add(dpanel,BorderLayout.CENTER);
        shell.setVisible(true);
        shell.setLocationRelativeTo(null);
    }

    public drawfill() throws AWTException{
        rb = new Robot();
        super.setBackground(Color.white);
        changeCursor(true);
        super.addMouseMotionListener(this);
        super.addMouseListener(this);           
    }

    public void paint(Graphics g){
        G = (Graphics2D)g;
        super.paint(G);
        G.setColor(boundaryColor);
        G.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        G.drawRect(xInit, yInit, xFinal - xInit, yFinal - yInit);
    }

    public void changeCursor(boolean b){
        //true for draw 
        //flase for fill
        if (b)
            super.setCursor(Cursor.getPredefinedCursor (Cursor.CROSSHAIR_CURSOR));
        else
            super.setCursor(Cursor.getPredefinedCursor (Cursor.HAND_CURSOR));
    }

    public Color getPixel(int x,int y){
        return rb.getPixelColor(x,y);
    }

    public void setPixel(int x,int y,Color color){
        G.setColor(color);
        G.fillOval(x, y, 1, 1);

    }

    public void boundaryFill(int x, int y, Color fill,Color boundary) {
        Color interior = getPixel(x,y); 
        //System.out.println(interior.toString());
        if (interior != boundary && interior != fill){
            setPixel(x,y,fill);
            boundaryFill(x+1,y,fill,boundary);
            boundaryFill(x-1,y,fill,boundary);
            boundaryFill(x,y+1,fill,boundary);
            boundaryFill(x,y-1,fill,boundary);
        }
    }


    @Override
    public void mouseClicked(MouseEvent e) {
        if (fill){
            int x = e.getX();
            int y = e.getY();
            boundaryFill(x,y,fillColor,boundaryColor);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {
        if (!fill){
            xInit = e.getX();
            yInit = e.getY();
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseDragged(MouseEvent e) {
        if (!fill){
            xFinal = e.getX();
            yFinal = e.getY();
            repaint();
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {}

    class RadioPanelClass extends JPanel implements ActionListener {

        RadioPanelClass(){

            JRadioButton draw = new JRadioButton("draw");
                draw.setActionCommand("draw");
                draw.setSelected(true); 
            JRadioButton fill = new JRadioButton("fill");
                fill.setActionCommand("fill");
            super.add(draw);
            super.add(fill);

            ButtonGroup TypeRadio = new ButtonGroup();
            TypeRadio.add(draw);
            TypeRadio.add(fill);

            // Register a listener for the radio buttons.
            draw.addActionListener(this);
            fill.addActionListener(this);

        }

        @Override
        public void actionPerformed(ActionEvent e) {
            String actionCommand = e.getActionCommand();
            if (actionCommand == "draw") {
                changeCursor(true);
            }
            else if (actionCommand == "fill"){
                changeCursor(false);
                fill = true;
            }
        }
    }
}

错误:

Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
    at sun.nio.cs.SingleByte.withResult(Unknown Source)
    at sun.nio.cs.SingleByte.access$000(Unknown Source)
    at sun.nio.cs.SingleByte$Encoder.encodeArrayLoop(Unknown Source)
    at sun.nio.cs.SingleByte$Encoder.encodeLoop(Unknown Source)
    at java.nio.charset.CharsetEncoder.encode(Unknown Source)
    at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
    at sun.nio.cs.StreamEncoder.write(Unknown Source)
    at java.io.OutputStreamWriter.write(Unknown Source)
    at java.io.BufferedWriter.flushBuffer(Unknown Source)
    at java.io.PrintStream.write(Unknown Source)
    at java.io.PrintStream.print(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at test.drawfill.boundaryFill(drawfill.java:99)
    at test.drawfill.boundaryFill(drawfill.java:102)
    at test.drawfill.boundaryFill(drawfill.java:102)

更新:

我尝试更改代码并使用BufferedImage代替,但我仍然得到相同的stackOverFlow错误,这是更新后的代码:

public void paintComponent(Graphics g){
        G = (Graphics2D)g;
        super.paintComponent(G);
        super.setBackground(Color.white);
        bi = new BufferedImage(super.getWidth(),super.getHeight(),BufferedImage.TYPE_INT_RGB);
        gbi = bi.createGraphics();

        gbi.setBackground(Color.WHITE);
        gbi.clearRect(0,0,super.getWidth(),super.getHeight());
        gbi.setColor(boundaryColor);
        gbi.drawRect(xInit, yInit, xFinal - xInit, yFinal - yInit);
        G.drawImage(bi, 0,0,null);
        gbi.dispose();
    }

public Color getPixel(int x,int y){
        return new Color(bi.getRGB(x, y));
    }

    public void setPixel(int x,int y,Color color){
        bi.setRGB(x, y, color.getRGB());
        repaint();
    }

    public void boundaryFill(int x, int y, Color fill,Color boundary) {
        if ( (x>= xInit && x<= xFinal) && (y>= yInit && y<=yFinal) ){
            Color interior = getPixel(x,y); 
            //System.out.println(interior.toString());
            if (interior != boundary && interior != fill){
                setPixel(x,y,fill);
                boundaryFill(x+1,y,fill,boundary);
                boundaryFill(x-1,y,fill,boundary);
                boundaryFill(x,y+1,fill,boundary);
                boundaryFill(x,y-1,fill,boundary);
            }
            else
                return;
        }
        else
            return;
    }

3 个答案:

答案 0 :(得分:0)

您正在使用rb来确定给定像素的颜色,该像素应该控制boundaryFill的递归。但是当你设置一个像素时,你会操纵G,并且不清楚rb被告知这些像素变化(更不用说如何);如果rb永远不会改变,那么就没有任何东西可以阻止boundaryFill的递归。

答案 1 :(得分:0)

这里有几个问题正在结合:

正如Scott Hunter的回答所示,java.awt.Robot对屏幕上的实际像素颜色进行操作,而不是Graphics2D中的颜色。这意味着Robot.getPixelColor(screenX, screenY)返回的颜色不会更新,直到Graphics2D实际绘制到屏幕上 - 这不会发生在boundaryFill()的中间调用

此外,Robot在屏幕坐标中运行,而Graphics2D在({在这种情况下)您的JPanel坐标空间中运行 - 这意味着即使您重新绘制, Robot.getPixelColor的参数需要与G.fillOval的参数不同。

接下来,你没有对传递给boundaryFill()的坐标进行边界检查 - 这意味着如果你的递归到达区域的边缘,你将继续递归,直到x为{{ 1}}或你的堆栈溢出。

作为额外的奖励,我必须仔细检查,但我非常确定保留传递给Integer.MAX_VALUE的{​​{1}}不是可能是乖巧的。执行您尝试操作的传统方法是创建Graphics2D屏幕外,渲染到屏幕外,然后在Component.paint()覆盖中调用BufferedImage。 (由于您正在扩展Graphics2D.drawImage()并因此使用Swing,因此无论如何都应该覆盖paintComponent()而不是JPanel。这样做也可以避免使用paintComponent ,因为您可以使用paint来确定像素颜色。

答案 2 :(得分:0)

我已经回答了这个: https://stackoverflow.com/a/67253198/8196026

您提供的图形尺寸比它可以处理的尺寸更大,这就是为什么重复调用和递归堆栈内存不足的原因,例如在您的情况下为 500。我建议尝试减少这个维度,它应该可以正常工作。