确定系统剪贴板图像是否相等

发布于 2024-12-01 08:14:38 字数 2394 浏览 2 评论 0原文

我不确定我的问题是否特定于平台,但我认为不是。
因为我的经验是基于 Windows 特定的 java.awt.Toolkit 和 Windows-Clipboard。

以下示例类显示了我面临的问题。
注意:在运行程序之前,请确保系统剪贴板中没有图像。

如果系统剪贴板中没有图像,程序将向其添加新的屏幕截图。

然后我得到剪贴板数据两次!

所有 3 个图像都是相同的! - 原始屏幕截图和我从剪贴板获得的每张图像。
没问题。

但是现在第二次运行该程序。 注意:剪贴板中有旧的屏幕截图!

该程序会生成一张新的屏幕截图,并从剪贴板中获取旧的屏幕截图两次。

没有图像等于任何图像! - 第一个(新屏幕截图)应该不相等,没关系

但是我得到的每个下一个图像都不相等。

Q1:如果我得到的下一个图像不相等,为什么第一次是相等的?
Q2:更大的问题:如何比较java.awt.Image以使下一个图像相等。

我正在寻找一种简单快速地比较两个图像的方法,或者一种简单的方法来确定剪贴板没有改变。

public class Example {

    public static void main( String[] args ) throws Exception {

        final Toolkit   toolkit   = Toolkit.getDefaultToolkit();
        final Clipboard clipboard = toolkit.getSystemClipboard();
        final Image     origImage = new Robot().createScreenCapture( new Rectangle( toolkit.getScreenSize() ) );

        if( !clipboard.isDataFlavorAvailable( DataFlavor.imageFlavor )
            || clipboard.getData( DataFlavor.imageFlavor ) == null ) {
            clipboard.setContents( new ImageSelection( origImage ), null );
        }

        Image clipImage1 = (Image)clipboard.getData( DataFlavor.imageFlavor );
        Image clipImage2 = (Image)clipboard.getData( DataFlavor.imageFlavor );

        System.out.println(origImage.hashCode());
        System.out.println(clipImage1.hashCode());
        System.out.println(clipImage2.hashCode());
        System.out.println(clipImage1.equals( clipImage2 ));


    }

    public static class ImageSelection implements Transferable {
        private Image image;
        public ImageSelection(Image image) {
            this.image = image;
        }
        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{DataFlavor.imageFlavor};
        }
        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return DataFlavor.imageFlavor.equals(flavor);
        }
        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (!DataFlavor.imageFlavor.equals(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return image;
        }
    }    
}

I am not sure if my problem is platform specific, but I think it is not.
Because my expirience is based on the Windows specific java.awt.Toolkit and the Windows-Clipboard.

The following example class shows the problem i am faced with.
NOTE: Before running the program, make sure you have no Image in you system clipboard.

If there is no Image in the system clipboard the Program put a new screenshot to it.

Then I get the Clipboard data two times!

All 3 Images are equal! - the original Screenshot and every Image I get from the clipboard.
which is ok.

But now running the program a second time.
NOTE: There is the old screenshot in the clipboard!

The Program generates a new screenshot and get the old one from the clipboard two times.

No Images is equal to any! - The first (new screenshot) should be not equal, it is okay

But every next Image I get is not equal.

Q1: If every next Image I get is not equal, why it was equal at the first time?
Q2: The bigger Question: How can I compare a java.awt.Image to get every next Image equal.

I am looking for an easy and fast comparison of two Images or an easy way to figure out that the clipboard doesn't has changed.

public class Example {

    public static void main( String[] args ) throws Exception {

        final Toolkit   toolkit   = Toolkit.getDefaultToolkit();
        final Clipboard clipboard = toolkit.getSystemClipboard();
        final Image     origImage = new Robot().createScreenCapture( new Rectangle( toolkit.getScreenSize() ) );

        if( !clipboard.isDataFlavorAvailable( DataFlavor.imageFlavor )
            || clipboard.getData( DataFlavor.imageFlavor ) == null ) {
            clipboard.setContents( new ImageSelection( origImage ), null );
        }

        Image clipImage1 = (Image)clipboard.getData( DataFlavor.imageFlavor );
        Image clipImage2 = (Image)clipboard.getData( DataFlavor.imageFlavor );

        System.out.println(origImage.hashCode());
        System.out.println(clipImage1.hashCode());
        System.out.println(clipImage2.hashCode());
        System.out.println(clipImage1.equals( clipImage2 ));


    }

