更改BufferedImage的内容,然后更新JFrame以反映它

我正在尝试使用GUI制作Mandelbrot Set渲染器,您可以单击并拖动以放大特定区域。 运行时,它将进行初始计算和渲染,但是当您尝试单击并拖动以放大时,控制台会说它正在进行计算,但JFrame的内容未更新。

但是,我甚至不肯认为它正在重新计算,因为初始计算大约需要8秒,但是当您单击/拖动进行缩放时,大约需要6毫秒。

我在下面发布了我的代码。

复数类

public class Complex { private double real, imag; // Constructors public Complex(){ real=0.0; imag=0.0; } public Complex(double real, double imag) { this.real=real; this.imag=imag; } // add given complex number to this one, returning the Complex result public Complex add(Complex other) { return new Complex(this.real+other.real, this.imag+other.imag); } // multiply given complex number by this one, returning the Complex result public Complex multiply(Complex other) { return new Complex((this.real*other.real)-(this.imag*other.imag), (this.imag*other.real)+(this.real*other.imag)); } // get the magnitude of this complex number public double getMagnitude() { return Math.sqrt((real*real)+(imag*imag)); } } 

Runnable MandelbrotTask类

 public class MandelbrotTask implements Runnable { private double x1, y1, x2, y2; private int startCol, endCol, startRow, endRow, maxIters; private int[][] iterCounts; public MandelbrotTask(int maxIters, double x1, double y1, double x2, double y2, int startCol, int endCol, int startRow, int endRow, int[][] iterCounts) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.startCol = startCol; this.endCol = endCol; this.startRow = startRow; this.endRow = endRow; this.iterCounts = iterCounts; this.maxIters=maxIters; } @Override public void run() { for (int i = startRow; i < endRow; i++) { for (int j = startCol; j < endCol; j++) { Complex c = getComplex(i, j); int iterCount = countIters(c); iterCounts[i][j] = iterCount; } } } public Complex getComplex(int i, int j){ //output image is 600 X 600 pixels double incrementX; double incrementY; if(x2!=x1){ incrementX=(Math.abs(x2-x1)/600); } else{ throw new ArithmeticException("Error: area=0"); } if(y2!=y1){ incrementY=(Math.abs(y2-y1)/600); } else{ throw new ArithmeticException("Error: area=0"); } return new Complex(x1+((double)i*incrementX), y1+((double)j*incrementY)); } public int countIters(Complex c){ Complex z=new Complex(0, 0); int iters=0; while(z.getMagnitude()<2 && iters<=maxIters){ z=z.multiply(z).add(c); iters++; } return iters; } } 

主要Mandelbrot类

