blob: ae6d72cd7a0ee443686de9fdaa8162d2fcfae2ec [file] [log] [blame]
bungeman@google.come3c8ddf2012-12-05 20:13:12 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "skdiff.h"
9#include "SkBitmap.h"
10#include "SkColor.h"
11#include "SkColorPriv.h"
12#include "SkTypes.h"
13
14/*static*/ char const * const DiffRecord::ResultNames[DiffRecord::kResultCount] = {
15 "EqualBits",
16 "EqualPixels",
17 "DifferentPixels",
18 "DifferentSizes",
19 "CouldNotCompare",
20 "Unknown",
21};
22
23DiffRecord::Result DiffRecord::getResultByName(const char *name) {
24 for (int result = 0; result < DiffRecord::kResultCount; ++result) {
25 if (0 == strcmp(DiffRecord::ResultNames[result], name)) {
26 return static_cast<DiffRecord::Result>(result);
27 }
28 }
29 return DiffRecord::kResultCount;
30}
31
32static char const * const ResultDescriptions[DiffRecord::kResultCount] = {
33 "contain exactly the same bits",
34 "contain the same pixel values, but not the same bits",
35 "have identical dimensions but some differing pixels",
36 "have differing dimensions",
37 "could not be compared",
38 "not compared yet",
39};
40
41const char* DiffRecord::getResultDescription(DiffRecord::Result result) {
42 return ResultDescriptions[result];
43}
44
45/*static*/ char const * const DiffResource::StatusNames[DiffResource::kStatusCount] = {
46 "Decoded",
47 "CouldNotDecode",
48
49 "Read",
50 "CouldNotRead",
51
52 "Exists",
53 "DoesNotExist",
54
55 "Specified",
56 "Unspecified",
57
58 "Unknown",
59};
60
61DiffResource::Status DiffResource::getStatusByName(const char *name) {
62 for (int status = 0; status < DiffResource::kStatusCount; ++status) {
63 if (0 == strcmp(DiffResource::StatusNames[status], name)) {
64 return static_cast<DiffResource::Status>(status);
65 }
66 }
67 return DiffResource::kStatusCount;
68}
69
70static char const * const StatusDescriptions[DiffResource::kStatusCount] = {
71 "decoded",
72 "could not be decoded",
73
74 "read",
75 "could not be read",
76
77 "found",
78 "not found",
79
80 "specified",
81 "unspecified",
82
83 "unknown",
84};
85
86const char* DiffResource::getStatusDescription(DiffResource::Status status) {
87 return StatusDescriptions[status];
88}
89
90bool DiffResource::isStatusFailed(DiffResource::Status status) {
91 return DiffResource::kCouldNotDecode_Status == status ||
92 DiffResource::kCouldNotRead_Status == status ||
93 DiffResource::kDoesNotExist_Status == status ||
94 DiffResource::kUnspecified_Status == status ||
95 DiffResource::kUnknown_Status == status;
96}
97
98bool DiffResource::getMatchingStatuses(char* selector, bool statuses[kStatusCount]) {
99 if (!strcmp(selector, "any")) {
100 for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
101 statuses[statusIndex] = true;
102 }
103 return true;
104 }
105
106 for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
107 statuses[statusIndex] = false;
108 }
109
110 static const char kDelimiterChar = ',';
111 bool understood = true;
112 while (true) {
113 char* delimiterPtr = strchr(selector, kDelimiterChar);
114
115 if (delimiterPtr) {
116 *delimiterPtr = '\0';
117 }
118
119 if (!strcmp(selector, "failed")) {
120 for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
121 Status status = static_cast<Status>(statusIndex);
122 statuses[statusIndex] |= isStatusFailed(status);
123 }
124 } else {
125 Status status = getStatusByName(selector);
126 if (status == kStatusCount) {
127 understood = false;
128 } else {
129 statuses[status] = true;
130 }
131 }
132
133 if (!delimiterPtr) {
134 break;
135 }
136
137 *delimiterPtr = kDelimiterChar;
138 selector = delimiterPtr + 1;
139 }
140 return understood;
141}
142
143static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1, const int threshold) {
144 int da = SkGetPackedA32(c0) - SkGetPackedA32(c1);
145 int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
146 int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
147 int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
148
149 return ((SkAbs32(da) <= threshold) &&
150 (SkAbs32(dr) <= threshold) &&
151 (SkAbs32(dg) <= threshold) &&
152 (SkAbs32(db) <= threshold));
153}
154
155const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
156const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
157
158void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold) {
159 const int w = dr->fComparison.fBitmap.width();
160 const int h = dr->fComparison.fBitmap.height();
161 if (w != dr->fBase.fBitmap.width() || h != dr->fBase.fBitmap.height()) {
162 dr->fResult = DiffRecord::kDifferentSizes_Result;
163 return;
164 }
165
166 SkAutoLockPixels alpDiff(dr->fDifference.fBitmap);
167 SkAutoLockPixels alpWhite(dr->fWhite.fBitmap);
168 int mismatchedPixels = 0;
rmistry@google.comee5a5ee2013-01-03 19:23:22 +0000169 int totalMismatchA = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000170 int totalMismatchR = 0;
171 int totalMismatchG = 0;
172 int totalMismatchB = 0;
173
174 // Accumulate fractionally different pixels, then divide out
175 // # of pixels at the end.
176 dr->fWeightedFraction = 0;
177 for (int y = 0; y < h; y++) {
178 for (int x = 0; x < w; x++) {
179 SkPMColor c0 = *dr->fBase.fBitmap.getAddr32(x, y);
180 SkPMColor c1 = *dr->fComparison.fBitmap.getAddr32(x, y);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000181 SkPMColor outputDifference = diffFunction(c0, c1);
rmistry@google.comee5a5ee2013-01-03 19:23:22 +0000182 uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
183 uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
184 uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
185 uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
186 totalMismatchA += thisA;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000187 totalMismatchR += thisR;
188 totalMismatchG += thisG;
189 totalMismatchB += thisB;
190 // In HSV, value is defined as max RGB component.
191 int value = MAX3(thisR, thisG, thisB);
192 dr->fWeightedFraction += ((float) value) / 255;
rmistry@google.comee5a5ee2013-01-03 19:23:22 +0000193 if (thisA > dr->fMaxMismatchA) {
194 dr->fMaxMismatchA = thisA;
195 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000196 if (thisR > dr->fMaxMismatchR) {
197 dr->fMaxMismatchR = thisR;
198 }
199 if (thisG > dr->fMaxMismatchG) {
200 dr->fMaxMismatchG = thisG;
201 }
202 if (thisB > dr->fMaxMismatchB) {
203 dr->fMaxMismatchB = thisB;
204 }
205 if (!colors_match_thresholded(c0, c1, colorThreshold)) {
206 mismatchedPixels++;
207 *dr->fDifference.fBitmap.getAddr32(x, y) = outputDifference;
208 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_WHITE;
209 } else {
210 *dr->fDifference.fBitmap.getAddr32(x, y) = 0;
211 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_BLACK;
212 }
213 }
214 }
215 if (0 == mismatchedPixels) {
216 dr->fResult = DiffRecord::kEqualPixels_Result;
217 return;
218 }
219 dr->fResult = DiffRecord::kDifferentPixels_Result;
220 int pixelCount = w * h;
221 dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
222 dr->fWeightedFraction /= pixelCount;
rmistry@google.comee5a5ee2013-01-03 19:23:22 +0000223 dr->fTotalMismatchA = totalMismatchA;
224 dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000225 dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
226 dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
227 dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
228}