简体   繁体   English

Java,如何绘制不断变化的图形

[英]Java, how to draw constantly changing graphics

Have not done this before, so obviously I suck at it. 之前没有这样做过,所以显然我很沮丧。 Here 64 pixels around current mouse position get drawn little bigger on a form. 这里,当前鼠标位置周围的64个像素在表单上绘制得更大。 Problem is, that it's 'kind of' to slow, and I have no idea where to start fixing. 问题是,它有点'缓慢',我不知道从哪里开始修复。

Besides that, I made a thread, that constantly calls update graphics when it's finished and a little fps like text, to show really how fast things are drawn. 除此之外,我创建了一个线程,它在完成后不断调用更新图形,并且像文本一样点fps,以显示事物的绘制速度。

Image example: (Image is from letter 'a' in Eclipse) 图像示例:(图像来自Eclipse中的字母'a')

替代文字

Code example : 代码示例:

@SuppressWarnings("serial")
public static class AwtZoom extends Frame {
    private BufferedImage image;
    private long timeRef = new Date().getTime();
    Robot robot = null;

    public AwtZoom() {
        super("Image zoom");
        setLocation(new Point(640, 0));
        setSize(400, 400);
        setVisible(true);
        final Ticker t = new Ticker();

        this.image = (BufferedImage) (this.createImage(320, 330));
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                t.done();
                dispose();
            }
        });
        try {
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
        t.start();
    }

    private class Ticker extends Thread {
        public boolean update = true;
        public void done() {
            update = false;
        }
        public void run() {
            try {

                while (update == true) {
                    update(getGraphics());
                    // try {
                    // Thread.sleep(200);
                    // } catch (InterruptedException e) {
                    // e.printStackTrace();
                    // return;
                    // }
                }
            } catch (Exception e) {

                update=false;
            }
        }
    }

    public void update(Graphics g) {
        paint(g);
    }

    boolean isdone = true;
    public void paint(Graphics g) {
        if (isdone) {
            isdone=false;
            int step = 40;
            Point p = MouseInfo.getPointerInfo().getLocation();

            Graphics2D gc = this.image.createGraphics();

            try {

                for (int x = 0; x < 8; x++) {
                    for (int y = 0; y < 8; y++) {
                        gc.setColor(robot.getPixelColor(p.x - 4 + x, p.y
                                - 4 + y));
                        gc.fillOval(x * step, y * step, step - 3, step - 3);
                        gc.setColor(Color.GRAY);
                        gc.drawOval(x * step, y * step, step - 3, step - 3);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            gc.dispose();
            isdone = true;
            iter++;
        }
        g.drawImage(image, 40, 45, this);
        g.setColor(Color.black);
        StringBuilder sb = new StringBuilder();
        sb.append(iter)
                .append(" frames in ")
                .append((double) (new Date().getTime() - this.timeRef) / 1000)
                .append("s.");
        g.drawString(sb.toString(), 50, 375);
    }

    int iter = 0;
}

Changes made: 做出的改变:
* added "gc.dispose();" *添加了“gc.dispose();”
* added "isdone", so redraw could not be called faster, then it should. *添加了“isdone”,因此无法更快地调用重绘,那么应该。
* added this link to thrashgod source rewrite *添加此链接到thrashgod source rewrite
* added this link to thrashgod source rewrite 2 * 将此链接添加到thrashgod source rewrite 2

Here's my major rewrite with the following noteworthy changes: 这是我的主要改写,具有以下值得注意的变化:

  • I've separated the task of detecting pixel colours from the task of drawing 我将检测像素颜色的任务与绘图任务分开了
  • I've replaced robot.getPixelColor(...) with robot.createScreenCapture(...) to fetch all 64 pixels at once, rather than one at a time 我用robot.createScreenCapture(...)替换了robot.getPixelColor(...)来一次获取所有64个像素,而不是一次获取一个
  • I've introduced smart clipping - only what needs to be redrawn is redrawn. 我已经介绍了智能剪辑 - 只重新绘制需要重绘的内容。
  • I've fixed up threading so all updates to the model and view happen on the Event Dispatch Thread 我修复了线程,因此模型和视图的所有更新都发生在Event Dispatch Thread上

The ticker runs constantly. 股票代码不断运行。 When it detects a change in pixel colour (either due to the mouse moving to a different region or the pixels under the mouse changing) it detects exactly what changed, updates the model, then requests the view to repaint. 当它检测到像素颜色的变化时(由于鼠标移动到不同的区域或鼠标变化下的像素),它会准确检测到更改的内容,更新模型,然后请求重新绘制视图。 This approach updates instantly to the human eye. 这种方法立即更新到人眼。 289 screen updates took cumulatively 1 second. 289次屏幕更新累计耗时1秒。

It was an enjoyable challenge for a quiet Saturday evening. 对于一个安静的周六晚上来说,这是一个愉快的挑战。

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

public class ZoomPanel extends JPanel {

    private static final int STEP = 40;
    private int iter = 0;
    private long cumulativeTimeTaken = 0;


    public static void main(String[] args) {
        final JFrame frame = new JFrame("Image zoom");

        final ZoomPanel zoomPanel = new ZoomPanel();
        frame.getContentPane().add(zoomPanel);
        final Ticker t = new Ticker(zoomPanel);

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                t.done();
                frame.dispose();
            }
        });
        t.start();

        frame.setLocation(new Point(640, 0));
        frame.pack();
        frame.setVisible(true);
    }

    private final Color[][] model = new Color[8][8];

    public ZoomPanel() {
        setSize(new Dimension(400, 400));
        setMinimumSize(new Dimension(400, 400));
        setPreferredSize(new Dimension(400, 400));
        setOpaque(true);
    }

    private void setColorAt(int x, int y, Color pixelColor) {
        model[x][y] = pixelColor;
        repaint(40 + x * STEP, 45 + y * STEP, 40 + (x * STEP) - 3, 45 + (y * STEP) - 3);
    }

    private Color getColorAt(int x, int y) {
        return model[x][y];
    }

    public void paintComponent(Graphics g) {
        long start = System.currentTimeMillis();
        if (!SwingUtilities.isEventDispatchThread()) {
            throw new RuntimeException("Repaint attempt is not on event dispatch thread");
        }
        final Graphics2D g2 = (Graphics2D) g;
        g2.setColor(getBackground());
        try {

            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 8; y++) {
                    g2.setColor(model[x][y]);
                    Ellipse2D e = new Ellipse2D.Double(40 + x * STEP, 45 + y * STEP, STEP - 3, STEP - 3);
                    g2.fill(e);
                    g2.setColor(Color.GRAY);
                    g2.draw(e);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        iter++;
        g2.setColor(Color.black);
        long stop = System.currentTimeMillis();
        cumulativeTimeTaken += stop - start;
        StringBuilder sb = new StringBuilder();
        sb.append(iter)
                .append(" frames in ")
                .append((double) (cumulativeTimeTaken) / 1000)
                .append("s.");

        System.out.println(sb);
    }

    private static class Ticker extends Thread {

        private final Robot robot;

        public boolean update = true;
        private final ZoomPanel view;

        public Ticker(ZoomPanel zoomPanel) {
            view = zoomPanel;
            try {
                robot = new Robot();
            } catch (AWTException e) {
                throw new RuntimeException(e);
            }
        }

        public void done() {
            update = false;
        }

        public void run() {
            int runCount = 0;
            while (update) {
                runCount++;
                if (runCount % 100 == 0) {
                    System.out.println("Ran ticker " + runCount + " times");
                }
                final Point p = MouseInfo.getPointerInfo().getLocation();

                Rectangle rect = new Rectangle(p.x - 4, p.y - 4, 8, 8);
                final BufferedImage capture = robot.createScreenCapture(rect);

                for (int x = 0; x < 8; x++) {
                    for (int y = 0; y < 8; y++) {
                        final Color pixelColor = new Color(capture.getRGB(x, y));

                        if (!pixelColor.equals(view.getColorAt(x, y))) {
                            final int finalX = x;
                            final int finalY = y;
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    view.setColorAt(finalX, finalY, pixelColor);
                                }
                            });
                        }
                    }
                }

            }
        }

    }

}

