ADDITIONAL SYSTEM INFORMATION :
Tested with JavaFX 16, JDK 16 on Ubuntu 20.04 and Fedora 34.
A DESCRIPTION OF THE PROBLEM :
When setting an image as drag view during drag and drop it will be shown with incorrect colors. The red and blue color channels will be swapped.
Does not happen in JavaFX 13, happens in 14 and later. Most likely related to this change: https://bugs.openjdk.java.net/browse/JDK-8225571 - "Port Linux glass drag source (DND) to use gtk instead of gdk".
REGRESSION : Last worked in version 13
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a draggable control.
2. At drag start, set an image as drag view.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Drag view image displayed with correct colors.
ACTUAL -
Drag view image displayed with incorrect colors, red and blue channels are swapped.
---------- BEGIN SOURCE ----------
package com.example.bug;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.awt.image.BufferedImage;
/**
* Example code for JavaFX bug "GTK3 drag view image swaps red and blue color channels".
*
* Does not happen in JavaFX 13, happens in 14 and later.
* Most likely related to this change: https://bugs.openjdk.java.net/browse/JDK-8225571 - "Port Linux glass drag source
* (DND) to use gtk instead of gdk".
*
* Workaround:
* #1: Swap red and blue color channels before setting drag view image, see code.
* #2: Use JavaFX 13.
* #3: Use GTK2 at runtime with JVM argument "-Djdk.gtk.version=2".
*
* Tested with JavaFX 16, JDK 16 on Ubuntu 20.04 and Fedora 34.
*/
public class Main extends Application {
Image image = createImage(240, 240);
CheckBox workaround = new CheckBox("Workaround");
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
ImageView imageView = new ImageView(image);
imageView.setOnDragDetected(event -> {
ClipboardContent content = new ClipboardContent();
content.putImage(image);
Dragboard dragboard = imageView.startDragAndDrop(TransferMode.ANY);
dragboard.setContent(content);
if (workaround.isSelected()) {
dragboard.setDragView(swapRedAndBlueColorChannels(image));
} else {
dragboard.setDragView(image);
}
});
VBox vBox = new VBox(new Label("Drag image"), imageView, workaround);
vBox.setSpacing(5.0);
vBox.setAlignment(Pos.CENTER);
stage.setScene(new Scene(vBox, 480, 480));
stage.setTitle("GTK3 Drag View Image Wrong Colors");
stage.show();
}
private static Image createImage(int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
if (x < width * 0.33) {
image.setRGB(x, y, 0xFF0000);
} else if (x < width * 0.66) {
image.setRGB(x, y, 0x00FF00);
} else {
image.setRGB(x, y, 0x0000FF);
}
}
}
return SwingFXUtils.toFXImage(image, null);
}
private static Image swapRedAndBlueColorChannels(Image image) {
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
int p = bufferedImage.getRGB(x, y);
int a = (p>>24) & 0xFF;
int r = (p>>16) & 0xFF;
int g = (p>>8) & 0xFF;
int b = p & 0xFF;
p = (a<<24) | (b<<16) | (g<<8) | r; // Swap red and blue values.
bufferedImage.setRGB(x, y, p);
}
}
return SwingFXUtils.toFXImage(bufferedImage, null);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
#1: Swap red and blue color channels before setting drag view image, see code.
#2: Use JavaFX 13.
#3: Use GTK2 at runtime with JVM argument "-Djdk.gtk.version=2".
FREQUENCY : always