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

时间:2018-02-17 01:08:59

标签: image canvas javafx clipboard

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

这是代码示例:

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

这是绘制的图像和相关的数据类型: 从我的屏幕打印。 enter image description here

来自互联网的图片。 enter image description here

然而,当我从油漆中复制并粘贴直接原始图像时...... enter image description here

1 个答案:

答案 0 :(得分:1)

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

并显示您的图片:

AWT

此部分基于此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图像的提取方式。如果您想深入了解本机实现,请查看native-glass/win/GlassClipboard.cpp代码。

因此我们可以尝试使用PixelReader。让我们读取图像并返回一个字节数组:

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:

MSPaint with JavaFX