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 |