Java conversión de imagen a imagen con búfer

82

Ya hay una pregunta como este enlace en StackOverflow y la respuesta aceptada es "casting":

Image image = ImageIO.read(new File(file));
BufferedImage buffered = (BufferedImage) image;

En mi programa intento:

final float FACTOR  = 4f;
BufferedImage img = ImageIO.read(new File("graphic.png"));
int scaleX = (int) (img.getWidth() * FACTOR);
int scaleY = (int) (img.getHeight() * FACTOR);
Image image = img.getScaledInstance(scaleX, scaleY, Image.SCALE_SMOOTH);
BufferedImage buffered = (BufferedImage) image;

Desafortunadamente, aparece un error de tiempo de ejecución:

sun.awt.image.ToolkitImage no se puede convertir en java.awt.image.BufferedImage

Obviamente, el casting no funciona.
La pregunta es: ¿Cuál es (o existe) la forma correcta de convertir una imagen en una imagen con búfer?

Arek Wilk
fuente
si desea escalar la imagen almacenada en búfer, intente esto, pruebe esto [ stackoverflow.com/questions/4216123/… [1]: stackoverflow.com/questions/4216123/…
user902383
7
Para que conste, NO es el compilador el que dice eso. En realidad, está viendo un error de tiempo de ejecución ... no un error de compilación.
Stephen C
1
Tienes razón. Gracias por señalar esto. Editaré la pregunta en consecuencia.
Arek Wilk
@ user902383 Incluso si no están respondiendo a mi pregunta directamente, esas también son excelentes soluciones.
Arek Wilk
Small Thing to OP: Method ImageIO.read(File)devuelve a BufferedImagepor su firma. ( Referencia ) No es necesario asignar primero a una Imagevariable y luego convertir para escribir BufferedImage. Eso podría confundir a la gente que lee su código.
kevinarpe

Respuestas:

123

Desde un motor de juegos Java :

/**
 * Converts a given Image into a BufferedImage
 *
 * @param img The Image to be converted
 * @return The converted BufferedImage
 */
public static BufferedImage toBufferedImage(Image img)
{
    if (img instanceof BufferedImage)
    {
        return (BufferedImage) img;
    }

    // Create a buffered image with transparency
    BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);

    // Draw the image on to the buffered image
    Graphics2D bGr = bimage.createGraphics();
    bGr.drawImage(img, 0, 0, null);
    bGr.dispose();

    // Return the buffered image
    return bimage;
}
Sri Harsha Chilakapati
fuente
6
@SriHarshaChilakapati You shouldn't be worrying about the memory when using Java.A juzgar por el consumo medio de memoria de las aplicaciones Java, otros desarrolladores definitivamente no se preocupan un poco por la memoria.
Tomáš Zato - Reincorpora a Monica
1
@ TomášZato Me pareció que eras del mundo C ++, pensando en reutilizar la memoria de los píxeles de los datos de la imagen. Con mi comentario anterior me refiero a que no hay necesidad de preocuparse por la memoria en este caso, ya que el GC se encargará de ello en Java.
Sri Harsha Chilakapati
2
@SriHarshaChilakapati ¿Entonces los recolectores de basura no consumen recursos? GC solo te salva del código que rodea la memoria. No erradica un límite de hardware.
Andrew Sannier
5
Esto no funcionará si Imageaún no está cargado, ya que getWidth(null)o getHeight(null)volverá -1en el estuche
atenúa el
1
Tuve que usar en BufferedImage.TYPE_INT_RGBlugar de ARGB para obtener los colores de mi miniatura correctos
Stephanie
20

Una forma de manejar esto es crear una nueva BufferedImage y decirle a su objeto gráfico que dibuje su imagen escalada en la nueva BufferedImage:

final float FACTOR  = 4f;
BufferedImage img = ImageIO.read(new File("graphic.png"));
int scaleX = (int) (img.getWidth() * FACTOR);
int scaleY = (int) (img.getHeight() * FACTOR);
Image image = img.getScaledInstance(scaleX, scaleY, Image.SCALE_SMOOTH);
BufferedImage buffered = new BufferedImage(scaleX, scaleY, TYPE);
buffered.getGraphics().drawImage(image, 0, 0 , null);

Eso debería funcionar sin lanzar.

salbeira
fuente
2
No se olvide nunca de deshacerse de las instancias de gráficos creados
Sri Harsha Chilakapati
¿Cómo "deshacerse de las interfaces gráficas creadas"?
Buffalo
¿Debe el tipo del constructor BufferedImage coincidir con el de getScaledInstance?
Drazen Bjelovuk
No, TYPE es algo así como ARGB o RGBA, la forma en que los datos de la imagen se almacenan internamente: elija lo que le resulte más natural a su aplicación; Solo mire en su Autocompletar cuando escriba BufferedImage. [Autocompletar] para ver todas las opciones
salbeira
3

Si está recuperando un sun.awt.image.ToolkitImage, puede convertir la imagen a eso y luego usar getBufferedImage () para obtener el BufferedImage.

Entonces, en lugar de su última línea de código donde está transmitiendo, simplemente haría:

BufferedImage buffered = ((ToolkitImage) image).getBufferedImage();
Hermann Hans
fuente
7
1) Está mal visto usar clases del sunpaquete. No se garantiza que estén disponibles en JDK que no sean de Oracle. 2) El método getBufferedImage()solo funciona si la imagen almacenada en búfer ha sido generada internamente por TookitImage. La mayoría de las veces (99%) no lo es, por lo que el valor de retorno es null. Fuente de referencia
kevinarpe
3

Si usa Kotlin, puede agregar un método de extensión a la imagen de la misma manera que sugiere Sri Harsha Chilakapati.

fun Image.toBufferedImage(): BufferedImage {
    if (this is BufferedImage) {
        return this
    }
    val bufferedImage = BufferedImage(this.getWidth(null), this.getHeight(null), BufferedImage.TYPE_INT_ARGB)

    val graphics2D = bufferedImage.createGraphics()
    graphics2D.drawImage(this, 0, 0, null)
    graphics2D.dispose()

    return bufferedImage
}

Y utilícelo así:

myImage.toBufferedImage()
Steven Spungin
fuente