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 | 7 |
8 #include "gm_expectations.h" | |
8 #include "SkBitmap.h" | 9 #include "SkBitmap.h" |
10 #include "SkBitmapHasher.h" | |
9 #include "SkColorPriv.h" | 11 #include "SkColorPriv.h" |
10 #include "SkCommandLineFlags.h" | 12 #include "SkCommandLineFlags.h" |
11 #include "SkData.h" | 13 #include "SkData.h" |
12 #include "SkGraphics.h" | 14 #include "SkGraphics.h" |
13 #include "SkImageDecoder.h" | 15 #include "SkImageDecoder.h" |
14 #include "SkImageEncoder.h" | 16 #include "SkImageEncoder.h" |
15 #include "SkOSFile.h" | 17 #include "SkOSFile.h" |
16 #include "SkRandom.h" | 18 #include "SkRandom.h" |
17 #include "SkStream.h" | 19 #include "SkStream.h" |
18 #include "SkTArray.h" | 20 #include "SkTArray.h" |
19 #include "SkTemplates.h" | 21 #include "SkTemplates.h" |
20 | 22 |
23 DEFINE_string(createExpectationsPath, "", "Path to write JSON expectations."); | |
21 DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required. "); | 24 DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required. "); |
25 DEFINE_string(readExpectationsPath, "", "Path to read JSON expectations from."); | |
22 DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); | 26 DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); |
23 DEFINE_bool(reencode, true, "Reencode the images to test encoding."); | 27 DEFINE_bool(reencode, true, "Reencode the images to test encoding."); |
24 DEFINE_bool(testSubsetDecoding, true, "Test decoding subsets of images."); | 28 DEFINE_bool(testSubsetDecoding, true, "Test decoding subsets of images."); |
25 | 29 |
26 struct Format { | 30 struct Format { |
27 SkImageEncoder::Type fType; | 31 SkImageEncoder::Type fType; |
28 SkImageDecoder::Format fFormat; | 32 SkImageDecoder::Format fFormat; |
29 const char* fSuffix; | 33 const char* fSuffix; |
30 }; | 34 }; |
31 | 35 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
90 // Store the names of the filenames to report later which ones failed, succeeded , and were | 94 // Store the names of the filenames to report later which ones failed, succeeded , and were |
91 // invalid. | 95 // invalid. |
92 static SkTArray<SkString, false> gInvalidStreams; | 96 static SkTArray<SkString, false> gInvalidStreams; |
93 static SkTArray<SkString, false> gMissingCodecs; | 97 static SkTArray<SkString, false> gMissingCodecs; |
94 static SkTArray<SkString, false> gDecodeFailures; | 98 static SkTArray<SkString, false> gDecodeFailures; |
95 static SkTArray<SkString, false> gEncodeFailures; | 99 static SkTArray<SkString, false> gEncodeFailures; |
96 static SkTArray<SkString, false> gSuccessfulDecodes; | 100 static SkTArray<SkString, false> gSuccessfulDecodes; |
97 static SkTArray<SkString, false> gSuccessfulSubsetDecodes; | 101 static SkTArray<SkString, false> gSuccessfulSubsetDecodes; |
98 static SkTArray<SkString, false> gFailedSubsetDecodes; | 102 static SkTArray<SkString, false> gFailedSubsetDecodes; |
99 | 103 |
104 // Expections read from a file specified by readExpectationsPath. The expectatio ns must have been | |
105 // previously written using createExpectationsPath. | |
106 SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations; | |
107 | |
100 static bool write_bitmap(const char outName[], SkBitmap* bm) { | 108 static bool write_bitmap(const char outName[], SkBitmap* bm) { |
101 SkBitmap bitmap8888; | 109 SkBitmap bitmap8888; |
102 if (SkBitmap::kARGB_8888_Config != bm->config()) { | 110 if (SkBitmap::kARGB_8888_Config != bm->config()) { |
103 if (!bm->copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config)) { | 111 if (!bm->copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config)) { |
104 return false; | 112 return false; |
105 } | 113 } |
106 bm = &bitmap8888; | 114 bm = &bitmap8888; |
107 } | 115 } |
108 // FIXME: This forces all pixels to be opaque, like the many implementations | 116 // FIXME: This forces all pixels to be opaque, like the many implementations |
109 // of force_all_opaque. These should be unified if they cannot be eliminated . | 117 // of force_all_opaque. These should be unified if they cannot be eliminated . |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
150 rect.fTop--; | 158 rect.fTop--; |
151 } else { | 159 } else { |
152 rect.fBottom++; | 160 rect.fBottom++; |
153 // Again, this must be in range. | 161 // Again, this must be in range. |
154 SkASSERT(rect.fBottom < maxY); | 162 SkASSERT(rect.fBottom < maxY); |
155 } | 163 } |
156 } | 164 } |
157 return rect; | 165 return rect; |
158 } | 166 } |
159 | 167 |
168 // Stored expectations to be written to a file createExpectationsPath is specifi ed. | |
epoger
2013/05/08 17:24:48
"written to a file createExpectationsPath" -> "wri
scroggo
2013/05/08 18:46:33
Done.
| |
169 static Json::Value gExpectationsToWrite; | |
170 | |
171 /** | |
172 * If expectations are to be recorded, record the expected checksum of bitmap i nto global | |
173 * expectations array. | |
174 */ | |
175 static void write_expectations(const SkBitmap& bitmap, const char* filename) { | |
176 if (!FLAGS_createExpectationsPath.isEmpty()) { | |
177 // Creates an Expectations object, and add it to the list to write. | |
178 skiagm::Expectations expectation(bitmap); | |
179 Json::Value value = expectation.asJsonValue(); | |
180 gExpectationsToWrite[filename] = value; | |
181 } | |
182 } | |
183 | |
184 /** | |
185 * Compare against an expectation for this filename, if there is one. | |
186 * @param bitmap SkBitmap to compare to the expected value. | |
187 * @param filename String used to find the expected value. | |
188 * @return bool True if the bitmap matched the expectation, or if there was no expectation. False | |
189 * if there was an expecation that the bitmap did not match, or if an expec tation could not be | |
190 * computed from an expectation. | |
191 */ | |
192 static bool compare_to_expectations_if_necessary(const SkBitmap& bitmap, const c har* filename, | |
193 SkTArray<SkString, false>* fail ureArray) { | |
epoger
2013/05/08 17:24:48
could you pass failureArray by reference instead,
scroggo
2013/05/08 18:46:33
That makes sense to me, but according to our codin
| |
194 if (NULL == gJsonExpectations.get()) { | |
195 return true; | |
196 } | |
197 skiagm::Expectations jsExpectation = gJsonExpectations->get(filename); | |
198 if (jsExpectation.empty()) { | |
199 return true; | |
200 } | |
201 SkHashDigest checksum; | |
202 if (SkBitmapHasher::ComputeDigest(bitmap, &checksum)) { | |
epoger
2013/05/08 17:24:48
I think it would be a bit easier to follow if you
scroggo
2013/05/08 18:46:33
Yes, that is much clearer! Done!
| |
203 if (jsExpectation.match(checksum)) { | |
204 return true; | |
205 } else { | |
206 failureArray->push_back().printf("decoded %s, but the result does no t match " | |
207 "expectations.", | |
208 filename); | |
209 return false; | |
210 } | |
211 } else { | |
212 failureArray->push_back().printf("decoded %s, but could not create a che cksum.", filename); | |
213 return false; | |
214 } | |
215 } | |
216 | |
160 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { | 217 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { |
161 SkBitmap bitmap; | 218 SkBitmap bitmap; |
162 SkFILEStream stream(srcPath); | 219 SkFILEStream stream(srcPath); |
163 if (!stream.isValid()) { | 220 if (!stream.isValid()) { |
164 gInvalidStreams.push_back().set(srcPath); | 221 gInvalidStreams.push_back().set(srcPath); |
165 return; | 222 return; |
166 } | 223 } |
167 | 224 |
168 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); | 225 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); |
169 if (NULL == codec) { | 226 if (NULL == codec) { |
170 gMissingCodecs.push_back().set(srcPath); | 227 gMissingCodecs.push_back().set(srcPath); |
171 return; | 228 return; |
172 } | 229 } |
173 | 230 |
174 SkAutoTDelete<SkImageDecoder> ad(codec); | 231 SkAutoTDelete<SkImageDecoder> ad(codec); |
175 | 232 |
176 stream.rewind(); | 233 stream.rewind(); |
177 if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, | 234 if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, |
178 SkImageDecoder::kDecodePixels_Mode)) { | 235 SkImageDecoder::kDecodePixels_Mode)) { |
179 gDecodeFailures.push_back().set(srcPath); | 236 gDecodeFailures.push_back().set(srcPath); |
180 return; | 237 return; |
181 } | 238 } |
182 | 239 |
183 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.width(), bitmap.height()); | 240 // Create a string representing just the filename itself, for use in json ex pectations. |
241 const char* filename = strrchr(srcPath, SkPATH_SEPARATOR); | |
epoger
2013/05/08 17:24:48
Maybe split this out into its own SkBasename() met
scroggo
2013/05/08 18:46:33
Moved into its own method. Sure enough, other file
| |
242 if (NULL == filename || ++filename == '\0') { | |
243 filename = srcPath; | |
244 } | |
245 | |
246 if (compare_to_expectations_if_necessary(bitmap, filename, &gDecodeFailures) ) { | |
247 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(), | |
248 bitmap.height()); | |
249 } | |
250 | |
251 write_expectations(bitmap, filename); | |
184 | 252 |
185 if (FLAGS_testSubsetDecoding) { | 253 if (FLAGS_testSubsetDecoding) { |
186 SkDEBUGCODE(bool couldRewind =) stream.rewind(); | 254 SkDEBUGCODE(bool couldRewind =) stream.rewind(); |
187 SkASSERT(couldRewind); | 255 SkASSERT(couldRewind); |
188 int width, height; | 256 int width, height; |
189 // Build the tile index for decoding subsets. If the image is 1x1, skip subset | 257 // Build the tile index for decoding subsets. If the image is 1x1, skip subset |
190 // decoding since there are no smaller subsets. | 258 // decoding since there are no smaller subsets. |
191 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { | 259 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { |
192 SkASSERT(bitmap.width() == width && bitmap.height() == height); | 260 SkASSERT(bitmap.width() == width && bitmap.height() == height); |
193 // Call decodeSubset multiple times: | 261 // Call decodeSubset multiple times: |
194 SkRandom rand(0); | 262 SkRandom rand(0); |
195 for (int i = 0; i < 5; i++) { | 263 for (int i = 0; i < 5; i++) { |
196 SkBitmap bitmapFromDecodeSubset; | 264 SkBitmap bitmapFromDecodeSubset; |
197 // FIXME: Come up with a more representative set of rectangles. | 265 // FIXME: Come up with a more representative set of rectangles. |
198 SkIRect rect = generate_random_rect(&rand, width, height); | 266 SkIRect rect = generate_random_rect(&rand, width, height); |
199 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, | 267 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, |
200 rect.fRight, rect.fBottom); | 268 rect.fRight, rect.fBottom); |
201 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, SkBitmap: :kNo_Config)) { | 269 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, SkBitmap: :kNo_Config)) { |
202 gSuccessfulSubsetDecodes.push_back().printf("Decoded subset %s from %s", | 270 SkString subsetName = SkStringPrintf("%s_%s", filename, subs etDim.c_str()); |
203 subsetDim.c_str(), src Path); | 271 if (compare_to_expectations_if_necessary(bitmapFromDecodeSub set, |
272 subsetName.c_str(), | |
273 &gFailedSubsetDecod es)) { | |
274 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s", | |
275 subsetDim.c_str(), srcPath); | |
276 } | |
277 | |
278 write_expectations(bitmapFromDecodeSubset, subsetName.c_str( )); | |
279 | |
204 if (writePath != NULL) { | 280 if (writePath != NULL) { |
205 // Write the region to a file whose name includes the di mensions. | 281 // Write the region to a file whose name includes the di mensions. |
206 SkString suffix = SkStringPrintf("_%s.png", subsetDim.c_ str()); | 282 SkString suffix = SkStringPrintf("_%s.png", subsetDim.c_ str()); |
207 SkString outPath; | 283 SkString outPath; |
208 make_outname(&outPath, writePath->c_str(), srcPath, suff ix.c_str()); | 284 make_outname(&outPath, writePath->c_str(), srcPath, suff ix.c_str()); |
209 SkDEBUGCODE(bool success =) | 285 SkDEBUGCODE(bool success =) |
210 write_bitmap(outPath.c_str(), &bitmapFromDecodeSubset); | 286 write_bitmap(outPath.c_str(), &bitmapFromDecodeSubset); |
211 SkASSERT(success); | 287 SkASSERT(success); |
212 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s" , outPath.c_str()); | 288 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s" , outPath.c_str()); |
213 // Also use extractSubset from the original for visual c omparison. | 289 // Also use extractSubset from the original for visual c omparison. |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
328 SkCommandLineFlags::Parse(argc, argv); | 404 SkCommandLineFlags::Parse(argc, argv); |
329 | 405 |
330 if (FLAGS_readPath.count() < 1) { | 406 if (FLAGS_readPath.count() < 1) { |
331 SkDebugf("Folder(s) or image(s) to decode are required.\n"); | 407 SkDebugf("Folder(s) or image(s) to decode are required.\n"); |
332 return -1; | 408 return -1; |
333 } | 409 } |
334 | 410 |
335 | 411 |
336 SkAutoGraphics ag; | 412 SkAutoGraphics ag; |
337 | 413 |
414 if (!FLAGS_readExpectationsPath.isEmpty()) { | |
415 gJsonExpectations.reset(SkNEW_ARGS(skiagm::JsonExpectationsSource, | |
416 (FLAGS_readExpectationsPath[0]))); | |
417 } | |
418 | |
338 SkString outDir; | 419 SkString outDir; |
339 SkString* outDirPtr; | 420 SkString* outDirPtr; |
340 | 421 |
341 if (FLAGS_writePath.count() == 1) { | 422 if (FLAGS_writePath.count() == 1) { |
342 outDir.set(FLAGS_writePath[0]); | 423 outDir.set(FLAGS_writePath[0]); |
343 if (outDir.c_str()[outDir.size() - 1] != '/') { | 424 if (outDir.c_str()[outDir.size() - 1] != SkPATH_SEPARATOR) { |
scroggo
2013/05/08 18:46:33
Also moved into its own function.
| |
344 outDir.append("/"); | 425 outDir.append(&SkPATH_SEPARATOR); |
345 } | 426 } |
346 outDirPtr = &outDir; | 427 outDirPtr = &outDir; |
347 } else { | 428 } else { |
348 outDirPtr = NULL; | 429 outDirPtr = NULL; |
349 } | 430 } |
350 | 431 |
351 for (int i = 0; i < FLAGS_readPath.count(); i++) { | 432 for (int i = 0; i < FLAGS_readPath.count(); i++) { |
352 if (strlen(FLAGS_readPath[i]) < 1) { | 433 if (strlen(FLAGS_readPath[i]) < 1) { |
353 break; | 434 break; |
354 } | 435 } |
355 SkOSFile::Iter iter(FLAGS_readPath[i]); | 436 SkOSFile::Iter iter(FLAGS_readPath[i]); |
356 SkString filename; | 437 SkString filename; |
357 if (iter.next(&filename)) { | 438 if (iter.next(&filename)) { |
358 SkString directory(FLAGS_readPath[i]); | 439 SkString directory(FLAGS_readPath[i]); |
359 if (directory[directory.size() - 1] != '/') { | 440 if (directory[directory.size() - 1] != SkPATH_SEPARATOR) { |
360 directory.append("/"); | 441 directory.append(&SkPATH_SEPARATOR); |
361 } | 442 } |
362 do { | 443 do { |
363 SkString fullname(directory); | 444 SkString fullname(directory); |
364 fullname.append(filename); | 445 fullname.append(filename); |
365 decodeFileAndWrite(fullname.c_str(), outDirPtr); | 446 decodeFileAndWrite(fullname.c_str(), outDirPtr); |
366 } while (iter.next(&filename)); | 447 } while (iter.next(&filename)); |
367 } else { | 448 } else { |
368 decodeFileAndWrite(FLAGS_readPath[i], outDirPtr); | 449 decodeFileAndWrite(FLAGS_readPath[i], outDirPtr); |
369 } | 450 } |
370 } | 451 } |
371 | 452 |
453 if (!FLAGS_createExpectationsPath.isEmpty()) { | |
454 // Use an empty value for everything besides expectations, since the rea der only cares | |
455 // about the expectations. | |
456 Json::Value nullValue; | |
457 Json::Value root = skiagm::CreateJsonTree(gExpectationsToWrite, nullValu e, nullValue, | |
458 nullValue, nullValue); | |
459 std::string jsonStdString = root.toStyledString(); | |
460 SkString path = SkStringPrintf("%s%cresults.json", FLAGS_createExpectati onsPath[0], | |
461 SkPATH_SEPARATOR); | |
462 SkFILEWStream stream(path.c_str()); | |
463 stream.write(jsonStdString.c_str(), jsonStdString.length()); | |
464 } | |
372 // Add some space, since codecs may print warnings without newline. | 465 // Add some space, since codecs may print warnings without newline. |
373 SkDebugf("\n\n"); | 466 SkDebugf("\n\n"); |
374 | 467 |
375 bool failed = print_strings("Invalid files", gInvalidStreams); | 468 bool failed = print_strings("Invalid files", gInvalidStreams); |
376 failed |= print_strings("Missing codec", gMissingCodecs); | 469 failed |= print_strings("Missing codec", gMissingCodecs); |
377 failed |= print_strings("Failed to decode", gDecodeFailures); | 470 failed |= print_strings("Failed to decode", gDecodeFailures); |
378 failed |= print_strings("Failed to encode", gEncodeFailures); | 471 failed |= print_strings("Failed to encode", gEncodeFailures); |
379 print_strings("Decoded", gSuccessfulDecodes); | 472 print_strings("Decoded", gSuccessfulDecodes); |
380 | 473 |
381 if (FLAGS_testSubsetDecoding) { | 474 if (FLAGS_testSubsetDecoding) { |
382 failed |= print_strings("Failed subset decodes", gFailedSubsetDecodes); | 475 failed |= print_strings("Failed subset decodes", gFailedSubsetDecodes); |
383 print_strings("Decoded subsets", gSuccessfulSubsetDecodes); | 476 print_strings("Decoded subsets", gSuccessfulSubsetDecodes); |
384 } | 477 } |
385 | 478 |
386 return failed ? -1 : 0; | 479 return failed ? -1 : 0; |
387 } | 480 } |
388 | 481 |
389 #if !defined SK_BUILD_FOR_IOS | 482 #if !defined SK_BUILD_FOR_IOS |
390 int main(int argc, char * const argv[]) { | 483 int main(int argc, char * const argv[]) { |
391 return tool_main(argc, (char**) argv); | 484 return tool_main(argc, (char**) argv); |
392 } | 485 } |
393 #endif | 486 #endif |
OLD | NEW |