From 63bb909613db165559af3d24516da3e45f735eee Mon Sep 17 00:00:00 2001 From: Matthew Khouzam Date: Wed, 25 Mar 2015 15:58:57 -0400 Subject: [PATCH] swtbot: Introducing ImageHelper The ImageHelper is a way to compare screenshots quickly and efficiently. It has many helpers made to aid in comparisson. Each pixel is queried as an RGB, the image can give the following info: * its dimensions and place in the screen * its pixels * all the pixel values of a query (for sampling) * a row of the image * a column of the image Change-Id: Id7a4c168cad18448a4d0d5c14ef395e05b6f018b Signed-off-by: Matthew Khouzam Reviewed-on: https://git.eclipse.org/r/44635 Reviewed-by: Patrick Tasse Tested-by: Patrick Tasse Reviewed-by: Hudson CI --- .../META-INF/MANIFEST.MF | 3 +- .../ui/swtbot/tests/shared/ImageHelper.java | 216 ++++++++++++++++++ 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.tracecompass.tmf.ui.swtbot.tests/shared/org/eclipse/tracecompass/tmf/ui/swtbot/tests/shared/ImageHelper.java diff --git a/org.eclipse.tracecompass.tmf.ui.swtbot.tests/META-INF/MANIFEST.MF b/org.eclipse.tracecompass.tmf.ui.swtbot.tests/META-INF/MANIFEST.MF index 08e48254b9..5976495ba1 100644 --- a/org.eclipse.tracecompass.tmf.ui.swtbot.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.tracecompass.tmf.ui.swtbot.tests/META-INF/MANIFEST.MF @@ -18,7 +18,8 @@ Require-Bundle: org.junit;bundle-version="4.0.0", org.eclipse.tracecompass.tmf.core.tests, org.eclipse.swtbot.junit4_x, org.eclipse.swtbot.swt.finder -Import-Package: org.apache.log4j, +Import-Package: com.google.common.collect, + org.apache.log4j, org.apache.log4j.varia, org.eclipse.swtbot.eclipse.finder, org.eclipse.swtbot.eclipse.finder.matchers, diff --git a/org.eclipse.tracecompass.tmf.ui.swtbot.tests/shared/org/eclipse/tracecompass/tmf/ui/swtbot/tests/shared/ImageHelper.java b/org.eclipse.tracecompass.tmf.ui.swtbot.tests/shared/org/eclipse/tracecompass/tmf/ui/swtbot/tests/shared/ImageHelper.java new file mode 100644 index 0000000000..33763d0484 --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui.swtbot.tests/shared/org/eclipse/tracecompass/tmf/ui/swtbot/tests/shared/ImageHelper.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2015 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Khouzam - Initial API and implementation + *******************************************************************************/ +package org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared; + +import java.awt.AWTException; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; +import org.eclipse.swtbot.swt.finder.results.Result; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; + +/** + * Test helpers, allow looking up the frame buffer and testing what is really + * displayed + */ +public final class ImageHelper { + + private final int[] fPixels; + private final Rectangle fBounds; + + /** + * Constructor + * + * @param pixels + * the pixel map + * @param bounds + * the bounds + */ + private ImageHelper(int[] pixels, Rectangle bounds) { + if (pixels.length != bounds.height * bounds.width) { + throw new IllegalArgumentException("Incoherent image"); + } + fPixels = Arrays.copyOf(pixels, pixels.length); + fBounds = bounds; + } + + /** + * Gets a screen grab of the rectangle r; the way to access a given pixel is + * pixel = rect.width * y + x; + * + * @param rect + * the area to grab in display relative coordinates (top left is + * the origin) + * @return an ImageHelper, cannot be null + */ + public static ImageHelper grabImage(final Rectangle rect) { + return UIThreadRunnable.syncExec(new Result() { + @Override + public ImageHelper run() { + try { + // note: awt is explicitly called until we can use SWT to + // replace it. + java.awt.Robot rb = new java.awt.Robot(); + java.awt.image.BufferedImage bi = rb.createScreenCapture(new java.awt.Rectangle(rect.x, rect.y, rect.width, rect.height)); + return new ImageHelper(bi.getRGB(0, 0, rect.width, rect.height, null, 0, rect.width), rect); + } catch (AWTException e) { + } + return new ImageHelper(new int[0], new Rectangle(0, 0, 0, 0)); + } + }); + } + + /** + * Get the bounds + * + * @return the bounds + */ + public Rectangle getBounds() { + return fBounds; + } + + /** + * Get the pixel for a given set of coordinates + * + * @param x + * x + * @param y + * y + * @return the RGB, can return an {@link ArrayIndexOutOfBoundsException} + */ + public RGB getPixel(int x, int y) { + return getRgbFromRGBPixel(fPixels[x + y * fBounds.width]); + } + + /** + * Sample an image at n points + * + * @param samplePoints + * a list of points to sample at + * @return a list of RGBs corresponding to the pixel coordinates. Can throw + * an {@link IllegalArgumentException} if the point is outside of + * the image bounds + */ + public List sample(List samplePoints) { + for (Point p : samplePoints) { + if (!getBounds().contains(p)) { + throw new IllegalArgumentException("Point outside of the image"); + } + + } + List retVal = new ArrayList<>(samplePoints.size()); + for (Point p : samplePoints) { + retVal.add(getPixel(p.x, p.y)); + } + return retVal; + } + + /** + * Get the color histogram of the image + * + * @return The color density of the image + */ + public Multiset getHistogram() { + Multiset colors = HashMultiset.create(); + for (int pixel : fPixels) { + RGB pixelColor = getRgbFromRGBPixel(pixel); + colors.add(pixelColor); + } + return colors; + } + + /** + * Get the color histogram of the row of the image + * + * @param row + * the row to lookup + * + * @return The x oriented line + */ + public List getPixelRow(int row) { + List retVal = new ArrayList<>(); + for (int x = 0; x < getBounds().width; x++) { + retVal.add(getPixel(x, row)); + } + return retVal; + } + + /** + * Get the color histogram of a column of the image + * + * @param col + * the column to lookup + * + * @return The y oriented line + */ + public List getPixelColumn(int col) { + List retVal = new ArrayList<>(); + for (int y = 0; y < getBounds().height; y++) { + retVal.add(getPixel(col, y)); + } + return retVal; + } + + /** + * Difference between two images (this - other) + * + * @param other + * the other image to compare + * @return an {@link ImageHelper} that is the per pixel difference between + * the two images + * + */ + public ImageHelper diff(ImageHelper other) { + if (other.getBounds().width != fBounds.width && other.getBounds().height != fBounds.height) { + throw new IllegalArgumentException("Different sized images"); + } + int[] fBuffer = new int[fPixels.length]; + for (int i = 0; i < fPixels.length; i++) { + RGB local = getRgbFromRGBPixel(fPixels[i]); + RGB otherPixel = getRgbFromRGBPixel(other.fPixels[i]); + byte r = (byte) (local.red - otherPixel.red); + byte g = (byte) (local.green - otherPixel.green); + byte b = (byte) (local.blue - otherPixel.blue); + fBuffer[i] = r << 16 + g << 8 + b; + } + return new ImageHelper(fBuffer, getBounds()); + } + + /** + * Write the image to disk in PNG form + * + * @param outputFile + * the file to write it to + * @throws IOException + * file not found and such + */ + public void writePng(File outputFile) throws IOException { + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(fBounds.width, fBounds.height, java.awt.image.BufferedImage.TYPE_INT_RGB); + image.setRGB(0, 0, fBounds.width, fBounds.height, fPixels, 0, fBounds.width); + ImageIO.write(image, "png", outputFile); + } + + private static RGB getRgbFromRGBPixel(int pixel) { + return new RGB(((pixel >> 16) & 0xff), ((pixel >> 8) & 0xff), ((pixel) & 0xff)); + } +} -- 2.34.1