Java, как рисовать постоянно меняющуюся графику

Не делал этого раньше, поэтому, очевидно, я сосать его. Здесь 64 пикселя вокруг текущей позиции мыши немного нарисованы на форме. Проблема в том, что она "вроде" замедляется, и я не знаю, с чего начать исправление.

Кроме того, я создал поток, который постоянно вызывает обновление графики, когда он заканчивается, и немного fps, как текст, чтобы действительно показать, как быстро рисуются вещи.

Пример изображения: (Изображение из буквы "a" в Eclipse)

alt text

Пример кода:

@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;
}

Сделано изменений:
* добавлено "gc.dispose();"
* добавлено "isdone", поэтому перерисовать нельзя было бы называть быстрее, тогда это нужно.
* добавлено эта ссылка для перезаписи источника thrashgod
* добавлено эта ссылка в трэшгод исходную запись 2

Ответ 1

Здесь моя главная запись переписывается со следующими примечательными изменениями:

  • Я отделил задачу определения цветов пикселей из задачи рисования
  • Я заменил robot.getPixelColor(...) с помощью robot.createScreenCapture(...), чтобы извлекать все 64 пикселя одновременно, а не по одному за раз
  • Я ввел интеллектуальное обрезание - только то, что нужно перерисовать, перерисовывается.
  • Я исправил потоки, поэтому все обновления к модели и просмотру происходят в потоке Dispatch Event

Тикер работает постоянно. Когда он обнаруживает изменение цвета пикселя (либо из-за перемещения мыши в другую область, либо с пикселей под сменой мыши), он точно определяет, что изменилось, обновляет модель, а затем запрашивает просмотр для перерисовки. Этот подход мгновенно обновляется человеческим глазом. 289 обновлений экрана составляли кумулятивно 1 секунду.

Это была приятная задача для спокойного субботнего вечера.

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);
                                }
                            });
                        }
                    }
                }

            }
        }

    }

}

Ответ 2

Если вы не против использования Swing, этот пример показывает, как быстро увеличить масштаб BufferedImage, полученный из Icon. В вашем случае вам понадобится 8x8 BufferedImage, который заполняется в mouseMoved() пикселями, видимыми роботом.

Addendum: Здесь снимок верхнего, левого угла вашего примера.

Приложение:

Масштабирование само по себе не важно...

Медленная часть получает пиксели от рабочего стола; масштабирование незначительно. Если вы просто хотите увидеть множество методов анимации, посмотрите на этот пример.

Добавление: поскольку получение отдельных пикселей происходит медленно, а метод createScreenCapture(), предложенный @Steve McLeod, быстро, вот идея, по которой я ехал. Вы можете видеть, что он также обновляется гораздо плавно. Обратите внимание, что отпустите кнопку мыши, чтобы увидеть захваченные цвета.

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();
            }
        });
    }
}

Ответ 3

Добавьте этот метод к методу 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

Итак, используйте таймер для замедления задержки, а не для потоковой передачи, что плохо.

Ответ 4

Просто используйте цикл временной задержки. Затем вы можете точно настроить задержку, отрегулировав пределы i.It также дает вам возможность регулировать скорости перехода с помощью некоторых ударов и испытаний.

для (long я = 0; я <= 100000000000; я ++);

canvas.repaint();

Он отлично работает со мной, а также не нужно использовать буферизованные изображения.