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