Copying images to the clipboard with Java

I wanted to be able to copy an image to the clipboard in FatBits, my Java replacement for xmag(1) or Pixie.app. I was surprised to see the JDK doesn't have an Image equivalent of StringSelection, so I knocked one up:

* A Transferable able to transfer an AWT Image.
* Similar to the JDK StringSelection class.
public class ImageSelection implements Transferable {
private Image image;

public static void copyImageToClipboard(Image image) {
ImageSelection imageSelection = new ImageSelection(image);
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemClipboard().setContents(imageSelection, null);

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

public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (flavor.equals(DataFlavor.imageFlavor) == false) {
throw new UnsupportedFlavorException(flavor);
return image;

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

public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {

I also raised a bug with Sun, and had it closed as a duplicate shortly afterwards.

Anyway, imagine my disappointment when Apple's JVM just hangs:

2005-09-10T10:02:55.916-0700 unknown: (hang #1) event dispatch thread stuck processing event for 1099 ms:
java.lang.Object.wait(Native Method)

The hang lasts indefinitely. The 1099 ms is just how long it takes for my EDT hang monitor to notice something's up. (If you have it run much quicker than that, it's constantly reporting slow font rendering and the like, especially on Linux.)

I wondered if there was some problem calling this off the EDT, but running in a new thread:

public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
public void run() {

just causes the new thread to hang instead:

"Thread-3" prio=6 tid=0x00514700 nid=0x18f6600 in Object.wait() [0xf0c89000..0xf0c89ad0]
at java.lang.Object.wait(Native Method)
- waiting on <0x26d635e0> (a apple.awt.OSXImageRepresentation)
at java.lang.Object.wait(Object.java:474)
at sun.awt.image.ImageRepresentation.reconstruct(ImageRepresentation.java:103)
- locked <0x26d635e0> (a apple.awt.OSXImageRepresentation)
at apple.awt.CDataTransferer.imageToPlatformBytes(CDataTransferer.java:238)
at sun.awt.datatransfer.DataTransferer.translateTransferable(DataTransferer.java:1180)
at apple.awt.CDataTransferer.translateTransferable(CDataTransferer.java:126)
at apple.awt.CClipboard.setContentsNative(CClipboard.java:67)
at sun.awt.datatransfer.SunClipboard.setContents(SunClipboard.java:93)
- locked <0x26d63528> (a apple.awt.CClipboard)
at e.util.ImageSelection.copyImageToClipboard(ImageSelection.java:16)
at e.tools.FatBits$CopyImageAction$1.run(FatBits.java:316)
at java.lang.Thread.run(Thread.java:613)

Amusingly, pasting into Mail at that point crashes Mail. (Somewhere in WebKit, thanks to which Mail seems to have made great strides forward in both its "crashing" and "incorrect text redraw" areas of functionality for 10.4.)

I couldn't find anything obviously related in Apple's release notes. (I'm using "1.5.0_02-36 mixed mode, sharing".) I didn't find anything pertinent with Google.

Investigating a bit more, I discovered that this worked:


So maybe it was relevant that my Image came from Robot.createScreenCapture via BufferedImage.getScaledInstance?

Removing the call to BufferedImage.getScaledInstance, I tried to put the result of Robot.createScreenCapture directly onto the clipboard. Preview's "New From Clipboard" showed a mangled image, as if it believed the image's width was greater than the amount pixel data per line. I'm sure you've all seen that kind of diagonal smear before.

Reverting, then removing the call to Robot.createScreenCapture and instead scaling an image loaded from disk showed me a similarly-mangled image in Preview.

So: two more Apple Java bugs, then?

1. Putting an Image that came from Image.getScaledInstance on the clipboard results in mangled images on paste (seemingly because of incorrect width information), regardless of where the original Image came from.

2. Putting an Image that came from Robot.createScreenCapture on the clipboard causes a hang in Apple's code.

Reported as Apple 4252224 (which I can't link to, because Apple aren't as enlightened as Sun).

Martin points out that Win32 hangs in a very similar place:

2005-09-11T19:41:55.845-0700 unknown: (hang #1) event dispatch thread stuck processing event for 1015 ms:
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Unknown Source)
sun.awt.image.ImageRepresentation.reconstruct(Unknown Source)
sun.awt.datatransfer.DataTransferer.imageToStandardBytes(Unknown Source)
sun.awt.windows.WDataTransferer.imageToPlatformBytes(Unknown Source)
sun.awt.datatransfer.DataTransferer.translateTransferable(Unknown Source)
sun.awt.windows.WDataTransferer.translateTransferable(Unknown Source)
sun.awt.windows.WClipboard.setContentsNative(Unknown Source)
sun.awt.datatransfer.SunClipboard.setContents(Unknown Source)

He also claims that Linux doesn't hang, but doesn't convince the GIMP that it's put an image on the clipboard.

I can't wait for my Ultra 20 to arrive so I can see what Solaris does!

At this point it looks like the hang (as opposed to the mangled image) is actually a cross-platform Sun bug. Searching for "sun.awt.image.ImageRepresentation.reconstruct" on the bug parade turns up a fair cluster of related bugs, mostly relating to animated GIF images (such as Sun 6176679).

Drawing the original image into a new BufferedImage and then putting that on the clipboard seems to avoid the hang:

public static void copyImageToClipboard(Image image) {
// Work around a Sun bug that causes a hang in "sun.awt.image.ImageRepresentation.reconstruct".
new javax.swing.ImageIcon(image); // Force load.
BufferedImage newImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
newImage.createGraphics().drawImage(image, 0, 0, null);
image = newImage;

ImageSelection imageSelection = new ImageSelection(image);
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemClipboard().setContents(imageSelection, null);

On Win32, you can paste the resulting image into pbrush.exe no problem. On Mac OS, Preview shows a mangled image. On Linux, GIMP offers to paste but silently fails to paste anything.

2005-10-07: Apple's latest Java 5 "preview" (they continue to lag years behind the rest of the world) fixes the image-mangling.