ChatGPT解决这个技术问题 Extra ChatGPT

How do you clone a BufferedImage

I have an object which has many bufferedimages in it, I want to create a new object copying all the bufferedimages into the new object, but these new images may be altered and i don't want the original object images to be altered by altering the new objects images.

is that clear?

Is this possible to do and can anyone suggest a good way to do it please? I have thought of getSubImage but read somewhere that any changes to the subimage are relected back to the parent image.

I just want to be able to get a fresh entirely separate copy or clone of a BufferedImage

can't you call the clone() method? Or have I missed something? I don't know a great deal about the BufferedImage class
clone only provides a shallow copy so it would contain the references to the buffered images; not copies of them.
@NoelM, UltimateGobblement: BufferedImage does not implement Cloneable and the clone() method has protected access.

K
Klark

Something like this?

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

I'm also borrowing this in my program =)
have issue with this method on copying subimage
While this works under most circumstances, it doesn't work properly when that BufferedImage has been cropped(it returns the whole image before it was cropped). A simple fix to this is to change that last line to:
return new BufferedImage(cm, raster, isAlphaPremultiplied, null).getSubimage(0, 0, bi.getWidth(), bi.getHeight());
copyData(null) does not always work because it may work on a parent raster (ie. when the image is a sub image), see my modified answer
C
Community

I do this:

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

It works fairly well and it is simple to use.


This looks pretty simple. Why this is n't the best answer? Is there a flaw that I'm not aware of?
@WVrock It doesn't work if the image type is 0 (custom)
replace Graphics g = b.getGraphics(); by Graphics2D g = b.createGraphics(); and it is perfect
I think this is the cleanest answer. Although is there any performance difference between this and the accepted answer? I feel like negligible if any no? Could this be faster purely cause object creation is optimized in the jvm. Also using openjdk 11. If anyone can answer that question.
u
user1050755

The previously mentioned procedure fails when applied to sub images. Here is a more complete solution:

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

Thank you, I was getting an offset error trying to clone a subimage. This version is exactly what I needed.
h
hyper-neutrino

Another way is to use the Graphics2D class to draw the image onto a new blank image. This doesn't really clone the image, but it results in a copy of the image being produced.

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

P
PixelMaster

I know that this question is pretty old, but for future visitors, here's the solution I'd use:

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

Please correct me if changing the just obtained newImage also affects the original image in any way.
--> Javadoc for getScaledInstance
--> Javadoc for SCALE_DEFAULT (the other constants are listed just below that one)


I think that would not actually copy the image,ie if you changed the original the scaled willalso change, but its been a while so ill let someone else say for sure.
This does actually copy the image, in that changes to the original will not change the copy. This answer is short and concise and isn't even limited to BufferedImages. The only issue is that it returns Image, not BufferedImage.
M
Mike

Class BufferedImage does not implement the Cloneable interface. Thus the clone method is not overriden. Here's an alternative for a deep copy technique: Java Tip 76: An alternative to the deep copy technique


BufferedImage is not serializable, which makes this alternative hard to use ..
T
Tomerikoo

The following solution using arraycopy is about 3-4 times faster than the accepted answer:

public static BufferedImage copyImage(BufferedImage source){
    BufferedImage bi = new BufferedImage(source.getWidth(), source.getHeight(), source.getType());
    byte[] sourceData = ((DataBufferByte)source.getRaster().getDataBuffer()).getData();
    byte[] biData = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
    System.arraycopy(sourceData, 0, biData, 0, sourceData.length);
    return bi;
}

By the way, the answers using Graphics2D provide similarly good results.


May fail: java Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException: Unknown image type 0 at java.desktop/java.awt.image.BufferedImage.<init>(BufferedImage.java:501)
@JoséRobertoAraújoJúnior which image format, which Java version and which operating system are you using? There are some reported issues with PNG and TIFF. A hack exists where in case the image type is 0 it is manually set to 5...
@JoséRobertoAraújoJúnior try to replace "source.getType()" with "source.getType()==0?5:source.getType()"
D
Dave The Dane

Here is a solution I wrote many years ago for JFreeChart.

It will also copy any Properties which may be present in the BufferedImage.

I believe it was tested with all known Colour Models (Image Types).

Whether it will work with Image Type 0 discussed by @JoséRobertoAraújoJúnior I don't know.
But: Image Type 0 is invalid & should not occur.

/**
 * Copied from
 * <a href=https://github.com/jfree/jfreechart/blob/master/src/main/java/org/jfree/chart/util/PaintAlpha.java>JFreeChart PaintAlpha</a>
 * 
 * @param   srcImage
 * @return  
 */
public static BufferedImage cloneImage(final BufferedImage srcImage) {

    final WritableRaster srcRaster = srcImage.getRaster();
    final WritableRaster dstRaster = srcRaster.createCompatibleWritableRaster();
    /*
     * This is the code that actually COPIES the pixels...
     */
    dstRaster.setRect(srcRaster);
    /*
     * Images hardly ever have Properties, but we copy them anyway...
     */
    final String[]                  propNames = srcImage.getPropertyNames();
    final Hashtable<String, Object> props;

    if (propNames == null) {
        props     =  null;
    } else {
        props     =  new Hashtable<>();

        for (int i = 0; i < propNames.length; i++) {
            props.put(propNames[i], srcImage.getProperty(propNames[i]));
        }
    }
    /*
     * That's it folks! Return the new clone...
     */
    return new BufferedImage(srcImage.getColorModel(), dstRaster, srcImage.isAlphaPremultiplied(), props);
}