If you don't mind using Swing, this example shows how to quickly zoom in on a BufferedImage obtained from an Icon . 如果你不介意使用Swing,这个例子展示了如何快速放大从Icon获得的BufferedImage In your case, you'd want an 8x8 BufferedImage that gets filled in mouseMoved() with the pixels seen by the robot. 在你的情况下,你需要一个8x8 BufferedImage ,用mouseMoved()填充机器人看到的像素。

Addendum: Here's a snapshot of the top, left corner of your example. 附录:这是您示例左上角的快照。

Addendum: 附录:

Zooming itself is not important... 缩放本身并不重要......

The slow part is getting pixels from the desktop; 缓慢的部分是从桌面获取像素; scaling is minor. 缩放是次要的。 If you just want to see a variety of animation techniques, have a look at this example . 如果您只是想看各种动画技术,请看一下这个例子

Addendum: As getting individual pixels is slow and the createScreenCapture() method suggested by @Steve McLeod is fast, here's the idea I was driving at. 附录:由于获得单个像素很慢并且@Steve McLeod建议的createScreenCapture()方法很快,这就是我开车的想法。 You can see it also updates much more smoothly. 您可以看到它也更顺畅地更新。 Note that releasing the mouse button allows one to see the captured colors. 请注意,释放鼠标按钮可以查看捕获的颜色。

