| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * Copyright 2013 Google Inc. | 2  * Copyright 2013 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 | 7 | 
| 8 #include "SkBitmap.h" | 8 #include "SkBitmap.h" | 
| 9 #include "SkImageDecoder.h" | 9 #include "SkImageDecoder.h" | 
| 10 #include "SkOSFile.h" | 10 #include "SkOSFile.h" | 
| 11 #include "SkRunnable.h" | 11 #include "SkRunnable.h" | 
|  | 12 #include "SkSize.h" | 
| 12 #include "SkStream.h" | 13 #include "SkStream.h" | 
| 13 #include "SkTDict.h" | 14 #include "SkTDict.h" | 
| 14 #include "SkThreadPool.h" | 15 #include "SkThreadPool.h" | 
| 15 | 16 | 
| 16 #include "SkDiffContext.h" | 17 #include "SkDiffContext.h" | 
|  | 18 #include "SkImageDiffer.h" | 
| 17 #include "skpdiff_util.h" | 19 #include "skpdiff_util.h" | 
| 18 | 20 | 
| 19 SkDiffContext::SkDiffContext() { | 21 SkDiffContext::SkDiffContext() { | 
| 20     fDiffers = NULL; | 22     fDiffers = NULL; | 
| 21     fDifferCount = 0; | 23     fDifferCount = 0; | 
| 22     fThreadCount = SkThreadPool::kThreadPerCore; | 24     fThreadCount = SkThreadPool::kThreadPerCore; | 
| 23 } | 25 } | 
| 24 | 26 | 
| 25 SkDiffContext::~SkDiffContext() { | 27 SkDiffContext::~SkDiffContext() { | 
| 26     if (NULL != fDiffers) { | 28     if (NULL != fDiffers) { | 
| 27         SkDELETE_ARRAY(fDiffers); | 29         SkDELETE_ARRAY(fDiffers); | 
| 28     } | 30     } | 
| 29 } | 31 } | 
| 30 | 32 | 
| 31 void SkDiffContext::setDifferenceDir(const SkString& path) { | 33 void SkDiffContext::setAlphaMaskDir(const SkString& path) { | 
| 32     if (!path.isEmpty() && sk_mkdir(path.c_str())) { | 34     if (!path.isEmpty() && sk_mkdir(path.c_str())) { | 
| 33         fDifferenceDir = path; | 35         fAlphaMaskDir = path; | 
| 34     } | 36     } | 
| 35 } | 37 } | 
| 36 | 38 | 
|  | 39 void SkDiffContext::setRgbDiffDir(const SkString& path) { | 
|  | 40     if (!path.isEmpty() && sk_mkdir(path.c_str())) { | 
|  | 41         fRgbDiffDir = path; | 
|  | 42     } | 
|  | 43 } | 
|  | 44 | 
|  | 45 void SkDiffContext::setWhiteDiffDir(const SkString& path) { | 
|  | 46     if (!path.isEmpty() && sk_mkdir(path.c_str())) { | 
|  | 47         fWhiteDiffDir = path; | 
|  | 48     } | 
|  | 49 } | 
|  | 50 | 
| 37 void SkDiffContext::setDiffers(const SkTDArray<SkImageDiffer*>& differs) { | 51 void SkDiffContext::setDiffers(const SkTDArray<SkImageDiffer*>& differs) { | 
| 38     // Delete whatever the last array of differs was | 52     // Delete whatever the last array of differs was | 
| 39     if (NULL != fDiffers) { | 53     if (NULL != fDiffers) { | 
| 40         SkDELETE_ARRAY(fDiffers); | 54         SkDELETE_ARRAY(fDiffers); | 
| 41         fDiffers = NULL; | 55         fDiffers = NULL; | 
| 42         fDifferCount = 0; | 56         fDifferCount = 0; | 
| 43     } | 57     } | 
| 44 | 58 | 
| 45     // Copy over the new differs | 59     // Copy over the new differs | 
| 46     fDifferCount = differs.count(); | 60     fDifferCount = differs.count(); | 
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 83     DiffRecord* newRecord = fRecords.addToHead(DiffRecord()); | 97     DiffRecord* newRecord = fRecords.addToHead(DiffRecord()); | 
| 84     fRecordMutex.release(); | 98     fRecordMutex.release(); | 
| 85 | 99 | 
| 86     // compute the common name | 100     // compute the common name | 
| 87     SkString baseName = SkOSPath::SkBasename(baselinePath); | 101     SkString baseName = SkOSPath::SkBasename(baselinePath); | 
| 88     SkString testName = SkOSPath::SkBasename(testPath); | 102     SkString testName = SkOSPath::SkBasename(testPath); | 
| 89     newRecord->fCommonName = get_common_prefix(baseName, testName); | 103     newRecord->fCommonName = get_common_prefix(baseName, testName); | 
| 90 | 104 | 
| 91     newRecord->fBaselinePath = baselinePath; | 105     newRecord->fBaselinePath = baselinePath; | 
| 92     newRecord->fTestPath = testPath; | 106     newRecord->fTestPath = testPath; | 
|  | 107     newRecord->fSize = SkISize::Make(baselineBitmap.width(), baselineBitmap.heig
     ht()); | 
| 93 | 108 | 
| 94     bool alphaMaskPending = false; | 109     // only generate diff images if we have a place to store them | 
| 95 | 110     SkImageDiffer::BitmapsToCreate bitmapsToCreate; | 
| 96     // only enable alpha masks if a difference dir has been provided | 111     bitmapsToCreate.alphaMask = !fAlphaMaskDir.isEmpty(); | 
| 97     if (!fDifferenceDir.isEmpty()) { | 112     bitmapsToCreate.rgbDiff = !fRgbDiffDir.isEmpty(); | 
| 98         alphaMaskPending = true; | 113     bitmapsToCreate.whiteDiff = !fWhiteDiffDir.isEmpty(); | 
| 99     } |  | 
| 100 | 114 | 
| 101     // Perform each diff | 115     // Perform each diff | 
| 102     for (int differIndex = 0; differIndex < fDifferCount; differIndex++) { | 116     for (int differIndex = 0; differIndex < fDifferCount; differIndex++) { | 
| 103         SkImageDiffer* differ = fDiffers[differIndex]; | 117         SkImageDiffer* differ = fDiffers[differIndex]; | 
| 104 | 118 | 
| 105         // Copy the results into data for this record | 119         // Copy the results into data for this record | 
| 106         DiffData& diffData = newRecord->fDiffs.push_back(); | 120         DiffData& diffData = newRecord->fDiffs.push_back(); | 
| 107         diffData.fDiffName = differ->getName(); | 121         diffData.fDiffName = differ->getName(); | 
| 108 | 122 | 
| 109         if (!differ->diff(&baselineBitmap, &testBitmap, alphaMaskPending, &diffD
     ata.fResult)) { | 123         if (!differ->diff(&baselineBitmap, &testBitmap, bitmapsToCreate, &diffDa
     ta.fResult)) { | 
| 110             // if the diff failed record -1 as the result | 124             // if the diff failed, record -1 as the result | 
|  | 125             // TODO(djsollen): Record more detailed information about exactly wh
     at failed. | 
|  | 126             // (Image dimension mismatch? etc.)  See http://skbug.com/2710 ('mak
     e skpdiff | 
|  | 127             // report more detail when it fails to compare two images') | 
| 111             diffData.fResult.result = -1; | 128             diffData.fResult.result = -1; | 
| 112             continue; | 129             continue; | 
| 113         } | 130         } | 
| 114 | 131 | 
| 115         if (alphaMaskPending | 132         if (bitmapsToCreate.alphaMask | 
| 116                 && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result | 133                 && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result | 
| 117                 && !diffData.fResult.poiAlphaMask.empty() | 134                 && !diffData.fResult.poiAlphaMask.empty() | 
| 118                 && !newRecord->fCommonName.isEmpty()) { | 135                 && !newRecord->fCommonName.isEmpty()) { | 
| 119 | 136 | 
| 120             newRecord->fDifferencePath = SkOSPath::SkPathJoin(fDifferenceDir.c_s
     tr(), | 137             newRecord->fAlphaMaskPath = SkOSPath::SkPathJoin(fAlphaMaskDir.c_str
     (), | 
| 121                                                               newRecord->fCommon
     Name.c_str()); | 138                                                              newRecord->fCommonN
     ame.c_str()); | 
| 122 | 139 | 
| 123             // compute the image diff and output it | 140             // compute the image diff and output it | 
| 124             SkBitmap copy; | 141             SkBitmap copy; | 
| 125             diffData.fResult.poiAlphaMask.copyTo(©, kN32_SkColorType); | 142             diffData.fResult.poiAlphaMask.copyTo(©, kN32_SkColorType); | 
| 126             SkImageEncoder::EncodeFile(newRecord->fDifferencePath.c_str(), copy, | 143             SkImageEncoder::EncodeFile(newRecord->fAlphaMaskPath.c_str(), copy, | 
| 127                                        SkImageEncoder::kPNG_Type, 100); | 144                                        SkImageEncoder::kPNG_Type, 100); | 
| 128 | 145 | 
| 129             // cleanup the existing bitmap to free up resources; | 146             // cleanup the existing bitmap to free up resources; | 
| 130             diffData.fResult.poiAlphaMask.reset(); | 147             diffData.fResult.poiAlphaMask.reset(); | 
| 131 | 148 | 
| 132             alphaMaskPending = false; | 149             bitmapsToCreate.alphaMask = false; | 
|  | 150         } | 
|  | 151 | 
|  | 152         if (bitmapsToCreate.rgbDiff | 
|  | 153                 && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result | 
|  | 154                 && !diffData.fResult.rgbDiffBitmap.empty() | 
|  | 155                 && !newRecord->fCommonName.isEmpty()) { | 
|  | 156             // TODO(djsollen): Rather than taking the max r/g/b diffs that come 
     back from | 
|  | 157             // a particular differ and storing them as toplevel fields within | 
|  | 158             // newRecord, we should extend outputRecords() to report optional | 
|  | 159             // fields for each differ (not just "result" and "pointsOfInterest")
     . | 
|  | 160             // See http://skbug.com/2712 ('allow skpdiff to report different set
     s | 
|  | 161             // of result fields for different comparison algorithms') | 
|  | 162             newRecord->fMaxRedDiff = diffData.fResult.maxRedDiff; | 
|  | 163             newRecord->fMaxGreenDiff = diffData.fResult.maxGreenDiff; | 
|  | 164             newRecord->fMaxBlueDiff = diffData.fResult.maxBlueDiff; | 
|  | 165 | 
|  | 166             newRecord->fRgbDiffPath = SkOSPath::SkPathJoin(fRgbDiffDir.c_str(), | 
|  | 167                                                            newRecord->fCommonNam
     e.c_str()); | 
|  | 168             SkImageEncoder::EncodeFile(newRecord->fRgbDiffPath.c_str(), | 
|  | 169                                        diffData.fResult.rgbDiffBitmap, | 
|  | 170                                        SkImageEncoder::kPNG_Type, 100); | 
|  | 171             diffData.fResult.rgbDiffBitmap.reset(); | 
|  | 172             bitmapsToCreate.rgbDiff = false; | 
|  | 173         } | 
|  | 174 | 
|  | 175         if (bitmapsToCreate.whiteDiff | 
|  | 176                 && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result | 
|  | 177                 && !diffData.fResult.whiteDiffBitmap.empty() | 
|  | 178                 && !newRecord->fCommonName.isEmpty()) { | 
|  | 179             newRecord->fWhiteDiffPath = SkOSPath::SkPathJoin(fWhiteDiffDir.c_str
     (), | 
|  | 180                                                              newRecord->fCommonN
     ame.c_str()); | 
|  | 181             SkImageEncoder::EncodeFile(newRecord->fWhiteDiffPath.c_str(), | 
|  | 182                                        diffData.fResult.whiteDiffBitmap, | 
|  | 183                                        SkImageEncoder::kPNG_Type, 100); | 
|  | 184             diffData.fResult.whiteDiffBitmap.reset(); | 
|  | 185             bitmapsToCreate.whiteDiff = false; | 
| 133         } | 186         } | 
| 134     } | 187     } | 
| 135 } | 188 } | 
| 136 | 189 | 
| 137 class SkThreadedDiff : public SkRunnable { | 190 class SkThreadedDiff : public SkRunnable { | 
| 138 public: | 191 public: | 
| 139     SkThreadedDiff() : fDiffContext(NULL) { } | 192     SkThreadedDiff() : fDiffContext(NULL) { } | 
| 140 | 193 | 
| 141     void setup(SkDiffContext* diffContext, const SkString& baselinePath, const S
     kString& testPath) { | 194     void setup(SkDiffContext* diffContext, const SkString& baselinePath, const S
     kString& testPath) { | 
| 142         fDiffContext = diffContext; | 195         fDiffContext = diffContext; | 
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 222 | 275 | 
| 223 void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) { | 276 void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) { | 
| 224     SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_
     IterStart); | 277     SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_
     IterStart); | 
| 225     DiffRecord* currentRecord = iter.get(); | 278     DiffRecord* currentRecord = iter.get(); | 
| 226 | 279 | 
| 227     if (useJSONP) { | 280     if (useJSONP) { | 
| 228         stream.writeText("var SkPDiffRecords = {\n"); | 281         stream.writeText("var SkPDiffRecords = {\n"); | 
| 229     } else { | 282     } else { | 
| 230         stream.writeText("{\n"); | 283         stream.writeText("{\n"); | 
| 231     } | 284     } | 
|  | 285 | 
|  | 286     // TODO(djsollen): Would it be better to use the jsoncpp library to write ou
     t the JSON? | 
|  | 287     // This manual approach is probably more efficient, but it sure is ugly. | 
|  | 288     // See http://skbug.com/2713 ('make skpdiff use jsoncpp library to write out | 
|  | 289     // JSON output, instead of manual writeText() calls?') | 
| 232     stream.writeText("    \"records\": [\n"); | 290     stream.writeText("    \"records\": [\n"); | 
| 233     while (NULL != currentRecord) { | 291     while (NULL != currentRecord) { | 
| 234         stream.writeText("        {\n"); | 292         stream.writeText("        {\n"); | 
| 235 | 293 | 
| 236             SkString differenceAbsPath = get_absolute_path(currentRecord->fDiffe
     rencePath); |  | 
| 237             SkString baselineAbsPath = get_absolute_path(currentRecord->fBaselin
     ePath); | 294             SkString baselineAbsPath = get_absolute_path(currentRecord->fBaselin
     ePath); | 
| 238             SkString testAbsPath = get_absolute_path(currentRecord->fTestPath); | 295             SkString testAbsPath = get_absolute_path(currentRecord->fTestPath); | 
| 239 | 296 | 
| 240             stream.writeText("            \"commonName\": \""); | 297             stream.writeText("            \"commonName\": \""); | 
| 241             stream.writeText(currentRecord->fCommonName.c_str()); | 298             stream.writeText(currentRecord->fCommonName.c_str()); | 
| 242             stream.writeText("\",\n"); | 299             stream.writeText("\",\n"); | 
| 243 | 300 | 
| 244             stream.writeText("            \"differencePath\": \""); | 301             stream.writeText("            \"differencePath\": \""); | 
| 245             stream.writeText(differenceAbsPath.c_str()); | 302             stream.writeText(get_absolute_path(currentRecord->fAlphaMaskPath).c_
     str()); | 
|  | 303             stream.writeText("\",\n"); | 
|  | 304 | 
|  | 305             stream.writeText("            \"rgbDiffPath\": \""); | 
|  | 306             stream.writeText(get_absolute_path(currentRecord->fRgbDiffPath).c_st
     r()); | 
|  | 307             stream.writeText("\",\n"); | 
|  | 308 | 
|  | 309             stream.writeText("            \"whiteDiffPath\": \""); | 
|  | 310             stream.writeText(get_absolute_path(currentRecord->fWhiteDiffPath).c_
     str()); | 
| 246             stream.writeText("\",\n"); | 311             stream.writeText("\",\n"); | 
| 247 | 312 | 
| 248             stream.writeText("            \"baselinePath\": \""); | 313             stream.writeText("            \"baselinePath\": \""); | 
| 249             stream.writeText(baselineAbsPath.c_str()); | 314             stream.writeText(baselineAbsPath.c_str()); | 
| 250             stream.writeText("\",\n"); | 315             stream.writeText("\",\n"); | 
| 251 | 316 | 
| 252             stream.writeText("            \"testPath\": \""); | 317             stream.writeText("            \"testPath\": \""); | 
| 253             stream.writeText(testAbsPath.c_str()); | 318             stream.writeText(testAbsPath.c_str()); | 
| 254             stream.writeText("\",\n"); | 319             stream.writeText("\",\n"); | 
| 255 | 320 | 
|  | 321             stream.writeText("            \"width\": "); | 
|  | 322             stream.writeDecAsText(currentRecord->fSize.width()); | 
|  | 323             stream.writeText(",\n"); | 
|  | 324             stream.writeText("            \"height\": "); | 
|  | 325             stream.writeDecAsText(currentRecord->fSize.height()); | 
|  | 326             stream.writeText(",\n"); | 
|  | 327 | 
|  | 328             stream.writeText("            \"maxRedDiff\": "); | 
|  | 329             stream.writeDecAsText(currentRecord->fMaxRedDiff); | 
|  | 330             stream.writeText(",\n"); | 
|  | 331             stream.writeText("            \"maxGreenDiff\": "); | 
|  | 332             stream.writeDecAsText(currentRecord->fMaxGreenDiff); | 
|  | 333             stream.writeText(",\n"); | 
|  | 334             stream.writeText("            \"maxBlueDiff\": "); | 
|  | 335             stream.writeDecAsText(currentRecord->fMaxBlueDiff); | 
|  | 336             stream.writeText(",\n"); | 
|  | 337 | 
| 256             stream.writeText("            \"diffs\": [\n"); | 338             stream.writeText("            \"diffs\": [\n"); | 
| 257             for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); d
     iffIndex++) { | 339             for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); d
     iffIndex++) { | 
| 258                 DiffData& data = currentRecord->fDiffs[diffIndex]; | 340                 DiffData& data = currentRecord->fDiffs[diffIndex]; | 
| 259                 stream.writeText("                {\n"); | 341                 stream.writeText("                {\n"); | 
| 260 | 342 | 
| 261                     stream.writeText("                    \"differName\": \""); | 343                     stream.writeText("                    \"differName\": \""); | 
| 262                     stream.writeText(data.fDiffName); | 344                     stream.writeText(data.fDiffName); | 
| 263                     stream.writeText("\",\n"); | 345                     stream.writeText("\",\n"); | 
| 264 | 346 | 
| 265                     stream.writeText("                    \"result\": "); | 347                     stream.writeText("                    \"result\": "); | 
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 351         for (int i = 0; i < cntColumns; i++) { | 433         for (int i = 0; i < cntColumns; i++) { | 
| 352             SkString str; | 434             SkString str; | 
| 353             str.printf(", %f", values[i]); | 435             str.printf(", %f", values[i]); | 
| 354             stream.writeText(str.c_str()); | 436             stream.writeText(str.c_str()); | 
| 355         } | 437         } | 
| 356         stream.writeText("\n"); | 438         stream.writeText("\n"); | 
| 357 | 439 | 
| 358         currentRecord = iter2.next(); | 440         currentRecord = iter2.next(); | 
| 359     } | 441     } | 
| 360 } | 442 } | 
| OLD | NEW | 
|---|