Java, как рисовать постоянно меняющуюся графику
Не делали этого раньше, поэтому, очевидно, я сосу это. Здесь 64 пикселя вокруг текущей позиции мыши нарисованы немного больше на форме. Проблема в том, что это "вроде" медленно, и я понятия не имею, с чего начать.
Кроме того, я создал поток, который постоянно вызывает обновление графики, когда она закончена, и немного fps, как текст, чтобы действительно показать, как быстро все прорисовывается.
Пример изображения: (Изображение из буквы 'a' в Eclipse)
Пример кода:
@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
* добавил эту ссылку на переписать источник thrashgod 2
4 ответа
Вот мое основное переписывание со следующими заслуживающими внимания изменениями:
- Я отделил задачу определения цвета пикселей от задачи рисования
- Я заменил robot.getPixelColor(...) на robot.createScreenCapture(...), чтобы получать все 64 пикселя одновременно, а не по одному за раз
- Я ввел умное усечение - перерисовывается только то, что нужно перерисовать.
- Я исправил потоки, поэтому все обновления модели и представления происходят в потоке отправки событий.
Тикер работает постоянно. Когда он обнаруживает изменение цвета пикселей (либо из-за перемещения мыши в другую область, либо из-за изменения пикселей под мышью), он точно определяет, что изменилось, обновляет модель, а затем запрашивает перерисовку вида. Этот подход мгновенно обновляется для человеческого глаза. 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);
}
});
}
}
}
}
}
}
}
Если вы не возражаете против использования Swing, этот пример показывает, как быстро увеличить масштаб BufferedImage
полученный из Icon
, В вашем случае вы бы хотели 8x8 BufferedImage
что заполняется mouseMoved()
с пикселями, видимыми роботом.
Приложение: Вот снимок верхнего левого угла вашего примера.
Приложение:
Масштабирование само по себе не важно...
Медленная часть - получение пикселей с рабочего стола; масштабирование незначительное. Если вы просто хотите увидеть различные техники анимации, взгляните на этот пример.
Приложение: поскольку получение отдельных пикселей идет медленно и createScreenCapture()
Метод, предложенный @Steve McLeod, быстр, вот идея, которой я руководствовался. Вы можете видеть, что это также обновляет намного более гладко. Обратите внимание, что отпускание кнопки мыши позволяет увидеть захваченные цвета.
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://stackru.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();
}
});
}
}
Добавьте этот метод в метод 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
Итак, используйте таймер для замедления задержки, а не многопоточность, это плохо.
Просто используйте петлю задержки. Затем вы можете точно настроить задержку, отрегулировав пределы i. Это также дает вам возможность регулировать скорость перехода с помощью некоторых попаданий и испытаний.
for (long i = 0; i<= 100000000000; i ++);
canvas.repaint ();
Это работает очень хорошо со мной, а также нет необходимости использовать буферизованные изображения.