Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: tools/skimage_main.cpp

Issue 14670021: Write/compare against expectations in skimage tool. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« gyp/tools.gyp ('K') | « gyp/tools.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« gyp/tools.gyp ('K') | « gyp/tools.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698