Home | History | Annotate | Download | only in skdiff
      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 #include "skdiff.h"
      9 #include "SkBitmap.h"
     10 #include "SkColor.h"
     11 #include "SkColorPriv.h"
     12 #include "SkTypes.h"
     13 
     14 /*static*/ char const * const DiffRecord::ResultNames[DiffRecord::kResultCount] = {
     15     "EqualBits",
     16     "EqualPixels",
     17     "DifferentPixels",
     18     "DifferentSizes",
     19     "CouldNotCompare",
     20     "Unknown",
     21 };
     22 
     23 DiffRecord::Result DiffRecord::getResultByName(const char *name) {
     24     for (int result = 0; result < DiffRecord::kResultCount; ++result) {
     25         if (0 == strcmp(DiffRecord::ResultNames[result], name)) {
     26             return static_cast<DiffRecord::Result>(result);
     27         }
     28     }
     29     return DiffRecord::kResultCount;
     30 }
     31 
     32 static char const * const ResultDescriptions[DiffRecord::kResultCount] = {
     33     "contain exactly the same bits",
     34     "contain the same pixel values, but not the same bits",
     35     "have identical dimensions but some differing pixels",
     36     "have differing dimensions",
     37     "could not be compared",
     38     "not compared yet",
     39 };
     40 
     41 const char* DiffRecord::getResultDescription(DiffRecord::Result result) {
     42     return ResultDescriptions[result];
     43 }
     44 
     45 /*static*/ char const * const DiffResource::StatusNames[DiffResource::kStatusCount] = {
     46     "Decoded",
     47     "CouldNotDecode",
     48 
     49     "Read",
     50     "CouldNotRead",
     51 
     52     "Exists",
     53     "DoesNotExist",
     54 
     55     "Specified",
     56     "Unspecified",
     57 
     58     "Unknown",
     59 };
     60 
     61 DiffResource::Status DiffResource::getStatusByName(const char *name) {
     62     for (int status = 0; status < DiffResource::kStatusCount; ++status) {
     63         if (0 == strcmp(DiffResource::StatusNames[status], name)) {
     64             return static_cast<DiffResource::Status>(status);
     65         }
     66     }
     67     return DiffResource::kStatusCount;
     68 }
     69 
     70 static char const * const StatusDescriptions[DiffResource::kStatusCount] = {
     71     "decoded",
     72     "could not be decoded",
     73 
     74     "read",
     75     "could not be read",
     76 
     77     "found",
     78     "not found",
     79 
     80     "specified",
     81     "unspecified",
     82 
     83     "unknown",
     84 };
     85 
     86 const char* DiffResource::getStatusDescription(DiffResource::Status status) {
     87     return StatusDescriptions[status];
     88 }
     89 
     90 bool DiffResource::isStatusFailed(DiffResource::Status status) {
     91     return DiffResource::kCouldNotDecode_Status == status ||
     92            DiffResource::kCouldNotRead_Status == status ||
     93            DiffResource::kDoesNotExist_Status == status ||
     94            DiffResource::kUnspecified_Status == status ||
     95            DiffResource::kUnknown_Status == status;
     96 }
     97 
     98 bool DiffResource::getMatchingStatuses(char* selector, bool statuses[kStatusCount]) {
     99     if (!strcmp(selector, "any")) {
    100         for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
    101             statuses[statusIndex] = true;
    102         }
    103         return true;
    104     }
    105 
    106     for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
    107         statuses[statusIndex] = false;
    108     }
    109 
    110     static const char kDelimiterChar = ',';
    111     bool understood = true;
    112     while (true) {
    113         char* delimiterPtr = strchr(selector, kDelimiterChar);
    114 
    115         if (delimiterPtr) {
    116             *delimiterPtr = '\0';
    117         }
    118 
    119         if (!strcmp(selector, "failed")) {
    120             for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
    121                 Status status = static_cast<Status>(statusIndex);
    122                 statuses[statusIndex] |= isStatusFailed(status);
    123             }
    124         } else {
    125             Status status = getStatusByName(selector);
    126             if (status == kStatusCount) {
    127                 understood = false;
    128             } else {
    129                 statuses[status] = true;
    130             }
    131         }
    132 
    133         if (!delimiterPtr) {
    134             break;
    135         }
    136 
    137         *delimiterPtr = kDelimiterChar;
    138         selector = delimiterPtr + 1;
    139     }
    140     return understood;
    141 }
    142 
    143 static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1, const int threshold) {
    144     int da = SkGetPackedA32(c0) - SkGetPackedA32(c1);
    145     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
    146     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
    147     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
    148 
    149     return ((SkAbs32(da) <= threshold) &&
    150             (SkAbs32(dr) <= threshold) &&
    151             (SkAbs32(dg) <= threshold) &&
    152             (SkAbs32(db) <= threshold));
    153 }
    154 
    155 const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
    156 const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
    157 
    158 void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold) {
    159     const int w = dr->fComparison.fBitmap.width();
    160     const int h = dr->fComparison.fBitmap.height();
    161     if (w != dr->fBase.fBitmap.width() || h != dr->fBase.fBitmap.height()) {
    162         dr->fResult = DiffRecord::kDifferentSizes_Result;
    163         return;
    164     }
    165 
    166     int mismatchedPixels = 0;
    167     int totalMismatchA = 0;
    168     int totalMismatchR = 0;
    169     int totalMismatchG = 0;
    170     int totalMismatchB = 0;
    171 
    172     // Accumulate fractionally different pixels, then divide out
    173     // # of pixels at the end.
    174     dr->fWeightedFraction = 0;
    175     for (int y = 0; y < h; y++) {
    176         for (int x = 0; x < w; x++) {
    177             SkPMColor c0 = *dr->fBase.fBitmap.getAddr32(x, y);
    178             SkPMColor c1 = *dr->fComparison.fBitmap.getAddr32(x, y);
    179             SkPMColor outputDifference = diffFunction(c0, c1);
    180             uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
    181             uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
    182             uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
    183             uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
    184             totalMismatchA += thisA;
    185             totalMismatchR += thisR;
    186             totalMismatchG += thisG;
    187             totalMismatchB += thisB;
    188             // In HSV, value is defined as max RGB component.
    189             int value = MAX3(thisR, thisG, thisB);
    190             dr->fWeightedFraction += ((float) value) / 255;
    191             if (thisA > dr->fMaxMismatchA) {
    192                 dr->fMaxMismatchA = thisA;
    193             }
    194             if (thisR > dr->fMaxMismatchR) {
    195                 dr->fMaxMismatchR = thisR;
    196             }
    197             if (thisG > dr->fMaxMismatchG) {
    198                 dr->fMaxMismatchG = thisG;
    199             }
    200             if (thisB > dr->fMaxMismatchB) {
    201                 dr->fMaxMismatchB = thisB;
    202             }
    203             if (!colors_match_thresholded(c0, c1, colorThreshold)) {
    204                 mismatchedPixels++;
    205                 *dr->fDifference.fBitmap.getAddr32(x, y) = outputDifference;
    206                 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_WHITE;
    207             } else {
    208                 *dr->fDifference.fBitmap.getAddr32(x, y) = 0;
    209                 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_BLACK;
    210             }
    211         }
    212     }
    213     if (0 == mismatchedPixels) {
    214         dr->fResult = DiffRecord::kEqualPixels_Result;
    215         return;
    216     }
    217     dr->fResult = DiffRecord::kDifferentPixels_Result;
    218     int pixelCount = w * h;
    219     dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
    220     dr->fWeightedFraction /= pixelCount;
    221     dr->fTotalMismatchA = totalMismatchA;
    222     dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount;
    223     dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
    224     dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
    225     dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
    226 }
    227