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:
所以我做了一个小应用程序,基本上可以绘制剪贴板(内存)中的任何图像并尝试绘制它。
这是代码示例:
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: