1 /*
2  * Copyright 2013 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 SkDiffContext_DEFINED
9 #define SkDiffContext_DEFINED
10 
11 #include "SkImageDiffer.h"
12 #include "SkString.h"
13 #include "SkTArray.h"
14 #include "SkTDArray.h"
15 #include "SkTLList.h"
16 #include "SkThread.h"
17 
18 class SkWStream;
19 
20 /**
21  * Collects records of diffs and outputs them as JSON.
22  */
23 class SkDiffContext {
24 public:
25     SkDiffContext();
26     ~SkDiffContext();
27 
setThreadCount(int threadCount)28     void setThreadCount(int threadCount) { fThreadCount = threadCount; }
29 
30     /**
31      * Sets the directory within which to store alphaMasks (images that
32      * are transparent for each pixel that differs between baseline and test).
33      *
34      * If the directory does not exist yet, it will be created.
35      */
36     void setAlphaMaskDir(const SkString& directory);
37 
38     /**
39      * Sets the directory within which to store rgbDiffs (images showing the
40      * per-channel difference between baseline and test at each pixel).
41      *
42      * If the directory does not exist yet, it will be created.
43      */
44     void setRgbDiffDir(const SkString& directory);
45 
46     /**
47      * Sets the directory within which to store whiteDiffs (images showing white
48      * for each pixel that differs between baseline and test).
49      *
50      * If the directory does not exist yet, it will be created.
51      */
52     void setWhiteDiffDir(const SkString& directory);
53 
54     /**
55      * Modify the pattern used to generate commonName (= the
56      * basename of rgb/white diff files).
57      *
58      * - true: basename is a combination of the input file names.
59      * - false: basename is the common prefix of the input file names.
60      *
61      * For example, for:
62      *   baselinePath=/tmp/dir/image-before.png
63      *   testPath=/tmp/dir/image-after.png
64      *
65      * If setLongNames(true), commonName would be:
66      *    image-before-png-vs-image-after-png.png
67      *
68      * If setLongNames(false), commonName would be:
69      *   image-.png
70      */
71     void setLongNames(const bool useLongNames);
72 
73     /**
74      * Sets the differs to be used in each diff. Already started diffs will not retroactively use
75      * these.
76      * @param differs An array of differs to use. The array is copied, but not the differs
77      *                themselves.
78      */
79     void setDiffers(const SkTDArray<SkImageDiffer*>& differs);
80 
81     /**
82      * Compares two directories of images with the given differ
83      * @param baselinePath The baseline directory's path
84      * @param testPath     The test directory's path
85      */
86     void diffDirectories(const char baselinePath[], const char testPath[]);
87 
88     /**
89      * Compares two sets of images identified by glob style patterns with the given differ
90      * @param baselinePattern A pattern for baseline files
91      * @param testPattern     A pattern for test files that matches each file of the baseline file
92      */
93     void diffPatterns(const char baselinePattern[], const char testPattern[]);
94 
95     /**
96      * Compares the images at the given paths
97      * @param baselinePath The baseline file path
98      * @param testPath     The matching test file path
99      */
100     void addDiff(const char* baselinePath, const char* testPath);
101 
102     /**
103      * Output the records of each diff in JSON.
104      *
105      * The format of the JSON document is one top level array named "records".
106      * Each record in the array is an object with the following values:
107      *    "commonName"     : string containing the output filename (basename)
108      *                       depending on the value of 'longNames'.
109      *                       (see 'setLongNames' for an explanation and example).
110      *    "baselinePath"   : string containing the path to the baseline image
111      *    "testPath"       : string containing the path to the test image
112      *    "differencePath" : (optional) string containing the path to an alpha
113      *                       mask of the pixel difference between the baseline
114      *                       and test images
115      *                       TODO(epoger): consider renaming this "alphaMaskPath"
116      *                       to distinguish from other difference types?
117      *    "rgbDiffPath"    : (optional) string containing the path to a bitmap
118      *                       showing per-channel differences between the
119      *                       baseline and test images at each pixel
120      *    "whiteDiffPath"  : (optional) string containing the path to a bitmap
121      *                       showing every pixel that differs between the
122      *                       baseline and test images as white
123      *
124      * They also have an array named "diffs" with each element being one diff record for the two
125      * images indicated in the above field.
126      * A diff record includes:
127      *    "differName"       : string name of the diff metric used
128      *    "result"           : numerical result of the diff
129      *
130      * Here is an example:
131      *
132      * {
133      *     "records": [
134      *         {
135      *             "commonName": "queue.png",
136      *             "baselinePath": "/a/queue.png",
137      *             "testPath": "/b/queue.png",
138      *             "diffs": [
139      *                 {
140      *                     "differName": "different_pixels",
141      *                     "result": 1,
142      *                 }
143      *             ]
144      *         }
145      *     ]
146      * }
147      *
148      * @param stream   The stream to output the diff to
149      * @param useJSONP True to adding padding to the JSON output to make it cross-site requestable.
150      */
151     void outputRecords(SkWStream& stream, bool useJSONP);
152 
153     /**
154      * Output the records score in csv format.
155      */
156     void outputCsv(SkWStream& stream);
157 
158 
159 private:
160     struct DiffData {
161         const char* fDiffName;
162         SkImageDiffer::Result fResult;
163     };
164 
165     struct DiffRecord {
166         // TODO(djsollen): Some of these fields are required, while others are optional
167         // (e.g., fRgbDiffPath is only filled in if SkDifferentPixelsMetric
168         // was run).  Figure out a way to note that.  See http://skbug.com/2712
169         // ('allow skpdiff to report different sets of result fields for
170         // different comparison algorithms')
171         SkString           fCommonName;
172         SkString           fAlphaMaskPath;
173         SkString           fRgbDiffPath;
174         SkString           fWhiteDiffPath;
175         SkString           fBaselinePath;
176         SkString               fTestPath;
177         SkISize                    fSize;
178         int                  fMaxRedDiff;
179         int                fMaxGreenDiff;
180         int                 fMaxBlueDiff;
181         SkTArray<DiffData>        fDiffs;
182     };
183 
184     // Used to protect access to fRecords and ensure only one thread is
185     // adding new entries at a time.
186     SkMutex fRecordMutex;
187 
188     // We use linked list for the records so that their pointers remain stable. A resizable array
189     // might change its pointers, which would make it harder for async diffs to record their
190     // results.
191     SkTLList<DiffRecord> fRecords;
192 
193     SkImageDiffer** fDiffers;
194     int fDifferCount;
195     int fThreadCount;
196 
197     SkString fAlphaMaskDir;
198     SkString fRgbDiffDir;
199     SkString fWhiteDiffDir;
200     bool longNames;
201 };
202 
203 #endif
204