1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skdiff_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skdiff_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorPriv.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
16*c8dee2aaSAndroid Build Coastguard Worker
17*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_WIN)
18*c8dee2aaSAndroid Build Coastguard Worker #define PATH_DIV_STR "\\"
19*c8dee2aaSAndroid Build Coastguard Worker #define PATH_DIV_CHAR '\\'
20*c8dee2aaSAndroid Build Coastguard Worker #else
21*c8dee2aaSAndroid Build Coastguard Worker #define PATH_DIV_STR "/"
22*c8dee2aaSAndroid Build Coastguard Worker #define PATH_DIV_CHAR '/'
23*c8dee2aaSAndroid Build Coastguard Worker #endif
24*c8dee2aaSAndroid Build Coastguard Worker
25*c8dee2aaSAndroid Build Coastguard Worker #define MAX2(a,b) (((b) < (a)) ? (a) : (b))
26*c8dee2aaSAndroid Build Coastguard Worker #define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker struct DiffResource {
30*c8dee2aaSAndroid Build Coastguard Worker enum Status {
31*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified, exists, read, and decoded. */
32*c8dee2aaSAndroid Build Coastguard Worker kDecoded_Status,
33*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified, exists, read, but could not be decoded. */
34*c8dee2aaSAndroid Build Coastguard Worker kCouldNotDecode_Status,
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified, exists, and read. */
37*c8dee2aaSAndroid Build Coastguard Worker kRead_Status,
38*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified, exists, but could not be read. */
39*c8dee2aaSAndroid Build Coastguard Worker kCouldNotRead_Status,
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified and exists. */
42*c8dee2aaSAndroid Build Coastguard Worker kExists_Status,
43*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified, but does not exist. */
44*c8dee2aaSAndroid Build Coastguard Worker kDoesNotExist_Status,
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker /** The resource was specified. */
47*c8dee2aaSAndroid Build Coastguard Worker kSpecified_Status,
48*c8dee2aaSAndroid Build Coastguard Worker /** The resource was not specified. */
49*c8dee2aaSAndroid Build Coastguard Worker kUnspecified_Status,
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker /** Nothing is yet known about the resource. */
52*c8dee2aaSAndroid Build Coastguard Worker kUnknown_Status,
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker /** NOT A VALID VALUE -- used to set up arrays and to represent an unknown value. */
55*c8dee2aaSAndroid Build Coastguard Worker kStatusCount
56*c8dee2aaSAndroid Build Coastguard Worker };
57*c8dee2aaSAndroid Build Coastguard Worker static char const * const StatusNames[DiffResource::kStatusCount];
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker /** Returns the Status with this name.
60*c8dee2aaSAndroid Build Coastguard Worker * If there is no Status with this name, returns kStatusCount.
61*c8dee2aaSAndroid Build Coastguard Worker */
62*c8dee2aaSAndroid Build Coastguard Worker static Status getStatusByName(const char *name);
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker /** Returns a text description of the given Status type. */
65*c8dee2aaSAndroid Build Coastguard Worker static const char *getStatusDescription(Status status);
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker /** Returns true if the Status indicates some kind of failure. */
68*c8dee2aaSAndroid Build Coastguard Worker static bool isStatusFailed(Status status);
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker /** Sets statuses[i] if it is implied by selector, unsets it if not.
71*c8dee2aaSAndroid Build Coastguard Worker * Selector may be a comma delimited list of status names, "any", or "failed".
72*c8dee2aaSAndroid Build Coastguard Worker * Returns true if the selector was entirely understood, false otherwise.
73*c8dee2aaSAndroid Build Coastguard Worker */
74*c8dee2aaSAndroid Build Coastguard Worker static bool getMatchingStatuses(char* selector, bool statuses[kStatusCount]);
75*c8dee2aaSAndroid Build Coastguard Worker
DiffResourceDiffResource76*c8dee2aaSAndroid Build Coastguard Worker DiffResource() : fFilename(), fFullPath(), fBitmap(), fStatus(kUnknown_Status) { }
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker /** If isEmpty() indicates no filename available. */
79*c8dee2aaSAndroid Build Coastguard Worker SkString fFilename;
80*c8dee2aaSAndroid Build Coastguard Worker /** If isEmpty() indicates no path available. */
81*c8dee2aaSAndroid Build Coastguard Worker SkString fFullPath;
82*c8dee2aaSAndroid Build Coastguard Worker /** If empty() indicates the bitmap could not be created. */
83*c8dee2aaSAndroid Build Coastguard Worker SkBitmap fBitmap;
84*c8dee2aaSAndroid Build Coastguard Worker Status fStatus;
85*c8dee2aaSAndroid Build Coastguard Worker };
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker struct DiffRecord {
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker // Result of comparison for each pair of files.
90*c8dee2aaSAndroid Build Coastguard Worker // Listed from "better" to "worse", for sorting of results.
91*c8dee2aaSAndroid Build Coastguard Worker enum Result {
92*c8dee2aaSAndroid Build Coastguard Worker kEqualBits_Result,
93*c8dee2aaSAndroid Build Coastguard Worker kEqualPixels_Result,
94*c8dee2aaSAndroid Build Coastguard Worker kDifferentPixels_Result,
95*c8dee2aaSAndroid Build Coastguard Worker kDifferentSizes_Result,
96*c8dee2aaSAndroid Build Coastguard Worker kCouldNotCompare_Result,
97*c8dee2aaSAndroid Build Coastguard Worker kUnknown_Result,
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker kResultCount // NOT A VALID VALUE--used to set up arrays. Must be last.
100*c8dee2aaSAndroid Build Coastguard Worker };
101*c8dee2aaSAndroid Build Coastguard Worker static char const * const ResultNames[DiffRecord::kResultCount];
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker /** Returns the Result with this name.
104*c8dee2aaSAndroid Build Coastguard Worker * If there is no Result with this name, returns kResultCount.
105*c8dee2aaSAndroid Build Coastguard Worker */
106*c8dee2aaSAndroid Build Coastguard Worker static Result getResultByName(const char *name);
107*c8dee2aaSAndroid Build Coastguard Worker
108*c8dee2aaSAndroid Build Coastguard Worker /** Returns a text description of the given Result type. */
109*c8dee2aaSAndroid Build Coastguard Worker static const char *getResultDescription(Result result);
110*c8dee2aaSAndroid Build Coastguard Worker
DiffRecordDiffRecord111*c8dee2aaSAndroid Build Coastguard Worker DiffRecord()
112*c8dee2aaSAndroid Build Coastguard Worker : fBase()
113*c8dee2aaSAndroid Build Coastguard Worker , fComparison()
114*c8dee2aaSAndroid Build Coastguard Worker , fDifference()
115*c8dee2aaSAndroid Build Coastguard Worker , fWhite()
116*c8dee2aaSAndroid Build Coastguard Worker , fFractionDifference(0)
117*c8dee2aaSAndroid Build Coastguard Worker , fWeightedFraction(0)
118*c8dee2aaSAndroid Build Coastguard Worker , fAverageMismatchA(0)
119*c8dee2aaSAndroid Build Coastguard Worker , fAverageMismatchR(0)
120*c8dee2aaSAndroid Build Coastguard Worker , fAverageMismatchG(0)
121*c8dee2aaSAndroid Build Coastguard Worker , fAverageMismatchB(0)
122*c8dee2aaSAndroid Build Coastguard Worker , fTotalMismatchA(0)
123*c8dee2aaSAndroid Build Coastguard Worker , fMaxMismatchA(0)
124*c8dee2aaSAndroid Build Coastguard Worker , fMaxMismatchR(0)
125*c8dee2aaSAndroid Build Coastguard Worker , fMaxMismatchG(0)
126*c8dee2aaSAndroid Build Coastguard Worker , fMaxMismatchB(0)
127*c8dee2aaSAndroid Build Coastguard Worker , fResult(kUnknown_Result) {
128*c8dee2aaSAndroid Build Coastguard Worker }
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker DiffResource fBase;
131*c8dee2aaSAndroid Build Coastguard Worker DiffResource fComparison;
132*c8dee2aaSAndroid Build Coastguard Worker DiffResource fDifference;
133*c8dee2aaSAndroid Build Coastguard Worker DiffResource fWhite;
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker /// Arbitrary floating-point metric to be used to sort images from most
136*c8dee2aaSAndroid Build Coastguard Worker /// to least different from baseline; values of 0 will be omitted from the
137*c8dee2aaSAndroid Build Coastguard Worker /// summary webpage.
138*c8dee2aaSAndroid Build Coastguard Worker float fFractionDifference;
139*c8dee2aaSAndroid Build Coastguard Worker float fWeightedFraction;
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker float fAverageMismatchA;
142*c8dee2aaSAndroid Build Coastguard Worker float fAverageMismatchR;
143*c8dee2aaSAndroid Build Coastguard Worker float fAverageMismatchG;
144*c8dee2aaSAndroid Build Coastguard Worker float fAverageMismatchB;
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker uint32_t fTotalMismatchA;
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker uint32_t fMaxMismatchA;
149*c8dee2aaSAndroid Build Coastguard Worker uint32_t fMaxMismatchR;
150*c8dee2aaSAndroid Build Coastguard Worker uint32_t fMaxMismatchG;
151*c8dee2aaSAndroid Build Coastguard Worker uint32_t fMaxMismatchB;
152*c8dee2aaSAndroid Build Coastguard Worker
153*c8dee2aaSAndroid Build Coastguard Worker /// Which category of diff result.
154*c8dee2aaSAndroid Build Coastguard Worker Result fResult;
155*c8dee2aaSAndroid Build Coastguard Worker };
156*c8dee2aaSAndroid Build Coastguard Worker
157*c8dee2aaSAndroid Build Coastguard Worker typedef skia_private::TArray<DiffRecord> RecordArray;
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker /// A wrapper for any sortProc (comparison routine) which applies a first-order
160*c8dee2aaSAndroid Build Coastguard Worker /// sort beforehand, and a tiebreaker if the sortProc returns 0.
compare(const void * untyped_lhs,const void * untyped_rhs)161*c8dee2aaSAndroid Build Coastguard Worker template<typename T> int compare(const void* untyped_lhs, const void* untyped_rhs) {
162*c8dee2aaSAndroid Build Coastguard Worker const DiffRecord* lhs = reinterpret_cast<DiffRecord const *>(untyped_lhs);
163*c8dee2aaSAndroid Build Coastguard Worker const DiffRecord* rhs = reinterpret_cast<DiffRecord const *>(untyped_rhs);
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker // First-order sort... these comparisons should be applied before comparing
166*c8dee2aaSAndroid Build Coastguard Worker // pixel values, no matter what.
167*c8dee2aaSAndroid Build Coastguard Worker if (lhs->fResult != rhs->fResult) {
168*c8dee2aaSAndroid Build Coastguard Worker return (lhs->fResult < rhs->fResult) ? 1 : -1;
169*c8dee2aaSAndroid Build Coastguard Worker }
170*c8dee2aaSAndroid Build Coastguard Worker
171*c8dee2aaSAndroid Build Coastguard Worker // Passed first-order sort, so call the pixel comparison routine.
172*c8dee2aaSAndroid Build Coastguard Worker int result = T::comparePixels(lhs, rhs);
173*c8dee2aaSAndroid Build Coastguard Worker if (result != 0) {
174*c8dee2aaSAndroid Build Coastguard Worker return result;
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker // Tiebreaker... if we got to this point, we don't really care
178*c8dee2aaSAndroid Build Coastguard Worker // which order they are sorted in, but let's at least be consistent.
179*c8dee2aaSAndroid Build Coastguard Worker return strcmp(lhs->fBase.fFilename.c_str(), rhs->fBase.fFilename.c_str());
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker
182*c8dee2aaSAndroid Build Coastguard Worker /// Comparison routine for qsort; sorts by fFractionDifference
183*c8dee2aaSAndroid Build Coastguard Worker /// from largest to smallest.
184*c8dee2aaSAndroid Build Coastguard Worker class CompareDiffMetrics {
185*c8dee2aaSAndroid Build Coastguard Worker public:
comparePixels(const DiffRecord * lhs,const DiffRecord * rhs)186*c8dee2aaSAndroid Build Coastguard Worker static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
187*c8dee2aaSAndroid Build Coastguard Worker if (lhs->fFractionDifference < rhs->fFractionDifference) {
188*c8dee2aaSAndroid Build Coastguard Worker return 1;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker if (rhs->fFractionDifference < lhs->fFractionDifference) {
191*c8dee2aaSAndroid Build Coastguard Worker return -1;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker return 0;
194*c8dee2aaSAndroid Build Coastguard Worker }
195*c8dee2aaSAndroid Build Coastguard Worker };
196*c8dee2aaSAndroid Build Coastguard Worker
197*c8dee2aaSAndroid Build Coastguard Worker class CompareDiffWeighted {
198*c8dee2aaSAndroid Build Coastguard Worker public:
comparePixels(const DiffRecord * lhs,const DiffRecord * rhs)199*c8dee2aaSAndroid Build Coastguard Worker static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
200*c8dee2aaSAndroid Build Coastguard Worker if (lhs->fWeightedFraction < rhs->fWeightedFraction) {
201*c8dee2aaSAndroid Build Coastguard Worker return 1;
202*c8dee2aaSAndroid Build Coastguard Worker }
203*c8dee2aaSAndroid Build Coastguard Worker if (lhs->fWeightedFraction > rhs->fWeightedFraction) {
204*c8dee2aaSAndroid Build Coastguard Worker return -1;
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker return 0;
207*c8dee2aaSAndroid Build Coastguard Worker }
208*c8dee2aaSAndroid Build Coastguard Worker };
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker /// Comparison routine for qsort; sorts by max(fAverageMismatch{RGB})
211*c8dee2aaSAndroid Build Coastguard Worker /// from largest to smallest.
212*c8dee2aaSAndroid Build Coastguard Worker class CompareDiffMeanMismatches {
213*c8dee2aaSAndroid Build Coastguard Worker public:
comparePixels(const DiffRecord * lhs,const DiffRecord * rhs)214*c8dee2aaSAndroid Build Coastguard Worker static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
215*c8dee2aaSAndroid Build Coastguard Worker float leftValue = MAX3(lhs->fAverageMismatchR,
216*c8dee2aaSAndroid Build Coastguard Worker lhs->fAverageMismatchG,
217*c8dee2aaSAndroid Build Coastguard Worker lhs->fAverageMismatchB);
218*c8dee2aaSAndroid Build Coastguard Worker float rightValue = MAX3(rhs->fAverageMismatchR,
219*c8dee2aaSAndroid Build Coastguard Worker rhs->fAverageMismatchG,
220*c8dee2aaSAndroid Build Coastguard Worker rhs->fAverageMismatchB);
221*c8dee2aaSAndroid Build Coastguard Worker if (leftValue < rightValue) {
222*c8dee2aaSAndroid Build Coastguard Worker return 1;
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker if (rightValue < leftValue) {
225*c8dee2aaSAndroid Build Coastguard Worker return -1;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker return 0;
228*c8dee2aaSAndroid Build Coastguard Worker }
229*c8dee2aaSAndroid Build Coastguard Worker };
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker /// Comparison routine for qsort; sorts by max(fMaxMismatch{RGB})
232*c8dee2aaSAndroid Build Coastguard Worker /// from largest to smallest.
233*c8dee2aaSAndroid Build Coastguard Worker class CompareDiffMaxMismatches {
234*c8dee2aaSAndroid Build Coastguard Worker public:
comparePixels(const DiffRecord * lhs,const DiffRecord * rhs)235*c8dee2aaSAndroid Build Coastguard Worker static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
236*c8dee2aaSAndroid Build Coastguard Worker uint32_t leftValue = MAX3(lhs->fMaxMismatchR,
237*c8dee2aaSAndroid Build Coastguard Worker lhs->fMaxMismatchG,
238*c8dee2aaSAndroid Build Coastguard Worker lhs->fMaxMismatchB);
239*c8dee2aaSAndroid Build Coastguard Worker uint32_t rightValue = MAX3(rhs->fMaxMismatchR,
240*c8dee2aaSAndroid Build Coastguard Worker rhs->fMaxMismatchG,
241*c8dee2aaSAndroid Build Coastguard Worker rhs->fMaxMismatchB);
242*c8dee2aaSAndroid Build Coastguard Worker if (leftValue < rightValue) {
243*c8dee2aaSAndroid Build Coastguard Worker return 1;
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker if (rightValue < leftValue) {
246*c8dee2aaSAndroid Build Coastguard Worker return -1;
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker
249*c8dee2aaSAndroid Build Coastguard Worker return CompareDiffMeanMismatches::comparePixels(lhs, rhs);
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker };
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker
254*c8dee2aaSAndroid Build Coastguard Worker /// Parameterized routine to compute the color of a pixel in a difference image.
255*c8dee2aaSAndroid Build Coastguard Worker typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);
256*c8dee2aaSAndroid Build Coastguard Worker
257*c8dee2aaSAndroid Build Coastguard Worker // from gm
compute_diff_pmcolor(SkPMColor c0,SkPMColor c1)258*c8dee2aaSAndroid Build Coastguard Worker static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
259*c8dee2aaSAndroid Build Coastguard Worker int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
260*c8dee2aaSAndroid Build Coastguard Worker int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
261*c8dee2aaSAndroid Build Coastguard Worker int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
262*c8dee2aaSAndroid Build Coastguard Worker
263*c8dee2aaSAndroid Build Coastguard Worker return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
264*c8dee2aaSAndroid Build Coastguard Worker }
265*c8dee2aaSAndroid Build Coastguard Worker
266*c8dee2aaSAndroid Build Coastguard Worker /** When finished, dr->fResult should have some value other than kUnknown_Result.
267*c8dee2aaSAndroid Build Coastguard Worker * Expects dr->fWhite.fBitmap and dr->fDifference.fBitmap to have the same bounds as
268*c8dee2aaSAndroid Build Coastguard Worker * dr->fBase.fBitmap and have a valid pixelref.
269*c8dee2aaSAndroid Build Coastguard Worker */
270*c8dee2aaSAndroid Build Coastguard Worker void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold);
271*c8dee2aaSAndroid Build Coastguard Worker
272*c8dee2aaSAndroid Build Coastguard Worker #endif
273