| /* | 
 |  * Copyright 2012 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "skdiff.h" | 
 | #include "SkBitmap.h" | 
 | #include "SkColor.h" | 
 | #include "SkColorPriv.h" | 
 | #include "SkTypes.h" | 
 |  | 
 | /*static*/ char const * const DiffRecord::ResultNames[DiffRecord::kResultCount] = { | 
 |     "EqualBits", | 
 |     "EqualPixels", | 
 |     "DifferentPixels", | 
 |     "DifferentSizes", | 
 |     "CouldNotCompare", | 
 |     "Unknown", | 
 | }; | 
 |  | 
 | DiffRecord::Result DiffRecord::getResultByName(const char *name) { | 
 |     for (int result = 0; result < DiffRecord::kResultCount; ++result) { | 
 |         if (0 == strcmp(DiffRecord::ResultNames[result], name)) { | 
 |             return static_cast<DiffRecord::Result>(result); | 
 |         } | 
 |     } | 
 |     return DiffRecord::kResultCount; | 
 | } | 
 |  | 
 | static char const * const ResultDescriptions[DiffRecord::kResultCount] = { | 
 |     "contain exactly the same bits", | 
 |     "contain the same pixel values, but not the same bits", | 
 |     "have identical dimensions but some differing pixels", | 
 |     "have differing dimensions", | 
 |     "could not be compared", | 
 |     "not compared yet", | 
 | }; | 
 |  | 
 | const char* DiffRecord::getResultDescription(DiffRecord::Result result) { | 
 |     return ResultDescriptions[result]; | 
 | } | 
 |  | 
 | /*static*/ char const * const DiffResource::StatusNames[DiffResource::kStatusCount] = { | 
 |     "Decoded", | 
 |     "CouldNotDecode", | 
 |  | 
 |     "Read", | 
 |     "CouldNotRead", | 
 |  | 
 |     "Exists", | 
 |     "DoesNotExist", | 
 |  | 
 |     "Specified", | 
 |     "Unspecified", | 
 |  | 
 |     "Unknown", | 
 | }; | 
 |  | 
 | DiffResource::Status DiffResource::getStatusByName(const char *name) { | 
 |     for (int status = 0; status < DiffResource::kStatusCount; ++status) { | 
 |         if (0 == strcmp(DiffResource::StatusNames[status], name)) { | 
 |             return static_cast<DiffResource::Status>(status); | 
 |         } | 
 |     } | 
 |     return DiffResource::kStatusCount; | 
 | } | 
 |  | 
 | static char const * const StatusDescriptions[DiffResource::kStatusCount] = { | 
 |     "decoded", | 
 |     "could not be decoded", | 
 |  | 
 |     "read", | 
 |     "could not be read", | 
 |  | 
 |     "found", | 
 |     "not found", | 
 |  | 
 |     "specified", | 
 |     "unspecified", | 
 |  | 
 |     "unknown", | 
 | }; | 
 |  | 
 | const char* DiffResource::getStatusDescription(DiffResource::Status status) { | 
 |     return StatusDescriptions[status]; | 
 | } | 
 |  | 
 | bool DiffResource::isStatusFailed(DiffResource::Status status) { | 
 |     return DiffResource::kCouldNotDecode_Status == status || | 
 |            DiffResource::kCouldNotRead_Status == status || | 
 |            DiffResource::kDoesNotExist_Status == status || | 
 |            DiffResource::kUnspecified_Status == status || | 
 |            DiffResource::kUnknown_Status == status; | 
 | } | 
 |  | 
 | bool DiffResource::getMatchingStatuses(char* selector, bool statuses[kStatusCount]) { | 
 |     if (!strcmp(selector, "any")) { | 
 |         for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) { | 
 |             statuses[statusIndex] = true; | 
 |         } | 
 |         return true; | 
 |     } | 
 |  | 
 |     for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) { | 
 |         statuses[statusIndex] = false; | 
 |     } | 
 |  | 
 |     static const char kDelimiterChar = ','; | 
 |     bool understood = true; | 
 |     while (true) { | 
 |         char* delimiterPtr = strchr(selector, kDelimiterChar); | 
 |  | 
 |         if (delimiterPtr) { | 
 |             *delimiterPtr = '\0'; | 
 |         } | 
 |  | 
 |         if (!strcmp(selector, "failed")) { | 
 |             for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) { | 
 |                 Status status = static_cast<Status>(statusIndex); | 
 |                 statuses[statusIndex] |= isStatusFailed(status); | 
 |             } | 
 |         } else { | 
 |             Status status = getStatusByName(selector); | 
 |             if (status == kStatusCount) { | 
 |                 understood = false; | 
 |             } else { | 
 |                 statuses[status] = true; | 
 |             } | 
 |         } | 
 |  | 
 |         if (!delimiterPtr) { | 
 |             break; | 
 |         } | 
 |  | 
 |         *delimiterPtr = kDelimiterChar; | 
 |         selector = delimiterPtr + 1; | 
 |     } | 
 |     return understood; | 
 | } | 
 |  | 
 | static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1, const int threshold) { | 
 |     int da = SkGetPackedA32(c0) - SkGetPackedA32(c1); | 
 |     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1); | 
 |     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1); | 
 |     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1); | 
 |  | 
 |     return ((SkAbs32(da) <= threshold) && | 
 |             (SkAbs32(dr) <= threshold) && | 
 |             (SkAbs32(dg) <= threshold) && | 
 |             (SkAbs32(db) <= threshold)); | 
 | } | 
 |  | 
 | const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE); | 
 | const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK); | 
 |  | 
 | void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold) { | 
 |     const int w = dr->fComparison.fBitmap.width(); | 
 |     const int h = dr->fComparison.fBitmap.height(); | 
 |     if (w != dr->fBase.fBitmap.width() || h != dr->fBase.fBitmap.height()) { | 
 |         dr->fResult = DiffRecord::kDifferentSizes_Result; | 
 |         return; | 
 |     } | 
 |  | 
 |     SkAutoLockPixels alpDiff(dr->fDifference.fBitmap); | 
 |     SkAutoLockPixels alpWhite(dr->fWhite.fBitmap); | 
 |     int mismatchedPixels = 0; | 
 |     int totalMismatchA = 0; | 
 |     int totalMismatchR = 0; | 
 |     int totalMismatchG = 0; | 
 |     int totalMismatchB = 0; | 
 |  | 
 |     // Accumulate fractionally different pixels, then divide out | 
 |     // # of pixels at the end. | 
 |     dr->fWeightedFraction = 0; | 
 |     for (int y = 0; y < h; y++) { | 
 |         for (int x = 0; x < w; x++) { | 
 |             SkPMColor c0 = *dr->fBase.fBitmap.getAddr32(x, y); | 
 |             SkPMColor c1 = *dr->fComparison.fBitmap.getAddr32(x, y); | 
 |             SkPMColor outputDifference = diffFunction(c0, c1); | 
 |             uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1)); | 
 |             uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1)); | 
 |             uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1)); | 
 |             uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1)); | 
 |             totalMismatchA += thisA; | 
 |             totalMismatchR += thisR; | 
 |             totalMismatchG += thisG; | 
 |             totalMismatchB += thisB; | 
 |             // In HSV, value is defined as max RGB component. | 
 |             int value = MAX3(thisR, thisG, thisB); | 
 |             dr->fWeightedFraction += ((float) value) / 255; | 
 |             if (thisA > dr->fMaxMismatchA) { | 
 |                 dr->fMaxMismatchA = thisA; | 
 |             } | 
 |             if (thisR > dr->fMaxMismatchR) { | 
 |                 dr->fMaxMismatchR = thisR; | 
 |             } | 
 |             if (thisG > dr->fMaxMismatchG) { | 
 |                 dr->fMaxMismatchG = thisG; | 
 |             } | 
 |             if (thisB > dr->fMaxMismatchB) { | 
 |                 dr->fMaxMismatchB = thisB; | 
 |             } | 
 |             if (!colors_match_thresholded(c0, c1, colorThreshold)) { | 
 |                 mismatchedPixels++; | 
 |                 *dr->fDifference.fBitmap.getAddr32(x, y) = outputDifference; | 
 |                 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_WHITE; | 
 |             } else { | 
 |                 *dr->fDifference.fBitmap.getAddr32(x, y) = 0; | 
 |                 *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_BLACK; | 
 |             } | 
 |         } | 
 |     } | 
 |     if (0 == mismatchedPixels) { | 
 |         dr->fResult = DiffRecord::kEqualPixels_Result; | 
 |         return; | 
 |     } | 
 |     dr->fResult = DiffRecord::kDifferentPixels_Result; | 
 |     int pixelCount = w * h; | 
 |     dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount; | 
 |     dr->fWeightedFraction /= pixelCount; | 
 |     dr->fTotalMismatchA = totalMismatchA; | 
 |     dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount; | 
 |     dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount; | 
 |     dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount; | 
 |     dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount; | 
 | } |