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

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: Instead of appending a string of potentially infinite length, append a single character. 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
« no previous file with comments | « 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 if createExpectationsPath is spec ified.
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 * Return the name of the file, ignoring the directory structure.
186 * Does not create a new string.
187 * @param fullPath Full path to the file.
188 * @return string The basename of the file - anything beyond the final slash, o r the full name
189 * if there is no slash.
190 * TODO: Might this be useful as a utility function in SkOSFile? Would it be mo re appropriate to
191 * create a new string?
192 */
193 static const char* SkBasename(const char* fullPath) {
194 const char* filename = strrchr(fullPath, SkPATH_SEPARATOR);
195 if (NULL == filename || ++filename == '\0') {
196 filename = fullPath;
197 }
198 return filename;
199 }
200
201 /**
202 * Compare against an expectation for this filename, if there is one.
203 * @param bitmap SkBitmap to compare to the expected value.
204 * @param filename String used to find the expected value.
205 * @return bool True if the bitmap matched the expectation, or if there was no expectation. False
206 * if there was an expecation that the bitmap did not match, or if an expec tation could not be
207 * computed from an expectation.
208 */
209 static bool compare_to_expectations_if_necessary(const SkBitmap& bitmap, const c har* filename,
210 SkTArray<SkString, false>* fail ureArray) {
211 if (NULL == gJsonExpectations.get()) {
212 return true;
213 }
214
215 skiagm::Expectations jsExpectation = gJsonExpectations->get(filename);
216 if (jsExpectation.empty()) {
217 return true;
218 }
219
220 SkHashDigest checksum;
221 if (!SkBitmapHasher::ComputeDigest(bitmap, &checksum)) {
222 if (failureArray != NULL) {
223 failureArray->push_back().printf("decoded %s, but could not create a checksum.",
224 filename);
225 }
226 return false;
227 }
228
229 if (jsExpectation.match(checksum)) {
230 return true;
231 }
232
233 if (failureArray != NULL) {
234 failureArray->push_back().printf("decoded %s, but the result does not ma tch "
235 "expectations.",
236 filename);
237 }
238 return false;
239 }
240
160 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { 241 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) {
161 SkBitmap bitmap; 242 SkBitmap bitmap;
162 SkFILEStream stream(srcPath); 243 SkFILEStream stream(srcPath);
163 if (!stream.isValid()) { 244 if (!stream.isValid()) {
164 gInvalidStreams.push_back().set(srcPath); 245 gInvalidStreams.push_back().set(srcPath);
165 return; 246 return;
166 } 247 }
167 248
168 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); 249 SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
169 if (NULL == codec) { 250 if (NULL == codec) {
170 gMissingCodecs.push_back().set(srcPath); 251 gMissingCodecs.push_back().set(srcPath);
171 return; 252 return;
172 } 253 }
173 254
174 SkAutoTDelete<SkImageDecoder> ad(codec); 255 SkAutoTDelete<SkImageDecoder> ad(codec);
175 256
176 stream.rewind(); 257 stream.rewind();
177 if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, 258 if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config,
178 SkImageDecoder::kDecodePixels_Mode)) { 259 SkImageDecoder::kDecodePixels_Mode)) {
179 gDecodeFailures.push_back().set(srcPath); 260 gDecodeFailures.push_back().set(srcPath);
180 return; 261 return;
181 } 262 }
182 263
183 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.width(), bitmap.height()); 264 // Create a string representing just the filename itself, for use in json ex pectations.
265 const char* filename = SkBasename(srcPath);
266
267 if (compare_to_expectations_if_necessary(bitmap, filename, &gDecodeFailures) ) {
268 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(),
269 bitmap.height());
270 }
271
272 write_expectations(bitmap, filename);
184 273
185 if (FLAGS_testSubsetDecoding) { 274 if (FLAGS_testSubsetDecoding) {
186 SkDEBUGCODE(bool couldRewind =) stream.rewind(); 275 SkDEBUGCODE(bool couldRewind =) stream.rewind();
187 SkASSERT(couldRewind); 276 SkASSERT(couldRewind);
188 int width, height; 277 int width, height;
189 // Build the tile index for decoding subsets. If the image is 1x1, skip subset 278 // Build the tile index for decoding subsets. If the image is 1x1, skip subset
190 // decoding since there are no smaller subsets. 279 // decoding since there are no smaller subsets.
191 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { 280 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) {
192 SkASSERT(bitmap.width() == width && bitmap.height() == height); 281 SkASSERT(bitmap.width() == width && bitmap.height() == height);
193 // Call decodeSubset multiple times: 282 // Call decodeSubset multiple times:
194 SkRandom rand(0); 283 SkRandom rand(0);
195 for (int i = 0; i < 5; i++) { 284 for (int i = 0; i < 5; i++) {
196 SkBitmap bitmapFromDecodeSubset; 285 SkBitmap bitmapFromDecodeSubset;
197 // FIXME: Come up with a more representative set of rectangles. 286 // FIXME: Come up with a more representative set of rectangles.
198 SkIRect rect = generate_random_rect(&rand, width, height); 287 SkIRect rect = generate_random_rect(&rand, width, height);
199 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, 288 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop,
200 rect.fRight, rect.fBottom); 289 rect.fRight, rect.fBottom);
201 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, SkBitmap: :kNo_Config)) { 290 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, SkBitmap: :kNo_Config)) {
202 gSuccessfulSubsetDecodes.push_back().printf("Decoded subset %s from %s", 291 SkString subsetName = SkStringPrintf("%s_%s", filename, subs etDim.c_str());
203 subsetDim.c_str(), src Path); 292 if (compare_to_expectations_if_necessary(bitmapFromDecodeSub set,
293 subsetName.c_str(),
294 &gFailedSubsetDecod es)) {
295 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s",
296 subsetDim.c_str(), srcPath);
297 }
298
299 write_expectations(bitmapFromDecodeSubset, subsetName.c_str( ));
300
204 if (writePath != NULL) { 301 if (writePath != NULL) {
205 // Write the region to a file whose name includes the di mensions. 302 // Write the region to a file whose name includes the di mensions.
206 SkString suffix = SkStringPrintf("_%s.png", subsetDim.c_ str()); 303 SkString suffix = SkStringPrintf("_%s.png", subsetDim.c_ str());
207 SkString outPath; 304 SkString outPath;
208 make_outname(&outPath, writePath->c_str(), srcPath, suff ix.c_str()); 305 make_outname(&outPath, writePath->c_str(), srcPath, suff ix.c_str());
209 SkDEBUGCODE(bool success =) 306 SkDEBUGCODE(bool success =)
210 write_bitmap(outPath.c_str(), &bitmapFromDecodeSubset); 307 write_bitmap(outPath.c_str(), &bitmapFromDecodeSubset);
211 SkASSERT(success); 308 SkASSERT(success);
212 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s" , outPath.c_str()); 309 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s" , outPath.c_str());
213 // Also use extractSubset from the original for visual c omparison. 310 // Also use extractSubset from the original for visual c omparison.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 SkDebugf("%s:\n", title); 412 SkDebugf("%s:\n", title);
316 for (int i = 0; i < strings.count(); i++) { 413 for (int i = 0; i < strings.count(); i++) {
317 SkDebugf("\t%s\n", strings[i].c_str()); 414 SkDebugf("\t%s\n", strings[i].c_str());
318 } 415 }
319 SkDebugf("\n"); 416 SkDebugf("\n");
320 return true; 417 return true;
321 } 418 }
322 return false; 419 return false;
323 } 420 }
324 421
422 /**
423 * If directory is non null and does not end with a path separator, append one.
424 * @param directory SkString representing the path to a directory. If the last character is not a
425 * path separator (specific to the current OS), append one.
426 */
427 static void append_path_separator_if_necessary(SkString* directory) {
428 if (directory != NULL && directory->c_str()[directory->size() - 1] != SkPATH _SEPARATOR) {
429 directory->appendf("%c", SkPATH_SEPARATOR);
430 }
431 }
432
325 int tool_main(int argc, char** argv); 433 int tool_main(int argc, char** argv);
326 int tool_main(int argc, char** argv) { 434 int tool_main(int argc, char** argv) {
327 SkCommandLineFlags::SetUsage("Decode files, and optionally write the results to files."); 435 SkCommandLineFlags::SetUsage("Decode files, and optionally write the results to files.");
328 SkCommandLineFlags::Parse(argc, argv); 436 SkCommandLineFlags::Parse(argc, argv);
329 437
330 if (FLAGS_readPath.count() < 1) { 438 if (FLAGS_readPath.count() < 1) {
331 SkDebugf("Folder(s) or image(s) to decode are required.\n"); 439 SkDebugf("Folder(s) or image(s) to decode are required.\n");
332 return -1; 440 return -1;
333 } 441 }
334 442
335 443
336 SkAutoGraphics ag; 444 SkAutoGraphics ag;
337 445
446 if (!FLAGS_readExpectationsPath.isEmpty()) {
447 gJsonExpectations.reset(SkNEW_ARGS(skiagm::JsonExpectationsSource,
448 (FLAGS_readExpectationsPath[0])));
449 }
450
338 SkString outDir; 451 SkString outDir;
339 SkString* outDirPtr; 452 SkString* outDirPtr;
340 453
341 if (FLAGS_writePath.count() == 1) { 454 if (FLAGS_writePath.count() == 1) {
342 outDir.set(FLAGS_writePath[0]); 455 outDir.set(FLAGS_writePath[0]);
343 if (outDir.c_str()[outDir.size() - 1] != '/') { 456 append_path_separator_if_necessary(&outDir);
344 outDir.append("/");
345 }
346 outDirPtr = &outDir; 457 outDirPtr = &outDir;
347 } else { 458 } else {
348 outDirPtr = NULL; 459 outDirPtr = NULL;
349 } 460 }
350 461
351 for (int i = 0; i < FLAGS_readPath.count(); i++) { 462 for (int i = 0; i < FLAGS_readPath.count(); i++) {
352 if (strlen(FLAGS_readPath[i]) < 1) { 463 if (strlen(FLAGS_readPath[i]) < 1) {
353 break; 464 break;
354 } 465 }
355 SkOSFile::Iter iter(FLAGS_readPath[i]); 466 SkOSFile::Iter iter(FLAGS_readPath[i]);
356 SkString filename; 467 SkString filename;
357 if (iter.next(&filename)) { 468 if (iter.next(&filename)) {
358 SkString directory(FLAGS_readPath[i]); 469 SkString directory(FLAGS_readPath[i]);
359 if (directory[directory.size() - 1] != '/') { 470 append_path_separator_if_necessary(&directory);
360 directory.append("/");
361 }
362 do { 471 do {
363 SkString fullname(directory); 472 SkString fullname(directory);
364 fullname.append(filename); 473 fullname.append(filename);
365 decodeFileAndWrite(fullname.c_str(), outDirPtr); 474 decodeFileAndWrite(fullname.c_str(), outDirPtr);
366 } while (iter.next(&filename)); 475 } while (iter.next(&filename));
367 } else { 476 } else {
368 decodeFileAndWrite(FLAGS_readPath[i], outDirPtr); 477 decodeFileAndWrite(FLAGS_readPath[i], outDirPtr);
369 } 478 }
370 } 479 }
371 480
481 if (!FLAGS_createExpectationsPath.isEmpty()) {
482 // Use an empty value for everything besides expectations, since the rea der only cares
483 // about the expectations.
484 Json::Value nullValue;
485 Json::Value root = skiagm::CreateJsonTree(gExpectationsToWrite, nullValu e, nullValue,
486 nullValue, nullValue);
487 std::string jsonStdString = root.toStyledString();
488 SkString path = SkStringPrintf("%s%cresults.json", FLAGS_createExpectati onsPath[0],
489 SkPATH_SEPARATOR);
490 SkFILEWStream stream(path.c_str());
491 stream.write(jsonStdString.c_str(), jsonStdString.length());
492 }
372 // Add some space, since codecs may print warnings without newline. 493 // Add some space, since codecs may print warnings without newline.
373 SkDebugf("\n\n"); 494 SkDebugf("\n\n");
374 495
375 bool failed = print_strings("Invalid files", gInvalidStreams); 496 bool failed = print_strings("Invalid files", gInvalidStreams);
376 failed |= print_strings("Missing codec", gMissingCodecs); 497 failed |= print_strings("Missing codec", gMissingCodecs);
377 failed |= print_strings("Failed to decode", gDecodeFailures); 498 failed |= print_strings("Failed to decode", gDecodeFailures);
378 failed |= print_strings("Failed to encode", gEncodeFailures); 499 failed |= print_strings("Failed to encode", gEncodeFailures);
379 print_strings("Decoded", gSuccessfulDecodes); 500 print_strings("Decoded", gSuccessfulDecodes);
380 501
381 if (FLAGS_testSubsetDecoding) { 502 if (FLAGS_testSubsetDecoding) {
382 failed |= print_strings("Failed subset decodes", gFailedSubsetDecodes); 503 failed |= print_strings("Failed subset decodes", gFailedSubsetDecodes);
383 print_strings("Decoded subsets", gSuccessfulSubsetDecodes); 504 print_strings("Decoded subsets", gSuccessfulSubsetDecodes);
384 } 505 }
385 506
386 return failed ? -1 : 0; 507 return failed ? -1 : 0;
387 } 508 }
388 509
389 #if !defined SK_BUILD_FOR_IOS 510 #if !defined SK_BUILD_FOR_IOS
390 int main(int argc, char * const argv[]) { 511 int main(int argc, char * const argv[]) {
391 return tool_main(argc, (char**) argv); 512 return tool_main(argc, (char**) argv);
392 } 513 }
393 #endif 514 #endif
OLDNEW
« no previous file with comments | « gyp/tools.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698