Определите, являются ли изображения Clipboard System равными

Я не уверен, что моя проблема связана с платформой, но я думаю, что это не так. Поскольку мой опыт основан на специфических для Windows java.awt.Toolkit и Windows-Clipboard.

Следующий примерный класс показывает проблему, с которой я столкнулся.
ПРИМЕЧАНИЕ. Перед запуском программы убедитесь, что у вас нет изображения в системном буфере обмена.

Если в системном буфере обмена нет изображения, программа добавит к нему новый снимок экрана.

Затем я получаю данные 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;
        }
    }    
}

Ответ 1

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;
  }

Ответ 2

Хорошо, я заглянул в исходный код JDK, который мы используем в нашей среде IDE (который, как оказалось, является JDK для IBM). java.awt.Image выглядит как абстрактный класс, а equals в нем не определен (пожалуйста, проверьте свой JDK). Так как это так, либо подкласс должен реализовывать equals, либо мы возвращаемся на java.lang.Object.equals(java.lang.Object), который, согласно нашему JDK, реализует метод equals как return this == arg.

Опять же, пожалуйста, подтвердите это с помощью вашего JDK. Но вот то, что я бы предположил, произойдет, если ваш JDK и наш "совпадут" с реализацией. Если используется Object equals, то я предполагаю, что в первый раз через вашу программу JVM отслеживает объекты в буфере обмена и может сказать, что они являются одним и тем же изображением. Но когда JVM завершается и затем перезапускается, он больше не может знать, были ли те же объекты или нет. Поэтому он присваивает им разные пространства памяти (т.е. Ссылки) как разные объекты и, следовательно, они больше не равны.

Конечно, не зная, какие подклассы используются или если ваш JDK/JVM реализован по-разному, я не могу сказать это с полной уверенностью. Но это кажется весьма вероятным, тем более, что Clipboard технически находится за пределами JVM и доступен через JVM. Как JVM может сказать, что есть и не равно, если там что-то уже есть? Если кто-то не реализует способ сказать, что полагается на знание ОС, я предполагаю, что это невозможно.

Итак, это означает, что вам лучше всего реализовать свое собственное решение, о котором Клинт, похоже, хорошо разбирается в нем.

Ответ 3

В дополнение к Клинту его ответ, вы должны преобразовать изображение в буфер обмена в 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.

Ответ 4

Если вы хотите сравнить экраны, есть несколько способов,

  • Чтобы преобразовать его в массив байтов и сравнить массивы
  • Чтобы выполнить расчет хеша 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

Если бы вы уточнили контекст больше, у вас могло появиться больше ответов. Например, это для удаленного доступа к рабочему столу и т.д. И т.д.