aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/skdiff.h
blob: b9e69ced8508866ecd92782fc71b25dc7766757e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef skdiff_DEFINED
#define skdiff_DEFINED

#include "SkBitmap.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkString.h"
#include "SkTDArray.h"

#if SK_BUILD_FOR_WIN32
    #define PATH_DIV_STR "\\"
    #define PATH_DIV_CHAR '\\'
#else
    #define PATH_DIV_STR "/"
    #define PATH_DIV_CHAR '/'
#endif

#define MAX2(a,b) (((b) < (a)) ? (a) : (b))
#define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))


struct DiffResource {
    enum Status {
        /** The resource was specified, exists, read, and decoded. */
        kDecoded_Status,
        /** The resource was specified, exists, read, but could not be decoded. */
        kCouldNotDecode_Status,

        /** The resource was specified, exists, and read. */
        kRead_Status,
        /** The resource was specified, exists, but could not be read. */
        kCouldNotRead_Status,

        /** The resource was specified and exists. */
        kExists_Status,
        /** The resource was specified, but does not exist. */
        kDoesNotExist_Status,

        /** The resource was specified. */
        kSpecified_Status,
        /** The resource was not specified. */
        kUnspecified_Status,

        /** Nothing is yet known about the resource. */
        kUnknown_Status,

        /** NOT A VALID VALUE -- used to set up arrays and to represent an unknown value. */
        kStatusCount
    };
    static char const * const StatusNames[DiffResource::kStatusCount];

    /** Returns the Status with this name.
     *  If there is no Status with this name, returns kStatusCount.
     */
    static Status getStatusByName(const char *name);

    /** Returns a text description of the given Status type. */
    static const char *getStatusDescription(Status status);

    /** Returns true if the Status indicates some kind of failure. */
    static bool isStatusFailed(Status status);

    /** Sets statuses[i] if it is implied by selector, unsets it if not.
     *  Selector may be a comma delimited list of status names, "any", or "failed".
     *  Returns true if the selector was entirely understood, false otherwise.
     */
    static bool getMatchingStatuses(char* selector, bool statuses[kStatusCount]);

    DiffResource() : fFilename(), fFullPath(), fBitmap(), fStatus(kUnknown_Status) { };

    /** If isEmpty() indicates no filename available. */
    SkString fFilename;
    /** If isEmpty() indicates no path available. */
    SkString fFullPath;
    /** If empty() indicates the bitmap could not be created. */
    SkBitmap fBitmap;
    Status fStatus;
};

struct DiffRecord {

    // Result of comparison for each pair of files.
    // Listed from "better" to "worse", for sorting of results.
    enum Result {
        kEqualBits_Result,
        kEqualPixels_Result,
        kDifferentPixels_Result,
        kDifferentSizes_Result,
        kCouldNotCompare_Result,
        kUnknown_Result,

        kResultCount  // NOT A VALID VALUE--used to set up arrays. Must be last.
    };
    static char const * const ResultNames[DiffRecord::kResultCount];

    /** Returns the Result with this name.
     *  If there is no Result with this name, returns kResultCount.
     */
    static Result getResultByName(const char *name);

    /** Returns a text description of the given Result type. */
    static const char *getResultDescription(Result result);

    DiffRecord()
        : fBase()
        , fComparison()
        , fDifference()
        , fWhite()
        , fFractionDifference(0)
        , fWeightedFraction(0)
        , fAverageMismatchR(0)
        , fAverageMismatchG(0)
        , fAverageMismatchB(0)
        , fMaxMismatchR(0)
        , fMaxMismatchG(0)
        , fMaxMismatchB(0)
        , fResult(kUnknown_Result) {
    };

    DiffResource fBase;
    DiffResource fComparison;
    DiffResource fDifference;
    DiffResource fWhite;

    /// Arbitrary floating-point metric to be used to sort images from most
    /// to least different from baseline; values of 0 will be omitted from the
    /// summary webpage.
    float fFractionDifference;
    float fWeightedFraction;

