-
Bug
-
Resolution: Unresolved
-
P3
-
8, 11, 17, 19, 20, 21
-
x86_64
-
os_x
ADDITIONAL SYSTEM INFORMATION :
Observed on a MacBook Air, M2 2022
Mac OS 13.2.1
OpenJDK 19.0.2
A DESCRIPTION OF THE PROBLEM :
When we repaint a JComponent in a translucent window: the window can flicker. The severity of the flicker can vary depending on the rate of the repaint.
Here is a video of the attached test case launched using OpenJDK 19:
<short clipping link>
The flickering is sometimes constant.
Here is a video of the same test case launched from OpenJDK 18.0.1:
<short clipping link>
The flickering is reduced, but still very conspicuous.
I believe this is the same root cause as this ticket:
https://bugs.openjdk.org/browse/JDK-8209329
... but the way the flicker manifests is different in this ticket. I'd argue flickering while resizing is a tolerable (if annoying) bug, but this is more severe: the user is not doing anything and the window can still flicker.
I agree with that author that the flickering stems from Window#paint(Graphics) calling `gg.fillRect(0, 0, getWidth(), getHeight());`
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached java source code; see if the throbber flickers.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The throbber should animate and the window background color should remain constant.
ACTUAL -
The rectangle where the throbber animates often flickers so it is completely transparent. (You can see whatever windows are underneath the translucent window.)
---------- BEGIN SOURCE ----------
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PanelUI;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.*;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.*;
/**
* This is a simple translucent JDialog that demonstrates flickering on Mac and a potential work-around.
* <p>
* Instructions are shown in the app.
* </p>
*/
public class FlickerTest extends JDialog {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
FlickerTest t = new FlickerTest();
t.pack();
t.setLocationRelativeTo(null);
t.setVisible(true);
}
});
}
JTextPane instructions = new JTextPane();
JCheckBox applyWorkaroundCheckbox = new JCheckBox("Apply Work-Around");
public FlickerTest() {
instructions.setText("Instructions for Mac\nIn OpenJDK 18: move the mouse in a circle in the middle of this throbber.\nIn OpenJDK 19: Do nothing.\n\nIf the window flickers: this test fails\nIf the window does not flicker: this test passes.");
instructions.setBorder(new EmptyBorder(10,10,10,10));
instructions.setOpaque(false);
instructions.setEditable(false);
setUndecorated(true);
JPanel p = new JPanel();
p.setOpaque(false);
p.setBorder(new EmptyBorder(10,10,10,10));
p.setUI(new PanelUI() {
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(220, 180, 0, 200));
g2.fill(new RoundRectangle2D.Double(0,0,c.getWidth(),c.getHeight(),20,20));
}
});
p.setLayout(new BorderLayout());
p.add(instructions, BorderLayout.NORTH);
p.add(new JThrobber(), BorderLayout.CENTER);
p.add(applyWorkaroundCheckbox, BorderLayout.SOUTH);
getContentPane().add(p);
setBackground(new Color(0,0,0,0));
}
public Graphics getGraphics() {
Graphics2D superG = (Graphics2D) super.getGraphics();
if (applyWorkaroundCheckbox.isSelected()) {
return new IgnoreFillRectGraphics2D( superG );
}
return superG;
}
}
/**
* This is a Graphics2D that ignores all calls to {@link #fillRect(int, int, int, int)}
*/
class IgnoreFillRectGraphics2D extends DelegateGraphics2D {
public IgnoreFillRectGraphics2D(Graphics2D delegate) {
super(delegate);
}
@Override
public Graphics create() {
return new IgnoreFillRectGraphics2D((Graphics2D) delegate.create());
}
@Override
public void fillRect(int x, int y, int width, int height) {
// intentionally empty
}
}
/**
* Also known as a "spinny widget" or an "indeterminate progress indicator": this is used to indicate
* the application is doing something and the user should wait for the task to complete.
*
* This FlickerTest demo is designed so this throbber repaints ridiculously fast to showcase the
* flickering bug; in a real-world case the repaints should be controlled by some sort of timer.
*/
class JThrobber extends JComponent {
public JThrobber() {
setPreferredSize(new Dimension(300, 300));
setUI(new ThrobberUI());
}
}
class ThrobberUI extends ComponentUI {
// copied and pasted from another class
public static void paint(Graphics2D g, float fraction, Color foreground,
int centerX, int centerY, int r1, int r2, float strokeWidth) {
if (fraction < 0)
throw new IllegalArgumentException(
"fraction (" + fraction + ") must be within [0, 1]");
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int i = (int) (fraction * 12);
int red = foreground.getRed();
int green = foreground.getGreen();
int blue = foreground.getBlue();
Color[] colors = new Color[] { new Color(red, green, blue, 255),
new Color(red, green, blue, 240),
new Color(red, green, blue, 225),
new Color(red, green, blue, 200),
new Color(red, green, blue, 160),
new Color(red, green, blue, 130),
new Color(red, green, blue, 115),
new Color(red, green, blue, 100),
new Color(red, green, blue, 90),
new Color(red, green, blue, 80),
new Color(red, green, blue, 70),
new Color(red, green, blue, 60)
};
g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_BEVEL));
double theta;
Line2D line = new Line2D.Float();
for (int a = 0; a < colors.length; a++) {
g.setColor(colors[(i + a) % colors.length]);
theta = -((double) a) / (colors.length) * Math.PI * 2;
line.setLine(centerX + r1 * Math.cos(theta),
centerY + r1 * Math.sin(theta),
centerX + r2 * Math.cos(theta),
centerY + r2 * Math.sin(theta));
g.draw(line);
}
}
@Override
public void paint(Graphics g, JComponent c) {
long elapsed = System.currentTimeMillis() % 1000;
float f = ((float) elapsed) / 1000f;
int r1 = (int)(Math.min(c.getWidth(), c.getHeight())/2 * .9);
int r2 = r1 * 4 / 8;
Graphics2D g2 = (Graphics2D)g;
float strokeWidth = r1 / 6f;
paint( g2, f, Color.gray, c.getWidth() / 2, c.getHeight() /2, r1, r2, strokeWidth);
c.repaint();
}
}
/**
* This delegates every method to its {@link #delegate} field. Subclasses must implement their own {@link #create()}
* that returns the appropriate subclass.
* <p>
* (This is a weird/heavy-handed approach to make the Graphics2D act a little more like an interface than a class.)
* </p>
*/
abstract class DelegateGraphics2D extends Graphics2D {
protected final Graphics2D delegate;
public DelegateGraphics2D(Graphics2D delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public void draw3DRect(int x, int y, int width, int height, boolean raised) {
delegate.draw3DRect(x, y, width, height, raised);
}
@Override
public void fill3DRect(int x, int y, int width, int height, boolean raised) {
delegate.fill3DRect(x, y, width, height, raised);
}
@Override
public FontMetrics getFontMetrics() {
return delegate.getFontMetrics();
}
@Override
public void drawRect(int x, int y, int width, int height) {
delegate.drawRect(x, y, width, height);
}
@Override
public void drawPolygon(Polygon p) {
delegate.drawPolygon(p);
}
@Override
public void fillPolygon(Polygon p) {
delegate.fillPolygon(p);
}
@Override
public void drawChars(char[] data, int offset, int length, int x, int y) {
delegate.drawChars(data, offset, length, x, y);
}
@Override
public void drawBytes(byte[] data, int offset, int length, int x, int y) {
delegate.drawBytes(data, offset, length, x, y);
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public Rectangle getClipRect() {
return delegate.getClipRect();
}
@Override
public boolean hitClip(int x, int y, int width, int height) {
return delegate.hitClip(x, y, width, height);
}
@Override
public Rectangle getClipBounds(Rectangle r) {
return delegate.getClipBounds(r);
}
@Override
public void draw(Shape s) {
delegate.draw(s);
}
@Override
public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
return delegate.drawImage(img, xform, obs);
}
@Override
public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
delegate.drawImage(img, op, x, y);
}
@Override
public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
delegate.drawRenderedImage(img, xform);
}
@Override
public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
delegate.drawRenderableImage(img, xform);
}
@Override
public void drawString(String str, int x, int y) {
delegate.drawString(str, x, y);
}
@Override
public void drawString(String str, float x, float y) {
delegate.drawString(str, x, y);
}
@Override
public void drawString(AttributedCharacterIterator iterator, int x, int y) {
delegate.drawString(iterator, x, y);
}
@Override
public void drawString(AttributedCharacterIterator iterator, float x, float y) {
delegate.drawString(iterator, x, y);
}
@Override
public void drawGlyphVector(GlyphVector g, float x, float y) {
delegate.drawGlyphVector(g, x, y);
}
@Override
public void fill(Shape s) {
delegate.fill(s);
}
@Override
public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
return delegate.hit(rect, s, onStroke);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return delegate.getDeviceConfiguration();
}
@Override
public void setComposite(Composite comp) {
delegate.setComposite(comp);
}
@Override
public void setPaint(Paint paint) {
delegate.setPaint(paint);
}
@Override
public void setStroke(Stroke s) {
delegate.setStroke(s);
}
@Override
public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
delegate.setRenderingHint(hintKey, hintValue);
}
@Override
public Object getRenderingHint(RenderingHints.Key hintKey) {
return delegate.getRenderingHint(hintKey);
}
@Override
public void setRenderingHints(Map<?, ?> hints) {
delegate.setRenderingHints(hints);
}
@Override
public void addRenderingHints(Map<?, ?> hints) {
delegate.addRenderingHints(hints);
}
@Override
public RenderingHints getRenderingHints() {
return delegate.getRenderingHints();
}
@Override
public void translate(int x, int y) {
delegate.translate(x, y);
}
@Override
public void translate(double tx, double ty) {
delegate.translate(tx, ty);
}
@Override
public void rotate(double theta) {
delegate.rotate(theta);
}
@Override
public void rotate(double theta, double x, double y) {
delegate.rotate(theta, x, y);
}
@Override
public void scale(double sx, double sy) {
delegate.scale(sx, sy);
}
@Override
public void shear(double shx, double shy) {
delegate.shear(shx, shy);
}
@Override
public void transform(AffineTransform Tx) {
delegate.transform(Tx);
}
@Override
public void setTransform(AffineTransform Tx) {
delegate.setTransform(Tx);
}
@Override
public AffineTransform getTransform() {
return delegate.getTransform();
}
@Override
public Paint getPaint() {
return delegate.getPaint();
}
@Override
public Composite getComposite() {
return delegate.getComposite();
}
@Override
public void setBackground(Color color) {
delegate.setBackground(color);
}
@Override
public Color getBackground() {
return delegate.getBackground();
}
@Override
public Stroke getStroke() {
return delegate.getStroke();
}
@Override
public void clip(Shape s) {
delegate.clip(s);
}
@Override
public FontRenderContext getFontRenderContext() {
return delegate.getFontRenderContext();
}
@Override
public Color getColor() {
return delegate.getColor();
}
@Override
public void setColor(Color c) {
delegate.setColor(c);
}
@Override
public void setPaintMode() {
delegate.setPaintMode();
}
@Override
public void setXORMode(Color c1) {
delegate.setXORMode(c1);
}
@Override
public Font getFont() {
return delegate.getFont();
}
@Override
public void setFont(Font font) {
delegate.setFont(font);
}
@Override
public FontMetrics getFontMetrics(Font f) {
return delegate.getFontMetrics();
}
@Override
public Rectangle getClipBounds() {
return delegate.getClipBounds();
}
@Override
public void clipRect(int x, int y, int width, int height) {
delegate.clipRect(x, y, width, height);
}
@Override
public void setClip(int x, int y, int width, int height) {
delegate.setClip(x, y, width, height);
}
@Override
public Shape getClip() {
return delegate.getClip();
}
@Override
public void setClip(Shape clip) {
delegate.setClip(clip);
}
@Override
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
delegate.copyArea(x, y, width, height, dx, dy);
}
@Override
public void drawLine(int x1, int y1, int x2, int y2) {
delegate.drawLine(x1, y1, x2, y2);
}
@Override
public void fillRect(int x, int y, int width, int height) {
delegate.fillRect(x, y, width, height);
}
@Override
public void clearRect(int x, int y, int width, int height) {
delegate.clearRect(x, y, width, height);
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
delegate.drawRoundRect(x,y,width,height, arcWidth,arcHeight);
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
delegate.fillRoundRect(x,y,width,height, arcWidth,arcHeight);
}
@Override
public void drawOval(int x, int y, int width, int height) {
delegate.drawOval(x,y,width,height);
}
@Override
public void fillOval(int x, int y, int width, int height) {
delegate.fillOval(x,y,width,height);
}
@Override
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
delegate.drawArc(x,y,width,height,startAngle,arcAngle);
}
@Override
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
delegate.fillArc(x,y,width,height,startAngle,arcAngle);
}
@Override
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
delegate.drawPolyline(xPoints, yPoints, nPoints);
}
@Override
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
delegate.drawPolygon(xPoints, yPoints, nPoints);
}
@Override
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
return delegate.drawImage(img, x, y, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
return delegate.drawImage(img, x, y, width, height, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, x, y, bgcolor, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, x, y, width, height, bgcolor, observer);
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer);
}
@Override
public void dispose() {
delegate.dispose();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The source code includes a checkbox that demonstrates the following work-around:
We can override our Dialog's getGraphics() method so it resembles:
```
public Graphics getGraphics() {
Graphics2D superG = (Graphics2D) super.getGraphics();
if (applyWorkaroundCheckbox.isSelected()) {
return new IgnoreFillRectGraphics2D( superG );
}
return superG;
}
```
IgnoreFillRectGraphics2D is a Graphics2D that ignores all calls to `fillRect(..)`.
My (imperfect) understanding is: Swing's RepaintManager handles double buffering to an offscreen image for us, so all we really need to do with the Graphics2D in Window.paint(Graphics) is impose our buffered offscreen image. So we never need to call `fillRect(..)`, and for whatever reason calling it actually causes harm. (The call to `fillRect(..)` may (?) be important in other contexts; the observation that it's unnecessary is limited to repaints coming from the RepaintManager class.)
FREQUENCY : always
Observed on a MacBook Air, M2 2022
Mac OS 13.2.1
OpenJDK 19.0.2
A DESCRIPTION OF THE PROBLEM :
When we repaint a JComponent in a translucent window: the window can flicker. The severity of the flicker can vary depending on the rate of the repaint.
Here is a video of the attached test case launched using OpenJDK 19:
<short clipping link>
The flickering is sometimes constant.
Here is a video of the same test case launched from OpenJDK 18.0.1:
<short clipping link>
The flickering is reduced, but still very conspicuous.
I believe this is the same root cause as this ticket:
https://bugs.openjdk.org/browse/JDK-8209329
... but the way the flicker manifests is different in this ticket. I'd argue flickering while resizing is a tolerable (if annoying) bug, but this is more severe: the user is not doing anything and the window can still flicker.
I agree with that author that the flickering stems from Window#paint(Graphics) calling `gg.fillRect(0, 0, getWidth(), getHeight());`
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached java source code; see if the throbber flickers.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The throbber should animate and the window background color should remain constant.
ACTUAL -
The rectangle where the throbber animates often flickers so it is completely transparent. (You can see whatever windows are underneath the translucent window.)
---------- BEGIN SOURCE ----------
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PanelUI;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.*;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.*;
/**
* This is a simple translucent JDialog that demonstrates flickering on Mac and a potential work-around.
* <p>
* Instructions are shown in the app.
* </p>
*/
public class FlickerTest extends JDialog {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
FlickerTest t = new FlickerTest();
t.pack();
t.setLocationRelativeTo(null);
t.setVisible(true);
}
});
}
JTextPane instructions = new JTextPane();
JCheckBox applyWorkaroundCheckbox = new JCheckBox("Apply Work-Around");
public FlickerTest() {
instructions.setText("Instructions for Mac\nIn OpenJDK 18: move the mouse in a circle in the middle of this throbber.\nIn OpenJDK 19: Do nothing.\n\nIf the window flickers: this test fails\nIf the window does not flicker: this test passes.");
instructions.setBorder(new EmptyBorder(10,10,10,10));
instructions.setOpaque(false);
instructions.setEditable(false);
setUndecorated(true);
JPanel p = new JPanel();
p.setOpaque(false);
p.setBorder(new EmptyBorder(10,10,10,10));
p.setUI(new PanelUI() {
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(220, 180, 0, 200));
g2.fill(new RoundRectangle2D.Double(0,0,c.getWidth(),c.getHeight(),20,20));
}
});
p.setLayout(new BorderLayout());
p.add(instructions, BorderLayout.NORTH);
p.add(new JThrobber(), BorderLayout.CENTER);
p.add(applyWorkaroundCheckbox, BorderLayout.SOUTH);
getContentPane().add(p);
setBackground(new Color(0,0,0,0));
}
public Graphics getGraphics() {
Graphics2D superG = (Graphics2D) super.getGraphics();
if (applyWorkaroundCheckbox.isSelected()) {
return new IgnoreFillRectGraphics2D( superG );
}
return superG;
}
}
/**
* This is a Graphics2D that ignores all calls to {@link #fillRect(int, int, int, int)}
*/
class IgnoreFillRectGraphics2D extends DelegateGraphics2D {
public IgnoreFillRectGraphics2D(Graphics2D delegate) {
super(delegate);
}
@Override
public Graphics create() {
return new IgnoreFillRectGraphics2D((Graphics2D) delegate.create());
}
@Override
public void fillRect(int x, int y, int width, int height) {
// intentionally empty
}
}
/**
* Also known as a "spinny widget" or an "indeterminate progress indicator": this is used to indicate
* the application is doing something and the user should wait for the task to complete.
*
* This FlickerTest demo is designed so this throbber repaints ridiculously fast to showcase the
* flickering bug; in a real-world case the repaints should be controlled by some sort of timer.
*/
class JThrobber extends JComponent {
public JThrobber() {
setPreferredSize(new Dimension(300, 300));
setUI(new ThrobberUI());
}
}
class ThrobberUI extends ComponentUI {
// copied and pasted from another class
public static void paint(Graphics2D g, float fraction, Color foreground,
int centerX, int centerY, int r1, int r2, float strokeWidth) {
if (fraction < 0)
throw new IllegalArgumentException(
"fraction (" + fraction + ") must be within [0, 1]");
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int i = (int) (fraction * 12);
int red = foreground.getRed();
int green = foreground.getGreen();
int blue = foreground.getBlue();
Color[] colors = new Color[] { new Color(red, green, blue, 255),
new Color(red, green, blue, 240),
new Color(red, green, blue, 225),
new Color(red, green, blue, 200),
new Color(red, green, blue, 160),
new Color(red, green, blue, 130),
new Color(red, green, blue, 115),
new Color(red, green, blue, 100),
new Color(red, green, blue, 90),
new Color(red, green, blue, 80),
new Color(red, green, blue, 70),
new Color(red, green, blue, 60)
};
g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_BEVEL));
double theta;
Line2D line = new Line2D.Float();
for (int a = 0; a < colors.length; a++) {
g.setColor(colors[(i + a) % colors.length]);
theta = -((double) a) / (colors.length) * Math.PI * 2;
line.setLine(centerX + r1 * Math.cos(theta),
centerY + r1 * Math.sin(theta),
centerX + r2 * Math.cos(theta),
centerY + r2 * Math.sin(theta));
g.draw(line);
}
}
@Override
public void paint(Graphics g, JComponent c) {
long elapsed = System.currentTimeMillis() % 1000;
float f = ((float) elapsed) / 1000f;
int r1 = (int)(Math.min(c.getWidth(), c.getHeight())/2 * .9);
int r2 = r1 * 4 / 8;
Graphics2D g2 = (Graphics2D)g;
float strokeWidth = r1 / 6f;
paint( g2, f, Color.gray, c.getWidth() / 2, c.getHeight() /2, r1, r2, strokeWidth);
c.repaint();
}
}
/**
* This delegates every method to its {@link #delegate} field. Subclasses must implement their own {@link #create()}
* that returns the appropriate subclass.
* <p>
* (This is a weird/heavy-handed approach to make the Graphics2D act a little more like an interface than a class.)
* </p>
*/
abstract class DelegateGraphics2D extends Graphics2D {
protected final Graphics2D delegate;
public DelegateGraphics2D(Graphics2D delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public void draw3DRect(int x, int y, int width, int height, boolean raised) {
delegate.draw3DRect(x, y, width, height, raised);
}
@Override
public void fill3DRect(int x, int y, int width, int height, boolean raised) {
delegate.fill3DRect(x, y, width, height, raised);
}
@Override
public FontMetrics getFontMetrics() {
return delegate.getFontMetrics();
}
@Override
public void drawRect(int x, int y, int width, int height) {
delegate.drawRect(x, y, width, height);
}
@Override
public void drawPolygon(Polygon p) {
delegate.drawPolygon(p);
}
@Override
public void fillPolygon(Polygon p) {
delegate.fillPolygon(p);
}
@Override
public void drawChars(char[] data, int offset, int length, int x, int y) {
delegate.drawChars(data, offset, length, x, y);
}
@Override
public void drawBytes(byte[] data, int offset, int length, int x, int y) {
delegate.drawBytes(data, offset, length, x, y);
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public Rectangle getClipRect() {
return delegate.getClipRect();
}
@Override
public boolean hitClip(int x, int y, int width, int height) {
return delegate.hitClip(x, y, width, height);
}
@Override
public Rectangle getClipBounds(Rectangle r) {
return delegate.getClipBounds(r);
}
@Override
public void draw(Shape s) {
delegate.draw(s);
}
@Override
public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
return delegate.drawImage(img, xform, obs);
}
@Override
public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
delegate.drawImage(img, op, x, y);
}
@Override
public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
delegate.drawRenderedImage(img, xform);
}
@Override
public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
delegate.drawRenderableImage(img, xform);
}
@Override
public void drawString(String str, int x, int y) {
delegate.drawString(str, x, y);
}
@Override
public void drawString(String str, float x, float y) {
delegate.drawString(str, x, y);
}
@Override
public void drawString(AttributedCharacterIterator iterator, int x, int y) {
delegate.drawString(iterator, x, y);
}
@Override
public void drawString(AttributedCharacterIterator iterator, float x, float y) {
delegate.drawString(iterator, x, y);
}
@Override
public void drawGlyphVector(GlyphVector g, float x, float y) {
delegate.drawGlyphVector(g, x, y);
}
@Override
public void fill(Shape s) {
delegate.fill(s);
}
@Override
public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
return delegate.hit(rect, s, onStroke);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return delegate.getDeviceConfiguration();
}
@Override
public void setComposite(Composite comp) {
delegate.setComposite(comp);
}
@Override
public void setPaint(Paint paint) {
delegate.setPaint(paint);
}
@Override
public void setStroke(Stroke s) {
delegate.setStroke(s);
}
@Override
public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
delegate.setRenderingHint(hintKey, hintValue);
}
@Override
public Object getRenderingHint(RenderingHints.Key hintKey) {
return delegate.getRenderingHint(hintKey);
}
@Override
public void setRenderingHints(Map<?, ?> hints) {
delegate.setRenderingHints(hints);
}
@Override
public void addRenderingHints(Map<?, ?> hints) {
delegate.addRenderingHints(hints);
}
@Override
public RenderingHints getRenderingHints() {
return delegate.getRenderingHints();
}
@Override
public void translate(int x, int y) {
delegate.translate(x, y);
}
@Override
public void translate(double tx, double ty) {
delegate.translate(tx, ty);
}
@Override
public void rotate(double theta) {
delegate.rotate(theta);
}
@Override
public void rotate(double theta, double x, double y) {
delegate.rotate(theta, x, y);
}
@Override
public void scale(double sx, double sy) {
delegate.scale(sx, sy);
}
@Override
public void shear(double shx, double shy) {
delegate.shear(shx, shy);
}
@Override
public void transform(AffineTransform Tx) {
delegate.transform(Tx);
}
@Override
public void setTransform(AffineTransform Tx) {
delegate.setTransform(Tx);
}
@Override
public AffineTransform getTransform() {
return delegate.getTransform();
}
@Override
public Paint getPaint() {
return delegate.getPaint();
}
@Override
public Composite getComposite() {
return delegate.getComposite();
}
@Override
public void setBackground(Color color) {
delegate.setBackground(color);
}
@Override
public Color getBackground() {
return delegate.getBackground();
}
@Override
public Stroke getStroke() {
return delegate.getStroke();
}
@Override
public void clip(Shape s) {
delegate.clip(s);
}
@Override
public FontRenderContext getFontRenderContext() {
return delegate.getFontRenderContext();
}
@Override
public Color getColor() {
return delegate.getColor();
}
@Override
public void setColor(Color c) {
delegate.setColor(c);
}
@Override
public void setPaintMode() {
delegate.setPaintMode();
}
@Override
public void setXORMode(Color c1) {
delegate.setXORMode(c1);
}
@Override
public Font getFont() {
return delegate.getFont();
}
@Override
public void setFont(Font font) {
delegate.setFont(font);
}
@Override
public FontMetrics getFontMetrics(Font f) {
return delegate.getFontMetrics();
}
@Override
public Rectangle getClipBounds() {
return delegate.getClipBounds();
}
@Override
public void clipRect(int x, int y, int width, int height) {
delegate.clipRect(x, y, width, height);
}
@Override
public void setClip(int x, int y, int width, int height) {
delegate.setClip(x, y, width, height);
}
@Override
public Shape getClip() {
return delegate.getClip();
}
@Override
public void setClip(Shape clip) {
delegate.setClip(clip);
}
@Override
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
delegate.copyArea(x, y, width, height, dx, dy);
}
@Override
public void drawLine(int x1, int y1, int x2, int y2) {
delegate.drawLine(x1, y1, x2, y2);
}
@Override
public void fillRect(int x, int y, int width, int height) {
delegate.fillRect(x, y, width, height);
}
@Override
public void clearRect(int x, int y, int width, int height) {
delegate.clearRect(x, y, width, height);
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
delegate.drawRoundRect(x,y,width,height, arcWidth,arcHeight);
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
delegate.fillRoundRect(x,y,width,height, arcWidth,arcHeight);
}
@Override
public void drawOval(int x, int y, int width, int height) {
delegate.drawOval(x,y,width,height);
}
@Override
public void fillOval(int x, int y, int width, int height) {
delegate.fillOval(x,y,width,height);
}
@Override
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
delegate.drawArc(x,y,width,height,startAngle,arcAngle);
}
@Override
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
delegate.fillArc(x,y,width,height,startAngle,arcAngle);
}
@Override
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
delegate.drawPolyline(xPoints, yPoints, nPoints);
}
@Override
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
delegate.drawPolygon(xPoints, yPoints, nPoints);
}
@Override
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
return delegate.drawImage(img, x, y, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
return delegate.drawImage(img, x, y, width, height, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, x, y, bgcolor, observer);
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, x, y, width, height, bgcolor, observer);
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer);
}
@Override
public void dispose() {
delegate.dispose();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The source code includes a checkbox that demonstrates the following work-around:
We can override our Dialog's getGraphics() method so it resembles:
```
public Graphics getGraphics() {
Graphics2D superG = (Graphics2D) super.getGraphics();
if (applyWorkaroundCheckbox.isSelected()) {
return new IgnoreFillRectGraphics2D( superG );
}
return superG;
}
```
IgnoreFillRectGraphics2D is a Graphics2D that ignores all calls to `fillRect(..)`.
My (imperfect) understanding is: Swing's RepaintManager handles double buffering to an offscreen image for us, so all we really need to do with the Graphics2D in Window.paint(Graphics) is impose our buffered offscreen image. So we never need to call `fillRect(..)`, and for whatever reason calling it actually causes harm. (The call to `fillRect(..)` may (?) be important in other contexts; the observation that it's unnecessary is limited to repaints coming from the RepaintManager class.)
FREQUENCY : always
- duplicates
-
JDK-8209329 [macos] non-opaque Swing window flickers
-
- Closed
-
- relates to
-
JDK-8209329 [macos] non-opaque Swing window flickers
-
- Closed
-