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 "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 Loading... |
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 29 matching lines...) Expand all Loading... |
73 return gFormats[i].fFormat; | 75 return gFormats[i].fFormat; |
74 } | 76 } |
75 } | 77 } |
76 return SkImageDecoder::kUnknown_Format; | 78 return SkImageDecoder::kUnknown_Format; |
77 } | 79 } |
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 dst->append(suffix); |
84 const char* cstyleDst = dst->c_str(); | |
85 const char* dot = strrchr(cstyleDst, '.'); | |
86 if (dot != NULL) { | |
87 int32_t index = SkToS32(dot - cstyleDst); | |
88 dst->remove(index, dst->size() - index); | |
89 } | |
90 dst->append(suffix); | |
91 } | |
92 } | 86 } |
93 | 87 |
94 // Store the names of the filenames to report later which ones failed, succeeded
, and were | 88 // Store the names of the filenames to report later which ones failed, succeeded
, and were |
95 // invalid. | 89 // invalid. |
96 // FIXME: Add more arrays, for more specific types of errors, and make the outpu
t simpler. | 90 // 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: | 91 // If each array holds one type of error, the output can change from: |
98 // | 92 // |
99 // Failures: | 93 // Failures: |
100 // <image> failed for such and such reason | 94 // <image> failed for such and such reason |
101 // <image> failed for some different reason | 95 // <image> failed for some different reason |
(...skipping 20 matching lines...) Expand all Loading... |
122 // For files that are expected to fail. | 116 // For files that are expected to fail. |
123 static SkTArray<SkString, false> gKnownFailures; | 117 static SkTArray<SkString, false> gKnownFailures; |
124 static SkTArray<SkString, false> gKnownSubsetFailures; | 118 static SkTArray<SkString, false> gKnownSubsetFailures; |
125 | 119 |
126 static SkBitmap::Config gPrefConfig(SkBitmap::kNo_Config); | 120 static SkBitmap::Config gPrefConfig(SkBitmap::kNo_Config); |
127 | 121 |
128 // Expections read from a file specified by readExpectationsPath. The expectatio
ns must have been | 122 // Expections read from a file specified by readExpectationsPath. The expectatio
ns must have been |
129 // previously written using createExpectationsPath. | 123 // previously written using createExpectationsPath. |
130 SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations; | 124 SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations; |
131 | 125 |
132 static bool write_bitmap(const char outName[], const SkBitmap& bm) { | 126 /** |
133 if (SkImageEncoder::EncodeFile(outName, bm, SkImageEncoder::kPNG_Type, 100))
{ | 127 * Encode the bitmap to a file, written one of two ways, depending on |
| 128 * FLAGS_writeChecksumBasedFilenames. If true, the final image will be |
| 129 * written to: |
| 130 * outDir/hashType/src/digestValue.png |
| 131 * If false, the final image will be written out to: |
| 132 * outDir/src.png |
| 133 * The function returns whether the file was successfully written. |
| 134 */ |
| 135 static bool write_bitmap(const char outDir[], const char src[], |
| 136 const skiagm::BitmapAndDigest& bitmapAndDigest) { |
| 137 SkString filename; |
| 138 if (FLAGS_writeChecksumBasedFilenames) { |
| 139 // First create the directory for the hashtype. |
| 140 const SkString hashType = bitmapAndDigest.fDigest.getHashType(); |
| 141 const SkString hashDir = SkOSPath::SkPathJoin(outDir, hashType.c_str()); |
| 142 if (!sk_mkdir(hashDir.c_str())) { |
| 143 return false; |
| 144 } |
| 145 |
| 146 // Now create the name of the folder specific to this image. |
| 147 SkString basename = SkOSPath::SkBasename(src); |
| 148 const SkString imageDir = SkOSPath::SkPathJoin(hashDir.c_str(), basename
.c_str()); |
| 149 if (!sk_mkdir(imageDir.c_str())) { |
| 150 return false; |
| 151 } |
| 152 |
| 153 // Name the file <digest>.png |
| 154 SkString checksumBasedName = bitmapAndDigest.fDigest.getDigestValue(); |
| 155 checksumBasedName.append(".png"); |
| 156 |
| 157 filename = SkOSPath::SkPathJoin(imageDir.c_str(), checksumBasedName.c_st
r()); |
| 158 } else { |
| 159 make_outname(&filename, outDir, src, ".png"); |
| 160 } |
| 161 |
| 162 const SkBitmap& bm = bitmapAndDigest.fBitmap; |
| 163 if (SkImageEncoder::EncodeFile(filename.c_str(), bm, SkImageEncoder::kPNG_Ty
pe, 100)) { |
134 return true; | 164 return true; |
135 } | 165 } |
136 | 166 |
137 if (bm.config() == SkBitmap::kARGB_8888_Config) { | 167 if (bm.config() == SkBitmap::kARGB_8888_Config) { |
138 // First attempt at encoding failed, and the bitmap was already 8888. Ma
king | 168 // First attempt at encoding failed, and the bitmap was already 8888. Ma
king |
139 // a copy is not going to help. | 169 // a copy is not going to help. |
140 return false; | 170 return false; |
141 } | 171 } |
142 | 172 |
143 // Encoding failed. Copy to 8888 and try again. | 173 // Encoding failed. Copy to 8888 and try again. |
144 SkBitmap bm8888; | 174 SkBitmap bm8888; |
145 if (!bm.copyTo(&bm8888, SkBitmap::kARGB_8888_Config)) { | 175 if (!bm.copyTo(&bm8888, SkBitmap::kARGB_8888_Config)) { |
146 return false; | 176 return false; |
147 } | 177 } |
148 return SkImageEncoder::EncodeFile(outName, bm8888, SkImageEncoder::kPNG_Type
, 100); | 178 return SkImageEncoder::EncodeFile(filename.c_str(), bm8888, SkImageEncoder::
kPNG_Type, 100); |
149 } | 179 } |
150 | 180 |
151 /** | 181 /** |
152 * Return a random SkIRect inside the range specified. | 182 * Return a random SkIRect inside the range specified. |
153 * @param rand Random number generator. | 183 * @param rand Random number generator. |
154 * @param maxX Exclusive maximum x-coordinate. SkIRect's fLeft and fRight will
be | 184 * @param maxX Exclusive maximum x-coordinate. SkIRect's fLeft and fRight will
be |
155 * in the range [0, maxX) | 185 * in the range [0, maxX) |
156 * @param maxY Exclusive maximum y-coordinate. SkIRect's fTop and fBottom will
be | 186 * @param maxY Exclusive maximum y-coordinate. SkIRect's fTop and fBottom will
be |
157 * in the range [0, maxY) | 187 * in the range [0, maxY) |
158 * @return SkIRect Non-empty, non-degenerate rectangle. | 188 * @return SkIRect Non-empty, non-degenerate rectangle. |
(...skipping 23 matching lines...) Expand all Loading... |
182 rect.fTop--; | 212 rect.fTop--; |
183 } else { | 213 } else { |
184 rect.fBottom++; | 214 rect.fBottom++; |
185 // Again, this must be in range. | 215 // Again, this must be in range. |
186 SkASSERT(rect.fBottom < maxY); | 216 SkASSERT(rect.fBottom < maxY); |
187 } | 217 } |
188 } | 218 } |
189 return rect; | 219 return rect; |
190 } | 220 } |
191 | 221 |
| 222 /** |
| 223 * Return a string which includes the name of the file and the preferred config
, |
| 224 * as specified by "--config". The resulting string will match the pattern of |
| 225 * gm_json.py's IMAGE_FILENAME_PATTERN: "filename_config.png" |
| 226 */ |
| 227 static SkString create_json_key(const char* filename) { |
| 228 SkASSERT(FLAGS_config.count() == 1); |
| 229 return SkStringPrintf("%s_%s.png", filename, FLAGS_config[0]); |
| 230 } |
| 231 |
192 // Stored expectations to be written to a file if createExpectationsPath is spec
ified. | 232 // Stored expectations to be written to a file if createExpectationsPath is spec
ified. |
193 static Json::Value gExpectationsToWrite; | 233 static Json::Value gExpectationsToWrite; |
194 | 234 |
195 /** | 235 /** |
196 * If expectations are to be recorded, record the bitmap expectations into glob
al | 236 * If expectations are to be recorded, record the bitmap expectations into the
global |
197 * expectations array. | 237 * expectations array. |
| 238 * As is the case with reading expectations, the key used will combine the file
name |
| 239 * parameter with the preferred config, as specified by "--config", matching th
e |
| 240 * pattern of gm_json.py's IMAGE_FILENAME_PATTERN: "filename_config.png" |
198 */ | 241 */ |
199 static void write_expectations(const SkBitmap& bitmap, const char* filename) { | 242 static void write_expectations(const skiagm::BitmapAndDigest& bitmapAndDigest, |
| 243 const char* filename) { |
| 244 const SkString name_config = create_json_key(filename); |
200 if (!FLAGS_createExpectationsPath.isEmpty()) { | 245 if (!FLAGS_createExpectationsPath.isEmpty()) { |
201 // Creates an Expectations object, and add it to the list to write. | 246 // Creates an Expectations object, and add it to the list to write. |
202 skiagm::Expectations expectation(bitmap); | 247 skiagm::Expectations expectation(bitmapAndDigest); |
203 Json::Value value = expectation.asJsonValue(); | 248 Json::Value value = expectation.asJsonValue(); |
204 gExpectationsToWrite[filename] = value; | 249 gExpectationsToWrite[name_config.c_str()] = value; |
205 } | 250 } |
206 } | 251 } |
207 | 252 |
208 /** | 253 /** |
209 * Compare against an expectation for this filename, if there is one. | 254 * If --readExpectationsPath is set, compare this bitmap to the json expectatio
ns |
210 * @param digest GmResultDigest, computed from the decoded bitmap, to compare t
o the | 255 * provided. |
211 * expectation. | 256 * |
212 * @param filename String used to find the expected value. | 257 * @param digest GmResultDigest, computed from the decoded bitmap, to compare t
o |
| 258 * the existing expectation. |
| 259 * @param filename String used to find the expected value. Will be combined wit
h the |
| 260 * preferred config, as specified by "--config", to match the pattern of |
| 261 * gm_json.py's IMAGE_FILENAME_PATTERN: "filename_config.png". The resul
ting |
| 262 * key will be used to find the proper expectations. |
213 * @param failureArray Array to add a failure message to on failure. | 263 * @param failureArray Array to add a failure message to on failure. |
214 * @param missingArray Array to add failure message to when missing image | 264 * @param missingArray Array to add failure message to when missing image |
215 * expectation. | 265 * expectation. |
216 * @param ignoreArray Array to add failure message to when the image does not m
atch | 266 * @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. | 267 * the expectation, but this is a failure we can ignore. |
218 * @return bool True in any of these cases: | 268 * @return bool True in any of these cases: |
219 * - the bitmap matches the expectation. | 269 * - the bitmap matches the expectation. |
220 * False in any of these cases: | 270 * False in any of these cases: |
221 * - there is no expectations file. | 271 * - there is no expectations file. |
222 * - there is an expectations file, but no expectation for this
bitmap. | 272 * - 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. | 273 * - there is an expectation for this bitmap, but it did not ma
tch. |
224 * - expectation could not be computed from the bitmap. | 274 * - expectation could not be computed from the bitmap. |
225 */ | 275 */ |
226 static bool compare_to_expectations_if_necessary(const skiagm::GmResultDigest& d
igest, | 276 static bool compare_to_expectations_if_necessary(const skiagm::GmResultDigest& d
igest, |
227 const char* filename, | 277 const char* filename, |
228 SkTArray<SkString, false>* fail
ureArray, | 278 SkTArray<SkString, false>* fail
ureArray, |
229 SkTArray<SkString, false>* miss
ingArray, | 279 SkTArray<SkString, false>* miss
ingArray, |
230 SkTArray<SkString, false>* igno
reArray) { | 280 SkTArray<SkString, false>* igno
reArray) { |
| 281 // For both writing and reading, the key for this entry will include the nam
e |
| 282 // of the file and the pref config, matching the pattern of gm_json.py's |
| 283 // IMAGE_FILENAME_PATTERN: "name_config.png" |
| 284 const SkString name_config = create_json_key(filename); |
| 285 |
231 if (!digest.isValid()) { | 286 if (!digest.isValid()) { |
232 if (failureArray != NULL) { | 287 if (failureArray != NULL) { |
233 failureArray->push_back().printf("decoded %s, but could not create a
GmResultDigest.", | 288 failureArray->push_back().printf("decoded %s, but could not create a
GmResultDigest.", |
234 filename); | 289 filename); |
235 } | 290 } |
236 return false; | 291 return false; |
237 } | 292 } |
238 | 293 |
239 if (NULL == gJsonExpectations.get()) { | 294 if (NULL == gJsonExpectations.get()) { |
240 return false; | 295 return false; |
241 } | 296 } |
242 | 297 |
243 skiagm::Expectations jsExpectation = gJsonExpectations->get(filename); | 298 skiagm::Expectations jsExpectation = gJsonExpectations->get(name_config.c_st
r()); |
244 if (jsExpectation.empty()) { | 299 if (jsExpectation.empty()) { |
245 if (missingArray != NULL) { | 300 if (missingArray != NULL) { |
246 missingArray->push_back().printf("decoded %s, but could not find exp
ectation.", | 301 missingArray->push_back().printf("decoded %s, but could not find exp
ectation.", |
247 filename); | 302 filename); |
248 } | 303 } |
249 return false; | 304 return false; |
250 } | 305 } |
251 | 306 |
252 if (jsExpectation.match(digest)) { | 307 if (jsExpectation.match(digest)) { |
253 return true; | 308 return true; |
254 } | 309 } |
255 | 310 |
256 if (jsExpectation.ignoreFailure()) { | 311 if (jsExpectation.ignoreFailure()) { |
257 ignoreArray->push_back().printf("%s does not match expectation, but this
is known.", | 312 ignoreArray->push_back().printf("%s does not match expectation, but this
is known.", |
258 filename); | 313 filename); |
259 } else if (failureArray != NULL) { | 314 } else if (failureArray != NULL) { |
260 failureArray->push_back().printf("decoded %s, but the result does not ma
tch " | 315 failureArray->push_back().printf("decoded %s, but the result does not ma
tch " |
261 "expectations.", | 316 "expectations.", |
262 filename); | 317 filename); |
263 } | 318 } |
264 return false; | 319 return false; |
265 } | 320 } |
266 | 321 |
267 /** | 322 /** |
268 * Helper function to write a bitmap subset to a file. Only called if subsets w
ere created | 323 * 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 | 324 * and a writePath was provided. Behaves differently depending on |
270 * that directory. Also creates a subdirectory called 'extracted' and writes a
bitmap created | 325 * FLAGS_writeChecksumBasedFilenames. If true: |
271 * using extractSubset to a PNG in that directory. Both files will represent th
e same | 326 * 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. | 327 * write_bitmap. |
| 328 * If false: |
| 329 * Creates a subdirectory called 'subsets' and writes a PNG to that directo
ry. Also |
| 330 * creates a subdirectory called 'extracted' and writes a bitmap created us
ing |
| 331 * extractSubset to a PNG in that directory. Both files will represent the
same |
| 332 * subrectangle and have the same name for convenient comparison. In this c
ase, the |
| 333 * digest is ignored. |
| 334 * |
273 * @param writePath Parent directory to hold the folders for the PNG files to w
rite. Must | 335 * @param writePath Parent directory to hold the folders for the PNG files to w
rite. Must |
274 * not be NULL. | 336 * not be NULL. |
275 * @param filename Basename of the original file. Used to name the new files. M
ust not be | 337 * @param subsetName Basename of the original file, with the dimensions of the
subset tacked |
276 * NULL. | 338 * on. Used to name the new file/folder. |
277 * @param subsetDim String representing the dimensions of the subset. Used to n
ame the new | 339 * @param bitmapAndDigestFromDecodeSubset SkBitmap (with digest) created by |
278 * files. Must not be NULL. | 340 * 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 | 341 * @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 | 342 * extractSubset on originalBitmap to create a bitmap with the same dimensi
ons/pixels as |
283 * bitmapFromDecodeSubset (assuming decodeSubset worked properly). | 343 * bitmapFromDecodeSubset (assuming decodeSubset worked properly). |
284 * @param originalBitmap SkBitmap decoded from the same stream as bitmapFromDec
odeSubset, | 344 * @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 | 345 * using SkImageDecoder::decode to get the entire image. Used to create a P
NG file for |
286 * comparison to the PNG created by bitmapFromDecodeSubset. | 346 * 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 | 347 * @return bool Whether the function succeeded at drawing the decoded subset an
d the extracted |
288 * subset to files. | 348 * subset to files. |
289 */ | 349 */ |
290 static bool write_subset(const char* writePath, const char* filename, const char
* subsetDim, | 350 static bool write_subset(const char* writePath, const SkString& subsetName, |
291 SkBitmap* bitmapFromDecodeSubset, SkIRect rect, | 351 const skiagm::BitmapAndDigest bitmapAndDigestFromDecod
eSubset, |
292 const SkBitmap& originalBitmap) { | 352 SkIRect rect, const SkBitmap& originalBitmap) { |
293 // All parameters must be valid. | 353 // All parameters must be valid. |
294 SkASSERT(writePath != NULL); | 354 SkASSERT(writePath != NULL); |
295 SkASSERT(filename != NULL); | |
296 SkASSERT(subsetDim != NULL); | |
297 SkASSERT(bitmapFromDecodeSubset != NULL); | |
298 | 355 |
299 // Create a subdirectory to hold the results of decodeSubset. | 356 SkString subsetPath; |
300 SkString dir = SkOSPath::SkPathJoin(writePath, "subsets"); | 357 if (FLAGS_writeChecksumBasedFilenames) { |
301 if (!sk_mkdir(dir.c_str())) { | 358 subsetPath.set(writePath); |
302 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s
, but failed to " | 359 } else { |
303 "create a directory to write to.
", subsetDim, | 360 // Create a subdirectory to hold the results of decodeSubset. |
304 filename); | 361 subsetPath = SkOSPath::SkPathJoin(writePath, "subsets"); |
305 return false; | 362 if (!sk_mkdir(subsetPath.c_str())) { |
| 363 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset
%s, but " |
| 364 "failed to create a director
y to write to.", |
| 365 subsetName.c_str()); |
| 366 return false; |
| 367 } |
306 } | 368 } |
| 369 SkAssertResult(write_bitmap(subsetPath.c_str(), subsetName.c_str(), |
| 370 bitmapAndDigestFromDecodeSubset)); |
| 371 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", subsetName.c_str()
); |
307 | 372 |
308 // Write the subset to a file whose name includes the dimensions. | 373 if (!FLAGS_writeChecksumBasedFilenames) { |
309 SkString suffix = SkStringPrintf("_%s.png", subsetDim); | 374 // FIXME: The goal of extracting the subset is for visual comparison/usi
ng skdiff/skpdiff. |
310 SkString outPath; | 375 // Currently disabling for writeChecksumBasedFilenames since it will be
trickier to |
311 make_outname(&outPath, dir.c_str(), filename, suffix.c_str()); | 376 // determine which files to compare. |
312 SkAssertResult(write_bitmap(outPath.c_str(), *bitmapFromDecodeSubset)); | |
313 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", outPath.c_str()); | |
314 | 377 |
315 // Also use extractSubset from the original for visual comparison. | 378 // Also use extractSubset from the original for visual comparison. |
316 // Write the result to a file in a separate subdirectory. | 379 // Write the result to a file in a separate subdirectory. |
317 SkBitmap extractedSubset; | 380 SkBitmap extractedSubset; |
318 if (!originalBitmap.extractSubset(&extractedSubset, rect)) { | 381 if (!originalBitmap.extractSubset(&extractedSubset, rect)) { |
319 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s
, but failed to " | 382 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset
%s, but failed " |
320 "extract a similar subset for co
mparison.", | 383 "to extract a similar subset
for comparison.", |
321 subsetDim, filename); | 384 subsetName.c_str()); |
322 return false; | 385 return false; |
| 386 } |
| 387 |
| 388 SkString dirExtracted = SkOSPath::SkPathJoin(writePath, "extracted"); |
| 389 if (!sk_mkdir(dirExtracted.c_str())) { |
| 390 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset
%s, but failed " |
| 391 "to create a directory for e
xtractSubset " |
| 392 "comparison.", |
| 393 subsetName.c_str()); |
| 394 return false; |
| 395 } |
| 396 |
| 397 skiagm::BitmapAndDigest bitmapAndDigestFromExtractSubset(extractedSubset
); |
| 398 SkAssertResult(write_bitmap(dirExtracted.c_str(), subsetName.c_str(), |
| 399 bitmapAndDigestFromExtractSubset)); |
323 } | 400 } |
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; | 401 return true; |
336 } | 402 } |
337 | 403 |
338 // FIXME: This test could be run on windows/mac once we remove their dependence
on | 404 // 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 | 405 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570 |
340 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) | 406 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
341 | 407 |
342 /** | 408 /** |
343 * Dummy class for testing to ensure that a stream without a length decodes the
same | 409 * Dummy class for testing to ensure that a stream without a length decodes the
same |
344 * as a stream with a length. | 410 * as a stream with a length. |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
390 "a digest\n", srcPath); | 456 "a digest\n", srcPath); |
391 return; | 457 return; |
392 } | 458 } |
393 if (!lengthLessDigest.equals(digest)) { | 459 if (!lengthLessDigest.equals(digest)) { |
394 gDecodeFailures.push_back().appendf("Without using getLength, %s did not
match digest " | 460 gDecodeFailures.push_back().appendf("Without using getLength, %s did not
match digest " |
395 "that uses getLength\n", srcPath); | 461 "that uses getLength\n", srcPath); |
396 } | 462 } |
397 } | 463 } |
398 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) | 464 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
399 | 465 |
| 466 /** |
| 467 * Replace all instances of oldChar with newChar in str. |
| 468 * TODO: Add this function to SkString and write tests for it. |
| 469 */ |
| 470 static void replace_char(SkString* str, const char oldChar, const char newChar)
{ |
| 471 if (NULL == str) { |
| 472 return; |
| 473 } |
| 474 for (size_t i = 0; i < str->size(); ++i) { |
| 475 if (oldChar == str->operator[](i)) { |
| 476 str->operator[](i) = newChar; |
| 477 } |
| 478 } |
| 479 } |
| 480 |
400 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath)
{ | 481 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath)
{ |
401 SkBitmap bitmap; | 482 SkBitmap bitmap; |
402 SkFILEStream stream(srcPath); | 483 SkFILEStream stream(srcPath); |
403 if (!stream.isValid()) { | 484 if (!stream.isValid()) { |
404 gInvalidStreams.push_back().set(srcPath); | 485 gInvalidStreams.push_back().set(srcPath); |
405 return; | 486 return; |
406 } | 487 } |
407 | 488 |
408 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); | 489 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); |
409 if (NULL == codec) { | 490 if (NULL == codec) { |
410 gMissingCodecs.push_back().set(srcPath); | 491 gMissingCodecs.push_back().set(srcPath); |
411 return; | 492 return; |
412 } | 493 } |
413 | 494 |
414 SkAutoTDelete<SkImageDecoder> ad(codec); | 495 SkAutoTDelete<SkImageDecoder> ad(codec); |
415 | 496 |
416 codec->setSkipWritingZeroes(FLAGS_skip); | 497 codec->setSkipWritingZeroes(FLAGS_skip); |
417 codec->setSampleSize(FLAGS_sampleSize); | 498 codec->setSampleSize(FLAGS_sampleSize); |
418 stream.rewind(); | 499 stream.rewind(); |
419 | 500 |
420 // Create a string representing just the filename itself, for use in json ex
pectations. | 501 // Create a string representing just the filename itself, for use in json ex
pectations. |
421 SkString basename = SkOSPath::SkBasename(srcPath); | 502 SkString basename = SkOSPath::SkBasename(srcPath); |
| 503 // Replace '_' with '-', so that the names can fit gm_json.py's IMAGE_FILENA
ME_PATTERN |
| 504 replace_char(&basename, '_', '-'); |
| 505 // Replace '.' with '-', so the output filename can still retain the origina
l file extension, |
| 506 // but still end up with only one '.', which denotes the actual extension of
the final file. |
| 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 Loading... |
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 (compare_to_expectations_if_necessary(bitmapAndDigest.fDigest, filename,
&gDecodeFailures, |
467 &gDecodeFailures, | 554 &gMissingExpectations, &gKnownFailu
res)) { |
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); | 579 write_expectations(bitmapAndDigest, filename); |
499 | 580 |
500 if (FLAGS_testSubsetDecoding) { | 581 if (FLAGS_testSubsetDecoding) { |
501 SkDEBUGCODE(bool couldRewind =) stream.rewind(); | 582 SkDEBUGCODE(bool couldRewind =) stream.rewind(); |
502 SkASSERT(couldRewind); | 583 SkASSERT(couldRewind); |
503 int width, height; | 584 int width, height; |
504 // Build the tile index for decoding subsets. If the image is 1x1, skip
subset | 585 // Build the tile index for decoding subsets. If the image is 1x1, skip
subset |
505 // decoding since there are no smaller subsets. | 586 // decoding since there are no smaller subsets. |
506 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig
ht > 1) { | 587 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig
ht > 1) { |
507 SkASSERT(bitmap.width() == width && bitmap.height() == height); | 588 SkASSERT(bitmap.width() == width && bitmap.height() == height); |
508 // Call decodeSubset multiple times: | 589 // Call decodeSubset multiple times: |
509 SkRandom rand(0); | 590 SkRandom rand(0); |
510 for (int i = 0; i < 5; i++) { | 591 for (int i = 0; i < 5; i++) { |
511 SkBitmap bitmapFromDecodeSubset; | 592 SkBitmap bitmapFromDecodeSubset; |
512 // FIXME: Come up with a more representative set of rectangles. | 593 // FIXME: Come up with a more representative set of rectangles. |
513 SkIRect rect = generate_random_rect(&rand, width, height); | 594 SkIRect rect = generate_random_rect(&rand, width, height); |
514 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft,
rect.fTop, | 595 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft,
rect.fTop, |
515 rect.fRight, rect.fBottom); | 596 rect.fRight, rect.fBottom); |
516 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf
ig)) { | 597 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf
ig)) { |
517 SkString subsetName = SkStringPrintf("%s_%s", filename, subs
etDim.c_str()); | 598 SkString subsetName = SkStringPrintf("%s-%s", filename, subs
etDim.c_str()); |
518 skiagm::GmResultDigest subsetDigest(bitmapFromDecodeSubset); | 599 skiagm::BitmapAndDigest subsetBitmapAndDigest(bitmapFromDeco
deSubset); |
519 if (compare_to_expectations_if_necessary(subsetDigest, | 600 if (compare_to_expectations_if_necessary(subsetBitmapAndDige
st.fDigest, |
520 subsetName.c_str(), | 601 subsetName.c_str(), |
521 &gFailedSubsetDecod
es, | 602 &gFailedSubsetDecod
es, |
522 &gMissingSubsetExpe
ctations, | 603 &gMissingSubsetExpe
ctations, |
523 &gKnownSubsetFailur
es)) { | 604 &gKnownSubsetFailur
es)) { |
524 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub
set %s from %s", | 605 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub
set %s from %s", |
525 subsetDim.c_str(),
srcPath); | 606 subsetDim.c_str(),
srcPath); |
526 } else if (!FLAGS_mismatchPath.isEmpty()) { | 607 } else if (!FLAGS_mismatchPath.isEmpty()) { |
527 write_subset(FLAGS_mismatchPath[0], filename, subsetDim.
c_str(), | 608 write_subset(FLAGS_mismatchPath[0], subsetName, |
528 &bitmapFromDecodeSubset, rect, bitmap); | 609 subsetBitmapAndDigest, rect, bitmap); |
529 } | 610 } |
530 | 611 |
531 write_expectations(bitmapFromDecodeSubset, subsetName.c_str(
)); | 612 write_expectations(subsetBitmapAndDigest, subsetName.c_str()
); |
| 613 |
532 if (writePath != NULL) { | 614 if (writePath != NULL) { |
533 write_subset(writePath->c_str(), filename, subsetDim.c_s
tr(), | 615 write_subset(writePath->c_str(), subsetName, |
534 &bitmapFromDecodeSubset, rect, bitmap); | 616 subsetBitmapAndDigest, rect, bitmap); |
535 } | 617 } |
536 } else { | 618 } else { |
537 gFailedSubsetDecodes.push_back().printf("Failed to decode re
gion %s from %s", | 619 gFailedSubsetDecodes.push_back().printf("Failed to decode re
gion %s from %s", |
538 subsetDim.c_str(), s
rcPath); | 620 subsetDim.c_str(), s
rcPath); |
539 } | 621 } |
540 } | 622 } |
541 } | 623 } |
542 } | 624 } |
543 | 625 |
544 // Do not attempt to re-encode A8, since our image encoders do not support e
ncoding to A8. | 626 // Do not attempt to re-encode A8, since our image encoders do not support e
ncoding to A8. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 if (!encoder->encodeStream(&wStream, bitmap, 100)) { | 662 if (!encoder->encodeStream(&wStream, bitmap, 100)) { |
581 gEncodeFailures.push_back().printf("Failed to reencode %s to type '%
s'", srcPath, | 663 gEncodeFailures.push_back().printf("Failed to reencode %s to type '%
s'", srcPath, |
582 suffix_for_type(type)); | 664 suffix_for_type(type)); |
583 return; | 665 return; |
584 } | 666 } |
585 | 667 |
586 SkAutoTUnref<SkData> data(wStream.copyToData()); | 668 SkAutoTUnref<SkData> data(wStream.copyToData()); |
587 if (writePath != NULL && type != SkImageEncoder::kPNG_Type) { | 669 if (writePath != NULL && type != SkImageEncoder::kPNG_Type) { |
588 // Write the encoded data to a file. Do not write to PNG, which was
already written. | 670 // Write the encoded data to a file. Do not write to PNG, which was
already written. |
589 SkString outPath; | 671 SkString outPath; |
590 make_outname(&outPath, writePath->c_str(), srcPath, suffix_for_type(
type)); | 672 make_outname(&outPath, writePath->c_str(), filename, suffix_for_type
(type)); |
591 SkFILEWStream file(outPath.c_str()); | 673 SkFILEWStream file(outPath.c_str()); |
592 if(file.write(data->data(), data->size())) { | 674 if(file.write(data->data(), data->size())) { |
593 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_s
tr()); | 675 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_s
tr()); |
594 } else { | 676 } else { |
595 gEncodeFailures.push_back().printf("Failed to write %s", outPath
.c_str()); | 677 gEncodeFailures.push_back().printf("Failed to write %s", outPath
.c_str()); |
596 } | 678 } |
597 } | 679 } |
598 // Ensure that the reencoded data can still be decoded. | 680 // Ensure that the reencoded data can still be decoded. |
599 SkMemoryStream memStream(data); | 681 SkMemoryStream memStream(data); |
600 SkBitmap redecodedBitmap; | 682 SkBitmap redecodedBitmap; |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
749 print_strings("Known failures", gKnownFailures); | 831 print_strings("Known failures", gKnownFailures); |
750 | 832 |
751 return failed ? -1 : 0; | 833 return failed ? -1 : 0; |
752 } | 834 } |
753 | 835 |
754 #if !defined SK_BUILD_FOR_IOS | 836 #if !defined SK_BUILD_FOR_IOS |
755 int main(int argc, char * const argv[]) { | 837 int main(int argc, char * const argv[]) { |
756 return tool_main(argc, (char**) argv); | 838 return tool_main(argc, (char**) argv); |
757 } | 839 } |
758 #endif | 840 #endif |
OLD | NEW |