example - render java
Problemas de rendimiento de Java2D (3)
Creo que encontré una solución al investigar y juntar partes de muchas búsquedas de Google.
Aquí está, comentarios y todo:
private BufferedImage toCompatibleImage(BufferedImage image)
{
// obtain the current system graphical settings
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
/*
* if image is already compatible and optimized for current system
* settings, simply return it
*/
if (image.getColorModel().equals(gfx_config.getColorModel()))
return image;
// image is not optimized, so create a new image that is
BufferedImage new_image = gfx_config.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
Graphics2D g2d = (Graphics2D) new_image.getGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return new_image;
}
En mi publicación anterior, GraphicsConfiguration era lo que contenía la información necesaria para crear imágenes optimizadas en un sistema. Parece funcionar bastante bien, pero hubiera pensado que Java automáticamente haría esto por ti. Obviamente, no puede sentirse demasiado cómodo con Java. :) Supongo que terminé respondiendo mi propia pregunta. Bueno, espero que ayude a algunos de ustedes que he visto tratando de usar Java para juegos en 2D.
Estoy teniendo rarezas de rendimiento con Java2D. Conozco el parámetro sun.java2d.opengl VM para habilitar la aceleración 3D para 2D, pero incluso usar eso tiene algunos problemas extraños.
Estos son los resultados de las pruebas que ejecuté:
Dibujar un mapa de 25x18 con mosaicos de 32x32 píxeles en un JComponent
Imagen 1 = formato .bmp, Imagen 2 = formato .png
Sin -Dsun.java2d.opengl = true
120 FPS con la imagen .BMP 1
13 FPS usando .PNG image 2
Con -Dsun.java2d.opengl = true
12 FPS con la imagen .BMP 1
700 FPS usando .PNG image 2
Sin aceleración, supongo que se está produciendo algún tipo de transformación con cada drawImage () que tengo en el software, y está reduciendo considerablemente el FPS en el caso de .PNG. ¿Por qué, sin embargo, con la aceleración, cambiarían los resultados (y PNG en realidad se comporta increíblemente más rápido)? ¡Locura!
.BMP Imagen 1 se traduce a un tipo de imagen de TYPE_INT_RGB. .PNG La imagen 2 se traduce a un tipo de imagen de TYPE_CUSTOM. Para obtener una velocidad constante con y sin aceleración de apertura, tengo que crear una nueva Imagen Buffered con un tipo de imagen de TYPE_INT_ARGB, y dibujar la Imagen 1 o la Imagen 2 en esta nueva imagen.
Aquí están los resultados que se ejecutan con eso:
Sin -Dsun.java2d.opengl = true
120 FPS con la imagen .BMP 1
120 FPS usando .PNG image 2
Con -Dsun.java2d.opengl = true
700 FPS usando la imagen .BMP 1
700 FPS usando .PNG image 2
Mi verdadera pregunta es, ¿puedo suponer que TYPE_INT_ARGB será el tipo de imagen nativa para todos los sistemas y plataformas? Estoy asumiendo que este valor podría ser diferente. ¿Hay alguna forma de que obtenga el valor nativo para que siempre pueda crear nuevas Imágenes Buffered para el máximo rendimiento?
Gracias por adelantado...
Por lo que recuerdo cuando estaba pensando en hacer programación de gráficos en Java, las bibliotecas integradas son lentas. Me informaron en GameDev.Net que cualquier persona que haga algo serio debería usar algo como jogl
Bueno, esta es una publicación anterior, pero me gustaría compartir mis hallazgos sobre el dibujo directo con Swing / AWT, sin BufferedImage.
Algún tipo de dibujo, como 3D, se hace mejor cuando se pinta directamente en un buffer int [] . Una vez hechas las imágenes, puede usar una instancia de ImageProducer , como MemoryImageSource , para producir imágenes. Supongo que sabes cómo realizar tus dibujos directamente, sin ayuda de Graphics / Graphics2.
/**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {
public int pixel[];
public int width;
public int height;
private Image imageBuffer;
private MemoryImageSource mImageProducer;
private ColorModel cm;
private Thread thread;
public MyCanvas() {
super(true);
thread = new Thread(this, "MyCanvas Thread");
}
/**
* Call it after been visible and after resizes.
*/
public void init(){
cm = getCompatibleColorModel();
width = getWidth();
height = getHeight();
int screenSize = width * height;
if(pixel == null || pixel.length < screenSize){
pixel = new int[screenSize];
}
mImageProducer = new MemoryImageSource(width, height, cm, pixel,0, width);
mImageProducer.setAnimated(true);
mImageProducer.setFullBufferUpdates(true);
imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);
if(thread.isInterrupted() || !thread.isAlive()){
thread.start();
}
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
// rubisch draw
int[] p = pixel; // this avoid crash when resizing
if(p.length != width * height) return;
for(int x=0; x < width; x++){
for(int y=0; y<height; y++){
int color = (((x + i) % 255) & 0xFF) << 16; //red
color |= (((y + j) % 255) & 0xFF) << 8; //green
color |= (((y/2 + x/2 - j) % 255) & 0xFF) ; //blue
p[ x + y * width] = color;
}
}
i += 1;
j += 1;
}
private int i=1,j=256;
@Override
public void run() {
while (true) {
// request a JPanel re-drawing
repaint();
try {Thread.sleep(5);} catch (InterruptedException e) {}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// perform draws on pixels
render();
// ask ImageProducer to update image
mImageProducer.newPixels();
// draw it on panel
g.drawImage(this.imageBuffer, 0, 0, this);
}
/**
* Overrides ImageObserver.imageUpdate.
* Always return true, assuming that imageBuffer is ready to go when called
*/
@Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
return true;
}
}// end class
Tenga en cuenta que necesitamos una instancia única de MemoryImageSource e Image . No cree una imagen nueva o un nuevo ImageProducer para cada cuadro, a menos que haya cambiado el tamaño de su JPanel. Ver el método init () arriba.
En un hilo de renderizado, pregunte un repintado () . En Swing, repaint () llamará al paintComponent () reemplazado, donde llamará a su método render () y luego le pedirá a su imageProducer que actualice la imagen. Con Image done, dibujar con Graphics.drawImage () .
Para tener una Imagen compatible, usa el ColorModel adecuado cuando crees tu Imagen . Yo uso GraphicsConfiguration.getColorModel () :
/**
* Get Best Color model available for current screen.
* @return color model
*/
protected static ColorModel getCompatibleColorModel(){
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
return gfx_config.getColorModel();
}