【问题标题】:How do you clone a BufferedImage如何克隆 BufferedImage
【发布时间】:2011-03-31 15:50:14
【问题描述】:

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

清楚吗?

这是可能的吗?有人可以提出一个好的方法吗? 我曾考虑过 getSubImage,但在某处读到,对子图像的任何更改都会被反射回父图像。

我只是希望能够获得一个全新的、完全独立的 BufferedImage 副本或克隆

【问题讨论】:

  • 不能调用clone()方法吗?还是我错过了什么?我对BufferedImage 类了解不多
  • clone 只提供一个浅拷贝,因此它包含对缓冲图像的引用;不是它们的副本。
  • @NoelM, UltimateGobblement: BufferedImage 没有实现 Cloneable 并且 clone() 方法具有受保护的访问权限。

标签: java copy clone bufferedimage


【解决方案1】:

以下使用 arraycopy 的解决方案比公认的答案快 3-4 倍

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

顺便说一句,使用 Graphics2D 的答案提供了同样好的结果。

【讨论】:

    【解决方案2】:

    我知道这个问题已经很老了,但是对于未来的访问者,这是我会使用的解决方案:

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

    如果更改刚刚获得的newImage 也会以任何方式影响原始图像,请纠正我。
    --> Javadoc for getScaledInstance
    --> Javadoc for SCALE_DEFAULT(其他常量列在该常量的下方)

    【讨论】:

    • 我认为这实际上不会复制图像,即如果您更改原始图像,缩放也会发生变化,但是已经有一段时间了,所以让其他人肯定地说。
    • 这确实复制了图像,因为对原始图像的更改不会更改副本。这个答案简短明了,甚至不限于 BufferedImages。唯一的问题是它返回Image,而不是BufferedImage
    【解决方案3】:

    另一种方法是使用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;
    }
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 谢谢,我在尝试克隆子图像时遇到偏移错误。这个版本正是我需要的。
      【解决方案5】:

      我这样做:

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

      效果很好,使用也很简单。

      【讨论】:

      • 这看起来很简单。为什么这不是最佳答案?是否有我不知道的缺陷?
      • @WVrock 如果图片类型为0(自定义)则不起作用
      • 替换图形 g = b.getGraphics();通过 Graphics2D g = b.createGraphics();它是完美的
      • 我认为这是最干净的答案。尽管这与接受的答案之间有任何性能差异吗?如果没有,我觉得可以忽略不计?这可能会更快吗,纯粹是因为在 jvm 中优化了对象创建。也使用openjdk 11。如果有人可以回答这个问题。
      【解决方案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);
      }
      

      【讨论】:

      • 我的程序中也借用了这个=)
      • 在复制子图像时遇到此方法问题
      • 虽然这在大多数情况下都有效,但是当 BufferedImage 被裁剪时它不能正常工作(它会在裁剪之前返回整个图像)。一个简单的解决方法是将最后一行更改为:
      • return new BufferedImage(cm, raster, isAlphaPremultiplied, null).getSubimage(0, 0, bi.getWidth(), bi.getHeight());
      • copyData(null) 并不总是有效,因为它可能适用于父光栅(即当图像是子图像时),请参阅我修改后的答案
      【解决方案7】:

      类 BufferedImage 没有实现 Cloneable 接口。因此,克隆方法不会被覆盖。这是深度复制技术的替代方案: Java Tip 76: An alternative to the deep copy technique

      【讨论】:

      • BufferedImage 不可序列化,这使得这种替代方案难以使用..
      猜你喜欢
      • 2010-10-17
      • 2012-05-27
      • 2012-06-17
      • 2010-12-30
      • 2015-01-26
      • 2011-08-20
      • 1970-01-01
      相关资源
      最近更新 更多