当前位置: 首页 > 知识库问答 >
问题:

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

曹泉
2023-03-14

我正在尝试制作一个带有GUI的Mandelbrot集渲染器,您可以在其中单击并拖动以放大到特定区域。运行时,它将进行初始计算并进行精细渲染,但当您尝试单击并拖动以放大时,控制台会说它正在进行计算,但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));
    }
}

可运行的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; i<4; i++){
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                System.err.println("A thread was interrupted.");
            }
        }
        //end timer
        long end=System.currentTimeMillis();
        long elapsed=end-start;
        System.out.println("Done.");
        System.out.println("Took " + elapsed + " ms.");

        return iterCounts;
    }

    protected static void handleMousePressed(MouseEvent e) {
        initialClick=e.getPoint();
    }

    protected static void handleMouseDragged(MouseEvent e) {
        if(e.getX()>e.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();
            }
        });
    }
}

共有1个答案

江阳冰
2023-03-14

首先,您在每次重新迭代时创建一个新的JLabel,而这个JLabel将被添加为空。

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

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

您似乎也没有对重新计算返回的int数组做任何处理。

难道你的HandleMouseRelested方法不应该有:

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(p.x, e.getX());
            int y = Math.min(p.y, e.getY());
            int width = Math.abs(p.x - e.getX());
            int height = Math.abs(p.y - 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<BufferedImage, Void> {
        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();
            }
        });
    }
}

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

 类似资料:
  • 我一直在努力让我的JFrame重新绘制它的内容。在将新组件添加到JFrame中后,我尝试同时使用revaluate()和repaint()方法,但没有看到框架发生变化。 当用户单击前3个按钮中的任一个时,他们将进入以下代码块 当点击一个按钮时,整个应用程序变成白色,但我看不到我添加的新按钮。当我删除两个setVisible()方法行时,单击一个按钮肯定会删除组件,因为我现在不能单击其中任何一个,但

  • 我使用Eclipse创建了一个网站,Servlet在其中将数据发送到jsp。但是我不知道为什么我在Servlet中更改数据。它仍然将旧数据发送到jsp。即使我尝试了这些选项.. 菜单-项目-清理(如果不使用自动构建,请单击此选项) 菜单-项目-项目自动构建(选中此选项) 下面是一个例子: Product.java ProductModel.java ProductController.java i

  • 问题内容: 我已经定义了一个模型,并通过创建了它的关联数据库。现在,我已经向模型添加了一些字段,我syncdb再次尝试了,但是没有输出出现。尝试从模板访问这些新字段时,出现“ No Such Column”异常,使我相信syncdb实际上并未更新数据库。这里正确的命令是什么? 问题答案: 似乎你需要的是迁移系统。South真的很棒,工作非常好,有一些自动化工具可以简化你的工作流程。 注意:sync

  • 我创建了一个TableView,并为每个TableColumn注册了属性。内部数据的编辑可以很好地反映在TableView中。 然而,对于ListView,情况就不同了。除非我关闭框架并再次打开它,否则更改不会立即显示。

  • 问题内容: 我有一个需要更改的地方(添加2列),然后更新同一张表。 这是我尝试过的查询: 我需要一次运行上述两个查询。 我正在使用Talend ETL工具,在这里我们有一个组件tMssqlrow,它允许我们运行多个查询(我在单个组件中使用10到15个更新查询)。 但是上面的查询不起作用。 我在数据库Microsoft SQL中进行了测试。我收到以下错误: 消息207,第16级,州1,第5行 无效的

  • 问题内容: 我的JTable有问题。我的JTable显示数据库的内容。一个数据库表具有名称类别。每个类别都显示在JComboBox中。如果单击类别,则应该更新表内容。 这是我的代码的简短摘要,因此可以更轻松地为我提供帮助。该代码应可运行: (TestClass-主要) (如果需要,则为TestTableModel-AbstractTableModel(您需要它来执行代码!) 使用此短代码,如果更改