Java透明PNG复制到剪贴板

10

我正在尝试在程序中复制一个png文件到剪贴板,并在粘贴到另一个程序中(例如ms office,paint,photoshop)保留它的alpha通道。问题是,在大多数程序中,alpha通道会变为黑色。我已经在网上搜索了几个小时,但找不到解决方案。我正在使用的代码:

setClipboard(Toolkit.getDefaultToolkit().getImage(parent.getSelectedPicturePath()));

public static void setClipboard(Image image) {
    ImageSelection imgSel;
if (OSDetector.isWindows()) {
    imgSel = new ImageSelection(image);
} else {
    imgSel = new ImageSelection(getBufferedImage(image));
}
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
}

有没有办法在Java中保留alpha通道? 我尝试将png转换为BufferedImage,Image等,然后将其粘贴到剪贴板上,但都没有成功。


你看过这个吗: https://dev59.com/AHRB5IYBdhLWcg3wUFrB?rq=1 ? - NullUserException
1
画图工具不支持透明度,所以不要指望它在那里起作用。其他程序可能也是这种情况。 - 1615903
@user1615903:你确定吗?在Photoshop中也没有显示透明度:背景是黑色的,应该是透明的。 - Chris
@NullUserException:是的,我已经找到了,谢谢。我没有进一步关注那个线程的原因是,1. 要加载到剪贴板中的图片已经有一个透明层,2. 如果我将透明变成黑色作为中间步骤,然后再将黑色变成透明,它也会将本应该是黑色的图片部分变成透明。我的理解正确吗? - Chris
有人有解决这个问题的想法吗?这相当令人烦恼,我找不到一个合适的解决方案。同时,我也尝试过使用DataFlavor.javaFileListFlavor进行工作,它可以很好地将剪贴板图像复制到MS Office,但无法在Photoshop和MS Paint中使用。 - Chris
@RAnders00,您能否分享一下您正在测试的图像?您尝试过其他图像吗?另外,获取有关您使用的Windows版本、JRE/JDK版本、OpenJDK/Oracle等更多信息也将非常有帮助。谢谢。 - Alejandro C De Baca
3个回答

2
假设 OSDetector 正常工作,我能够在运行 Oracle JDK 1.8.0_131 的 Windows Server 2008R2 64 位操作系统上直接使用 OP 的代码而无需修改。OP 忽略了 getBufferedImage() 的代码,但我猜测它是从这篇博客中的某个变种版本。
当我在 Windows 上使用博客中的getBufferedImage()版本进行测试(忽略了 OSDetector 检查),我能够重现问题的一个变体,整个图像都是黑色的,这实际上是一个关于异步调用 Image.getWidth()Image.getHeight()Graphics.drawImage() 的时间问题,所有这些方法都会立即返回并且需要 observer 进行异步更新。博客中的代码为所有这些调用传递了 null(没有 observer)并期望结果立即返回,但在我测试时不是这种情况。
一旦我修改了 getBufferedImage() 来使用回调函数,我就能够复现精确的问题:alpha 通道会变成黑色。造成这种行为的原因是将带有透明度的图像绘制到了默认为黑色画布的图形上下文中。你看到的正是你在黑色背景的网页上查看图像时能够看到的内容。
为了解决这个问题,我使用了这个 StackOverflow 答案中的提示,并将背景绘制成白色。
我使用 这个站点 中的 ImageSelection 实现,它只是使用 DataFlavor.imageFlavor 将一个 Image 实例包装在一个 Transferrable 中。
最后,在我的测试中,原始图像和缓冲图像变体都在 Windows 上工作。下面是代码:
public static void getBufferedImage(Image image, Consumer<Image> imageConsumer) {

    image.getWidth((img, info, x, y, w, h) -> {
        if (info == ImageObserver.ALLBITS) {
            BufferedImage buffered = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = buffered.createGraphics();
            g2.setColor(Color.WHITE); // You choose the background color
            g2.fillRect(0, 0, w, h);
            if (g2.drawImage(img, 0, 0, w, h, (img2, info2, x2, y2, w2, h2) -> {
                if (info2 == ImageObserver.ALLBITS) {
                    g2.dispose();
                    imageConsumer.accept(img2);
                    return false;
                }
                return true;
            })) {
                g2.dispose();
                imageConsumer.accept(buffered);
            }
            return false;
        }
        return true;
    });
}

