Я хотел бы сказать (надеясь, что это приведет к повышению производительности), что, возможно, если вы захотите go с подходом Image
для этого, было бы хорошо создать изображение размером 1x1 пикселей, а затем переведите созданную графику, чтобы она соответствовала запрошенной точке. А также повторно использовать это изображение для последующих выборок того же Component
(или даже GraphicsConfiguration
).
Я провел несколько тестов производительности с созданием следующих подходов:
- Метод с именем
getColorAtClipped
, который устанавливает клип созданного Graphics
из Image
, поэтому не все операции должны быть нарисованы. - Метод с именем
getColorAtRelocation
, который временно устанавливает местоположение компонента на необходимо определить местоположение, а затем (что на самом деле делает его быстрее) создать изображение размером 1x1 и нарисовать на нем родительский объект. Хотя этот метод на самом деле не является поточно-ориентированным для Swing, так как он требует установки местоположения Component
вперед и назад. Он также вызывает printAll
для родительского Container
, что означает, что нужно нарисовать больше Component
s. - Затем метод с именем
getColorAtTranslation
, который создает изображение 1x1 и переводит его экземпляр Graphics
так, чтобы требуемое местоположение будет нарисовано в точке (0,0), которая является единственным пикселем на изображении. Этот метод оказался самым быстрым для всех трех первых методов. - Тогда почему бы не использовать те же ресурсы для последующих выборок? ... Так что это приводит меня к окончательному подходу: классу, содержащему все необходимые Ресурсы, которые участвуют в выборке: тот, который называется
ComponentColorSampler
в следующем коде
Код тестирования:
В этом разделе приведен код для проверки производительности вышеуказанных подходов. Если это не правильно, дайте мне знать в комментариях, но имейте в виду, что я использовал каждый метод для примерно 3 миллионов выборок в надежде, что задержки с обеспечением будут уменьшены. На каждый миллион образцов тестового метода я печатал несколько таймингов, а затем перезапускал процесс для тестирования еще одного миллиона, вплоть до 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
. Проверка достоверности означает лишь некоторую проверку того, что выбранный цвет имеет правильное значение.
Примечание : тесты не являются Swing / Thread-безопасными, но показывают, какова будет производительность, если вы их правильно используете (например, выполнение выборок в потоке диспетчеризации событий).