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 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 Loading... |
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 |
OLD | NEW |