Home | History | Annotate | Download | only in skpdiff
      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 
     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