Zoom.png

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see https://stackoverflow.com/questions/3742731 */
public class Zoom extends JPanel implements MouseMotionListener {

    private static final int SIZE = 16;
    private static final int S2 = SIZE / 2;
    private static final int SCALE = 48;
    private BufferedImage img;
    private Robot robot;

    public Zoom() {
        super(true);
        this.setPreferredSize(new Dimension(SIZE * SCALE, SIZE * SCALE));
        img = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_RGB);
        try {
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace(System.err);
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        Point p = e.getPoint();
        int x = p.x * SIZE / getWidth();
        int y = p.y * SIZE / getHeight();
        int c = img.getRGB(x, y);
        this.setToolTipText(x + "," + y + ": "
            + String.format("%08X", c));
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        int x = e.getXOnScreen();
        int y = e.getYOnScreen();
        Rectangle rect = new Rectangle(x - S2, y - S2, SIZE, SIZE);
        img = robot.createScreenCapture(rect);
        repaint();
    }

    private static void create() {
        JFrame f = new JFrame("Click & drag to zoom.");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Zoom zoom = new Zoom();
        f.add(zoom);
        f.pack();
        f.setVisible(true);
        zoom.addMouseMotionListener(zoom);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                create();
            }
        });
    }
}

Add this method to the Paint method: 将此方法添加到Paint方法:

public void clear(Graphics g, Color currentColor) {
    g.setColor(backgroundColor);
    g.fillRect(0, 0, width, height);
    g.setColor(currentColor);
    int delay = 5; //milliseconds

    ActionListener taskPerformer = new ActionListener() {
    public void actionPerformed(ActionEvent evt) { } };

    new Timer(delay, taskPerformer).start();
} //run this right before you draw something

Okay so use timer to slow down delay, not a threading, that's bad. 好的,所以使用计时器来减慢延迟,而不是线程,这很糟糕。

Simply use a time delay loop.You can then fine tune the delay by adjusting the limits of i.It also gives you the control to adjust transition speeds by some hit and trials. 只需使用时间延迟循环。然后您可以通过调整i的限制来微调延迟。它还可以让您通过某些命中和试验来调整转换速度。

for(long i=0;i<=100000000000;i++); for(long i = 0; i <= 100000000000; i ++);

canvas.repaint(); canvas.repaint();

It works very fine with me and also no need to use buffered images. 它对我来说非常好,也不需要使用缓冲图像。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM