I created lots of working examples with translucency, only to find out another day, that minimal change, seemingly unrelated, renders whole code 'not working'. I finally managed to write 'minimal' ridiculous example of this behavior.
'Short' summary: in constructor ProblematicTranslucentyInteractiveExample you see 2 variants. Both variants uses SAME method to create JFrame. When you click inside that frame, it will start dividing whole area into 4 squares, and fill that area with color.
Variant A shows 2 such frames aside, one without perpixel translucency, and another one with translucency. Both frames work perfectly! Now comment it and try other variant.
Variant B shows just one frame, the one with translucency enabled. Same method to create frame as in variant A, yet it does not work. When I run it, alfa blends incorrectly, and also different X position is yielding differet graphics errors during invalid alfa blending. How bizarre is that? EDIT: just to stress the punch line: in this example creation of translucent frame works only if I create nontranslucent window with it. If I create only translucent window, it does not work.
Run on fedora 26, xfce spin. Tested with compton compositor, xfce compositor or without any compositor (ok, translucency won't work in such case, but incorrect rendering will be present). Tested several Oracle JDK vestions, also OpenJDK version. All of that has same result.
Any ideas??
EDIT: when tested on windows, both variants work, however while on linux whole frame is always visible, on windows only the portion being repainted last is visible. This is wildly inconsistent! Is this normal? Or do I have some error here?
Code in question:
package nomouse.transparencytest;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import nomouse.provider.LoggerProvider;
import org.apache.logging.log4j.Logger;
public class ProblematicTranslucentyInteractiveExample {
public static void main(String[] args) {
new ProblematicTranslucentyInteractiveExample();
}
public ProblematicTranslucentyInteractiveExample() {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
//------ VARIANT A --------
int rectangleWidth = 800;
showFrame(0, rectangleWidth, false);
showFrame(50 + rectangleWidth, rectangleWidth, true);
//------ VARIANT B --------
// showFrame(0 , 800, true);
});
}
private void showFrame(int x, int rectangleWidth, boolean withAlfa) {
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setContentPane(createTestComp(() -> {
frame.setVisible(false);
frame.dispose();
}, withAlfa, rectangleWidth));
frame.pack();
frame.setLocation(x,0);
frame.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
frame.setVisible(false);
frame.dispose();
}
});
frame.setVisible(true);
}
private TestComponent createTestComp(Runnable exit, boolean withAlfa, int rectangleWidth) {
TestComponent testComponent =
new TestComponent(LoggerProvider.getLogger(ProblematicTranslucentyInteractiveExample.class), exit, withAlfa);
testComponent.setPreferredSize(new Dimension(rectangleWidth,rectangleWidth));
return testComponent;
}
public static class TestComponent extends JPanel {
private final Logger logger;
private int i = 1;
private final Color black;
private final Color red;
private final Color green;
private final Color blue;
private final Color yellow;
public TestComponent(Logger logger, Runnable exit, boolean withAlfa) {
this.logger = logger;
this.setOpaque(false);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
switch (e.getButton()) {
case MouseEvent.BUTTON1: i++;repaint();break;
case MouseEvent.BUTTON3: i=1;repaint();break;
case MouseEvent.BUTTON2: exit.run();break;
}
}
});
black = withAlfa ? new Color(0, 0, 0, 100) : new Color(0, 0, 0);
red = withAlfa ? new Color(255, 0, 0, 100) : new Color(255, 0, 0);
green = withAlfa ? new Color(0, 255, 0, 100) : new Color(0, 255, 0);
blue = withAlfa ? new Color(0, 0, 255, 100) : new Color(0, 0, 255);
yellow = withAlfa ? new Color(255, 255, 0, 100) : new Color(255, 255, 0);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
logger.debug("painting starts.");
Graphics2D g2d = (Graphics2D) g.create();
// g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
// g2d.setComposite(AlphaComposite.Src.derive(.25f));
g2d.setComposite(AlphaComposite.Src);
int width = getWidth();
int height = getHeight();
int halfWidth = width / 2;
int halfHeight = height / 2;
int quarterWidth = width / 4;
int quarterHeight = height / 4;
int oneEightWidth = width / 8;
int oneEightHeight = height / 8;
switch (i) {
case 1: fillRectangle(g2d, 0, 0, width, height, black);break;
case 2: fillRectangle(g2d, 0, 0, halfWidth, halfHeight, red);break;
case 3: fillRectangle(g2d, halfWidth, 0, halfWidth, halfHeight, green);break;
case 4: fillRectangle(g2d, 0, halfHeight, halfWidth, halfHeight, blue);break;
case 5: fillRectangle(g2d, halfWidth, halfHeight, halfWidth, halfHeight, yellow);break;
case 6:
fillRectangle(g2d, 0, 0, halfWidth, halfHeight, black);
fillRectangle(g2d, 0, 0, quarterWidth, quarterHeight, red);
break;
case 7: fillRectangle(g2d, quarterWidth, 0, quarterWidth, quarterHeight, green);break;
case 8: fillRectangle(g2d, 0, quarterHeight, quarterWidth, quarterHeight, blue);break;
case 9: fillRectangle(g2d, quarterWidth, quarterHeight, quarterWidth, quarterHeight, yellow);break;
case 10:
fillRectangle(g2d, 0, 0, quarterWidth, quarterHeight, black);
fillRectangle(g2d, 0, 0, oneEightWidth, oneEightHeight, red);
break;
case 11: fillRectangle(g2d, oneEightWidth, 0, oneEightWidth, oneEightHeight, green);break;
case 12: fillRectangle(g2d, 0, oneEightHeight, oneEightWidth, oneEightHeight, blue);break;
case 13: fillRectangle(g2d, oneEightWidth, oneEightHeight, oneEightWidth, oneEightHeight, yellow);break;
}
g2d.dispose();
logger.debug("painting done.");
}
private void fillRectangle(Graphics2D g, int x, int y, int width, int height, Color color) {
g.setColor(color);
logger.debug(String.format("Filling rect: %sx%s-%sx%s with color %s",
x, y, width, height, color));
g.fillRect(x, y, width, height);
g.setColor(Color.cyan);
g.setStroke(new BasicStroke(5));
g.drawRect(x, y, width, height);
}
}
}
screencast of behavior: two frames