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

Side by Side Diff: tools/skimage_main.cpp

Issue 26297004: More work to integrate skimage with rebaseline tools. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Fix a comment. Created 7 years, 2 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
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 "gm_expectations.h"
9 #include "SkBitmap.h" 9 #include "SkBitmap.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
(...skipping 13 matching lines...) Expand all
24 24
25 DEFINE_string(config, "None", "Preferred config to decode into. [None|8888|565|A 8]"); 25 DEFINE_string(config, "None", "Preferred config to decode into. [None|8888|565|A 8]");
26 DEFINE_string(createExpectationsPath, "", "Path to write JSON expectations."); 26 DEFINE_string(createExpectationsPath, "", "Path to write JSON expectations.");
27 DEFINE_string(mismatchPath, "", "Folder to write mismatched images to."); 27 DEFINE_string(mismatchPath, "", "Folder to write mismatched images to.");
28 DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required. "); 28 DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required. ");
29 DEFINE_string(readExpectationsPath, "", "Path to read JSON expectations from."); 29 DEFINE_string(readExpectationsPath, "", "Path to read JSON expectations from.");
30 DEFINE_bool(reencode, true, "Reencode the images to test encoding."); 30 DEFINE_bool(reencode, true, "Reencode the images to test encoding.");
31 DEFINE_int32(sampleSize, 1, "Set the sampleSize for decoding."); 31 DEFINE_int32(sampleSize, 1, "Set the sampleSize for decoding.");
32 DEFINE_bool(skip, false, "Skip writing zeroes."); 32 DEFINE_bool(skip, false, "Skip writing zeroes.");
33 DEFINE_bool(testSubsetDecoding, true, "Test decoding subsets of images."); 33 DEFINE_bool(testSubsetDecoding, true, "Test decoding subsets of images.");
34 DEFINE_bool(writeChecksumBasedFilenames, false, "When writing out actual images , use checksum-"
35 "based filenames, as rebaseline.py will use when downloading them fr om Google Storage");
34 DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); 36 DEFINE_string2(writePath, w, "", "Write rendered images into this directory.");
35 37
36 struct Format { 38 struct Format {
37 SkImageEncoder::Type fType; 39 SkImageEncoder::Type fType;
38 SkImageDecoder::Format fFormat; 40 SkImageDecoder::Format fFormat;
39 const char* fSuffix; 41 const char* fSuffix;
40 }; 42 };
41 43
42 static const Format gFormats[] = { 44 static const Format gFormats[] = {
43 { SkImageEncoder::kBMP_Type, SkImageDecoder::kBMP_Format, ".bmp" }, 45 { SkImageEncoder::kBMP_Type, SkImageDecoder::kBMP_Format, ".bmp" },
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 80
79 static void make_outname(SkString* dst, const char outDir[], const char src[], 81 static void make_outname(SkString* dst, const char outDir[], const char src[],
80 const char suffix[]) { 82 const char suffix[]) {
81 SkString basename = SkOSPath::SkBasename(src); 83 SkString basename = SkOSPath::SkBasename(src);
82 dst->set(SkOSPath::SkPathJoin(outDir, basename.c_str())); 84 dst->set(SkOSPath::SkPathJoin(outDir, basename.c_str()));
83 if (!dst->endsWith(suffix)) { 85 if (!dst->endsWith(suffix)) {
84 const char* cstyleDst = dst->c_str(); 86 const char* cstyleDst = dst->c_str();
85 const char* dot = strrchr(cstyleDst, '.'); 87 const char* dot = strrchr(cstyleDst, '.');
86 if (dot != NULL) { 88 if (dot != NULL) {
87 int32_t index = SkToS32(dot - cstyleDst); 89 int32_t index = SkToS32(dot - cstyleDst);
88 dst->remove(index, dst->size() - index); 90 const size_t amountToRemove = dst->size() - index;
91 // Only remove if this is a 4-5 character suffix (including '.').
epoger 2013/10/17 15:54:13 Why this "magic" constraint on the length of the e
scroggo 2013/10/18 21:28:03 Here is the motivation: It is used to create the f
epoger 2013/10/21 15:13:39 Thank you for: 1. removing the magic 2. the cogent
92 if (amountToRemove == 4 || amountToRemove == 5) {
93 dst->remove(index, amountToRemove);
94 }
89 } 95 }
90 dst->append(suffix); 96 dst->append(suffix);
91 } 97 }
92 } 98 }
93 99
94 // Store the names of the filenames to report later which ones failed, succeeded , and were 100 // Store the names of the filenames to report later which ones failed, succeeded , and were
95 // invalid. 101 // invalid.
96 // FIXME: Add more arrays, for more specific types of errors, and make the outpu t simpler. 102 // FIXME: Add more arrays, for more specific types of errors, and make the outpu t simpler.
97 // If each array holds one type of error, the output can change from: 103 // If each array holds one type of error, the output can change from:
98 // 104 //
(...skipping 23 matching lines...) Expand all
122 // For files that are expected to fail. 128 // For files that are expected to fail.
123 static SkTArray<SkString, false> gKnownFailures; 129 static SkTArray<SkString, false> gKnownFailures;
124 static SkTArray<SkString, false> gKnownSubsetFailures; 130 static SkTArray<SkString, false> gKnownSubsetFailures;
125 131
126 static SkBitmap::Config gPrefConfig(SkBitmap::kNo_Config); 132 static SkBitmap::Config gPrefConfig(SkBitmap::kNo_Config);
127 133
128 // Expections read from a file specified by readExpectationsPath. The expectatio ns must have been 134 // Expections read from a file specified by readExpectationsPath. The expectatio ns must have been
129 // previously written using createExpectationsPath. 135 // previously written using createExpectationsPath.
130 SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations; 136 SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations;
131 137
132 static bool write_bitmap(const char outName[], const SkBitmap& bm) { 138 /**
133 if (SkImageEncoder::EncodeFile(outName, bm, SkImageEncoder::kPNG_Type, 100)) { 139 * Encode the bitmap to a file, written one of two ways, depending on
140 * FLAGS_writeChecksumBasedFilenames. If true, the final image will be
epoger 2013/10/17 15:54:13 Maybe make writeChecksumBasedFilenames a parameter
epoger 2013/10/21 15:13:39 I still think it would be better for this to be a
141 * written to:
142 * outDir/hashType/src/digestValue.png
143 * If false, the final image will be written out to:
144 * outDir/src.png
145 * The function returns whether the file was successfully written.
146 */
147 static bool write_bitmap(const char outDir[], const char src[],
148 const skiagm::BitmapAndDigest& bitmapAndDigest) {
149 SkString filename;
150 if (FLAGS_writeChecksumBasedFilenames) {
151 // First create the directory for the hashtype.
152 const SkString hashType = bitmapAndDigest.fDigest.getHashType();
153 const SkString hashDir = SkOSPath::SkPathJoin(outDir, hashType.c_str());
154 if (!sk_mkdir(hashDir.c_str())) {
155 return false;
156 }
157
158 // Now create the name of the folder specific to this image.
159 SkString basename = SkOSPath::SkBasename(src);
160 const SkString imageDir = SkOSPath::SkPathJoin(hashDir.c_str(), basename .c_str());
161 if (!sk_mkdir(imageDir.c_str())) {
162 return false;
163 }
164
165 // Name the file <digest>.png
166 SkString checksumBasedName = bitmapAndDigest.fDigest.getDigestValue();
167 checksumBasedName.append(".png");
168
169 filename = SkOSPath::SkPathJoin(imageDir.c_str(), checksumBasedName.c_st r());
170 } else {
171 make_outname(&filename, outDir, src, ".png");
172 }
173
174 const SkBitmap& bm = bitmapAndDigest.fBitmap;
175 if (SkImageEncoder::EncodeFile(filename.c_str(), bm, SkImageEncoder::kPNG_Ty pe, 100)) {
134 return true; 176 return true;
135 } 177 }
136 178
137 if (bm.config() == SkBitmap::kARGB_8888_Config) { 179 if (bm.config() == SkBitmap::kARGB_8888_Config) {
138 // First attempt at encoding failed, and the bitmap was already 8888. Ma king 180 // First attempt at encoding failed, and the bitmap was already 8888. Ma king
139 // a copy is not going to help. 181 // a copy is not going to help.
140 return false; 182 return false;
141 } 183 }
142 184
143 // Encoding failed. Copy to 8888 and try again. 185 // Encoding failed. Copy to 8888 and try again.
144 SkBitmap bm8888; 186 SkBitmap bm8888;
145 if (!bm.copyTo(&bm8888, SkBitmap::kARGB_8888_Config)) { 187 if (!bm.copyTo(&bm8888, SkBitmap::kARGB_8888_Config)) {
146 return false; 188 return false;
147 } 189 }
148 return SkImageEncoder::EncodeFile(outName, bm8888, SkImageEncoder::kPNG_Type , 100); 190 return SkImageEncoder::EncodeFile(filename.c_str(), bm8888, SkImageEncoder:: kPNG_Type, 100);
149 } 191 }
150 192
151 /** 193 /**
152 * Return a random SkIRect inside the range specified. 194 * Return a random SkIRect inside the range specified.
153 * @param rand Random number generator. 195 * @param rand Random number generator.
154 * @param maxX Exclusive maximum x-coordinate. SkIRect's fLeft and fRight will be 196 * @param maxX Exclusive maximum x-coordinate. SkIRect's fLeft and fRight will be
155 * in the range [0, maxX) 197 * in the range [0, maxX)
156 * @param maxY Exclusive maximum y-coordinate. SkIRect's fTop and fBottom will be 198 * @param maxY Exclusive maximum y-coordinate. SkIRect's fTop and fBottom will be
157 * in the range [0, maxY) 199 * in the range [0, maxY)
158 * @return SkIRect Non-empty, non-degenerate rectangle. 200 * @return SkIRect Non-empty, non-degenerate rectangle.
(...skipping 23 matching lines...) Expand all
182 rect.fTop--; 224 rect.fTop--;
183 } else { 225 } else {
184 rect.fBottom++; 226 rect.fBottom++;
185 // Again, this must be in range. 227 // Again, this must be in range.
186 SkASSERT(rect.fBottom < maxY); 228 SkASSERT(rect.fBottom < maxY);
187 } 229 }
188 } 230 }
189 return rect; 231 return rect;
190 } 232 }
191 233
234 /**
235 * Return a string which includes the name of the file and the pref config,
epoger 2013/10/17 15:54:13 what does "pref" mean? ("prefix"? "preference"?
scroggo 2013/10/18 21:28:03 I've clarified the comment.
236 * matching the pattern of gm_json.py's IMAGE_FILENAME_PATTERN: "name_config.pn g"
237 */
238 static SkString create_json_key(const char* filename) {
239 SkASSERT(FLAGS_config.count() == 1);
240 return SkStringPrintf("%s_%s.png", filename, FLAGS_config[0]);
241 }
242
192 // Stored expectations to be written to a file if createExpectationsPath is spec ified. 243 // Stored expectations to be written to a file if createExpectationsPath is spec ified.
193 static Json::Value gExpectationsToWrite; 244 static Json::Value gExpectationsToWrite;
194 245
195 /** 246 /**
196 * If expectations are to be recorded, record the bitmap expectations into glob al 247 * This function performs two purposes:
197 * expectations array. 248 * 1) If --createExpectationsPath is set, record the expectations to the gl obal
198 */ 249 * expectations object, for future writing to a file, which will later b e
epoger 2013/10/17 15:54:13 I don't think this needs to be fixed within this C
scroggo 2013/10/18 21:28:03 I actually like using globals in this case. If I w
epoger 2013/10/21 15:13:39 Fair enough. Personally, I would prefer member va
199 static void write_expectations(const SkBitmap& bitmap, const char* filename) { 250 * used for comparisons.
200 if (!FLAGS_createExpectationsPath.isEmpty()) { 251 * 2) If --readExpectationsPath is set, compare this bitmap to the json exp ectations
201 // Creates an Expectations object, and add it to the list to write. 252 * provided. This portion determines the return value, as described belo w.
202 skiagm::Expectations expectation(bitmap); 253 * The two purposes share a function so they can share filenames and expectatio ns.
epoger 2013/10/17 15:54:13 IMHO it would be clearer for this to remain as two
scroggo 2013/10/18 21:28:03 Done.
203 Json::Value value = expectation.asJsonValue(); 254 * @param bitmapAndDigest BitmapAndDigest, computed from the decoded bitmap, to both
epoger 2013/10/17 15:54:13 add newline before first param
scroggo 2013/10/18 21:28:03 Done.
204 gExpectationsToWrite[filename] = value; 255 * write and compare to the existing expectation.
205 } 256 * @param filename String used to record the new expectation and to find the ex isting
206 } 257 * expectation.
207
208 /**
209 * Compare against an expectation for this filename, if there is one.
210 * @param digest GmResultDigest, computed from the decoded bitmap, to compare t o the
211 * expectation.
212 * @param filename String used to find the expected value.
213 * @param failureArray Array to add a failure message to on failure. 258 * @param failureArray Array to add a failure message to on failure.
214 * @param missingArray Array to add failure message to when missing image 259 * @param missingArray Array to add failure message to when missing image
215 * expectation. 260 * expectation.
216 * @param ignoreArray Array to add failure message to when the image does not m atch 261 * @param ignoreArray Array to add failure message to when the image does not m atch
217 * the expectation, but this is a failure we can ignore. 262 * the expectation, but this is a failure we can ignore.
218 * @return bool True in any of these cases: 263 * @return bool True in any of these cases:
219 * - the bitmap matches the expectation. 264 * - the bitmap matches the expectation.
220 * False in any of these cases: 265 * False in any of these cases:
221 * - there is no expectations file. 266 * - there is no expectations file.
222 * - there is an expectations file, but no expectation for this bitmap. 267 * - there is an expectations file, but no expectation for this bitmap.
223 * - there is an expectation for this bitmap, but it did not ma tch. 268 * - there is an expectation for this bitmap, but it did not ma tch.
224 * - expectation could not be computed from the bitmap. 269 * - expectation could not be computed from the bitmap.
225 */ 270 */
226 static bool compare_to_expectations_if_necessary(const skiagm::GmResultDigest& d igest, 271 static bool write_and_compare_expectations(const skiagm::BitmapAndDigest& bitmap AndDigest,
227 const char* filename, 272 const char* filename,
228 SkTArray<SkString, false>* fail ureArray, 273 SkTArray<SkString, false>* failureArr ay,
229 SkTArray<SkString, false>* miss ingArray, 274 SkTArray<SkString, false>* missingArr ay,
230 SkTArray<SkString, false>* igno reArray) { 275 SkTArray<SkString, false>* ignoreArra y) {
276 // For both writing and reading, the key for this entry will include the nam e
277 // of the file and the pref config, matching the pattern of gm_json.py's
278 // IMAGE_FILENAME_PATTERN: "name_config.png"
279 const SkString name_config = create_json_key(filename);
280
281 // Record the new expectation.
282 if (!FLAGS_createExpectationsPath.isEmpty()) {
283 // Creates an Expectations object, and add it to the list to write.
284 skiagm::Expectations expectation(bitmapAndDigest);
285 Json::Value value = expectation.asJsonValue();
286 gExpectationsToWrite[name_config.c_str()] = value;
287 }
288
289 // Compare to the existing expectation.
290 const skiagm::GmResultDigest& digest = bitmapAndDigest.fDigest;
231 if (!digest.isValid()) { 291 if (!digest.isValid()) {
232 if (failureArray != NULL) { 292 if (failureArray != NULL) {
233 failureArray->push_back().printf("decoded %s, but could not create a GmResultDigest.", 293 failureArray->push_back().printf("decoded %s, but could not create a GmResultDigest.",
234 filename); 294 filename);
235 } 295 }
236 return false; 296 return false;
237 } 297 }
238 298
239 if (NULL == gJsonExpectations.get()) { 299 if (NULL == gJsonExpectations.get()) {
240 return false; 300 return false;
241 } 301 }
242 302
243 skiagm::Expectations jsExpectation = gJsonExpectations->get(filename); 303 skiagm::Expectations jsExpectation = gJsonExpectations->get(name_config.c_st r());
244 if (jsExpectation.empty()) { 304 if (jsExpectation.empty()) {
245 if (missingArray != NULL) { 305 if (missingArray != NULL) {
246 missingArray->push_back().printf("decoded %s, but could not find exp ectation.", 306 missingArray->push_back().printf("decoded %s, but could not find exp ectation.",
247 filename); 307 filename);
248 } 308 }
249 return false; 309 return false;
250 } 310 }
251 311
252 if (jsExpectation.match(digest)) { 312 if (jsExpectation.match(digest)) {
253 return true; 313 return true;
254 } 314 }
255 315
256 if (jsExpectation.ignoreFailure()) { 316 if (jsExpectation.ignoreFailure()) {
257 ignoreArray->push_back().printf("%s does not match expectation, but this is known.", 317 ignoreArray->push_back().printf("%s does not match expectation, but this is known.",
258 filename); 318 filename);
259 } else if (failureArray != NULL) { 319 } else if (failureArray != NULL) {
260 failureArray->push_back().printf("decoded %s, but the result does not ma tch " 320 failureArray->push_back().printf("decoded %s, but the result does not ma tch "
261 "expectations.", 321 "expectations.",
262 filename); 322 filename);
263 } 323 }
264 return false; 324 return false;
265 } 325 }
266 326
267 /** 327 /**
268 * Helper function to write a bitmap subset to a file. Only called if subsets w ere created 328 * Helper function to write a bitmap subset to a file. Only called if subsets w ere created
269 * and a writePath was provided. Creates a subdirectory called 'subsets' and wr ites a PNG to 329 * and a writePath was provided. Behaves differently depending on
270 * that directory. Also creates a subdirectory called 'extracted' and writes a bitmap created 330 * FLAGS_writeChecksumBasedFilenames. If true:
epoger 2013/10/17 15:54:13 Again, rather than a static method depending on gl
271 * using extractSubset to a PNG in that directory. Both files will represent th e same 331 * Writes the image to a PNG file named according to the digest hash, as de scribed in
272 * subrectangle and have the same name for comparison. 332 * write_bitmap.
333 * If false:
334 * Creates a subdirectory called 'subsets' and writes a PNG to that directo ry. Also
335 * creates a subdirectory called 'extracted' and writes a bitmap created us ing
336 * extractSubset to a PNG in that directory. Both files will represent the same
337 * subrectangle and have the same name for convenient comparison. In this c ase, the
338 * digest is ignored.
273 * @param writePath Parent directory to hold the folders for the PNG files to w rite. Must 339 * @param writePath Parent directory to hold the folders for the PNG files to w rite. Must
epoger 2013/10/17 15:54:13 newline before first param (just to make it easier
scroggo 2013/10/18 21:28:03 Done.
274 * not be NULL. 340 * not be NULL.
275 * @param filename Basename of the original file. Used to name the new files. M ust not be 341 * @param subsetName Basename of the original file, with the dimensions of the subset tacked
276 * NULL. 342 * on. Used to name the new file/folder.
277 * @param subsetDim String representing the dimensions of the subset. Used to n ame the new 343 * @param bitmapAndDigestFromDecodeSubset SkBitmap (with digest) created by
278 * files. Must not be NULL. 344 * SkImageDecoder::DecodeSubset, using rect as the area to decode.
279 * @param bitmapFromDecodeSubset Pointer to SkBitmap created by SkImageDecoder: :DecodeSubset,
280 * using rect as the area to decode.
281 * @param rect Rectangle of the area decoded into bitmapFromDecodeSubset. Used to call 345 * @param rect Rectangle of the area decoded into bitmapFromDecodeSubset. Used to call
282 * extractSubset on originalBitmap to create a bitmap with the same dimensi ons/pixels as 346 * extractSubset on originalBitmap to create a bitmap with the same dimensi ons/pixels as
283 * bitmapFromDecodeSubset (assuming decodeSubset worked properly). 347 * bitmapFromDecodeSubset (assuming decodeSubset worked properly).
284 * @param originalBitmap SkBitmap decoded from the same stream as bitmapFromDec odeSubset, 348 * @param originalBitmap SkBitmap decoded from the same stream as bitmapFromDec odeSubset,
285 * using SkImageDecoder::decode to get the entire image. Used to create a P NG file for 349 * using SkImageDecoder::decode to get the entire image. Used to create a P NG file for
286 * comparison to the PNG created by bitmapFromDecodeSubset. 350 * comparison to the PNG created by bitmapAndDigestFromDecodeSubset's bitma p.
287 * @return bool Whether the function succeeded at drawing the decoded subset an d the extracted 351 * @return bool Whether the function succeeded at drawing the decoded subset an d the extracted
288 * subset to files. 352 * subset to files.
289 */ 353 */
290 static bool write_subset(const char* writePath, const char* filename, const char * subsetDim, 354 static bool write_subset(const char* writePath, const SkString& subsetName,
291 SkBitmap* bitmapFromDecodeSubset, SkIRect rect, 355 const skiagm::BitmapAndDigest bitmapAndDigestFromDecod eSubset,
292 const SkBitmap& originalBitmap) { 356 SkIRect rect, const SkBitmap& originalBitmap) {
293 // All parameters must be valid. 357 // All parameters must be valid.
294 SkASSERT(writePath != NULL); 358 SkASSERT(writePath != NULL);
295 SkASSERT(filename != NULL);
296 SkASSERT(subsetDim != NULL);
297 SkASSERT(bitmapFromDecodeSubset != NULL);
298 359
299 // Create a subdirectory to hold the results of decodeSubset. 360 SkString subsetPath;
300 SkString dir = SkOSPath::SkPathJoin(writePath, "subsets"); 361 if (FLAGS_writeChecksumBasedFilenames) {
301 if (!sk_mkdir(dir.c_str())) { 362 subsetPath.set(writePath);
302 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s , but failed to " 363 } else {
303 "create a directory to write to. ", subsetDim, 364 // Create a subdirectory to hold the results of decodeSubset.
304 filename); 365 subsetPath = SkOSPath::SkPathJoin(writePath, "subsets");
305 return false; 366 if (!sk_mkdir(subsetPath.c_str())) {
367 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but "
368 "failed to create a director y to write to.",
369 subsetName.c_str());
370 return false;
371 }
306 } 372 }
373 SkAssertResult(write_bitmap(subsetPath.c_str(), subsetName.c_str(),
374 bitmapAndDigestFromDecodeSubset));
375 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", subsetName.c_str() );
307 376
308 // Write the subset to a file whose name includes the dimensions. 377 if (!FLAGS_writeChecksumBasedFilenames) {
309 SkString suffix = SkStringPrintf("_%s.png", subsetDim); 378 // FIXME: The goal of extracting the subset is for visual comparison/usi ng skdiff/skpdiff.
310 SkString outPath; 379 // Currently disabling for writeChecksumBasedFilenames since it will be trickier to
311 make_outname(&outPath, dir.c_str(), filename, suffix.c_str()); 380 // determine which files to compare.
312 SkAssertResult(write_bitmap(outPath.c_str(), *bitmapFromDecodeSubset));
313 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", outPath.c_str());
314 381
315 // Also use extractSubset from the original for visual comparison. 382 // Also use extractSubset from the original for visual comparison.
316 // Write the result to a file in a separate subdirectory. 383 // Write the result to a file in a separate subdirectory.
317 SkBitmap extractedSubset; 384 SkBitmap extractedSubset;
318 if (!originalBitmap.extractSubset(&extractedSubset, rect)) { 385 if (!originalBitmap.extractSubset(&extractedSubset, rect)) {
319 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s , but failed to " 386 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but failed "
320 "extract a similar subset for co mparison.", 387 "to extract a similar subset for comparison.",
321 subsetDim, filename); 388 subsetName.c_str());
322 return false; 389 return false;
390 }
391
392 SkString dirExtracted = SkOSPath::SkPathJoin(writePath, "extracted");
393 if (!sk_mkdir(dirExtracted.c_str())) {
394 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but failed "
395 "to create a directory for e xtractSubset "
396 "comparison.",
397 subsetName.c_str());
398 return false;
399 }
400
401 skiagm::BitmapAndDigest bitmapAndDigestFromExtractSubset(extractedSubset );
402 SkAssertResult(write_bitmap(dirExtracted.c_str(), subsetName.c_str(),
403 bitmapAndDigestFromExtractSubset));
323 } 404 }
324
325 SkString dirExtracted = SkOSPath::SkPathJoin(writePath, "extracted");
326 if (!sk_mkdir(dirExtracted.c_str())) {
327 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s , but failed to "
328 "create a directory for extractS ubset comparison.",
329 subsetDim, filename);
330 return false;
331 }
332
333 make_outname(&outPath, dirExtracted.c_str(), filename, suffix.c_str());
334 SkAssertResult(write_bitmap(outPath.c_str(), extractedSubset));
335 return true; 405 return true;
336 } 406 }
337 407
338 // FIXME: This test could be run on windows/mac once we remove their dependence on 408 // FIXME: This test could be run on windows/mac once we remove their dependence on
339 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570 409 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570
340 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) 410 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
341 411
342 /** 412 /**
343 * Dummy class for testing to ensure that a stream without a length decodes the same 413 * Dummy class for testing to ensure that a stream without a length decodes the same
344 * as a stream with a length. 414 * as a stream with a length.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 "a digest\n", srcPath); 460 "a digest\n", srcPath);
391 return; 461 return;
392 } 462 }
393 if (!lengthLessDigest.equals(digest)) { 463 if (!lengthLessDigest.equals(digest)) {
394 gDecodeFailures.push_back().appendf("Without using getLength, %s did not match digest " 464 gDecodeFailures.push_back().appendf("Without using getLength, %s did not match digest "
395 "that uses getLength\n", srcPath); 465 "that uses getLength\n", srcPath);
396 } 466 }
397 } 467 }
398 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) 468 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
399 469
470 /**
471 * Replace all instances of oldChar with newChar in str.
472 */
473 static void replace_char(SkString* str, const char oldChar, const char newChar) {
epoger 2013/10/17 15:54:13 I don't think this needs to happen within this CL,
scroggo 2013/10/18 21:28:03 Done.
474 if (NULL == str) {
475 return;
476 }
477 for (size_t i = 0; i < str->size(); ++i) {
478 if (oldChar == str->operator[](i)) {
479 str->operator[](i) = newChar;
480 }
481 }
482 }
483
400 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { 484 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) {
401 SkBitmap bitmap; 485 SkBitmap bitmap;
402 SkFILEStream stream(srcPath); 486 SkFILEStream stream(srcPath);
403 if (!stream.isValid()) { 487 if (!stream.isValid()) {
404 gInvalidStreams.push_back().set(srcPath); 488 gInvalidStreams.push_back().set(srcPath);
405 return; 489 return;
406 } 490 }
407 491
408 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); 492 SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
409 if (NULL == codec) { 493 if (NULL == codec) {
410 gMissingCodecs.push_back().set(srcPath); 494 gMissingCodecs.push_back().set(srcPath);
411 return; 495 return;
412 } 496 }
413 497
414 SkAutoTDelete<SkImageDecoder> ad(codec); 498 SkAutoTDelete<SkImageDecoder> ad(codec);
415 499
416 codec->setSkipWritingZeroes(FLAGS_skip); 500 codec->setSkipWritingZeroes(FLAGS_skip);
417 codec->setSampleSize(FLAGS_sampleSize); 501 codec->setSampleSize(FLAGS_sampleSize);
418 stream.rewind(); 502 stream.rewind();
419 503
420 // Create a string representing just the filename itself, for use in json ex pectations. 504 // Create a string representing just the filename itself, for use in json ex pectations.
421 SkString basename = SkOSPath::SkBasename(srcPath); 505 SkString basename = SkOSPath::SkBasename(srcPath);
506 // Replace '_' with '-', so that the names can fit gm_json.py's IMAGE_FILENA ME_PATTERN
507 replace_char(&basename, '_', '-');
422 const char* filename = basename.c_str(); 508 const char* filename = basename.c_str();
423 509
424 if (!codec->decode(&stream, &bitmap, gPrefConfig, 510 if (!codec->decode(&stream, &bitmap, gPrefConfig,
425 SkImageDecoder::kDecodePixels_Mode)) { 511 SkImageDecoder::kDecodePixels_Mode)) {
426 if (NULL != gJsonExpectations.get()) { 512 if (NULL != gJsonExpectations.get()) {
427 skiagm::Expectations jsExpectations = gJsonExpectations->get(filenam e); 513 const SkString name_config = create_json_key(filename);
514 skiagm::Expectations jsExpectations = gJsonExpectations->get(name_co nfig.c_str());
428 if (jsExpectations.ignoreFailure()) { 515 if (jsExpectations.ignoreFailure()) {
429 // This is a known failure. 516 // This is a known failure.
430 gKnownFailures.push_back().appendf( 517 gKnownFailures.push_back().appendf(
431 "failed to decode %s, which is a known failure.", srcPath); 518 "failed to decode %s, which is a known failure.", srcPath);
432 return; 519 return;
433 } 520 }
434 if (jsExpectations.empty()) { 521 if (jsExpectations.empty()) {
435 // This is a failure, but it is a new file. Mark it as missing, with 522 // This is a failure, but it is a new file. Mark it as missing, with
436 // a note that it should be marked failing. 523 // a note that it should be marked failing.
437 gMissingExpectations.push_back().appendf( 524 gMissingExpectations.push_back().appendf(
(...skipping 17 matching lines...) Expand all
455 gDecodeFailures.push_back() = failure; 542 gDecodeFailures.push_back() = failure;
456 } else { 543 } else {
457 // Now check that the bounds match: 544 // Now check that the bounds match:
458 if (dim.width() != bitmap.width() || dim.height() != bitmap.height() ) { 545 if (dim.width() != bitmap.width() || dim.height() != bitmap.height() ) {
459 SkString failure = SkStringPrintf("bounds do not match for %s", srcPath); 546 SkString failure = SkStringPrintf("bounds do not match for %s", srcPath);
460 gDecodeFailures.push_back() = failure; 547 gDecodeFailures.push_back() = failure;
461 } 548 }
462 } 549 }
463 } 550 }
464 551
465 skiagm::GmResultDigest digest(bitmap); 552 skiagm::BitmapAndDigest bitmapAndDigest(bitmap);
466 if (compare_to_expectations_if_necessary(digest, filename, 553 if (write_and_compare_expectations(bitmapAndDigest, filename, &gDecodeFailur es,
467 &gDecodeFailures, 554 &gMissingExpectations, &gKnownFailures)) {
468 &gMissingExpectations,
469 &gKnownFailures)) {
470 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(), 555 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(),
471 bitmap.height()); 556 bitmap.height());
472 } else if (!FLAGS_mismatchPath.isEmpty()) { 557 } else if (!FLAGS_mismatchPath.isEmpty()) {
473 SkString outPath; 558 if (write_bitmap(FLAGS_mismatchPath[0], filename, bitmapAndDigest)) {
474 make_outname(&outPath, FLAGS_mismatchPath[0], srcPath, ".png"); 559 gSuccessfulDecodes.push_back().appendf("\twrote %s", filename);
475 if (write_bitmap(outPath.c_str(), bitmap)) {
476 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_str() );
477 } else { 560 } else {
478 gEncodeFailures.push_back().set(outPath); 561 gEncodeFailures.push_back().set(filename);
479 } 562 }
480 } 563 }
481 564
482 // FIXME: This test could be run on windows/mac once we remove their dependence on 565 // FIXME: This test could be run on windows/mac once we remove their dependence on
483 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570 566 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570
484 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) 567 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
485 test_stream_without_length(srcPath, codec, digest); 568 test_stream_without_length(srcPath, codec, bitmapAndDigest.fDigest);
486 #endif 569 #endif
487 570
488 if (writePath != NULL) { 571 if (writePath != NULL) {
489 SkString outPath; 572 if (write_bitmap(writePath->c_str(), filename, bitmapAndDigest)) {
490 make_outname(&outPath, writePath->c_str(), srcPath, ".png"); 573 gSuccessfulDecodes.push_back().appendf("\twrote %s", filename);
491 if (write_bitmap(outPath.c_str(), bitmap)) {
492 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_str() );
493 } else { 574 } else {
494 gEncodeFailures.push_back().set(outPath); 575 gEncodeFailures.push_back().set(filename);
495 } 576 }
496 } 577 }
497 578
498 write_expectations(bitmap, filename);
499
500 if (FLAGS_testSubsetDecoding) { 579 if (FLAGS_testSubsetDecoding) {
501 SkDEBUGCODE(bool couldRewind =) stream.rewind(); 580 SkDEBUGCODE(bool couldRewind =) stream.rewind();
502 SkASSERT(couldRewind); 581 SkASSERT(couldRewind);
503 int width, height; 582 int width, height;
504 // Build the tile index for decoding subsets. If the image is 1x1, skip subset 583 // Build the tile index for decoding subsets. If the image is 1x1, skip subset
505 // decoding since there are no smaller subsets. 584 // decoding since there are no smaller subsets.
506 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { 585 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) {
507 SkASSERT(bitmap.width() == width && bitmap.height() == height); 586 SkASSERT(bitmap.width() == width && bitmap.height() == height);
508 // Call decodeSubset multiple times: 587 // Call decodeSubset multiple times:
509 SkRandom rand(0); 588 SkRandom rand(0);
510 for (int i = 0; i < 5; i++) { 589 for (int i = 0; i < 5; i++) {
511 SkBitmap bitmapFromDecodeSubset; 590 SkBitmap bitmapFromDecodeSubset;
512 // FIXME: Come up with a more representative set of rectangles. 591 // FIXME: Come up with a more representative set of rectangles.
513 SkIRect rect = generate_random_rect(&rand, width, height); 592 SkIRect rect = generate_random_rect(&rand, width, height);
514 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, 593 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop,
515 rect.fRight, rect.fBottom); 594 rect.fRight, rect.fBottom);
516 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf ig)) { 595 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf ig)) {
517 SkString subsetName = SkStringPrintf("%s_%s", filename, subs etDim.c_str()); 596 SkString subsetName = SkStringPrintf("%s-%s", filename, subs etDim.c_str());
518 skiagm::GmResultDigest subsetDigest(bitmapFromDecodeSubset); 597 skiagm::BitmapAndDigest subsetBitmapAndDigest(bitmapFromDeco deSubset);
519 if (compare_to_expectations_if_necessary(subsetDigest, 598 if (write_and_compare_expectations(subsetBitmapAndDigest,
520 subsetName.c_str(), 599 subsetName.c_str(),
521 &gFailedSubsetDecod es, 600 &gFailedSubsetDecodes,
522 &gMissingSubsetExpe ctations, 601 &gMissingSubsetExpectatio ns,
523 &gKnownSubsetFailur es)) { 602 &gKnownSubsetFailures)) {
524 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s", 603 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s",
525 subsetDim.c_str(), srcPath); 604 subsetDim.c_str(), srcPath);
526 } else if (!FLAGS_mismatchPath.isEmpty()) { 605 } else if (!FLAGS_mismatchPath.isEmpty()) {
527 write_subset(FLAGS_mismatchPath[0], filename, subsetDim. c_str(), 606 write_subset(FLAGS_mismatchPath[0], subsetName,
528 &bitmapFromDecodeSubset, rect, bitmap); 607 subsetBitmapAndDigest, rect, bitmap);
529 } 608 }
530 609
531 write_expectations(bitmapFromDecodeSubset, subsetName.c_str( ));
532 if (writePath != NULL) { 610 if (writePath != NULL) {
533 write_subset(writePath->c_str(), filename, subsetDim.c_s tr(), 611 write_subset(writePath->c_str(), subsetName,
534 &bitmapFromDecodeSubset, rect, bitmap); 612 subsetBitmapAndDigest, rect, bitmap);
535 } 613 }
536 } else { 614 } else {
537 gFailedSubsetDecodes.push_back().printf("Failed to decode re gion %s from %s", 615 gFailedSubsetDecodes.push_back().printf("Failed to decode re gion %s from %s",
538 subsetDim.c_str(), s rcPath); 616 subsetDim.c_str(), s rcPath);
539 } 617 }
540 } 618 }
541 } 619 }
542 } 620 }
543 621
544 // Do not attempt to re-encode A8, since our image encoders do not support e ncoding to A8. 622 // Do not attempt to re-encode A8, since our image encoders do not support e ncoding to A8.
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
749 print_strings("Known failures", gKnownFailures); 827 print_strings("Known failures", gKnownFailures);
750 828
751 return failed ? -1 : 0; 829 return failed ? -1 : 0;
752 } 830 }
753 831
754 #if !defined SK_BUILD_FOR_IOS 832 #if !defined SK_BUILD_FOR_IOS
755 int main(int argc, char * const argv[]) { 833 int main(int argc, char * const argv[]) {
756 return tool_main(argc, (char**) argv); 834 return tool_main(argc, (char**) argv);
757 } 835 }
758 #endif 836 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698