¿Cómo puede producir resultados de pintura nítidos al girar una imagen de búfer?


Un intento de acercamiento fue usar TexturePaint y g.fillRect() para pintar la imagen. Sin embargo, esto requiere que cree un nuevo objeto TexturePaint y Rectangle2D cada vez que pinta una imagen, lo cual no es ideal, y de todos modos no ayuda.

Cuando uso g.drawImage(BufferedImage,...), las imágenes giradas parecen estar borrosas/blandas.

Estoy familiarizado con RenderingHints y doble buffering (que es lo que estoy haciendo, creo), simplemente me resulta difícil creer que no se puede fácilmente y eficientemente gire una imagen en Java que produzca resultados nítidos.

El código para usar TexturePaint se ve algo como esto.

Grahics2D g2d = (Graphics2D)g; 
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);

Estoy usando AffineTransform para rotar una mano de cartas en un ventilador. ¿Cuál sería el mejor enfoque para pintar imágenes atractivas rápidamente?

Aquí hay una captura de pantalla:
Ejemplo de rotaciones borrosas
El 9 es nítido, pero el resto de las cartas definitivamente no son tan nítidas.

Podría ser posible que el problema radica en cuando creo cada imagen de tarjeta y la almaceno en matriz.
Así es como lo estoy haciendo en este momento:

// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);

    Graphics2D g = img.createGraphics();
    setRenderingHints(g);
    g.drawImage(shadow, 0, 0, 86, 126, null);
    g.drawImage(white, 3, 3, 80, 120, null);
    g.drawImage(suit, 3, 3, 80, 120, null);
    g.drawImage(value, 3, 3, 80, 120, null);
    g.dispose();

    cardImages[i] = img;
}

private void setRenderingHints(Graphics2D g){
    g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}

¿Cómo debo abordar esto de manera diferente? Gracias.

Editar:
Ejemplo de mano sin RenderingHints
Sin RenderingHints

Establecer sugerencias AA no hizo ninguna diferencia. Además, establecer RenderingHints al crear las imágenes tampoco hace ninguna diferencia. Es solo cuando se rotan con AffineTransform y se pintan con g.drawImage(...) que parecen difuminarse.
La imagen de arriba muestra la diferencia entre por defecto (vecino más cercano) e interpolación bilineal.

Así es como los estoy pintando actualmente (mucho más rápido que TexturePaint):

// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
    g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
  }

// ImageLoader.java
public BufferedImage getCard(int code){
    return cardImages[code];
  }

Todas mis cartas son 80x120 y la sombra .png es 86x126, con el fin de dejar 3px sombra semitransparente alrededor de la tarjeta. No es una sombra realista, lo sé, pero se ve bien.

Y así se convierte la pregunta... ¿Cómo puede producir resultados de pintura nítidos al rotar una imagen de búfer?

Referencia a un anterior pregunta también con respecto a una mano de carta en abanico:
¿Cómo se puede detectar un evento de clic del ratón en un objeto de imagen en Java?

Bounty-Edit: Bien, después de mucha discusión hice algunas pruebas .tarjetas svg para ver cómo SVG Salamandra iría sobre la representación de ellos. Desafortunadamente, la actuación es terrible. Mi implementación es lo suficientemente limpia, ya que con BufferedImage de doble búfer la pintura era increíblemente rápida. Lo que significa que he cerrado el círculo y estoy de vuelta a mi problema original.

Le daré la recompensa de 50 a quien pueda darme una solución para obtener rotaciones nítidas de imágenes en búfer. Las sugerencias han sido hacer las imágenes más grandes de lo necesario y reducir la escala antes de pintar, y usar interpolación bicúbica. Si estas son las únicas soluciones posibles, entonces realmente no sé a dónde ir desde aquí y puede que tenga que lidiar con las rotaciones borrosas, porque ambas imponen contratiempos de rendimiento.

Puedo terminar mi juego si puede encontrar una manera de hacer esto bien. Gracias a todos. :)

Author: rtheunissen, 2011-07-17

3 answers

Cuando rotas una imagen rasterizada (como una imagen en búfer), pierdes datos. La mejor solución es guardar sus imágenes más grandes de lo que las necesitará y reducir la escala sobre la marcha cuando las pinte. He encontrado que 1.5 x el tamaño que necesita es un buen punto de partida.

Luego, cuando pintes la imagen, cambia el tamaño sobre la marcha:

g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Se recomienda

Rotaciones utilizando interpolación bilineal.

El crédito por la sugerencia va a guido.

 9
Author: paranoid-android,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-16 16:44:15

Este consejo es probablemente un poco tarde en su diseño, pero puede ser digno de mención.

Las imágenes rasterizadas son probablemente la tecnología incorrecta si muchas rotaciones y animaciones son parte de su interfaz de usuario; especialmente con imágenes complicadas con muchas curvas. Sólo espera a que intentes escalar tu escrutinio. Podría sugerir mirar una biblioteca gráfica basada en vectores. Harán el tipo de efectos que desea con menos potencial para artefacto.

Http://xmlgraphics.apache.org/batik/using/swing.htm

 5
Author: mbarnes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-17 19:41:20

Establecer el tipo de interpolación, así como el valor de suavizado, en un AffineTransformOp puede ofrecer alguna mejora. Type TYPE_BICUBIC, aunque más lento, es típicamente la mejor calidad; un ejemplo se describe aquí. Tenga en cuenta que puede suministrar múltiples RenderingHints. Otra trampa surge de no aplicar las sugerencias cada vez que se renderiza la imagen. También es posible que necesite ajustar la transparencia del fondo, como se sugiere aquí. Finalmente, considere crear un sscce eso incluye una de tus imágenes reales.

 4
Author: trashgod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:29:45