public static void setClipboard(Image image) {
    boolean testBuffered = true; // Both buffered and non-buffered worked for me
    if (!testBuffered) {
        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new ImageSelection(image), null);
    } else {
        getBufferedImage(image, (buffered) -> {
            ImageSelection imgSel = new ImageSelection(buffered);
            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
        });
    }
}

我希望这能有所帮助。祝你好运。

1
这是正确的答案吗?你试过了吗?
    public void doCopyToClipboardAction()
{
  // figure out which frame is in the foreground
  MetaFrame activeMetaFrame = null;
  for (MetaFrame mf : frames)
  {
    if (mf.isActive()) activeMetaFrame = mf;
  }
  // get the image from the current jframe
  Image image = activeMetaFrame.getCurrentImage();
  // place that image on the clipboard
  setClipboard(image);
}


// code below from exampledepot.com
//This method writes a image to the system clipboard.
//otherwise it returns null.
public static void setClipboard(Image image)
{
   ImageSelection imgSel = new ImageSelection(image);
   Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
}


// This class is used to hold an image while on the clipboard.
static class ImageSelection implements Transferable
{
  private Image image;

  public ImageSelection(Image image)
  {
    this.image = image;
  }

  // Returns supported flavors
  public DataFlavor[] getTransferDataFlavors()
  {
    return new DataFlavor[] { DataFlavor.imageFlavor };
  }

  // Returns true if flavor is supported
  public boolean isDataFlavorSupported(DataFlavor flavor)
  {
    return DataFlavor.imageFlavor.equals(flavor);
  }

  // Returns image
  public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException, IOException
  {
    if (!DataFlavor.imageFlavor.equals(flavor))
    {
      throw new UnsupportedFlavorException(flavor);
    }
    return image;
  }
}

来源: http://alvinalexander.com/java/java-copy-image-to-clipboard-example

我自己没有尝试过,也不确定是否正确。希望你能得到正确的答案。


你好!感谢您尝试解决这个问题。我在2013年发布了它,现在无法回忆起当时是否解决了它以及如何解决的。通常情况下,如果我有答案,我会发布一个答案。由于过去几年中我只一直在使用C#编程,所以我也无法确认您的解决方案是否可行。 - Chris
好的。你能给我的答案点个赞吗?或者将其标记为解决方案? - Kuvaaja
不好意思,我不能翻译这个,因为我无法确认它是否是解决问题的有效方法。 - Chris

1
这是一个非常简单、自包含的示例,与读取或创建图像无关。此代码只在alpha类型BufferedImage上绘制了一个红色圆形。当我将其粘贴到支持透明度的任何程序中时,它会正确显示。希望它有所帮助。
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class CopyImageToClipboard {
    public void createClipboardImageWithAlpha() {
        //Create a buffered image of the correct type, with alpha.
        BufferedImage image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = image.createGraphics();
        //Draw in the buffered image.
        g2d.setColor(Color.red);
        g2d.fillOval(10, 10, 580, 580);

        //Add the BufferedImage to the clipboard with transferable image flavor.
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        Transferable transferableImage = getTransferableImage(image);
        clipboard.setContents(transferableImage, null);
    }

    private Transferable getTransferableImage(final BufferedImage bufferedImage) {
        return new Transferable() {
            @Override
            public DataFlavor[] getTransferDataFlavors() {
                return new DataFlavor[] { DataFlavor.imageFlavor };
            }

            @Override
            public boolean isDataFlavorSupported(DataFlavor flavor) {
                return DataFlavor.imageFlavor.equals(flavor);
            }

            @Override
            public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
                if (DataFlavor.imageFlavor.equals(flavor)) {
                    return bufferedImage;
                }
                return null;
            }
        };
    }
}

谢谢您的回答。请看一下我在@Kuvaaja的回答中的评论。我无法确认这是否是一个有效的解决方案,因为我大约4年前遇到了这个问题,现在也没有时间去检查。 - Chris
1
对我不起作用。当我粘贴图像时,它有黑色背景。我正在使用Windows。 - ZhekaKozlov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接