    float fAverageMismatchR;
    float fAverageMismatchG;
    float fAverageMismatchB;

    uint32_t fMaxMismatchR;
    uint32_t fMaxMismatchG;
    uint32_t fMaxMismatchB;

    /// Which category of diff result.
    Result fResult;
};

typedef SkTDArray<DiffRecord*> RecordArray;

/// A wrapper for any sortProc (comparison routine) which applies a first-order
/// sort beforehand, and a tiebreaker if the sortProc returns 0.
template<typename T> static int compare(const void* untyped_lhs, const void* untyped_rhs) {
    const DiffRecord* lhs = *reinterpret_cast<DiffRecord* const *>(untyped_lhs);
    const DiffRecord* rhs = *reinterpret_cast<DiffRecord* const *>(untyped_rhs);

    // First-order sort... these comparisons should be applied before comparing
    // pixel values, no matter what.
    if (lhs->fResult != rhs->fResult) {
        return (lhs->fResult < rhs->fResult) ? 1 : -1;
    }

    // Passed first-order sort, so call the pixel comparison routine.
    int result = T::comparePixels(lhs, rhs);
    if (result != 0) {
        return result;
    }

    // Tiebreaker... if we got to this point, we don't really care
    // which order they are sorted in, but let's at least be consistent.
    return strcmp(lhs->fBase.fFilename.c_str(), rhs->fBase.fFilename.c_str());
}

/// Comparison routine for qsort; sorts by fFractionDifference
/// from largest to smallest.
class CompareDiffMetrics {
public:
    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
        if (lhs->fFractionDifference < rhs->fFractionDifference) {
          return 1;
        }
        if (rhs->fFractionDifference < lhs->fFractionDifference) {
          return -1;
        }
        return 0;
    }
};

class CompareDiffWeighted {
public:
    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
        if (lhs->fWeightedFraction < rhs->fWeightedFraction) {
            return 1;
        }
        if (lhs->fWeightedFraction > rhs->fWeightedFraction) {
            return -1;
        }
        return 0;
    }
};

/// Comparison routine for qsort;  sorts by max(fAverageMismatch{RGB})
/// from largest to smallest.
class CompareDiffMeanMismatches {
public:
    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
        float leftValue = MAX3(lhs->fAverageMismatchR,
                               lhs->fAverageMismatchG,
                               lhs->fAverageMismatchB);
        float rightValue = MAX3(rhs->fAverageMismatchR,
                                rhs->fAverageMismatchG,
                                rhs->fAverageMismatchB);
        if (leftValue < rightValue) {
            return 1;
        }
        if (rightValue < leftValue) {
            return -1;
        }
        return 0;
    }
};

/// Comparison routine for qsort;  sorts by max(fMaxMismatch{RGB})
/// from largest to smallest.
class CompareDiffMaxMismatches {
public:
    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
        uint32_t leftValue = MAX3(lhs->fMaxMismatchR,
                                  lhs->fMaxMismatchG,
                                  lhs->fMaxMismatchB);
        uint32_t rightValue = MAX3(rhs->fMaxMismatchR,
                                   rhs->fMaxMismatchG,
                                   rhs->fMaxMismatchB);
        if (leftValue < rightValue) {
            return 1;
        }
        if (rightValue < leftValue) {
            return -1;
        }

        return CompareDiffMeanMismatches::comparePixels(lhs, rhs);
    }
};


/// Parameterized routine to compute the color of a pixel in a difference image.
typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);

// from gm
static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
    int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
    int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
    int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);

    return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
}

/** When finished, dr->fResult should have some value other than kUnknown_Result.
 *  Expects dr->fWhite.fBitmap and dr->fDifference.fBitmap to have the same bounds as
 *  dr->fBase.fBitmap and have a valid pixelref.
 */
void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold);

#endif