    public static class ImageSelection implements Transferable {
        private Image image;
        public ImageSelection(Image image) {
            this.image = image;
        }
        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{DataFlavor.imageFlavor};
        }
        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return DataFlavor.imageFlavor.equals(flavor);
        }
        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (!DataFlavor.imageFlavor.equals(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return image;
        }
    }    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜无邪 2024-12-08 08:14:38

Q1:第一种情况下的对象引用是相同的。

Q2:这里有一种方法可以检查屏幕截图中的数据是否相等:

  //createScreenCapture() returns BufferedImage which is more useful for what you are doing.

  static boolean bufferedImageEquals( BufferedImage b1, BufferedImage b2 ) {
    if ( b1 == b2 ) {return true;} // true if both are null
    if ( b1 == null || b2 == null ) { return false; }
    if ( b1.getWidth() != b2.getWidth() ) { return false; }
    if ( b1.getHeight() != b2.getHeight() ) { return false; }
    for ( int i = 0; i < b1.getWidth(); i++) {
     for ( int j = 0; j < b1.getHeight(); j++ ) {
       if ( b1.getRGB(i,j) != b2.getRGB(i,j) ) { 
           return false;
       }
      }
    }
    return true;
  }

Q1: The object references were the same in the first case.

Q2: here is a way to check the data is equal in the screen shots:

  //createScreenCapture() returns BufferedImage which is more useful for what you are doing.

  static boolean bufferedImageEquals( BufferedImage b1, BufferedImage b2 ) {
    if ( b1 == b2 ) {return true;} // true if both are null
    if ( b1 == null || b2 == null ) { return false; }
    if ( b1.getWidth() != b2.getWidth() ) { return false; }
    if ( b1.getHeight() != b2.getHeight() ) { return false; }
    for ( int i = 0; i < b1.getWidth(); i++) {
     for ( int j = 0; j < b1.getHeight(); j++ ) {
       if ( b1.getRGB(i,j) != b2.getRGB(i,j) ) { 
           return false;
       }
      }
    }
    return true;
  }
当爱已成负担 2024-12-08 08:14:38

除了克林特的回答之外,如果你想比较它们,你还必须将剪贴板图像转换为 BufferedImage。

public static BufferedImage convert(Image img)
{
    BufferedImage i = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR_PRE);
    Graphics2D g = i.createGraphics();
    g.drawImage(img, 0, 0, null);
    g.dispose();
    return i;
}

使用此方法将两个图像转换为 BufferedImages 并按照 Clint 发布的方式对它们进行比较。

In addition to Clint his answer, you have to convert the clipboard Image to a BufferedImage, if you want to compare them.

public static BufferedImage convert(Image img)
{
    BufferedImage i = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR_PRE);
    Graphics2D g = i.createGraphics();
    g.drawImage(img, 0, 0, null);
    g.dispose();
    return i;
}

Use this method to convert the two Images to BufferedImages and compare them the way Clint posted.

夏尔 2024-12-08 08:14:38

如果你想比较屏幕,有几种方法,

  1. 将其转换为字节数组并比较数组
  2. 进行 MD5 哈希计算

,你甚至可以将该数组保存在 pngs/jpg 中以检查逻辑有什么问题。

        BufferedImage clipImage1 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage1 = createImage(clipImage1);
    File clipImage1png = new File("clipImage1.png");
    ImageIO.write(renderclipImage1, "png", clipImage1png);
    byte[] clipeImage1Bytes = bufImageToBytesConverter(clipImage1);
    MessageDigest mdInst1 = MessageDigest.getInstance("MD5");
    mdInst1.update(clipeImage1Bytes);
    byte[] md5hashClipImage1 = mdInst1.digest();
    System.out.println(returnHex(md5hashClipImage1));

    BufferedImage clipImage2 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage2 = createImage(clipImage2);
    File clipImage2png = new File("clipImage2.png");
    ImageIO.write(renderclipImage2, "png", clipImage2png);
    byte[] clipImage2Bytes = bufImageToBytesConverter(clipImage2);
    MessageDigest msInst2 = MessageDigest.getInstance("MD5");
    msInst2.update(clipImage2Bytes);
    byte[] md5hashClipImage2 = msInst2.digest();
    System.out.println(returnHex(md5hashClipImage2));

输出为“

