关于java:如何克隆BufferedImage

How do you clone a BufferedImage

我有一个对象,其中有许多BufferedImages,我想创建一个新对象,将所有BufferedImages复制到新对象中,但这些新图像可能会被更改,我不希望通过更改新对象图像来更改原始对象图像。

明白了吗?

这有可能吗?有人能建议一个好的方法吗?我想到了getsubimage,但在某个地方读到了子映像的任何更改都会被关联回父映像。

我只想得到一个全新的完全独立的bufferedimage拷贝或克隆。


像这样?

1
2
3
4
5
6
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);
}


我这样做:

1
2
3
4
5
6
7
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;
}

它工作得相当好,而且使用简单。


当应用于子图像时,前面提到的过程失败。下面是一个更完整的解决方案:

1
2
3
4
5
6
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);
}


另一种方法是使用Graphics2D类将图像绘制到新的空白图像上。这并不能真正克隆图像,但会生成图像的副本。

1
2
3
4
5
6
7
8
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;
}

类BufferedImage不实现可克隆接口。因此,克隆方法不会被重写。下面是深度复制技术的另一种选择:Java PoT 76:深度拷贝技术的另一种选择


我知道这个问题很古老,但对于未来的访问者,我会使用以下解决方案:

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

如果更改刚刚获得的newImage也以任何方式影响了原始图像,请纠正我。-->javaDoc for GetscaledInstance(用于GetscaledInstance的JavaDoc)-->javadoc for scale_默认值(其他常量列在该常量的正下方)


这对于我用来绘制东西的程序非常有用,并且由于堆栈上的bufferedImages实际上是相同的,所以无法实现撤消/重做状态。

顺便说一下,我建议一路使用两个堆栈来进行这种操作!每次你做什么,立即创建一个新的图像,使用上面提到的deepcopy方法

1
image = deepCopy((BufferedImage) stackUndo.peek());

根据需要更改图像,然后在停止编辑(如释放鼠标按钮)时进行更改。

1
stackUndo.push(image);                             

并始终在左堆栈顶部绘制元素

1
g.drawImage(stackUndo.peek(),x,y,null);

然后如果你做了一些撤消/重做操作,就按照这样做

1
2
3
4
5
6
7
8
9
10
public void undoOrRedo(String op) {
    if(op.equals("undo") && stackUndo.size()>1){
       stackRedo.push(stackUndo.pop());
        repaint();
    }
    if(op.equals("redo") && stackRedo.size()>0){
        stackUndo.push(stackRedo.pop());
        repaint();
    }
}

一定要在左边的堆栈中留下一些东西,因为在绘制时,它总是使用顶部的元素(peek)!