EDITAR DOS

Para evitar sarcástico, comentarios y uno de la línea de respuestas que falta el punto: IFF es tan sencillo como llamar a setDoubleBuffered(true), entonces ¿cómo puedo obtener acceso a la corriente sin conexión buffer para que yo pueda empezar a jugar con el BufferedImage subyacente del píxel databuffer?

Me tomé el tiempo para escribir un ejecutando la pieza de código (que se ve un poco de diversión también) así que agradecería respuestas realidad contestar (lo que es un choque 😉 mi pregunta y explicar en qué/cómo es que trabajan en lugar de uno-trazadores de líneas y sarcástico, comentarios 😉

He aquí una pieza de código que rebota una plaza a través de un JFrame. Me gustaría saber acerca de las diferentes formas que puede utilizar para transformar este trozo de código para que se utiliza el doble búfer.

Tenga en cuenta que la forma en que puedo borrar la pantalla y volver a dibujar el cuadrado no es el más eficiente, pero esto no es realmente lo que trata esta cuestión (en una forma, es mejor para el bien de este ejemplo, que es un poco lento).

Básicamente, necesito constantemente modificar un montón de píxeles en un BufferedImage (como para tener algún tipo de animación) y no quiero ver los artefactos visuales debido a que solo el almacenamiento en búfer de pantalla.

Tengo un JLabel cuyo Icono es un ImageIcon envolver un BufferedImage. Quiero modificar que BufferedImage.

Lo que ha de hacerse de modo que este se convierte en el doble buffer?

Entiendo que de alguna manera «imagen 1» será mostrada mientras voy a estar dibujando en «imagen 2». Pero una vez que he terminado el dibujo en «imagen 2», ¿cómo puedo «rápidamente» reemplazar «imagen 1» por «imagen 2»?

Es esto algo que se debe hacer manualmente, como, por ejemplo, mediante el intercambio de JLabel del ImageIcon mí?

Debería ser siempre dibujo en el mismo BufferedImage, a continuación, hacer un rápido ‘fundir’ de que BufferedImage de los píxeles en el JLabel del ImageIcon del BufferedImage? (Supongo que no y no veo cómo podría «sincronización» esto con el monitor de la «vertical» línea en blanco» [o el equivalente en la tv de pantalla: quiero decir, a la ‘sincronización’ sin interferir con el momento en que el monitor itselfs actualiza sus píxeles, como para evitar ruptura]).

¿Qué acerca de la «repintar» órdenes? Soy supongo que para activar estos mí mismo? Qué/cuándo debo llamar repaint() o algo más?

El requisito más importante es que yo debería ser la modificación de los píxeles en las imágenes de píxeles de databuffer.

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

Este es no para una pantalla completa de la aplicación Java, pero regular de la aplicación Java, que se ejecuta en su propio (algo pequeña) de la ventana.

  • El Swing es de doble buffer por defecto. Esto no es AWT.
  • Todavía a la espera de alguien para publicar código real que podemos probar para ver si el búfer doble vale la pena el esfuerzo.
  • Creo que se han malinterpretado swing. Usted no puede acceder (sin modificar) sin conexión de búfer. Swing le pasado el buffer a su padre, y su padre se modifique. … Para hacer lo que quieres, tienes que desactivar el swing doublebuffering y hacerlo con, digamos, BufferedImage. Si usted quiere hacer real «bitblt», tienes que usar «peso pesado» componente AWT.
InformationsquelleAutor SyntaxT3rr0r | 2010-12-13

4 Comentarios

  1. 13

    —- Editado para la dirección de pixel de configuración —-

    El elemento golpe direcciones de doble búfer, pero también hay un problema en cómo obtener los píxeles en una BufferedImage.

    Si usted llama

    WriteableRaster raster = bi.getRaster()

    sobre la BufferedImage devolverá un WriteableRaster. A partir de ahí, puede utilizar

    int[] pixels = new int[WIDTH*HEIGHT];
    //code to set array elements here
    raster.setPixel(0, 0, pixels);

    Tenga en cuenta que es probable que desee para optimizar el código para no crear una nueva matriz para cada representación. Además, es probable que desee para optimizar la matriz de compensación de código para no utilizar un bucle for.

    Arrays.fill(pixels, 0xFFFFFFFF);

    probablemente supere a la configuración del bucle el fondo blanco.

    —- Editado después de la respuesta —-

    La clave está en la configuración original de la JFrame y dentro de la ejecución de representación de bucle.

    Primer lugar usted necesita para contar SWING para detener la Rasterización cuando quiere; porque, vas a estar diciendo que cuando termines de dibujo para el búfer de imagen que desea cambiar en su totalidad. Hacer esto con JFrame del

    setIgnoreRepaint(true);

    A continuación, usted querrá crear un búfer de estrategia. Básicamente se especifica el número de búferes desea utilizar

    createBufferStrategy(2);

    Ahora que ha intentado crear el buffer de la estrategia, necesita agarrar el BufferStrategy objeto ya que la necesitará más tarde para cambiar de búferes.

    final BufferStrategy bufferStrategy = getBufferStrategy();

    Dentro de su Thread modificar el run() bucle contener:

    ...
    move();
    drawSqure(bi1);
    Graphics g = bufferStrategy.getDrawGraphics();
    g.drawImage(bi1, 0, 0, null);
    g.dispose();
    bufferStrategy.show();
    ...

    Los gráficos agarró de la bufferStrategy será el apagado de pantalla Graphics objeto, a la hora de crear triple buffering, será el «siguiente» en la pantalla Graphics objeto en un round-robin.

    La imagen y el contexto gráfico no están relacionados en un escenario de contención, y le dijo Swing te gustaría hacer el dibujo de ti mismo, entonces usted tiene que dibujar la imagen manualmente. Esto no es siempre una mala cosa, como puede especificar el búfer de voltear cuando la imagen es totalmente dibujado (y no antes).

    La eliminación de los gráficos objeto es sólo una buena idea, ya que ayuda en la recolección de basura. Mostrando la bufferStrategy le dará la vuelta de los buffers.

    Mientras que podría haber sido un paso en falso en algún lugar en el código anterior, esta debe conseguir que el 90% del camino. Buena suerte!

    —- Mensaje Original de la siguiente manera —-

    Puede parecer tonto para someter una cuestión a un javase tutorial, pero has mirado en BufferStrategy y BufferCapatbilites?

    El principal problema creo que se está encontrando es que usted se deje engañar por el nombre de la Imagen. Un BufferedImage no tiene nada que ver con el doble búfer, tiene que ver con «el almacenamiento en búfer los datos (normalmente desde el disco) en la memoria.» Como tal, usted necesitará dos BufferedImages si usted desea tener una «doble buffer de la imagen»; como no es prudente alterar los píxeles de la imagen que se muestra (que podría causar problemas de repintado).

    En su código de representación, agarras el objeto graphics. Si configura el doble búfer de acuerdo con el tutorial de arriba, esto significa que usted va a tomar (por defecto) el apagado de pantalla Graphics objeto, y todos los dibujos, se apagará la pantalla. A continuación, se dibuja su imagen (la de la derecha, por supuesto) para el objeto de la pantalla. Por último, decir que la estrategia show() el buffer, y se hará la reposición del contexto gráfico para usted.

    • Buck: +1 por la ayuda, pero yo no uso BufferedImage porque el nombre estaría engañando a mí: estoy usando porque la capacidad de manipular los píxeles en la imagen es un requisito y la única forma que conozco para tener acceso a una imagen subyacente de píxel databuffer es usar un BufferedImage.
    • bueno, yo estaba un poco apagado en el pensamiento de que iba a necesitar dos BufferedImages, en realidad, usted puede obtener sólo porque usted tiene que dibujar a cada uno de los búferes de forma individual. He actualizado mi post dando detalles sobre cómo posiblemente integrar giro de la doble búfer de apoyo en el ejemplo.
    • Esta es quizás una mejor estrategia de la que he publicado. Esta misma técnica también es utilizada por la clase Canvas para varios búferes así (y creo que esta es la técnica que he utilizado hace mucho tiempo en un proyecto de animación para mis estudiantes en Java).
    • gracias por el voto de apoyo.
    • añadido un poco acerca de la configuración de los píxeles. La clave es usar un objeto Raster (que conceptualmente contiene los píxeles) y acaba de darle una paliza a la BufferedImage interna de la Trama, gracias a granel.
    • Buck: muchas gracias. La parte acerca de la configuración de ignoreRepainting y por qué es muy muy interesante 🙂
    • ¿Usted cree que puede haber sido de doble buffer antes y por virtud de la Trama se ejecuta cada vez que querían, que estaban recogiendo la imagen antes de que fuera completamente dibujado? Que sería un nuevo giro a las cosas!

  2. 3

    Generalmente utilizamos la clase Canvas, el cual es adecuado para la animación en Java. Anyhoo, lo siguiente es cómo conseguir el doble búfer:

    class CustomCanvas extends Canvas {
    private Image dbImage;
    private Graphics dbg; 
    int x_pos, y_pos;
    public CustomCanvas () {
    }
    public void update (Graphics g) {
    //initialize buffer
    if (dbImage == null) {
    dbImage = createImage (this.getSize().width, this.getSize().height);
    dbg = dbImage.getGraphics ();
    }
    //clear screen in background
    dbg.setColor (getBackground ());
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);
    //draw elements in background
    dbg.setColor (getForeground());
    paint (dbg);
    //draw image on the screen
    g.drawImage (dbImage, 0, 0, this); 
    }
    public void paint (Graphics g)
    {
    g.setColor  (Color.red);
    g.fillOval (x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
    }
    }

    Ahora se puede actualizar el x_pos y y_pos de un hilo, seguido por el «repintar» llamada en el lienzo de un objeto. La misma técnica que se debe trabajar en un JPanel así.

    • Saleem: Hola Saleem, es genial ver a un recién llegado ayudar aquí 🙂 Cuando se trabaja con un Lienzo, también pueden tener acceso a los píxeles de la databuffer de la misma manera en que lo hago con un BufferedImage?
    • No debería llamar a repaint ser emitido en la EDT ?
    • Pero no hay necesidad de esto. Swing permite animación así. Que no es el doble búfer. O si es que el mismo código se puede utilizar en un Columpio JPanel ya que todo lo que están haciendo es dibujar una imagen.
    • Saleem: la técnica que mostró es el uso de un búfer posterior en el que puedes hacer todo el dibujo, luego un «fundir» el uso de g.drawIamge(…) a la derecha?
    • el uso de un backbuffer, a continuación, un blit como Usman Saleem publicado es una especie de doble búfer. En realidad, es el explicado en el Oracle/Sun artículo sobre el Doble Búfer (que lamentablemente es demasiado corto de un artículo). el ‘adjuntar’ es la parte de atrás de búfer y la ‘g’ es el principal tampón. Que es de dos buffer, que es (un tipo de) el doble búfer.
    • A la derecha, la idea está en primer plano, mientras está ocupado en la representación de la imagen en el contenedor del objeto graphics, el fondo se está preparando en el bufferimage del objeto graphics. Normalmente tenemos que introducir un Hilo.dormir en el hilo responsable de la actualización de x,y por lo que hay algunos intercalado de los involucrados.
    • Tienes este ejemplo, el trabajo todavía? Me gustaría comparar esta animación con la suya o trashgod de la animación para ver la diferencia.
    • Para el primer comentario, creo que la imagen real regresó de lienzo » createImage es un BufferedImage. Puedes probar con instanceof para estar seguro.

  3. 3

    Lo que quieres es básicamente imposible en modo de ventana con Swing. No hay soporte para la trama de sincronización para la ventana vuelve a pintar, esto sólo está disponible en modo de pantalla completa (e incluso entonces puede no ser compatible con todas las plataformas).

    Los componentes Swing se doble buffer por defecto, que es que van a hacer toda la representación a un búfer intermedio y que búfer, a continuación, finalmente copiar a la pantalla, evitando el parpadeo del fondo de compensación y, a continuación, la pintura en la parte superior de la misma.
    Y esa es la única estrategia razonable, bien apoyado en todas las plataformas. Evita sólo repintar el parpadeo, pero no se desgaste visual de movimiento de los elementos gráficos.

    Razonablemente una manera sencilla de tener acceso a las primas de píxeles de un área totalmente bajo el control sería extender un componente personalizado de JComponent y sobrescribir sus paintComponent()-método para pintar el área de un BufferedImage (de la memoria):

    public class PixelBufferComponent extends JComponent {
    private BufferedImage bufferImage;
    public PixelBufferComponent(int width, int height) {
    bufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    setPreferredSize(new Dimension(width, height));
    }
    public void paintComponent(Graphics g) {
    g.drawImage(bufferImage, 0, 0, null);
    }
    }

    A continuación, puede manipular que el búfer de imagen, cualquiera que sea la forma que usted desea. Para hacer que sus cambios se hacen visibles en la pantalla, basta con llamar a repaint() sobre él. Si usted no a la manipulación de píxeles de un hilo distinto de la EDT, se necesitan DOS búfer de imágenes para lidiar con condiciones de carrera entre el real repintar y su manipulación hilo.

    Tenga en cuenta que este esqueleto no pintar toda la superficie de la componente cuando se utiliza con un gestor de diseño que se extiende el componente más allá de su tamaño preferido.

    Tenga en cuenta también, el búfer de imagen enfoque de la mayoría sólo tiene sentido si usted hace real bajo nivel de manipulación de píxeles a través de setRGB(…) en la imagen o si usted acceder directamente al subyacente DataBuffer directamente. Si usted puede hacer todas las manipulaciones a través de Graphics2D métodos, usted puede hacer todas las cosas en el método paintComponent utilizando los gráficos (que en realidad es un Graphics2D y puede ser simplemente chapa).

  4. 3

    Aquí una variante en la que todo el dibujo se lleva a cabo en el envío de evento hilo.

    Anexo:

    Básicamente, necesito constantemente modificar un montón de píxeles en una BufferedImage

    Este modelo cinético ilustra varios enfoques de píxel de animación.

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.*;
    import java.awt.image.BufferedImage;
    /** @see http://stackoverflow.com/questions/4430356 */
    public class DemosDoubleBuffering extends JPanel implements ActionListener {
    private static final int W = 600;
    private static final int H = 400;
    private static final int r = 80;
    private int xs = 3;
    private int ys = xs;
    private int x = 0;
    private int y = 0;
    private final BufferedImage bi;
    private final JLabel jl = new JLabel();
    private final Timer t  = new Timer(10, this);
    public static void main(final String[] args) {
    EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new DemosDoubleBuffering());
    frame.pack();
    frame.setVisible(true);
    }
    });
    }
    public DemosDoubleBuffering() {
    super(true);
    this.setLayout(new GridLayout());
    this.setPreferredSize(new Dimension(W, H));
    bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
    jl.setIcon(new ImageIcon(bi));
    this.add(jl);
    t.start();
    }
    @Override
    public void actionPerformed(ActionEvent e) {
    move();
    drawSquare(bi);
    jl.repaint();
    }
    private void drawSquare(final BufferedImage bi) {
    Graphics2D g = bi.createGraphics();
    g.setColor(Color.white);
    g.fillRect(0, 0, W, H);
    g.setColor(Color.blue);
    g.fillRect(x, y, r, r);
    g.dispose();
    }
    private void move() {
    if (!(x + xs >= 0 && x + xs + r < bi.getWidth())) {
    xs = -xs;
    }
    if (!(y + ys >= 0 && y + ys + r < bi.getHeight())) {
    ys = -ys;
    }
    x += xs;
    y += ys;
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here