简体   繁体   English

Java Swing:如何获取 JFrame 像素的颜色

[英]Java Swing: how to get the color of a pixel of a JFrame

I'm trying to get the color of a Swing JFrame, for a chosen (x,y) pixel of the component.我正在尝试为组件的选定(x,y)像素获取 Swing JFrame 的颜色。

For example, I want to know the color of a given JFrame at their (0,0) point.例如,我想知道给定JFrame(0,0)点的颜色。

The reason is that my component is a partially transparent overlay, with a JPanel underneath.原因是我的组件是一个部分透明的覆盖层,下面有一个JPanel 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.对于透明的像素,鼠标事件应该转发到下面的JPanel

Is this a way to do this?这是一种方法吗?

Yes it's possible.是的,这是可能的。 Use the function getColorAt from the example below:使用以下示例中的函数getColorAt

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.我想说(希望这会带来更好的性能),如果您愿意为此使用Image方法,那么创建一个尺寸为 1x1 像素的图像然后转换其创建的图形以匹配要求的点。 And also reuse this image for subsequent samplings of the same Component (or even GraphicsConfiguration ).并且还将此图像重用于同一Component (甚至GraphicsConfiguration )的后续采样。

I did some performance tests with creating the following approaches:我通过创建以下方法进行了一些性能测试:

  1. A method called getColorAtClipped which sets the clip of the created Graphics of the Image so not all operations have to be drawn.一种名为getColorAtClipped的方法,它设置创建的Image Graphics的剪辑,因此不必绘制所有操作。
  2. 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.一个名为getColorAtRelocation的方法将组件的位置临时设置在需要采样的位置,然后(实际上使它更快)创建一个尺寸为 1x1 的图像并在其上绘制父级。 Although this method is not really thread safe for Swing as it requires to set the location of the Component back and forth.尽管这个方法对于 Swing 来说并不是真正的线程安全,因为它需要来回设置Component的位置。 It also calls printAll for the parent Container which means more Component s to be painted.它还为父Container调用printAll ,这意味着要绘制更多Component
  3. Then a method called 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.然后一个名为getColorAtTranslation的方法创建一个 1x1 图像并转换其Graphics实例,以便实际绘制所需的位置(0,0),这是图像中真正的唯一像素。 This method turned out to be the fastest for up to this 3 first methods.事实证明,对于前 3 种方法来说,这种方法是最快的。
  4. 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那么为什么不为后续样本重用相同的资源?...所以这导致我采用最后一种方法:包含参与采样的所有必需资源的类:在以下代码中称为ComponentColorSampler的类

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.如果不正确,请在评论中告诉我,但请注意,我对每种方法运行了大约 300 万次采样,希望可以减少附带延迟。 Every million samples of a test method, I printed some timings, and then restarted the process to test another million, up to 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.");
    }
}

Results:结果:

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).因此,对于一百万个样本,第一种方法大约为 37 秒,第二种方法大约为 22 秒,第三种方法为 5 秒,最后最后一种方法略高于 1 秒(对于一百万个样本)。 So ComponentColorSampler is the fastest implementation in these tests (about 865 samples per millisecond) and works on any Component .所以ComponentColorSampler是这些测试中最快的实现(大约每毫秒 865 个样本)并且适用于任何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).注意:这些测试不是 Swing/线程安全的,但会表明如果您正确使用它们会带来什么性能(例如在事件调度线程上执行采样)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM