Jak sklonować BufferedImage

120

Mam obiekt, który ma wiele buforowanych obrazów, chcę utworzyć nowy obiekt, kopiując wszystkie buforowane obrazy do nowego obiektu, ale te nowe obrazy mogą zostać zmienione i nie chcę, aby oryginalne obrazy obiektów były zmieniane przez zmianę nowe obrazy obiektów.

czy to jasne?

Czy jest to możliwe i czy ktoś może zasugerować dobry sposób, aby to zrobić? Pomyślałem o getSubImage, ale gdzieś przeczytałem, że wszelkie zmiany w podobrazie są przenoszone z powrotem do obrazu nadrzędnego.

Chcę tylko mieć możliwość uzyskania świeżej, całkowicie oddzielnej kopii lub klonu BufferedImage

f1wade
źródło
1
nie możesz wywołać clone()metody? A może coś przegapiłem? Nie wiem zbyt wiele o BufferedImageklasie
Noel M
1
clone dostarcza tylko płytką kopię, więc będzie zawierać odniesienia do buforowanych obrazów; nie ich kopie.
Ultimate Gobblement
7
@NoelM, UltimateGobblement: BufferedImagenie implementuje, Cloneablea clone()metoda ma chroniony dostęp.
Robert

Odpowiedzi:

173

Coś takiego?

static BufferedImage deepCopy(BufferedImage bi) {
 ColorModel cm = bi.getColorModel();
 boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
 WritableRaster raster = bi.copyData(null);
 return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
Klark
źródło
4
To też pożyczam w swoim programie =)
Daniel Kats
mam problem z tym sposobem na kopiowanie subimage
Mishka
7
Chociaż działa to w większości przypadków, nie działa poprawnie, gdy ten BufferedImage został przycięty (zwraca cały obraz przed przycięciem). Prostym rozwiązaniem jest zmiana ostatniej linii na:
HaydenStudios.
3
return new BufferedImage (cm, raster, isAlphaPremultiplied, null) .getSubimage (0, 0, bi.getWidth (), bi.getHeight ());
HaydenStudios
copyData (null) nie zawsze działa, ponieważ może działać na rastrze nadrzędnym (tj. gdy obraz jest obrazem podrzędnym), zobacz moją zmodyfikowaną odpowiedź
user1050755
46

Robię to:

public static BufferedImage copyImage(BufferedImage source){
    BufferedImage b = new BufferedImage(source.getWidth(), source.getHeight(), source.getType());
    Graphics g = b.getGraphics();
    g.drawImage(source, 0, 0, null);
    g.dispose();
    return b;
}

Działa dość dobrze i jest prosty w użyciu.

Osoba
źródło
3
Wygląda to całkiem prosto. Dlaczego to nie jest najlepsza odpowiedź? Czy jest wada, której nie jestem świadomy?
WVrock
2
@WVrock Nie działa, jeśli typ obrazu to 0 (niestandardowy)
Tilman Hausherr
3
replace Graphics g = b.getGraphics (); przez Graphics2D g = b.createGraphics (); i jest doskonały
Nadir
1
Myślę, że to najczystsza odpowiedź. Chociaż czy jest jakaś różnica w wydajności między tą a akceptowaną odpowiedzią? Czuję się nieistotny, jeśli w ogóle nie? Czy to może być szybsze, tylko dlatego, że tworzenie obiektów jest zoptymalizowane w jvm. Używam również openjdk 11. Jeśli ktoś może odpowiedzieć na to pytanie.
thekevshow
18

Wspomniana wcześniej procedura kończy się niepowodzeniem w przypadku zastosowania do obrazów podrzędnych. Oto bardziej kompletne rozwiązanie:

public static BufferedImage deepCopy(BufferedImage bi) {
    ColorModel cm = bi.getColorModel();
    boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
    WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
    return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
user1050755
źródło
Dziękuję, podczas próby sklonowania podobrazu wystąpił błąd przesunięcia. Ta wersja jest dokładnie tym, czego potrzebowałem.
rokoko
5

Innym sposobem jest użycie Graphics2Dklasy do narysowania obrazu na nowym pustym obrazie. To tak naprawdę nie klonuje obrazu, ale skutkuje utworzeniem kopii obrazu.

public static final BufferedImage clone(BufferedImage image) {
    BufferedImage clone = new BufferedImage(image.getWidth(),
            image.getHeight(), image.getType());
    Graphics2D g2d = clone.createGraphics();
    g2d.drawImage(image, 0, 0, null);
    g2d.dispose();
    return clone;
}
HyperNeutrino
źródło
4

Wiem, że to pytanie jest dość stare, ale dla przyszłych odwiedzających, oto rozwiązanie, którego użyłbym:

Image oldImage = getImage();
Image newImage = oldImage.getScaledInstance(oldImage.getWidth(null), oldImage.getHeight(null), Image.SCALE_DEFAULT);

Proszę poprawić mnie, jeśli zmiana właśnie uzyskanego newImagewpływa w jakikolwiek sposób na oryginalny obraz.
-> Javadoc dla getScaledInstance
-> Javadoc dla SCALE_DEFAULT (pozostałe stałe są wymienione tuż pod tą)

PixelMaster
źródło
Myślę, że tak naprawdę nie skopiowałoby to obrazu, tj. Gdybyś zmienił oryginał, przeskalowany również się zmieni, ale to było chwilę tak źle, niech ktoś inny powie na pewno.
f1wade
1
To faktycznie kopiuje obraz, ponieważ zmiany w oryginale nie zmieniają kopii. Ta odpowiedź jest krótka i zwięzła i nie ogranicza się nawet do BufferedImages. Jedynym problemem jest to, że wraca Image, a nie BufferedImage.
Kröw