Java konwertująca obraz na BufferedImage

83

Pytanie takie jak ten link jest już dostępne w StackOverflow, a akceptowaną odpowiedzią jest „przesyłanie”:

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

W swoim programie staram się:

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;

Niestety pojawia się błąd czasu wykonania:

sun.awt.image.ToolkitImage nie może być rzutowany na java.awt.image.BufferedImage

Oczywiście odlewanie nie działa.
Pytanie brzmi: Jaki jest (lub istnieje) właściwy sposób konwersji obrazu do BufferedImage?

Arek Wilk
źródło
jeśli chcesz skalować buforowany obraz, spróbuj tego [ stackoverflow.com/questions/4216123/… [1]: stackoverflow.com/questions/4216123/…
user902383
7
Dla przypomnienia, to NIE kompilator to mówi. W rzeczywistości widzisz błąd wykonania ... a nie błąd kompilacji.
Stephen C
1
Masz rację. Dzięki za zwrócenie uwagi. Odpowiednio zmodyfikuję pytanie.
Arek Wilk
@ user902383 Nawet jeśli nie odpowiadają bezpośrednio na moje pytanie - to też są świetne rozwiązania.
Arek Wilk
Mała rzecz do OP: Metoda ImageIO.read(File)zwraca a BufferedImageprzez swój podpis. ( Odniesienie ) Nie ma potrzeby najpierw przypisywać do Imagezmiennej, a następnie rzutować na typ BufferedImage. To może zmylić ludzi czytających Twój kod.
kevinarpe

Odpowiedzi:

124

Z silnika gier 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
źródło
7
@SriHarshaChilakapati You shouldn't be worrying about the memory when using Java.Sądząc po średnim zużyciu pamięci przez aplikacje Java, inni programiści zdecydowanie nie martwią się trochę o pamięć.
Tomáš Zato - Przywróć Monikę
1
@ TomášZato Wydawało mi się, że jesteś ze świata C ++ i myślisz o ponownym wykorzystaniu pamięci pikseli danych obrazu. Mam na myśli mój poprzedni komentarz, że o pamięć w tym przypadku nie trzeba się martwić, bo w Javie zajmie się nią GC.
Sri Harsha Chilakapati
2
@SriHarshaChilakapati Więc śmieciarze nie zużywają żadnych zasobów? GC oszczędza tylko z kodu otaczającego pamięć. Nie znosi ograniczeń sprzętowych.
Andrew Sannier,
5
To nie zadziała, jeśli Imagenie jest jeszcze załadowane, ponieważ getWidth(null)lub getHeight(null)wróci -1w przypadku
Dims
1
Musiałem użyć BufferedImage.TYPE_INT_RGBzamiast ARGB, aby uzyskać odpowiednie kolory mojej miniatury
Stephanie,
20

Jednym ze sposobów rozwiązania tego problemu jest utworzenie nowego BufferedImage i powiedzenie jego obiektowi graficznemu, aby narysował skalowany obraz do nowego 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);

To powinno załatwić sprawę bez rzucania.

salbeira
źródło
2
Nigdy nie zapomnij pozbyć się utworzonych instancji graficznych
Sri Harsha Chilakapati
Jak „pozbyć się utworzonych interfejsów graficznych”?
Buffalo
Czy typ w konstruktorze BufferedImage powinien być zgodny z typem w getScaledInstance?
Drazen Bjelovuk
Nie, TYPE to coś w rodzaju ARGB lub RGBA, sposób, w jaki dane obrazu są przechowywane wewnętrznie - wybierz to, co jest najbardziej naturalne dla twojej aplikacji; Wystarczy spojrzeć na swoje autouzupełnianie podczas wpisywania BufferedImage. [Autouzupełnianie], aby zobaczyć wszystkie opcje
salbeira,
3

Jeśli otrzymujesz z powrotem a sun.awt.image.ToolkitImage, możesz rzucić Image na to, a następnie użyć getBufferedImage (), aby uzyskać BufferedImage.

Więc zamiast ostatniego wiersza kodu, w którym przesyłasz, po prostu zrobiłbyś:

BufferedImage buffered = ((ToolkitImage) image).getBufferedImage();
Hermann Hans
źródło
7
1) Używanie klas z sunpakietu jest źle widziane. Nie ma gwarancji, że będą dostępne w zestawach JDK innych niż Oracle. 2) Metoda getBufferedImage()działa tylko wtedy, gdy buforowany obraz został wygenerowany wewnętrznie przez TookitImage. Najczęściej (99%) tak nie jest, więc zwracana jest wartość null. Odniesienie do źródła
kevinarpe
3

Jeśli używasz Kotlina, możesz dodać metodę rozszerzenia do Image w taki sam sposób, w jaki sugeruje 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
}

I użyj tego w ten sposób:

myImage.toBufferedImage()
Steven Spungin
źródło