137

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

Cœur
  • 37,241
  • 25
  • 195
  • 267
f1wade
  • 2,877
  • 6
  • 27
  • 43
  • 1
    can't you call the `clone()` method? Or have I missed something? I don't know a great deal about the `BufferedImage` class – Noel M Aug 18 '10 at 16:15
  • 1
    clone only provides a shallow copy so it would contain the references to the buffered images; not copies of them. – Ultimate Gobblement Aug 18 '10 at 16:19
  • 8
    @NoelM, UltimateGobblement: `BufferedImage` does not implement `Cloneable` and the `clone()` method has protected access. – Robert Aug 20 '12 at 16:19

9 Answers9

191

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);
}
Klark
  • 8,162
  • 3
  • 37
  • 61
  • 5
    I'm also borrowing this in my program =) – Daniel Kats Mar 10 '12 at 16:11
  • have issue with this method on copying subimage – mishka Dec 25 '13 at 09:02
  • 9
    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: – HaydenStudios Jun 29 '14 at 22:54
  • 3
    return new BufferedImage(cm, raster, isAlphaPremultiplied, null).getSubimage(0, 0, bi.getWidth(), bi.getHeight()); – HaydenStudios Jun 29 '14 at 22:55
  • 1
    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 – user1050755 Nov 12 '14 at 19:21
  • @f1wade @BlackSheep @HaydenStudios @user1050755 Is there a way to alter the size of the `BufferedImage` after cloning it? – Utku Ufuk Jan 20 '15 at 12:43
  • Should have a null check on bi (e.g. if you're using this in a copy constructor) shouldn't there? – Mgamerz Jun 22 '15 at 21:06
57

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.

Community
  • 1
  • 1
APerson
  • 702
  • 6
  • 10
  • 3
    This looks pretty simple. Why this is n't the best answer? Is there a flaw that I'm not aware of? – WVrock Feb 24 '15 at 08:53
  • 2
    @WVrock It doesn't work if the image type is 0 (custom) – Tilman Hausherr Feb 01 '16 at 12:34
  • 4
    replace Graphics g = b.getGraphics(); by Graphics2D g = b.createGraphics(); and it is perfect – Nadir Dec 17 '16 at 17:08
  • 1
    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. – thekevshow Jan 04 '19 at 00:34
27

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);
}
user1050755
  • 11,218
  • 4
  • 45
  • 56
  • Thank you, I was getting an offset error trying to clone a subimage. This version is exactly what I needed. – rococo May 27 '18 at 04:58
6

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;
}
hyper-neutrino
  • 5,272
  • 2
  • 29
  • 50
5

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)

PixelMaster
  • 895
  • 10
  • 28
  • 1
    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. – f1wade Dec 14 '16 at 22:20
  • 2
    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`. – Kröw Jul 06 '18 at 23:34
2

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

Mike
  • 19,267
  • 11
  • 56
  • 72
0

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.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
clic
  • 365
  • 1
  • 13
  • May fail: ```java Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException: Unknown image type 0 at java.desktop/java.awt.image.BufferedImage.(BufferedImage.java:501) ``` – Polyana Fontes Jan 02 '22 at 12:49
  • @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... – clic Jan 04 '22 at 12:51
  • @JoséRobertoAraújoJúnior try to replace "source.getType()" with "source.getType()==0?5:source.getType()" – clic Jan 05 '22 at 07:34
0

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);
}
Dave The Dane
  • 650
  • 1
  • 7
  • 18
0

I have made this solution using two functions it works on every possible image, even image by camcorder of laptop, I was facing problem that after croping image through camera it was not working but this solution will work

static BufferedImage deepCopy(BufferedImage bi) 
{
    try
    {
        ColorModel cm = bi.getColorModel();
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
        return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
    }
    catch(Exception ex)
    {
        
    }
    try{
        BufferedImage b = new BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
        Graphics g = b.getGraphics();
        g.drawImage(bi, 0, 0, null);
        g.dispose();
        return b;
        
    }
    catch(Exception ex){
        
        
        
    }
    
    return null;
}
hassan
  • 1
  • 2