Как клонировать BufferedImage

120

У меня есть объект, в котором много буферизованных изображений, я хочу создать новый объект, копирующий все буферизованные изображения в новый объект, но эти новые изображения могут быть изменены, и я не хочу, чтобы исходные изображения объектов изменялись путем изменения новые изображения объектов.

это ясно?

Возможно ли это сделать, и может ли кто-нибудь предложить хороший способ сделать это, пожалуйста? Я подумал о getSubImage, но где-то читал, что любые изменения во вспомогательном изображении возвращаются обратно к родительскому изображению.

Я просто хочу получить новую, совершенно отдельную копию или клон BufferedImage.

f1wade
источник
1
ты не можешь вызвать clone()метод? Или я что-то упустил? Я не очень разбираюсь в BufferedImageклассе,
Ноэль М.
1
clone предоставляет только неглубокую копию, поэтому она будет содержать ссылки на буферизованные изображения; а не их копии.
Ultimate Gobblement
7
@NoelM, UltimateGobblement: BufferedImageне реализует, Cloneableи clone()метод имеет защищенный доступ.
Роберт

Ответы:

173

Что-то вроде этого?

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
источник
4
Я тоже заимствую это в своей программе =)
Дэниел Кац
есть проблема с этим методом при копировании
фрагментов изображения
7
Хотя это работает в большинстве случаев, это не работает должным образом, когда это BufferedImage было обрезано (оно возвращает все изображение до того, как оно было обрезано). Простое решение - изменить последнюю строку на:
HaydenStudios
3
вернуть новый BufferedImage (см, растр, isAlphaPremultiplied, null) .getSubimage (0, 0, bi.getWidth (), bi.getHeight ());
HaydenStudios
copyData (null) не всегда работает, потому что он может работать с родительским растром (то есть, когда изображение является дополнительным изображением), см. мой измененный ответ
user1050755
46

Я сделаю это:

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;
}

Он работает довольно хорошо и прост в использовании.

Персона
источник
3
Это выглядит довольно просто. Почему это не лучший ответ? Есть ли недостаток, о котором я не знаю?
WVrock
2
@WVrock Это не работает, если тип изображения 0 (пользовательский)
Тилман Хаушерр
3
заменить Graphics g = b.getGraphics (); от Graphics2D g = b.createGraphics (); и это идеально
Надир
1
Думаю, это самый чистый ответ. Хотя есть ли разница в производительности между этим и принятым ответом? Я чувствую себя ничтожным, если нет? Может ли это быть быстрее, потому что создание объекта оптимизировано в jvm. Также использую openjdk 11. Если кто-нибудь может ответить на этот вопрос.
thekevshow
18

Ранее упомянутая процедура не выполняется при применении к вспомогательным изображениям. Вот более полное решение:

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
источник
Спасибо, я получал ошибку смещения при попытке клонировать фрагмент изображения. Эта версия как раз то, что мне нужно.
рококо
5

Другой способ - использовать Graphics2Dкласс для рисования изображения на новом пустом изображении. На самом деле это не клонирует изображение, но приводит к созданию копии изображения.

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
источник
4

Я знаю, что этот вопрос довольно старый, но для будущих посетителей я бы использовал следующее решение:

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

Пожалуйста, поправьте меня, если изменение только что полученного newImageтакже каким-либо образом влияет на исходное изображение.
-> Javadoc для getScaledInstance
-> Javadoc для SCALE_DEFAULT (остальные константы перечислены чуть ниже этого)

PixelMaster
источник
Я думаю, что это не будет фактически копировать изображение, т.е. если вы измените оригинал, масштабирование также изменится, но это было так плохо, пусть кто-то другой скажет наверняка.
f1wade
1
Это фактически копирует изображение, поскольку изменения оригинала не изменят копию. Этот ответ короткий и лаконичный и даже не ограничивается BufferedImages. Единственная проблема в том , что он возвращается Image, не BufferedImage.
Kröw 06