问题描述
我正在尝试为组件的选定(x,y)
像素获取Swing JFrame的颜色.
I'm trying to get the color of a Swing JFrame, for a chosen (x,y)
pixel of the component.
例如,我想知道给定 JFrame
在其(0,0)
点处的颜色.
For example, I want to know the color of a given JFrame
at their (0,0)
point.
原因是我的组件是部分透明的覆盖层,下面是 JPanel
.对于不透明的像素,鼠标事件应由叠加层处理.对于透明的像素,应该将鼠标事件转发到下面的 JPanel
.
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?
推荐答案
我想说(希望这样做会带来更好的性能),如果您愿意使用 Image
方法为此,最好创建一个尺寸为1x1像素的图像,然后转换其创建的图形以匹配请求的点.并将此图像重新用于同一 Component
(甚至是 GraphicsConfiguration
)的后续采样.
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
的方法,该方法设置了Image
的创建的Graphics
的剪辑,因此不必绘制所有操作. - 一种名为
getColorAtRelocation
的方法,该方法将组件的位置临时设置在需要采样的位置,然后(实际上使速度更快)创建尺寸为1x1的图像并在其上绘制父对象.尽管此方法对于Swing来说并不是真正的线程安全方法,因为它需要前后设置Component
的位置.它还为父容器Container
调用printAll
,这意味着需要绘制更多的Component
. - 然后一个名为
getColorAtTranslation
的方法将创建一个1x1图像并转换其Graphics
实例,以便所需的位置实际上将绘制在(0,0)处,即图像中实际上只有像素.事实证明,对于这前三种方法来说,这种方法是最快的. - 那为什么不为以后的样本重用相同的资源呢?...这导致了我的最终方法:一个包含所有参与样本的必需资源的类:一个名为
ComponentColorSampler
的类以下代码
- A method called
getColorAtClipped
which sets the clip of the createdGraphics
of theImage
so not all operations have to be drawn. - A method called
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 theComponent
back and forth. It also callsprintAll
for the parentContainer
which means moreComponent
s to be painted. - Then a method called
getColorAtTranslation
which creates an 1x1 image and translates itsGraphics
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. - Then why not reuse the same resources for subsequent samples?... So that leads me to the final approach: a class enclosing all required resources which participate in the sampling: the one called
ComponentColorSampler
in the following code
测试代码:
本节中将介绍用于测试上述方法的性能的代码.如果不正确,请在评论中让我知道,但请注意,我对每种方法进行了约300万次采样,以期避免附带的延迟.每百万种测试方法的样本中,我会打印一些时间,然后重新启动该过程以测试另外一百万种(最多3个).
Testing code:
A 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.");
}
}
结果:
程序输出:
#### 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.
因此,对于一百万个样本,第一种方法大约需要37秒,第二种方法大约需要22秒,第三种方法大约需要5秒,最后一种方法恰好超过1秒(对于一百万个样本).因此, ComponentColorSampler
是这些测试中最快的实现方式(每毫秒约865个样本),并且可以在任何 Component
上使用.有效性检查只是为了在某种程度上验证所采样的颜色是否具有正确的值.
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.
注意:这些测试不是Swing/线程安全的,但指出了正确使用它们(例如在事件调度线程上执行采样)的性能.
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).
这篇关于Java Swing:如何获取JFrame像素的颜色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!