ClipBoard 什么是对象描述符类型,我该如何编写它?

ClipBoard What is an Object Descriptor type and how can i write it?

所以我做了一个小应用程序,基本上可以绘制剪贴板(内存)中的任何图像并尝试绘制它。

这是代码示例:

private EventHandler<KeyEvent> copyPasteEvent =  new EventHandler() {
    final KeyCombination ctrl_V = new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN);
    @Override
    public void handle(Event event) {
        if (ctrl_V.match((KeyEvent) event)) {

            System.out.println("Ctrl+V pressed");
            Clipboard clipboard = Clipboard.getSystemClipboard();

            System.out.println(clipboard.getContentTypes());

            //Change canvas size if necessary to allow space for the image to fit
            Image copiedImage = clipboard.getImage();
            if (copiedImage.getHeight()>canvas.getHeight()){
                canvas.setHeight(copiedImage.getHeight());
            }
            if (copiedImage.getWidth()>canvas.getWidth()){
                canvas.setWidth(copiedImage.getWidth());
            }

            gc.drawImage(clipboard.getImage(), 0,0);
        }
    }
};

这是绘制的图像和相应的数据类型: 从我的屏幕打印。

图片来自网络。

但是,当我从画图复制并粘贴直接原始图像时...

Object Descriptor 是 Microsoft 的 OLE format

这就是为什么当您从 Microsoft 应用程序复制图像时,您会从 Clipboard.getSystemClipboard().getContentTypes():

获得这些描述符
[[application/x-java-rawimage], [Object Descriptor]]

至于从剪贴板中取出图像...让我们尝试两种可能的方法:AWT 和 JavaFX。

AWT

让我们使用awt 工具包获取系统剪贴板,如果上面有图像,检索BufferedImage。然后我们可以轻松地将其转换为 JavaFX Image 并将其放置在 ImageView:

try {
    DataFlavor[] availableDataFlavors = Toolkit.getDefaultToolkit().
        getSystemClipboard().getAvailableDataFlavors();

    for (DataFlavor f : availableDataFlavors) {
        System.out.println("AWT Flavor: " + f);
        if (f.equals(DataFlavor.imageFlavor)) {

            BufferedImage data = (BufferedImage) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.imageFlavor);
            System.out.println("data " + data);

            // Convert to JavaFX:
            WritableImage img = new WritableImage(data.getWidth(), data.getHeight());
            SwingFXUtils.toFXImage((BufferedImage) data, img);
            imageView.setImage(img);
        }
    }
} catch (UnsupportedFlavorException | IOException ex) {
    System.out.println("Error " + ex);
}

它打印:

AWT Flavor: java.awt.datatransfer.DataFlavor[mimetype=image/x-java-image;representationclass=java.awt.Image]

data BufferedImage@3e4eca95: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 350 height = 364 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0

并显示您的图像:

此部分基于此 answer

JavaFX

我们为什么不首先尝试使用 JavaFX 呢?好吧,我们可以直接尝试:

Image content = (Image) Clipboard.getSystemClipboard().getContent(DataFormat.IMAGE);
imageView.setImage(content);

你会得到一张有效的图片,但是当把它添加到 ImageView 时,它会像你已经注意到的那样是空白的,或者颜色无效。

那么我们怎样才能得到一张有效的图片呢?如果查看上面的BufferedImage,则显示type = 1,即BufferedImage.TYPE_INT_RGB = 1;,换句话说,它是一个将8位RGB颜色分量打包成整数像素的图像,没有alpha分量.

我的猜测是 Windows 的 JavaFX 实现不能正确处理这种图像格式,因为它可能需要 RGBA 格式。您可以查看 here how the image is extracted. And if you want to dive into the native implementation, check the native-glass/win/GlassClipboard.cpp 代码。

所以我们可以尝试用 PixelReader 来做。让我们读取图像和 return 一个字节数组:

private byte[] imageToData(Image image) {
    int width = (int) image.getWidth();
    int height = (int) image.getHeight();

    byte[] data = new byte[width * height * 3];

    int i = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int argb = image.getPixelReader().getArgb(x, y);
            int r = (argb >> 16) & 0xFF;
            int g = (argb >>  8) & 0xFF;
            int b =  argb        & 0xFF;
            data[i++] = (byte) r;
            data[i++] = (byte) g;
            data[i++] = (byte) b;
        }
      }
    return data;
}

现在,我们需要做的就是使用这个字节数组写入一个新图像并将其设置为 ImageView:

Image content = (Image) Clipboard.getSystemClipboard().getContent(DataFormat.IMAGE);
byte[] data = imageToData(content);

WritableImage writableImage = new WritableImage((int) content.getWidth(), (int) content.getHeight());
PixelWriter pixelWriter = writableImage.getPixelWriter();
pixelWriter.setPixels(0, 0, (int) content.getWidth(), (int) content.getHeight(), 
        PixelFormat.getByteRgbInstance(), data, 0, (int) content.getWidth() * 3);
imageView.setImage(writableImage);

现在您将获得相同的结果,但仅使用 JavaFX: