Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I correctly get an Image from the Clipboard in JavaFX; What is the difference between applications that struggle and those that don't?

The Problem

JavaFX's default way of getting an image from the Windows clipboard,

Clipboard.getSystemClipboard().getImage(); 

, appears to be broken.

Something seems to go wrong with the transparency of the image. Set on a black background, the image appears fine, but set on a white background, nothing shows at all.

Showing the JavaFX image transparency issue by changing the background of the application from dark to light.

You can test the clipboard using this Minimal, Complete, Verifiable example.

Environment: Windows 7, Java 8 update 202

What I Know

Below, I'll describe the things I already know.

There are Other People who Have Asked Similar Questions:

  • Getting Image from Clipboard Awt vs FX
  • Image from clipboard not correctly displayed in JavaFX 8 application

Yet, no one has gotten at the heart of the problem or received an answer.

No Official Bug

I can't seem to find a bug regarding this issue in the Java Bug Database.

No Problem for AWT

This problem with images doesn't occur on the AWT clipboard, but I want a solution that uses the JavaFX clipboard.

On the right is the image from AWT's clipboard, on the left is the same image from JavaFX's clipboard.

The Clipboard Contains Multiple Formats

I know that the Windows clipboard contains multiple versions of the same thing, just in different formats. This is easy to see using InsideClipboard or Free Clipboard Viewer.

A screenshot of InsideClipboard, showing the CF_DIB format.

The JavaFX Clipboard recognizes certain formats; sometimes it has different names for them. application/x-java-rawimage is what Java considers an image; in code you refer to this as DataFormat.IMAGE.

I suspect that the DIB clipboard format in Windows matches up with Java's application/x-java-rawimage, but can't find proof of that in the source code.

The Problem is Wide-Spread

Problem Applications

JavaFX seems to have this same transparency problem with various applications that copy an image to the clipboard:

  • Adobe Reader (from a PDF with images)
  • Foxit Reader (from a PDF with images)
  • Microsoft Word 365 (from a .docx file with images)
  • Windows 7's Paint (.png, .jpg, .gif, .bmp)
  • Greenshot (which is an enhanced screenshot utility)
  • Firefox 65.0.2 (copying the Google.com logo)

Applications Without The Issue

I've also found some applications that copy an image to the clipboard and JavaFX can pull it out using the default method no problem:

  • Paint.net
  • The PrtScn button
  • The Windows Snipping Tool
  • Google Chrome 72.0.3626.121 (copying the Google.com logo)

Answering The Question

An adequate answer should

  • explain simply, specifically, and with examples why the problem occurs with some applications, but not others, and
  • figure out where things go wrong in the JDK implementation and show specifically how to fix said implementation for the majority of the problem applications without breaking it for the applications that already work.
    • If it's not feasible to change the JDK's implementation, an adequate answer will provide a Minimal, Complete, and Verifiable example showing JavaFX code that produces an Image from the JavaFX clipboard when an image is copied from Adobe Reader.

If you can't help, but think this is a well researched question, consider voting, or sharing it with a friend.

like image 648
Brad Turek Avatar asked Mar 05 '19 04:03

Brad Turek


1 Answers

In both of your loading methods, the resulting JavaFx Image supports transparency when it shouldn't. However, the AWT loading method uses an intermediate Transferable that uses a non-transparent DirectColorModel to create the Image. As a result the AWT image appears to be correct despite the underlying image still supporting transparency.

Unfortunately this issue stems from a deeper issue that will probably not be fixed, see: https://bugs.openjdk.java.net/browse/JDK-8041459

When java saves image with alpha channel it encode image as YCbCr plus 4th alpha channel. The problem that other applications recognize 4 channels jpeg as RGB or CMYK image, that's why we have wrong color in image. The best solution is to convert image to other color type without alpha channel and only then save it it can't be fixed, the jpeg's spec doesn't specify this moment, it says that color space is application dependent, java decided to code alpha channel as YCbCrA, that's it.

Rather than trying to manually manipulate Image data, an easier fix is probably to blend the Image with a Rectangle and use a Group instead of an ImageView to display it. Here is an example:

private void loadImageFromJavaFXClipboard(final Group group) {     System.out.println("Adding an image from the JavaFX Clipboard...");     final Clipboard clipboard = Clipboard.getSystemClipboard();      if (clipboard.hasImage()) {         final Image image = clipboard.getImage();          setupImageFixingGroup(group, image);     } else {         new Alert(Alert.AlertType.INFORMATION, "No image detected on the Clipboard!").show();         group.getChildren().clear();     } }  private void setupImageFixingGroup(Group group, Image image) {     final ImageView view = new ImageView(image);     view.setBlendMode(BlendMode.LIGHTEN);      final Rectangle blend = new Rectangle(image.getWidth(), image.getHeight(), Color.BLACK);     blend.widthProperty().bind(image.widthProperty());     blend.heightProperty().bind(image.heightProperty());      group.getChildren().clear();     group.getChildren().addAll(blend, view); } 

Here is a working version of your Minimal, Verifiable, Complete example: https://pastebin.com/rzhzMui5. I tested it with a number of images including the one in your gif, but have not explicitly tested it against every case you included.

like image 168
user2815708 Avatar answered Oct 16 '22 12:10

user2815708