1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.theme.cts; 18 19import com.android.ddmlib.Log; 20import com.android.ddmlib.Log.LogLevel; 21 22import java.awt.Color; 23import java.awt.image.BufferedImage; 24import java.io.File; 25import java.io.IOException; 26import java.lang.String; 27import java.util.concurrent.Callable; 28 29import javax.imageio.ImageIO; 30 31/** 32 * Compares the images generated by the device with the reference images. 33 */ 34public class ComparisonTask implements Callable<Boolean> { 35 private static final String TAG = "ComparisonTask"; 36 37 private static final int IMAGE_THRESHOLD = 2; 38 39 private final File mExpected; 40 private final File mActual; 41 42 public ComparisonTask(File expected, File actual) { 43 mExpected = expected; 44 mActual = actual; 45 } 46 47 public Boolean call() { 48 boolean success = false; 49 50 try { 51 final BufferedImage expected = ImageIO.read(mExpected); 52 final BufferedImage actual = ImageIO.read(mActual); 53 if (compare(expected, actual, IMAGE_THRESHOLD)) { 54 success = true; 55 } else { 56 final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png"); 57 createDiff(expected, actual, diff); 58 Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath()); 59 } 60 } catch (IOException e) { 61 Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString()); 62 e.printStackTrace(); 63 } 64 65 return success; 66 } 67 68 /** 69 * Verifies that the pixels of reference and generated images are similar 70 * within a specified threshold. 71 * 72 * @param expected expected image 73 * @param actual actual image 74 * @param threshold maximum difference per channel 75 * @return {@code true} if the images are similar, false otherwise 76 */ 77 private static int getAlphaScaledBlue(final int color) { 78 return (color & 0x000000FF) * getAlpha(color) / 255; 79 } 80 81 private static int getAlphaScaledGreen(final int color) { 82 return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255; 83 } 84 85 private static int getAlphaScaledRed(final int color) { 86 return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255; 87 } 88 89 private static int getAlpha(final int color) { 90 // use logical shift for keeping an unsigned value 91 return (color & 0xFF000000) >>> 24; 92 } 93 94 private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) { 95 final int w = generated.getWidth(); 96 final int h = generated.getHeight(); 97 if (w != reference.getWidth() || h != reference.getHeight()) { 98 return false; 99 } 100 101 for (int i = 0; i < w; i++) { 102 for (int j = 0; j < h; j++) { 103 final int p1 = reference.getRGB(i, j); 104 final int p2 = generated.getRGB(i, j); 105 106 final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2); 107 final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2); 108 final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2); 109 110 if (Math.abs(db) > threshold || 111 Math.abs(dg) > threshold || 112 Math.abs(dr) > threshold) { 113 return false; 114 } 115 } 116 } 117 return true; 118 } 119 120 private static void createDiff(BufferedImage expected, BufferedImage actual, File out) 121 throws IOException { 122 final int w1 = expected.getWidth(); 123 final int h1 = expected.getHeight(); 124 final int w2 = actual.getWidth(); 125 final int h2 = actual.getHeight(); 126 final int width = Math.max(w1, w2); 127 final int height = Math.max(h1, h2); 128 129 // The diff will contain image1, image2 and the difference between the two. 130 final BufferedImage diff = new BufferedImage( 131 width * 3, height, BufferedImage.TYPE_INT_ARGB); 132 133 for (int i = 0; i < width; i++) { 134 for (int j = 0; j < height; j++) { 135 final boolean inBounds1 = i < w1 && j < h1; 136 final boolean inBounds2 = i < w2 && j < h2; 137 int colorExpected = Color.WHITE.getRGB(); 138 int colorActual = Color.WHITE.getRGB(); 139 int colorDiff; 140 if (inBounds1 && inBounds2) { 141 colorExpected = expected.getRGB(i, j); 142 colorActual = actual.getRGB(i, j); 143 colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB(); 144 } else if (inBounds1 && !inBounds2) { 145 colorExpected = expected.getRGB(i, j); 146 colorDiff = Color.BLUE.getRGB(); 147 } else if (!inBounds1 && inBounds2) { 148 colorActual = actual.getRGB(i, j); 149 colorDiff = Color.GREEN.getRGB(); 150 } else { 151 colorDiff = Color.MAGENTA.getRGB(); 152 } 153 154 int x = i; 155 diff.setRGB(x, j, colorExpected); 156 x += width; 157 diff.setRGB(x, j, colorActual); 158 x += width; 159 diff.setRGB(x, j, colorDiff); 160 } 161 } 162 163 ImageIO.write(diff, "png", out); 164 } 165 166} 167