blob: 0e11111a82589bb1ce3ec390c6d385b7851b5330 [file] [log] [blame]
Chenjie Luo42aaa5c2014-08-20 18:33:47 -07001/*
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;
21import com.android.tradefed.device.ITestDevice;
22
23import java.awt.Color;
24import java.awt.image.BufferedImage;
25import java.io.File;
Alan Viverettec15ea9f2015-08-26 16:33:17 -040026import java.io.IOException;
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070027import java.lang.String;
28import java.util.concurrent.Callable;
29
30import javax.imageio.ImageIO;
31
32/**
33 * Compares the images generated by the device with the reference images.
34 */
35public class ComparisonTask implements Callable<Boolean> {
Alan Viverettec15ea9f2015-08-26 16:33:17 -040036 private static final String TAG = "ComparisonTask";
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070037
38 private static final int IMAGE_THRESHOLD = 2;
39
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070040 private final ITestDevice mDevice;
Alan Viverettec15ea9f2015-08-26 16:33:17 -040041 private final File mExpected;
42 private final File mActual;
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070043
Alan Viverettec15ea9f2015-08-26 16:33:17 -040044 public ComparisonTask(ITestDevice device, File expected, File actual) {
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070045 mDevice = device;
Alan Viverettec15ea9f2015-08-26 16:33:17 -040046 mExpected = expected;
47 mActual = actual;
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070048 }
49
50 public Boolean call() {
51 boolean success = false;
Alan Viverettec15ea9f2015-08-26 16:33:17 -040052
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070053 try {
Alan Viverettec15ea9f2015-08-26 16:33:17 -040054 final BufferedImage expected = ImageIO.read(mExpected);
55 final BufferedImage actual = ImageIO.read(mActual);
56 if (compare(expected, actual, IMAGE_THRESHOLD)) {
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070057 success = true;
58 } else {
Alan Viverettec15ea9f2015-08-26 16:33:17 -040059 final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png");
60 createDiff(expected, actual, diff);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070061 Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
62 }
Alan Viverettec15ea9f2015-08-26 16:33:17 -040063 } catch (IOException e) {
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070064 Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
65 e.printStackTrace();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070066 }
Alan Viverettec15ea9f2015-08-26 16:33:17 -040067
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070068 return success;
69 }
70
Alan Viverettec15ea9f2015-08-26 16:33:17 -040071 /**
72 * Verifies that the pixels of reference and generated images are similar
73 * within a specified threshold.
74 *
75 * @param expected expected image
76 * @param actual actual image
77 * @param threshold maximum difference per channel
78 * @return {@code true} if the images are similar, false otherwise
79 */
Masanori OGINO3c4b6e62015-09-18 09:12:54 +090080 private static int getAlphaScaledBlue(final int color) {
81 return (color & 0x000000FF) * getAlpha(color) / 255;
82 }
83
84 private static int getAlphaScaledGreen(final int color) {
85 return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255;
86 }
87
88 private static int getAlphaScaledRed(final int color) {
89 return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255;
90 }
91
92 private static int getAlpha(final int color) {
93 // use logical shift for keeping an unsigned value
94 return (color & 0xFF000000) >>> 24;
95 }
96
Chenjie Luo42aaa5c2014-08-20 18:33:47 -070097 private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
98 final int w = generated.getWidth();
99 final int h = generated.getHeight();
100 if (w != reference.getWidth() || h != reference.getHeight()) {
101 return false;
102 }
103
104 for (int i = 0; i < w; i++) {
105 for (int j = 0; j < h; j++) {
106 final int p1 = reference.getRGB(i, j);
107 final int p2 = generated.getRGB(i, j);
Masanori OGINO3c4b6e62015-09-18 09:12:54 +0900108
109 final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2);
110 final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2);
111 final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700112
113 if (Math.abs(db) > threshold ||
114 Math.abs(dg) > threshold ||
Masanori OGINO3c4b6e62015-09-18 09:12:54 +0900115 Math.abs(dr) > threshold) {
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700116 return false;
117 }
118 }
119 }
120 return true;
121 }
122
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400123 private static void createDiff(BufferedImage expected, BufferedImage actual, File out)
124 throws IOException {
125 final int w1 = expected.getWidth();
126 final int h1 = expected.getHeight();
127 final int w2 = actual.getWidth();
128 final int h2 = actual.getHeight();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700129 final int width = Math.max(w1, w2);
130 final int height = Math.max(h1, h2);
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400131
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700132 // The diff will contain image1, image2 and the difference between the two.
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400133 final BufferedImage diff = new BufferedImage(
134 width * 3, height, BufferedImage.TYPE_INT_ARGB);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700135
136 for (int i = 0; i < width; i++) {
137 for (int j = 0; j < height; j++) {
138 final boolean inBounds1 = i < w1 && j < h1;
139 final boolean inBounds2 = i < w2 && j < h2;
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400140 int colorExpected = Color.WHITE.getRGB();
141 int colorActual = Color.WHITE.getRGB();
142 int colorDiff;
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700143 if (inBounds1 && inBounds2) {
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400144 colorExpected = expected.getRGB(i, j);
145 colorActual = actual.getRGB(i, j);
146 colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700147 } else if (inBounds1 && !inBounds2) {
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400148 colorExpected = expected.getRGB(i, j);
149 colorDiff = Color.BLUE.getRGB();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700150 } else if (!inBounds1 && inBounds2) {
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400151 colorActual = actual.getRGB(i, j);
152 colorDiff = Color.GREEN.getRGB();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700153 } else {
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400154 colorDiff = Color.MAGENTA.getRGB();
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700155 }
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400156
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700157 int x = i;
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400158 diff.setRGB(x, j, colorExpected);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700159 x += width;
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400160 diff.setRGB(x, j, colorActual);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700161 x += width;
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400162 diff.setRGB(x, j, colorDiff);
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700163 }
164 }
Alan Viverettec15ea9f2015-08-26 16:33:17 -0400165
Chenjie Luo42aaa5c2014-08-20 18:33:47 -0700166 ImageIO.write(diff, "png", out);
167 }
168
169}