EDICION DE DOSJava: ¿cómo hacer doble buffer en Swing?
Para evitar comentarios sarcásticos y una línea responde no viene al caso: IFF es tan simple como llamar setDoubleBuffered (verdadero), entonces, ¿cómo puedo obtener acceso a la memoria intermedia de línea actual para que pueda comenzar a jugar con el buffer de datos de píxeles subyacente de BufferedImage?
Me tomé el tiempo para escribir un fragmento de código (lo que también me pareció divertido) así que realmente agradecería las respuestas (¡qué sorpresa!) Y explicando qué y cómo funciona en lugar de uno -liners y comentarios snarky;)
Aquí hay un código de trabajo que rebota un cuadrado en un JFrame. Me gustaría saber sobre las diversas formas en que se puede utilizar para transformar este fragmento de código para que use doble almacenamiento en búfer.
Tenga en cuenta que la forma en que borro la pantalla y vuelvo a dibujar el cuadrado no es la más eficiente, pero de esto no se trata en realidad (en cierto modo, es mejor por el ejemplo que es algo lento)
Básicamente, tengo que modificar constantemente muchos píxeles en una Imagen Buffered (para tener algún tipo de animación) y no quiero ver los artefactos visuales debido a la función de almacenamiento en memoria única en la pantalla.
Tengo un JLabel cuyo Icono es un ImageIcon que envuelve una Imagen Buffered. Quiero modificar esa Imagen Buffered.
¿Qué se debe hacer para que esto se convierta en un doble buffer?
entiendo que de alguna manera "Imagen 1" se mostrará mientras yo vaya a dibujar en "imagen 2". Pero una vez que termine de dibujar en "imagen 2", ¿cómo puedo "reemplazar" rápidamente "imagen 1" por "imagen 2"?
¿Esto es algo que debería hacer manualmente, como, por ejemplo, intercambiando el ImageIcon de JLabel por mi cuenta?
¿Debo estar siempre dibujando en la misma Imagen Buffered y luego hacer una rápida 'blit' de los píxeles de esa Imagen Buffered en la Imagen BufferedImage de JLabel? (Supongo que no y no veo cómo podría "sincronizar" esto con la "línea en blanco vertical" del monitor [o equivalente en pantalla plana: me refiero a "sincronizar" sin interferir con el momento en que el monitor refresca su píxeles, como para evitar el cizallamiento]).
¿Qué pasa con los pedidos de "repintado"? ¿Debo desencadenar esto yo mismo? ¿Cuál/cuándo exactamente debería llamar repintado() o algo más?
El requisito más importante es que debería modificar los píxeles directamente en el buffer de datos de píxeles de las imágenes.
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class DemosDoubleBuffering extends JFrame {
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
int xs = 3;
int ys = xs;
int x = 0;
int y = 0;
final int r = 80;
final BufferedImage bi1;
public static void main(final String[] args) {
final DemosDoubleBuffering frame = new DemosDoubleBuffering();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setSize(WIDTH, HEIGHT);
frame.pack();
frame.setVisible(true);
}
public DemosDoubleBuffering() {
super("Trying to do double buffering");
final JLabel jl = new JLabel();
bi1 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
final Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
move();
drawSquare(bi1);
jl.repaint();
try {Thread.sleep(10);} catch (InterruptedException e) {}
}
}
});
t.start();
jl.setIcon(new ImageIcon(bi1));
getContentPane().add(jl);
}
private void drawSquare(final BufferedImage bi) {
final int[] buf = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
for (int i = 0; i < buf.length; i++) {
buf[i] = 0xFFFFFFFF; // clearing all white
}
for (int xx = 0; xx < r; xx++) {
for (int yy = 0; yy < r; yy++) {
buf[WIDTH*(yy+y)+xx+x] = 0xFF000000;
}
}
}
private void move() {
if (!(x + xs >= 0 && x + xs + r < bi1.getWidth())) {
xs = -xs;
}
if (!(y + ys >= 0 && y + ys + r < bi1.getHeight())) {
ys = -ys;
}
x += xs;
y += ys;
}
}
EDITAR
Ésta es no para una aplicación Java de pantalla completa, sino una aplicación regular de Java, se ejecuta en su propia ventana (un poco pequeña).
Swing tiene doble buffer por defecto. Esto no es AWT. – camickr
Todavía estamos esperando que alguien publique el código real que podemos probar para ver si el doble buffer vale la pena el esfuerzo. – camickr
Supongo que has entendido mal el swing. No puede acceder al búfer fuera de línea (sin modificar). Swing pasará el búfer a su padre, y su padre lo modificará. ... Para hacer lo que quiera, tiene que * desactivar * el doble buffer oscilante y hacerlo con, digamos, BufferedImage. Si quiere hacer un "bitblt" real, debe usar el componente "heavyweight" en AWT. –