I'm trying to get the color of a Swing JFrame, for a chosen (x,y)
pixel of the component.
For example, I want to know the color of a given JFrame
at their (0,0)
point.
The reason is that my component is a partially transparent overlay, with a JPanel
underneath. For pixels that are opaque, the mouse events should be dealt by the overlay. For pixels that are transparent, the mouse events should instead be forwarded to the JPanel
underneath.
Is this a way to do this?
Yes it's possible. Use the function getColorAt
from the example below:
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class GUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(GUI::startUp);
}
private static void startUp() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1020,760);
frame.setResizable(false);
frame.setLayout(new FlowLayout());
frame.getContentPane().setBackground(Color.BLUE);
JTextArea jta = new JTextArea(40,40);
jta.setEditable(false);
jta.setBackground(Color.WHITE);
frame.add(new JScrollPane(jta));
frame.setVisible(true);
SwingUtilities.invokeLater(() -> printColors(frame));
}
private static void printColors(JFrame frm) {
System.out.println("Color at (1, 1): " + getColorAt(frm, new Point(1, 1)));
System.out.println("Color at (300, 100): " + getColorAt(frm, new Point(300, 100)));
}
public static Color getColorAt(JFrame frm, Point p) {
Rectangle rect = frm.getContentPane().getBounds();
BufferedImage img = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_ARGB);
frm.getContentPane().paintAll(img.createGraphics());
return new Color(img.getRGB(p.x, p.y), true);
}
}
I would like to say (hoping this will introduce better performance) that maybe, if you are willing to go with the Image
approach for this, it could be good to create an image of dimensions 1x1 pixels and then translate its created graphics to match the requested point. And also reuse this image for subsequent samplings of the same Component
(or even GraphicsConfiguration
).
I did some performance tests with creating the following approaches:
getColorAtClipped
which sets the clip of the created Graphics
of the Image
so not all operations have to be drawn.getColorAtRelocation
which sets the location of the component temporarily at the location needed to be sampled and then (what actually makes it faster) create an image of dimensions 1x1 and draw the parent on it. Although this method is not really thread safe for Swing as it requires to set the location of the Component
back and forth. It also calls printAll
for the parent Container
which means more Component
s to be painted.getColorAtTranslation
which creates an 1x1 image and translates its Graphics
instance so as the required location will be drawn actually at (0,0) which is the only pixel really in the image. This method turned out to be the fastest for up to this 3 first methods.ComponentColorSampler
in the following codeA code to test the performance of the above approaches follows in this section. If it is not correct, let me know in the comments, but be aware that I ran each method for about 3 million samplings in hope that collateral delays will be dwarfed. Every million samples of a test method, I printed some timings, and then restarted the process to test another million, up to 3.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.function.IntBinaryOperator;
import java.util.function.Supplier;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
public static Color getColorAtClipped(final Component comp, final Point p) {
final BufferedImage bimg = comp.getGraphicsConfiguration().createCompatibleImage(comp.getWidth(), comp.getHeight());
final Graphics2D g2d = (Graphics2D) bimg.createGraphics();
g2d.setClip(p.x, p.y, 1, 1);
comp.printAll(g2d);
g2d.dispose();
final Color c = new Color(bimg.getRGB(p.x, p.y), true);
bimg.flush();
return c;
}
public static Color getColorAtRelocation(final Component comp, final Point p) {
final Point loc = comp.getLocation();
final BufferedImage bimg = comp.getGraphicsConfiguration().createCompatibleImage(1, 1);
comp.setLocation(loc.x - p.x, loc.y - p.y);
final Graphics2D g2d = (Graphics2D) bimg.createGraphics();
//g2d.setClip(0, 0, 1, 1);
comp.getParent().printAll(g2d);
comp.setLocation(loc);
g2d.dispose();
final Color c = new Color(bimg.getRGB(0, 0), true);
bimg.flush();
return c;
}
public static Color getColorAtTranslation(final Component comp, final Point p) {
final BufferedImage bimg = comp.getGraphicsConfiguration().createCompatibleImage(1, 1);
final Graphics2D g2d = (Graphics2D) bimg.createGraphics();
g2d.translate(-p.x, -p.y);
//g2d.setClip(0, 0, 1, 1);
comp.printAll(g2d);
g2d.dispose();
final Color c = new Color(bimg.getRGB(0, 0), true);
bimg.flush();
return c;
}
public static class ComponentColorSampler<C extends Component> implements AutoCloseable, IntBinaryOperator, Supplier<C> {
private final C comp;
private final BufferedImage bimg;
private final Graphics2D g2d;
private int x, y;
public ComponentColorSampler(final C comp) {
this.comp = Objects.requireNonNull(comp);
bimg = comp.getGraphicsConfiguration().createCompatibleImage(1, 1);
g2d = bimg.createGraphics();
//g2d.setClip(0, 0, 1, 1);
x = y = 0;
}
@Override
public C get() {
return comp;
}
@Override
public int applyAsInt(final int x, final int y) {
g2d.clearRect(0, 0, 1, 1);
g2d.translate(this.x - x, this.y - y);
this.x = x;
this.y = y;
comp.printAll(g2d);
return bimg.getRGB(0, 0);
}
public Color sample(final int x, final int y) {
return new Color(applyAsInt(x, y), true);
}
@Override
public void close() {
g2d.dispose();
bimg.flush();
}
}
public static class DrawPanel extends JPanel {
private final int x, y;
private Color c;
public DrawPanel(final int x, final int y) {
this.x = x;
this.y = y;
c = Color.BLUE;
}
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(c);
g.fillRect(x, y, 1, 1);
}
public void setColor(final Color c) {
this.c = Objects.requireNonNull(c);
paintImmediately(0, 0, getWidth(), getHeight()); //Not sure yet.
repaint(); //Just to be sure now.
}
}
//@SuppressWarnings("SleepWhileInLoop")
public static boolean checkValid(final DrawPanel dp, final Supplier<Color> sampler) throws InterruptedException, InvocationTargetException {
for (final Color c: new Color[]{Color.BLUE, Color.RED, Color.BLACK, Color.WHITE, Color.BLACK, Color.CYAN}) {
SwingUtilities.invokeAndWait(() -> dp.setColor(c));
Thread.sleep(250); //Let it some time to change (not sure if needed).
if (!Objects.equals(c, sampler.get()))
return false;
}
return true;
}
public static long checkTime(final Supplier<Color> sampler) {
final long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; ++i)
sampler.get();
return System.currentTimeMillis() - start;
}
public static void main(final String[] args) throws InterruptedException, InvocationTargetException {
final Point p = new Point(100, 100);
final DrawPanel contents = new DrawPanel(p.x, p.y);
contents.setPreferredSize(new Dimension(200, 200));
final JFrame frame = new JFrame("Printed!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
final ComponentColorSampler<Component> sampler = new ComponentColorSampler<>(contents);
final Supplier<Color> clipped = () -> getColorAtClipped(contents, p),
relocation = () -> getColorAtRelocation(contents, p),
translation = () -> getColorAtTranslation(contents, p),
samplerSampler = () -> sampler.sample(p.x, p.y);
System.out.println("#### Validity checks...");
for (int i = 0; i < 3; ++i) {
System.out.println("Batch " + (i + 1) + ':');
System.out.println("> Clipped: " + checkValid(contents, clipped) + '.');
System.out.println("> Relocation: " + checkValid(contents, relocation) + '.');
System.out.println("> Translation: " + checkValid(contents, translation) + '.');
System.out.println("> Sampler: " + checkValid(contents, samplerSampler) + '.');
}
System.out.println("#### Timings...");
for (int i = 0; i < 3; ++i) {
System.out.println("Batch " + (i + 1) + ':');
System.out.println("> Clipped: " + checkTime(clipped) + "ms.");
System.out.println("> Relocation: " + checkTime(relocation) + "ms.");
System.out.println("> Translation: " + checkTime(translation) + "ms.");
System.out.println("> Sampler: " + checkTime(samplerSampler) + "ms.");
}
System.out.println("#### Done.");
}
}
Output of the program:
#### Validity checks...
Batch 1:
> Clipped: true.
> Relocation: true.
> Translation: true.
> Sampler: true.
Batch 2:
> Clipped: true.
> Relocation: true.
> Translation: true.
> Sampler: true.
Batch 3:
> Clipped: true.
> Relocation: true.
> Translation: true.
> Sampler: true.
#### Timings...
Batch 1:
> Clipped: 34668ms.
> Relocation: 22737ms.
> Translation: 5416ms.
> Sampler: 1152ms.
Batch 2:
> Clipped: 38521ms.
> Relocation: 22805ms.
> Translation: 5451ms.
> Sampler: 1156ms.
Batch 3:
> Clipped: 38275ms.
> Relocation: 22864ms.
> Translation: 5415ms.
> Sampler: 1163ms.
#### Done.
So the first approach is about 37 seconds for a million samples, the second approach is about 22, the third 5 and finally the last approach is just above 1 second (for a million samples). So ComponentColorSampler
is the fastest implementation in these tests (about 865 samples per millisecond) and works on any Component
. Validity checks just stand for somewhat verifying that the sampled color has the correct value.
Note : The tests are not Swing/thread safe but indicate what would be the performance if you used them properly (for example executing the samplings on the Event Dispatch Thread).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.