Comment cloner une BufferedImage

120

J'ai un objet qui contient de nombreuses images tamponnées, je veux créer un nouvel objet en copiant toutes les images tamponnées dans le nouvel objet, mais ces nouvelles images peuvent être modifiées et je ne veux pas que les images de l'objet d'origine soient modifiées en modifiant le nouvelles images d'objets.

est-ce clair?

Est-ce possible de le faire et quelqu'un peut-il suggérer une bonne façon de le faire s'il vous plaît? J'ai pensé à getSubImage mais j'ai lu quelque part que toutes les modifications apportées à la sous-image sont reliées à l'image parente.

Je veux juste pouvoir obtenir une nouvelle copie ou un clone entièrement séparé d'une BufferedImage

f1wade
la source
1
tu ne peux pas appeler la clone()méthode? Ou ai-je manqué quelque chose? Je ne sais pas grand-chose sur la BufferedImageclasse
Noel M
1
clone ne fournit qu'une copie superficielle afin qu'elle contienne les références aux images tamponnées; pas des copies.
Ultimate Gobblement
7
@NoelM, UltimateGobblement: BufferedImagene met pas en œuvre Cloneableet la clone()méthode a un accès protégé.
Robert

Réponses:

173

Quelque chose comme ça?

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
la source
4
J'emprunte aussi ceci dans mon programme =)
Daniel Kats
J'ai un problème avec cette méthode lors de la copie de sous
image
7
Bien que cela fonctionne dans la plupart des cas, cela ne fonctionne pas correctement lorsque cette BufferedImage a été rognée (elle renvoie l'image entière avant qu'elle ne soit rognée). Une solution simple à cela est de changer cette dernière ligne en:
HaydenStudios
3
return new BufferedImage (cm, raster, isAlphaPremultiplied, null) .getSubimage (0, 0, bi.getWidth (), bi.getHeight ());
HaydenStudios
copyData (null) ne fonctionne pas toujours car il peut fonctionner sur un raster parent (c'est-à-dire lorsque l'image est une sous-image), voir ma réponse modifiée
user1050755
46

Je fais ça:

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

Cela fonctionne assez bien et c'est simple à utiliser.

Une personne
la source
3
Cela semble assez simple. Pourquoi ce n'est pas la meilleure réponse? Y a-t-il un défaut dont je ne suis pas conscient?
WVrock
2
@WVrock Cela ne fonctionne pas si le type d'image est 0 (personnalisé)
Tilman Hausherr
3
remplacer Graphics g = b.getGraphics (); par Graphics2D g = b.createGraphics (); et c'est parfait
Nadir
1
Je pense que c'est la réponse la plus claire. Bien qu'il y ait une différence de performance entre cette réponse et la réponse acceptée? Je me sens négligeable si non? Cela pourrait-il être plus rapide simplement parce que la création d'objets est optimisée dans le jvm. En utilisant également openjdk 11. Si quelqu'un peut répondre à cette question.
thekevshow
18

La procédure mentionnée précédemment échoue lorsqu'elle est appliquée aux sous-images. Voici une solution plus complète:

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
la source
Merci, je recevais une erreur de décalage en essayant de cloner une sous-image. Cette version est exactement ce dont j'avais besoin.
rococo
5

Une autre façon consiste à utiliser la Graphics2Dclasse pour dessiner l'image sur une nouvelle image vierge. Cela ne clone pas vraiment l'image, mais cela entraîne la production d'une copie de l'image.

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
la source
4

Je sais que cette question est assez ancienne, mais pour les futurs visiteurs, voici la solution que j'utiliserais:

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

Veuillez me corriger si le fait de changer le juste obtenu newImageaffecte également l'image originale de quelque manière que ce soit.
-> Javadoc pour getScaledInstance
-> Javadoc pour SCALE_DEFAULT (les autres constantes sont listées juste en dessous de celle-ci)

PixelMaster
la source
Je pense que cela ne copierait pas réellement l'image, c'est-à-dire que si vous changiez l'original, la mise à l'échelle changera également, mais cela fait un moment si mal que quelqu'un d'autre le dise à coup sûr.
f1wade
1
Cela copie réellement l'image, dans la mesure où les modifications apportées à l'original ne modifieront pas la copie. Cette réponse est courte et concise et ne se limite même pas à BufferedImages. Le seul problème est qu'il revient Image, non BufferedImage.
Kröw