2015-02-04 1 views
2

나는 자바에서 경계 채우기 알고리즘을 사용하여 간단한 응용 프로그램을 구현하려고하고 때마다 나는 stackoverflow 오류가 발생하고 이유를 모르겠다.경계 채우기 자바 일으키는 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) 

UPDATE : 내가 코드를 변경하고 대신 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; 
    } 
+0

을 내가하지 깊이 파고 들어 시간이 있지만 내 생각 엔 무한 재귀 루프에 의해 발생합니다. 나는'boundaryFill'가 종료 조건에 도달하지 않고 끊임없이 자신을 호출하는 것으로 의심합니다. 결국 스택이 실행됩니다. 공간을 벗어난다면 StackOverflowError를 볼 수 있습니다. – Bobulous

+0

'getPixel'은'rb'에 액세스하지만'setPixel'는'G'를 조작하는 이유는 무엇입니까? –

+0

@ScottHunter는이 작업을 수행하는 다른 방법이 있습니까? – Tarounen

답변

0

을 사용하여 boundaryFill의 재귀를 제어해야하는 지정된 픽셀의 색상을 결정합니다. 그러나 픽셀을 설정할 때 G을 조작하면 rb에 이러한 픽셀 변경 사항을 알리는 것이 명확하지 않습니다. rb이 변경되지 않으면 boundaryFill의 재귀를 중지 할 수있는 것이 없습니다.

스콧 헌터의 대답에서 알 수 있듯이

java.awt.Robot은하지 Graphics2D의 색상에, 화면에 픽셀의 실제 색상에서 작동 :

0

이 결합 여기에 몇 가지 문제가 있습니다. 즉, Robot.getPixelColor(screenX, screenY)에 의해 반환 된 색상은 Graphics2D이 실제로 화면에 그려 질 때까지 업데이트되지 않습니다. 이는 boundaryFill() 호출 중에 발생할 수 없습니다. Graphics2D (이 경우)의 좌표 공간에서 작동하는 동안

또한, Robot는 화면 좌표에서 작동하는 JPanel - 당신이 다시 칠 않은 경우에도 Robot.getPixelColor에 대한 인수가 다를 필요가 있음을 의미 인수는 G.fillOval입니다.

다음으로는 boundaryFill()에 전달 된 좌표에 대한 경계 검사를 수행하지 않습니다. 즉, 재귀가 영역의 가장자리에 도달하면 x가 Integer.MAX_VALUE이 될 때까지 계속 반복되거나 스택이 오버플로됩니다.

추가 보너스로 두 번 확인해야하지만, Graphics2D으로 전달 된 내용은 Component.paint()으로 전달되어야합니다. 기존의 방법은 화면에 BufferedImage을 만들고 화면에 렌더링 한 다음 paintComponent() 무시에서 Graphics2D.drawImage()으로 전화하는 것입니다. 당신이 JPanel을 확장하기 때문에 스윙을 사용하고 있기 때문에 (어쨌든 최우선 paintComponent 대신 paint의를해야한다. 이렇게하면 또한 픽셀 색상을 결정하는 BufferedImage.getRGB를 사용할 수 있기 때문에 당신의 Robot 사용하지 않도록 할 것입니다.

+0

그 의도는 픽셀의 색상을 검사하여 경계를 인식하는 것이라고 생각합니다. 경계에서 사용되는 색상 인 경우 경계에 있고 해당 픽셀은 색상이 지정되지 않았으며 (?)에 재귀 적으로 표시되지 않습니다. 따라서 코드는 경계 검사를 시도하고 있지만 작동 중이더라도 경계가 아닌 픽셀에 대한 제한없는 재귀가 계속 발생합니다. –

+0

나는 코드를 업데이트하고'BufferedImage'를 사용하려고했지만 같은 오류가 발생했습니다! – Tarounen

+0

! =를 사용하여 새로 색을 입힌 두 가지 색상을 다르게 비교할 수 있습니다. internal! = 경계를 (! interior.equals (boundary))로 바꾸고 채우기 테스트를 위해 동일한 작업을 수행 한 다음 다시 시도하십시오. 아직 코드를 디버거에 넣고 실행 해보면 문제를 파악할 수 있습니다. – Sbodd