2015-02-04 4 views
2

Я пытаюсь реализовать простое приложение с алгоритмом заполнения границ в java, и каждый раз, когда я получаю ошибку stackoverflow, и я не знаю почему.Boundary fill 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) 

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

Я не у меня есть время копать глубже, но я предполагаю, что это вызвано бесконечным рекурсивным циклом. Я подозреваю, что 'borderFill' бесконечно вызывает себя, не достигнув условия завершения, которое позволяет ему выйти. В конечном итоге это вызовет запуск стека вне пространства, и вы получите StackOverflowError, который вы видите. – Bobulous

+0

Почему 'getPixel' доступа' rb', но 'setPixel' управляет' G'? –

+0

@ScottHunter есть ли другой способ перенести эту операцию? – Tarounen

ответ

0

Вы используете rb для определения цвета данного пикселя, который должен контролировать рекурсию boundaryFill. Но когда вы устанавливаете пиксель, вы управляете G, и неясно, если (в меньшей степени) rb получает информацию об этих изменениях пикселей; если rb никогда не меняется, то нечего останавливать рекурсию boundaryFill.

0

Есть несколько проблем, которые здесь сочетающих:

Как подсказывает ответ Скотт Хантер, java.awt.Robot действует на фактический цвет пикселей на экране, а не на цвета в Graphics2D. Это означает, что цвет, возвращаемый Robot.getPixelColor(screenX, screenY), не будет обновляться до тех пор, пока на экране не будет нарисован Graphics2D - что не может произойти в середине вашего вызова boundaryFill().

Кроме того, Robot работает в координатах экрана, в то время как Graphics2D работает в пространстве координат (в данном случае) ваш JPanel - это означает, что даже если ты перекрашивать, аргументы Robot.getPixelColor должны были бы быть разными, чем аргументы G.fillOval.

Далее вы не выполняете проверку границ по координатам, переданным в boundaryFill(), что означает, что если ваша рекурсия когда-либо попадает на край области, вы будете продолжать рекурсировать до тех пор, пока x не будет Integer.MAX_VALUE или переполнения стека.

В качестве дополнительного бонуса мне придется дважды проверять, но я уверен, что держаться за Graphics2D, который прошел в Component.paint(), вряд ли будет хорошо себя вести. Традиционным способом сделать то, что вы пытаетесь, было бы создать BufferedImage за кадром, визуализировать в нем, а затем вызвать Graphics2D.drawImage() в paintComponent() переопределить. (Поскольку вы расширяете JPanel и, следовательно, с помощью свинга, вы должны быть наиважнейшей paintComponent вместо paint так или иначе. Делать это также позволит вам избежать с помощью Robot, так как вы можете использовать BufferedImage.getRGB для определения цвета пикселей.

+0

Я считаю, что цель состоит в том, чтобы распознать границу, проверив цвет пикселя: если это цвет, используемый границей, он находится на границе, и этот пиксель не окрашен и не рекурсирован (?). Таким образом, код * пытается * выполнять проверку границ, но даже если он работает, неограниченная рекурсия на неграничных пикселях все равно будет иметь место. –

+0

Я попытался обновить код и использовать 'BufferedImage', но получил ту же ошибку! – Tarounen

+0

Вы используете! = Сравнить два цвета, которые вы новичок по-другому. Замените ваш интерьер! = Граница с (! Interior.equals (граница)), сделайте то же самое для своего теста заполнения и повторите попытку. И если вы еще этого не сделали - попробуйте поместить свой код в отладчик и пройти его, чтобы выяснить проблемы. – Sbodd

Смежные вопросы