OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "skdiff.h" | 7 #include "skdiff.h" |
8 #include "skdiff_html.h" | 8 #include "skdiff_html.h" |
9 #include "skdiff_utils.h" | 9 #include "skdiff_utils.h" |
10 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
11 #include "SkData.h" | 11 #include "SkData.h" |
12 #include "SkForceLinking.h" | 12 #include "SkForceLinking.h" |
13 #include "SkImageDecoder.h" | 13 #include "SkImageDecoder.h" |
14 #include "SkImageEncoder.h" | 14 #include "SkImageEncoder.h" |
15 #include "SkOSFile.h" | 15 #include "SkOSFile.h" |
16 #include "SkStream.h" | 16 #include "SkStream.h" |
17 #include "SkTDArray.h" | 17 #include "SkTDArray.h" |
18 #include "SkTemplates.h" | 18 #include "SkTemplates.h" |
19 #include "SkTSearch.h" | 19 #include "SkTSearch.h" |
20 #include "SkTypes.h" | |
21 | 20 |
22 __SK_FORCE_IMAGE_DECODER_LINKING; | 21 __SK_FORCE_IMAGE_DECODER_LINKING; |
23 | 22 |
24 /** | 23 /** |
25 * skdiff | 24 * skdiff |
26 * | 25 * |
27 * Given three directory names, expects to find identically-named files in | 26 * Given three directory names, expects to find identically-named files in |
28 * each of the first two; the first are treated as a set of baseline, | 27 * each of the first two; the first are treated as a set of baseline, |
29 * the second a set of variant images, and a diff image is written into the | 28 * the second a set of variant images, and a diff image is written into the |
30 * third directory for each pair. | 29 * third directory for each pair. |
31 * Creates an index.html in the current third directory to compare each | 30 * Creates an index.html in the current third directory to compare each |
32 * pair that does not match exactly. | 31 * pair that does not match exactly. |
33 * Recursively descends directories, unless run with --norecurse. | 32 * Recursively descends directories, unless run with --norecurse. |
34 * | 33 * |
35 * Returns zero exit code if all images match across baseDir and comparisonDir. | 34 * Returns zero exit code if all images match across baseDir and comparisonDir. |
36 */ | 35 */ |
37 | 36 |
38 typedef SkTDArray<SkString*> StringArray; | 37 typedef SkTDArray<SkString*> StringArray; |
39 typedef StringArray FileArray; | 38 typedef StringArray FileArray; |
40 | 39 |
| 40 static void add_unique_basename(StringArray* array, const SkString& filename) { |
| 41 // trim off dirs |
| 42 const char* src = filename.c_str(); |
| 43 const char* trimmed = strrchr(src, SkPATH_SEPARATOR); |
| 44 if (trimmed) { |
| 45 trimmed += 1; // skip the separator |
| 46 } else { |
| 47 trimmed = src; |
| 48 } |
| 49 const char* end = strrchr(trimmed, '.'); |
| 50 if (!end) { |
| 51 end = trimmed + strlen(trimmed); |
| 52 } |
| 53 SkString result(trimmed, end - trimmed); |
| 54 |
| 55 // only add unique entries |
| 56 for (int i = 0; i < array->count(); ++i) { |
| 57 if (*array->getAt(i) == result) { |
| 58 return; |
| 59 } |
| 60 } |
| 61 *array->append() = new SkString(result); |
| 62 } |
| 63 |
41 struct DiffSummary { | 64 struct DiffSummary { |
42 DiffSummary () | 65 DiffSummary () |
43 : fNumMatches(0) | 66 : fNumMatches(0) |
44 , fNumMismatches(0) | 67 , fNumMismatches(0) |
45 , fMaxMismatchV(0) | 68 , fMaxMismatchV(0) |
46 , fMaxMismatchPercent(0) { }; | 69 , fMaxMismatchPercent(0) { }; |
47 | 70 |
48 ~DiffSummary() { | 71 ~DiffSummary() { |
49 for (int i = 0; i < DiffRecord::kResultCount; ++i) { | 72 for (int i = 0; i < DiffRecord::kResultCount; ++i) { |
50 fResultsOfType[i].deleteAll(); | 73 fResultsOfType[i].deleteAll(); |
51 } | 74 } |
52 for (int base = 0; base < DiffResource::kStatusCount; ++base) { | 75 for (int base = 0; base < DiffResource::kStatusCount; ++base) { |
53 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++
comparison) { | 76 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++
comparison) { |
54 fStatusOfType[base][comparison].deleteAll(); | 77 fStatusOfType[base][comparison].deleteAll(); |
55 } | 78 } |
56 } | 79 } |
57 } | 80 } |
58 | 81 |
59 uint32_t fNumMatches; | 82 uint32_t fNumMatches; |
60 uint32_t fNumMismatches; | 83 uint32_t fNumMismatches; |
61 uint32_t fMaxMismatchV; | 84 uint32_t fMaxMismatchV; |
62 float fMaxMismatchPercent; | 85 float fMaxMismatchPercent; |
63 | 86 |
64 FileArray fResultsOfType[DiffRecord::kResultCount]; | 87 FileArray fResultsOfType[DiffRecord::kResultCount]; |
65 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCou
nt]; | 88 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCou
nt]; |
66 | 89 |
| 90 StringArray fFailedBaseNames[DiffRecord::kResultCount]; |
| 91 |
67 void printContents(const FileArray& fileArray, | 92 void printContents(const FileArray& fileArray, |
68 const char* baseStatus, const char* comparisonStatus, | 93 const char* baseStatus, const char* comparisonStatus, |
69 bool listFilenames) { | 94 bool listFilenames) { |
70 int n = fileArray.count(); | 95 int n = fileArray.count(); |
71 printf("%d file pairs %s in baseDir and %s in comparisonDir", | 96 printf("%d file pairs %s in baseDir and %s in comparisonDir", |
72 n, baseStatus, comparisonStatus); | 97 n, baseStatus, comparisonStatus); |
73 if (listFilenames) { | 98 if (listFilenames) { |
74 printf(": "); | 99 printf(": "); |
75 for (int i = 0; i < n; ++i) { | 100 for (int i = 0; i < n; ++i) { |
76 printf("%s ", fileArray[i]->c_str()); | 101 printf("%s ", fileArray[i]->c_str()); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 } | 160 } |
136 } | 161 } |
137 printf("(results marked with [*] will cause nonzero return value)\n"); | 162 printf("(results marked with [*] will cause nonzero return value)\n"); |
138 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches); | 163 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches); |
139 if (fNumMismatches > 0) { | 164 if (fNumMismatches > 0) { |
140 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV); | 165 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV); |
141 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPe
rcent); | 166 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPe
rcent); |
142 } | 167 } |
143 } | 168 } |
144 | 169 |
| 170 void printfFailingBaseNames(const char separator[]) { |
| 171 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultIn
t) { |
| 172 const StringArray& array = fFailedBaseNames[resultInt]; |
| 173 if (array.count()) { |
| 174 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.co
unt(), separator); |
| 175 for (int j = 0; j < array.count(); ++j) { |
| 176 printf("%s%s", array[j]->c_str(), separator); |
| 177 } |
| 178 printf("\n"); |
| 179 } |
| 180 } |
| 181 } |
| 182 |
145 void add (DiffRecord* drp) { | 183 void add (DiffRecord* drp) { |
146 uint32_t mismatchValue; | 184 uint32_t mismatchValue; |
147 | 185 |
148 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) { | 186 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) { |
149 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename)
); | 187 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename)
); |
150 } else { | 188 } else { |
151 SkString* blame = new SkString("("); | 189 SkString* blame = new SkString("("); |
152 blame->append(drp->fBase.fFilename); | 190 blame->append(drp->fBase.fFilename); |
153 blame->append(", "); | 191 blame->append(", "); |
154 blame->append(drp->fComparison.fFilename); | 192 blame->append(drp->fComparison.fFilename); |
(...skipping 26 matching lines...) Expand all Loading... |
181 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push( | 219 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push( |
182 new SkString(drp->fBase.fFilename)); | 220 new SkString(drp->fBase.fFilename)); |
183 break; | 221 break; |
184 case DiffRecord::kUnknown_Result: | 222 case DiffRecord::kUnknown_Result: |
185 SkDEBUGFAIL("adding uncategorized DiffRecord"); | 223 SkDEBUGFAIL("adding uncategorized DiffRecord"); |
186 break; | 224 break; |
187 default: | 225 default: |
188 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value"); | 226 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value"); |
189 break; | 227 break; |
190 } | 228 } |
| 229 |
| 230 switch (drp->fResult) { |
| 231 case DiffRecord::kEqualBits_Result: |
| 232 case DiffRecord::kEqualPixels_Result: |
| 233 break; |
| 234 default: |
| 235 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.
fFilename); |
| 236 break; |
| 237 } |
191 } | 238 } |
192 }; | 239 }; |
193 | 240 |
194 /// Returns true if string contains any of these substrings. | 241 /// Returns true if string contains any of these substrings. |
195 static bool string_contains_any_of(const SkString& string, | 242 static bool string_contains_any_of(const SkString& string, |
196 const StringArray& substrings) { | 243 const StringArray& substrings) { |
197 for (int i = 0; i < substrings.count(); i++) { | 244 for (int i = 0; i < substrings.count(); i++) { |
198 if (string.contains(substrings[i]->c_str())) { | 245 if (string.contains(substrings[i]->c_str())) { |
199 return true; | 246 return true; |
200 } | 247 } |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 SkString outputDir; | 621 SkString outputDir; |
575 | 622 |
576 StringArray matchSubstrings; | 623 StringArray matchSubstrings; |
577 StringArray nomatchSubstrings; | 624 StringArray nomatchSubstrings; |
578 | 625 |
579 bool generateDiffs = true; | 626 bool generateDiffs = true; |
580 bool listFilenames = false; | 627 bool listFilenames = false; |
581 bool printDirNames = true; | 628 bool printDirNames = true; |
582 bool recurseIntoSubdirs = true; | 629 bool recurseIntoSubdirs = true; |
583 bool verbose = false; | 630 bool verbose = false; |
| 631 bool listFailingBase = false; |
584 | 632 |
585 RecordArray differences; | 633 RecordArray differences; |
586 DiffSummary summary; | 634 DiffSummary summary; |
587 | 635 |
588 bool failOnResultType[DiffRecord::kResultCount]; | 636 bool failOnResultType[DiffRecord::kResultCount]; |
589 for (int i = 0; i < DiffRecord::kResultCount; i++) { | 637 for (int i = 0; i < DiffRecord::kResultCount; i++) { |
590 failOnResultType[i] = false; | 638 failOnResultType[i] = false; |
591 } | 639 } |
592 | 640 |
593 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount
]; | 641 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount
]; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
698 continue; | 746 continue; |
699 case 2: | 747 case 2: |
700 outputDir.set(argv[i]); | 748 outputDir.set(argv[i]); |
701 continue; | 749 continue; |
702 default: | 750 default: |
703 SkDebugf("extra unflagged argument <%s>\n", argv[i]); | 751 SkDebugf("extra unflagged argument <%s>\n", argv[i]); |
704 usage(argv[0]); | 752 usage(argv[0]); |
705 return kGenericError; | 753 return kGenericError; |
706 } | 754 } |
707 } | 755 } |
| 756 if (!strcmp(argv[i], "--listFailingBase")) { |
| 757 listFailingBase = true; |
| 758 continue; |
| 759 } |
708 | 760 |
709 SkDebugf("Unrecognized argument <%s>\n", argv[i]); | 761 SkDebugf("Unrecognized argument <%s>\n", argv[i]); |
710 usage(argv[0]); | 762 usage(argv[0]); |
711 return kGenericError; | 763 return kGenericError; |
712 } | 764 } |
713 | 765 |
714 if (numUnflaggedArguments == 2) { | 766 if (numUnflaggedArguments == 2) { |
715 outputDir = comparisonDir; | 767 outputDir = comparisonDir; |
716 } else if (numUnflaggedArguments != 3) { | 768 } else if (numUnflaggedArguments != 3) { |
717 usage(argv[0]); | 769 usage(argv[0]); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
751 if (matchSubstrings.isEmpty()) { | 803 if (matchSubstrings.isEmpty()) { |
752 matchSubstrings.push(new SkString("")); | 804 matchSubstrings.push(new SkString("")); |
753 } | 805 } |
754 | 806 |
755 create_diff_images(diffProc, colorThreshold, &differences, | 807 create_diff_images(diffProc, colorThreshold, &differences, |
756 baseDir, comparisonDir, outputDir, | 808 baseDir, comparisonDir, outputDir, |
757 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, g
enerateDiffs, | 809 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, g
enerateDiffs, |
758 verbose, &summary); | 810 verbose, &summary); |
759 summary.print(listFilenames, failOnResultType, failOnStatusType); | 811 summary.print(listFilenames, failOnResultType, failOnStatusType); |
760 | 812 |
| 813 if (listFailingBase) { |
| 814 summary.printfFailingBaseNames("\n"); |
| 815 } |
| 816 |
761 if (differences.count()) { | 817 if (differences.count()) { |
762 qsort(differences.begin(), differences.count(), | 818 qsort(differences.begin(), differences.count(), |
763 sizeof(DiffRecord*), sortProc); | 819 sizeof(DiffRecord*), sortProc); |
764 } | 820 } |
765 | 821 |
766 if (generateDiffs) { | 822 if (generateDiffs) { |
767 print_diff_page(summary.fNumMatches, colorThreshold, differences, | 823 print_diff_page(summary.fNumMatches, colorThreshold, differences, |
768 baseDir, comparisonDir, outputDir); | 824 baseDir, comparisonDir, outputDir); |
769 } | 825 } |
770 | 826 |
(...skipping 23 matching lines...) Expand all Loading... |
794 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to | 850 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to |
795 // make sure that we only return 0 when there were no failures. | 851 // make sure that we only return 0 when there were no failures. |
796 return (num_failing_results > 255) ? 255 : num_failing_results; | 852 return (num_failing_results > 255) ? 255 : num_failing_results; |
797 } | 853 } |
798 | 854 |
799 #if !defined SK_BUILD_FOR_IOS | 855 #if !defined SK_BUILD_FOR_IOS |
800 int main(int argc, char * const argv[]) { | 856 int main(int argc, char * const argv[]) { |
801 return tool_main(argc, (char**) argv); | 857 return tool_main(argc, (char**) argv); |
802 } | 858 } |
803 #endif | 859 #endif |
OLD | NEW |