 import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Scanner; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class Mandelbrot { private static final int HEIGHT = 600; private static final int WIDTH = 600; private static final int maxIters=50000; private static Rectangle zoomBox; private static Point initialClick; private static JLabel content; //bufferedImage will be put into this JLabel private static int[][] iterCounts; private static BufferedImage bufferedImage; //rendering will be written to this bufferedImage private static JFrame frame; public static void main(String[] args) throws IOException { zoomBox=null; Scanner keyboard = new Scanner(System.in); double x1 = -2; double y1 = -2; double x2 = 2; double y2 = 2; /*System.out.print("Max iterations (16,581,375 supported): "); int maxIters=50000; if(maxIters>16581375){ throw new UnsupportedOperationException("Error: Max Iterations: Overflow."); } System.out.print("Output filename: "); String fileName = keyboard.next(); if(!fileName.endsWith(".png") && !fileName.endsWith(".PNG")){ fileName=fileName + ".png"; }*/ // TODO: create the rendering, save it to a file iterCounts=new int[WIDTH][HEIGHT]; recalculate(x1, y1, x2, y2, iterCounts); bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); MouseAdapter listener = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { handleMousePressed(e); } @Override public void mouseDragged(MouseEvent e) { handleMouseDragged(e); } @Override public void mouseReleased(MouseEvent e) { handleMouseReleased(e); } }; content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, true))); content.addMouseListener(listener); content.addMouseMotionListener(listener); /*OutputStream os = new BufferedOutputStream(new FileOutputStream(fileName)); try { ImageIO.write(bufferedImage, "PNG", os); } finally { os.close(); }*/ frame = new JFrame("Mandelbrot Viewer"); frame.getContentPane().add(content); frame.pack(); frame.setSize(new Dimension(WIDTH, HEIGHT)); frame.setResizable(false); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } public static BufferedImage render(int[][] iterCounts, BufferedImage bufferedImage, Rectangle zoomBox, boolean updated){ bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics g = bufferedImage.getGraphics(); Graphics2D g2=(Graphics2D) g; if(updated){ for(int i=0; i<WIDTH; i++){ for(int j=0; j<HEIGHT; j++){ if(iterCounts[i][j]<maxIters){ String hexCode= String.format("#%06x", (0xFFFFFF & (32*iterCounts[i][j]))); g.setColor(Color.decode(hexCode)); } else{ g.setColor(Color.CYAN); } g.drawLine(i, j, i, j); } } } else{ if(zoomBox!=null){ g2.setStroke(new BasicStroke(7)); g2.draw(zoomBox); } } return bufferedImage; } public static int[][] recalculate(double x1, double y1, double x2, double y2, int[][] iterCounts){ MandelbrotTask[] tasks=new MandelbrotTask[4]; tasks[0]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 0, HEIGHT/4, iterCounts); tasks[1]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, HEIGHT/4, 2*(HEIGHT/4), iterCounts); tasks[2]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 2*(HEIGHT/4), 3*(HEIGHT/4), iterCounts); tasks[3]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 3*(HEIGHT/4), 4*(HEIGHT/4), iterCounts); //parallelize computation Thread[] threads=new Thread[4]; for(int i=0; i<4; i++){ threads[i]=new Thread(tasks[i]); } System.out.println("Working..."); //start timer, start computation long start=System.currentTimeMillis(); for(int i=0; i<4; i++){ threads[i].start(); } for(int i=0; ie.getY()){ zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getX())); } else if(e.getY()>e.getX()){ zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getY()), (int)(e.getY()-initialClick.getY())); } else{ zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getY())); } content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false))); content.repaint(); } protected static void handleMouseReleased(MouseEvent e) { recalculate(initialClick.getX(), initialClick.getY(), e.getX(), e.getY(), iterCounts); SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { zoomBox=null; content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false))); content.repaint(); } }); } } 

首先,你在每次重复迭代时创建一个新的JLabel,而这个JLabel被添加到任何东西。

而是使用相同的JLabel,而是创建一个新的ImageIcon并设置查看的JLabel的Icon。

 ImageIcon icon = new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)); content.setIcon(icon); 

你也似乎没有对从recalculate返回的int数组做任何事情。

你的handleMouseReleased方法不应该有:

 iterCounts = recalculate(initialClick.getX(), initialClick.getY(), e.getX(), e.getY(), iterCounts); 

此外,你仍然有糟糕的线程 – 你在Swing事件线程中调用你的线程上的连接,这几乎肯定会冻结你的GUI。 使用SwingWorker,然后在工作完成后收到工作通知后,使用其数据。 你也非常在GUI中使用静态变量。 制作GUI组件实例字段,而不是静态字段。

还有更多的逻辑错误我还没发现我害怕…


程序的第二次迭代 – 使用 Mandelbrot计算:

在此处输入图像描述