nulle5c49978317c0151969cf63f212f7662
nulle5c49978317c0151969cf63f212f7662

如果您能进一步澄清上下文,您可能会得到更多回复”。例如,是否用于远程桌面共享等。

If you want to compare screens, there are couple of ways,

  1. To convert it to byte array and compare arrays
  2. To do a MD5 hash calculation

And you can even save that arrays in pngs/jpg to check what is wrong with the logic.

        BufferedImage clipImage1 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage1 = createImage(clipImage1);
    File clipImage1png = new File("clipImage1.png");
    ImageIO.write(renderclipImage1, "png", clipImage1png);
    byte[] clipeImage1Bytes = bufImageToBytesConverter(clipImage1);
    MessageDigest mdInst1 = MessageDigest.getInstance("MD5");
    mdInst1.update(clipeImage1Bytes);
    byte[] md5hashClipImage1 = mdInst1.digest();
    System.out.println(returnHex(md5hashClipImage1));

    BufferedImage clipImage2 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage2 = createImage(clipImage2);
    File clipImage2png = new File("clipImage2.png");
    ImageIO.write(renderclipImage2, "png", clipImage2png);
    byte[] clipImage2Bytes = bufImageToBytesConverter(clipImage2);
    MessageDigest msInst2 = MessageDigest.getInstance("MD5");
    msInst2.update(clipImage2Bytes);
    byte[] md5hashClipImage2 = msInst2.digest();
    System.out.println(returnHex(md5hashClipImage2));

The output comes as

nulle5c49978317c0151969cf63f212f7662
nulle5c49978317c0151969cf63f212f7662

If you would have clarified the context more , you might have got more replies. For example, Is it for remote desktop sharing etc etc ..

一萌ing 2024-12-08 08:14:38

好吧,我看了一下我们在 IDE 中使用的 JDK 背后的源代码(恰好是 IBM JDK)。 java.awt.Image 看起来像是一个抽象类,并且 equals 没有在其中定义(请检查您的 JDK 以确保)。既然是这种情况,要么子类必须实现 equals,要么我们依靠 java.lang.Object.equals(java.lang.Object),根据我们的 JDK 将 equals 方法实现为
返回这个==arg

再次,请使用您的 JDK 验证这一点。但如果您的 JDK 和我们的 JDK 在实现上“匹配”,我猜测会发生以下情况。如果使用 Object equals ,那么我猜测第一次通过你的程序时,JVM 会跟踪剪贴板上的对象并可以知道它们是相同的图像。但是当 JVM 终止然后重新启动时,它无法再知道它们是否是同一个对象。因此,它为它们分配不同的内存空间(即引用)作为不同的对象,因此它们不再相等。

当然,不知道使用了哪些子类,或者您的 JDK/JVM 的实现方式是否不同,我不能完全确定这一点。但这似乎很有可能,特别是因为剪贴板在技术上位于 JVM 之外,并且是通过 JVM 访问的。如果已经存在某些东西,JVM 如何判断什么是相等的,什么是不相等的?除非有人实现一种依赖于操作系统知识的方法来判断,否则我猜它不能。

因此,这意味着您最好实施自己的解决方案,克林特似乎对此很擅长。

Well, I took a peek at the source code behind the JDK we use in our IDE (which happens to be an IBM JDK). java.awt.Image looks like it is an abstract class and equals is not defined in it (please check your JDK to be sure). Since this is the case, either a subclass must implement equals, or we fall back on java.lang.Object.equals(java.lang.Object), which according to our JDK implements the equals method as
return this == arg.

Again, please validate this with your JDK. But here is what I would surmise would happen should your JDK and ours "match" on implementation. If the Object equals is used, then I'm guessing that the first time through your program, the JVM keeps track of the objects on the Clipboard and can tell that they are the same image. But when the JVM terminates and then restarts, it can no longer know if those were the same object or not. Therefore it assigns them different memory spaces (ie, references) as different objects and thus they no longer equal anymore.

Of course not knowing what subclasses are used or if your JDK/JVM is implemented differently, I can't say that with full certainty. But it seems highly likely, especially since the Clipboard is technically outside of the JVM and is accessed through the JVM. How can the JVM tell what is and is not equal if there is something already there? Unless somebody implements a way to tell that relies on knowledge of the OS, I'm guessing it can't.

So that means you are best off implementing your own solution, of which Clint seems to have a good crack at it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文