 import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.Dialog.ModalityType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.concurrent.ExecutionException; import javax.swing.*; @SuppressWarnings("serial") public class Mandel2 extends JPanel { private static final int GUI_HEIGHT = 600; private static final int GUI_WIDTH = 600; private static final int MAX_ITERS = 50000; private BufferedImage image = new BufferedImage(GUI_WIDTH, GUI_HEIGHT, BufferedImage.TYPE_INT_ARGB); private Rectangle zoomRect; private double myX0 = -2.5; private double myY0 = -2.0; private double myX1 = 1.5; private double myY1 = 2.0; private JDialog waitDialog; public Mandel2() { final MyMouse myMouse = new MyMouse(); int delayStartingCalc = 2 * 1000; // 2 second delay Timer timer = new Timer(delayStartingCalc, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { addMouseListener(myMouse); addMouseMotionListener(myMouse); Rectangle myRect = new Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT); createMandel(myRect); } }); timer.setRepeats(false); timer.start(); } @Override public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } return new Dimension(GUI_WIDTH, GUI_HEIGHT); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (image != null) { g.drawImage(image, 0, 0, this); } Graphics2D g2 = (Graphics2D) g; if (zoomRect == null) { return; } g2.setXORMode(Color.gray); g2.draw(zoomRect); } private double screenToLogicalX(double screenX) { return myX0 + (screenX * (myX1 - myX0)) / GUI_WIDTH; } private double screenToLogicalY(double screenY) { return myY0 + ((GUI_HEIGHT - screenY) * (myY1 - myY0)) / GUI_HEIGHT; } private void createMandel(Rectangle myRect) { double x0 = screenToLogicalX(myRect.x); double y0 = screenToLogicalY(myRect.y + myRect.height); double x1 = screenToLogicalX(myRect.x + myRect.width); double y1 = screenToLogicalY(myRect.y); myX0 = x0; myY0 = y0; myX1 = x1; myY1 = y1; MandelWorker mandelWorker = new MandelWorker(MAX_ITERS, x0, y0, x1, y1); mandelWorker.addPropertyChangeListener(new MandelWorkerListener()); mandelWorker.execute(); if (waitDialog == null) { Window win = SwingUtilities.getWindowAncestor(Mandel2.this); JProgressBar jProgressBar = new JProgressBar(); jProgressBar.setIndeterminate(true); waitDialog = new JDialog(win, "Please Wait", ModalityType.APPLICATION_MODAL); waitDialog.add(jProgressBar); waitDialog.pack(); waitDialog.setLocationRelativeTo(win); } waitDialog.setVisible(true); } private class MyMouse extends MouseAdapter { private Point p; @Override public void mousePressed(MouseEvent e) { p = e.getPoint(); } public void mouseDragged(MouseEvent e) { zoomRect = createRect(e); repaint(); }; @Override public void mouseReleased(MouseEvent e) { zoomRect = createRect(e); repaint(); createMandel(zoomRect); } private Rectangle createRect(MouseEvent e) { int x = Math.min(px, e.getX()); int y = Math.min(py, e.getY()); int width = Math.abs(px - e.getX()); int height = Math.abs(py - e.getY()); return new Rectangle(x, y, width, height); } } private class MandelWorkerListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() == SwingWorker.StateValue.DONE) { waitDialog.setVisible(false); waitDialog.dispose(); MandelWorker worker = (MandelWorker) evt.getSource(); try { image = worker.get(); zoomRect = null; repaint(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } } private class MandelWorker extends SwingWorker { private int maxIters; private double x1; private double y1; private double x2; private double y2; public MandelWorker(int maxIters, double x1, double y1, double x2, double y2) { this.maxIters = maxIters; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @Override protected BufferedImage doInBackground() throws Exception { int[][] iterGrid = new int[GUI_HEIGHT][GUI_WIDTH]; for (int i = 0; i < GUI_HEIGHT; i++) { double y = y1 + i * (y2 - y1) / GUI_HEIGHT; for (int j = 0; j < GUI_WIDTH; j++) { double x = x1 + j * (x2 - x1) / GUI_WIDTH; int iIndex = GUI_HEIGHT - i - 1; iterGrid[iIndex][j] = calcMandel(x, y); } } return render(iterGrid); } private BufferedImage render(int[][] iterGrid) { int w = GUI_WIDTH; int h = GUI_HEIGHT; BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = img.createGraphics(); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if (iterGrid[i][j] < maxIters) { String hexCode = String.format("#%06x", (0xFFFFFF & (32 * iterGrid[i][j]))); g2.setColor(Color.decode(hexCode)); } else { g2.setColor(Color.CYAN); } g2.drawLine(j, i, j, i); } } g2.dispose(); return img; } private int calcMandel(double x, double y) { Complex c = new Complex(x, y); Complex z = new Complex(); int iters = 0; while (z.getMagnitude() < 2 && iters <= maxIters) { z = z.multiply(z).add(c); iters++; } return iters; } } private class Complex { private double real, imag; // Constructors public Complex() { real = 0.0; imag = 0.0; } public Complex(double real, double imag) { this.real = real; this.imag = imag; } // add given complex number to this one, returning the Complex result public Complex add(Complex other) { return new Complex(this.real + other.real, this.imag + other.imag); } // multiply given complex number by this one, returning the Complex // result public Complex multiply(Complex other) { return new Complex((this.real * other.real) - (this.imag * other.imag), (this.imag * other.real) + (this.real * other.imag)); } // get the magnitude of this complex number public double getMagnitude() { return Math.sqrt((real * real) + (imag * imag)); } } private static void createAndShowGui() { Mandel2 mainPanel = new Mandel2(); JFrame frame = new JFrame("Mandel2"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.setResizable(false); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } 

第二次迭代 - 它进行计算,但效率不高。