Chromium Code Reviews| 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: "name_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. |
| 198 */ | 238 */ |
| 199 static void write_expectations(const SkBitmap& bitmap, const char* filename) { | 239 static void write_expectations(const skiagm::BitmapAndDigest& bitmapAndDigest, |
| 240 const char* filename) { | |
| 241 // For both writing and reading, the key for this entry will include the nam e | |
| 242 // of the file and the pref config, matching the pattern of gm_json.py's | |
|
epoger
2013/10/21 15:13:39
Again, I pref using the whole word, to aid readabi
scroggo
2013/10/21 16:18:05
Done.
| |
| 243 // IMAGE_FILENAME_PATTERN: "name_config.png" | |
| 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 * |
| 257 * @param digest GmResultDigest, computed from the decoded bitmap, to compare t o | |
| 258 * the existing expectation. | |
| 212 * @param filename String used to find the expected value. | 259 * @param filename String used to find the expected value. |
| 213 * @param failureArray Array to add a failure message to on failure. | 260 * @param failureArray Array to add a failure message to on failure. |
| 214 * @param missingArray Array to add failure message to when missing image | 261 * @param missingArray Array to add failure message to when missing image |
| 215 * expectation. | 262 * expectation. |
| 216 * @param ignoreArray Array to add failure message to when the image does not m atch | 263 * @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. | 264 * the expectation, but this is a failure we can ignore. |
| 218 * @return bool True in any of these cases: | 265 * @return bool True in any of these cases: |
| 219 * - the bitmap matches the expectation. | 266 * - the bitmap matches the expectation. |
| 220 * False in any of these cases: | 267 * False in any of these cases: |
| 221 * - there is no expectations file. | 268 * - there is no expectations file. |
| 222 * - there is an expectations file, but no expectation for this bitmap. | 269 * - 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. | 270 * - there is an expectation for this bitmap, but it did not ma tch. |
| 224 * - expectation could not be computed from the bitmap. | 271 * - expectation could not be computed from the bitmap. |
| 225 */ | 272 */ |
| 226 static bool compare_to_expectations_if_necessary(const skiagm::GmResultDigest& d igest, | 273 static bool compare_to_expectations_if_necessary(const skiagm::GmResultDigest& d igest, |
| 227 const char* filename, | 274 const char* filename, |
| 228 SkTArray<SkString, false>* fail ureArray, | 275 SkTArray<SkString, false>* fail ureArray, |
| 229 SkTArray<SkString, false>* miss ingArray, | 276 SkTArray<SkString, false>* miss ingArray, |
| 230 SkTArray<SkString, false>* igno reArray) { | 277 SkTArray<SkString, false>* igno reArray) { |
| 278 // For both writing and reading, the key for this entry will include the nam e | |
| 279 // of the file and the pref config, matching the pattern of gm_json.py's | |
| 280 // IMAGE_FILENAME_PATTERN: "name_config.png" | |
| 281 const SkString name_config = create_json_key(filename); | |
| 282 | |
| 231 if (!digest.isValid()) { | 283 if (!digest.isValid()) { |
| 232 if (failureArray != NULL) { | 284 if (failureArray != NULL) { |
| 233 failureArray->push_back().printf("decoded %s, but could not create a GmResultDigest.", | 285 failureArray->push_back().printf("decoded %s, but could not create a GmResultDigest.", |
| 234 filename); | 286 filename); |
| 235 } | 287 } |
| 236 return false; | 288 return false; |
| 237 } | 289 } |
| 238 | 290 |
| 239 if (NULL == gJsonExpectations.get()) { | 291 if (NULL == gJsonExpectations.get()) { |
| 240 return false; | 292 return false; |
| 241 } | 293 } |
| 242 | 294 |
| 243 skiagm::Expectations jsExpectation = gJsonExpectations->get(filename); | 295 skiagm::Expectations jsExpectation = gJsonExpectations->get(name_config.c_st r()); |
| 244 if (jsExpectation.empty()) { | 296 if (jsExpectation.empty()) { |
| 245 if (missingArray != NULL) { | 297 if (missingArray != NULL) { |
| 246 missingArray->push_back().printf("decoded %s, but could not find exp ectation.", | 298 missingArray->push_back().printf("decoded %s, but could not find exp ectation.", |
| 247 filename); | 299 filename); |
| 248 } | 300 } |
| 249 return false; | 301 return false; |
| 250 } | 302 } |
| 251 | 303 |
| 252 if (jsExpectation.match(digest)) { | 304 if (jsExpectation.match(digest)) { |
| 253 return true; | 305 return true; |
| 254 } | 306 } |
| 255 | 307 |
| 256 if (jsExpectation.ignoreFailure()) { | 308 if (jsExpectation.ignoreFailure()) { |
| 257 ignoreArray->push_back().printf("%s does not match expectation, but this is known.", | 309 ignoreArray->push_back().printf("%s does not match expectation, but this is known.", |
| 258 filename); | 310 filename); |
| 259 } else if (failureArray != NULL) { | 311 } else if (failureArray != NULL) { |
| 260 failureArray->push_back().printf("decoded %s, but the result does not ma tch " | 312 failureArray->push_back().printf("decoded %s, but the result does not ma tch " |
| 261 "expectations.", | 313 "expectations.", |
| 262 filename); | 314 filename); |
| 263 } | 315 } |
| 264 return false; | 316 return false; |
| 265 } | 317 } |
| 266 | 318 |
| 267 /** | 319 /** |
| 268 * Helper function to write a bitmap subset to a file. Only called if subsets w ere created | 320 * 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 | 321 * and a writePath was provided. Behaves differently depending on |
| 270 * that directory. Also creates a subdirectory called 'extracted' and writes a bitmap created | 322 * FLAGS_writeChecksumBasedFilenames. If true: |
| 271 * using extractSubset to a PNG in that directory. Both files will represent th e same | 323 * 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. | 324 * write_bitmap. |
| 325 * If false: | |
| 326 * Creates a subdirectory called 'subsets' and writes a PNG to that directo ry. Also | |
| 327 * creates a subdirectory called 'extracted' and writes a bitmap created us ing | |
| 328 * extractSubset to a PNG in that directory. Both files will represent the same | |
| 329 * subrectangle and have the same name for convenient comparison. In this c ase, the | |
| 330 * digest is ignored. | |
| 331 * | |
| 273 * @param writePath Parent directory to hold the folders for the PNG files to w rite. Must | 332 * @param writePath Parent directory to hold the folders for the PNG files to w rite. Must |
| 274 * not be NULL. | 333 * not be NULL. |
| 275 * @param filename Basename of the original file. Used to name the new files. M ust not be | 334 * @param subsetName Basename of the original file, with the dimensions of the subset tacked |
| 276 * NULL. | 335 * on. Used to name the new file/folder. |
| 277 * @param subsetDim String representing the dimensions of the subset. Used to n ame the new | 336 * @param bitmapAndDigestFromDecodeSubset SkBitmap (with digest) created by |
| 278 * files. Must not be NULL. | 337 * 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 | 338 * @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 | 339 * extractSubset on originalBitmap to create a bitmap with the same dimensi ons/pixels as |
| 283 * bitmapFromDecodeSubset (assuming decodeSubset worked properly). | 340 * bitmapFromDecodeSubset (assuming decodeSubset worked properly). |
| 284 * @param originalBitmap SkBitmap decoded from the same stream as bitmapFromDec odeSubset, | 341 * @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 | 342 * using SkImageDecoder::decode to get the entire image. Used to create a P NG file for |
| 286 * comparison to the PNG created by bitmapFromDecodeSubset. | 343 * 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 | 344 * @return bool Whether the function succeeded at drawing the decoded subset an d the extracted |
| 288 * subset to files. | 345 * subset to files. |
| 289 */ | 346 */ |
| 290 static bool write_subset(const char* writePath, const char* filename, const char * subsetDim, | 347 static bool write_subset(const char* writePath, const SkString& subsetName, |
| 291 SkBitmap* bitmapFromDecodeSubset, SkIRect rect, | 348 const skiagm::BitmapAndDigest bitmapAndDigestFromDecod eSubset, |
| 292 const SkBitmap& originalBitmap) { | 349 SkIRect rect, const SkBitmap& originalBitmap) { |
| 293 // All parameters must be valid. | 350 // All parameters must be valid. |
| 294 SkASSERT(writePath != NULL); | 351 SkASSERT(writePath != NULL); |
| 295 SkASSERT(filename != NULL); | |
| 296 SkASSERT(subsetDim != NULL); | |
| 297 SkASSERT(bitmapFromDecodeSubset != NULL); | |
| 298 | 352 |
| 299 // Create a subdirectory to hold the results of decodeSubset. | 353 SkString subsetPath; |
| 300 SkString dir = SkOSPath::SkPathJoin(writePath, "subsets"); | 354 if (FLAGS_writeChecksumBasedFilenames) { |
| 301 if (!sk_mkdir(dir.c_str())) { | 355 subsetPath.set(writePath); |
| 302 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s , but failed to " | 356 } else { |
| 303 "create a directory to write to. ", subsetDim, | 357 // Create a subdirectory to hold the results of decodeSubset. |
| 304 filename); | 358 subsetPath = SkOSPath::SkPathJoin(writePath, "subsets"); |
| 305 return false; | 359 if (!sk_mkdir(subsetPath.c_str())) { |
| 360 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but " | |
| 361 "failed to create a director y to write to.", | |
| 362 subsetName.c_str()); | |
| 363 return false; | |
| 364 } | |
| 306 } | 365 } |
| 366 SkAssertResult(write_bitmap(subsetPath.c_str(), subsetName.c_str(), | |
| 367 bitmapAndDigestFromDecodeSubset)); | |
| 368 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", subsetName.c_str() ); | |
| 307 | 369 |
| 308 // Write the subset to a file whose name includes the dimensions. | 370 if (!FLAGS_writeChecksumBasedFilenames) { |
| 309 SkString suffix = SkStringPrintf("_%s.png", subsetDim); | 371 // FIXME: The goal of extracting the subset is for visual comparison/usi ng skdiff/skpdiff. |
| 310 SkString outPath; | 372 // Currently disabling for writeChecksumBasedFilenames since it will be trickier to |
| 311 make_outname(&outPath, dir.c_str(), filename, suffix.c_str()); | 373 // determine which files to compare. |
| 312 SkAssertResult(write_bitmap(outPath.c_str(), *bitmapFromDecodeSubset)); | |
| 313 gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", outPath.c_str()); | |
| 314 | 374 |
| 315 // Also use extractSubset from the original for visual comparison. | 375 // Also use extractSubset from the original for visual comparison. |
| 316 // Write the result to a file in a separate subdirectory. | 376 // Write the result to a file in a separate subdirectory. |
| 317 SkBitmap extractedSubset; | 377 SkBitmap extractedSubset; |
| 318 if (!originalBitmap.extractSubset(&extractedSubset, rect)) { | 378 if (!originalBitmap.extractSubset(&extractedSubset, rect)) { |
| 319 gFailedSubsetDecodes.push_back().printf("Successfully decoded %s from %s , but failed to " | 379 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but failed " |
| 320 "extract a similar subset for co mparison.", | 380 "to extract a similar subset for comparison.", |
| 321 subsetDim, filename); | 381 subsetName.c_str()); |
| 322 return false; | 382 return false; |
| 383 } | |
| 384 | |
| 385 SkString dirExtracted = SkOSPath::SkPathJoin(writePath, "extracted"); | |
| 386 if (!sk_mkdir(dirExtracted.c_str())) { | |
| 387 gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but failed " | |
| 388 "to create a directory for e xtractSubset " | |
| 389 "comparison.", | |
| 390 subsetName.c_str()); | |
| 391 return false; | |
| 392 } | |
| 393 | |
| 394 skiagm::BitmapAndDigest bitmapAndDigestFromExtractSubset(extractedSubset ); | |
| 395 SkAssertResult(write_bitmap(dirExtracted.c_str(), subsetName.c_str(), | |
| 396 bitmapAndDigestFromExtractSubset)); | |
| 323 } | 397 } |
| 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; | 398 return true; |
| 336 } | 399 } |
| 337 | 400 |
| 338 // FIXME: This test could be run on windows/mac once we remove their dependence on | 401 // 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 | 402 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570 |
| 340 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) | 403 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
| 341 | 404 |
| 342 /** | 405 /** |
| 343 * Dummy class for testing to ensure that a stream without a length decodes the same | 406 * Dummy class for testing to ensure that a stream without a length decodes the same |
| 344 * as a stream with a length. | 407 * as a stream with a length. |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 390 "a digest\n", srcPath); | 453 "a digest\n", srcPath); |
| 391 return; | 454 return; |
| 392 } | 455 } |
| 393 if (!lengthLessDigest.equals(digest)) { | 456 if (!lengthLessDigest.equals(digest)) { |
| 394 gDecodeFailures.push_back().appendf("Without using getLength, %s did not match digest " | 457 gDecodeFailures.push_back().appendf("Without using getLength, %s did not match digest " |
| 395 "that uses getLength\n", srcPath); | 458 "that uses getLength\n", srcPath); |
| 396 } | 459 } |
| 397 } | 460 } |
| 398 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) | 461 #endif // defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
| 399 | 462 |
| 463 /** | |
| 464 * Replace all instances of oldChar with newChar in str. | |
| 465 * TODO: Add this function to SkString and write tests for it. | |
| 466 */ | |
| 467 static void replace_char(SkString* str, const char oldChar, const char newChar) { | |
| 468 if (NULL == str) { | |
| 469 return; | |
| 470 } | |
| 471 for (size_t i = 0; i < str->size(); ++i) { | |
| 472 if (oldChar == str->operator[](i)) { | |
| 473 str->operator[](i) = newChar; | |
| 474 } | |
| 475 } | |
| 476 } | |
| 477 | |
| 400 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { | 478 static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { |
| 401 SkBitmap bitmap; | 479 SkBitmap bitmap; |
| 402 SkFILEStream stream(srcPath); | 480 SkFILEStream stream(srcPath); |
| 403 if (!stream.isValid()) { | 481 if (!stream.isValid()) { |
| 404 gInvalidStreams.push_back().set(srcPath); | 482 gInvalidStreams.push_back().set(srcPath); |
| 405 return; | 483 return; |
| 406 } | 484 } |
| 407 | 485 |
| 408 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); | 486 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); |
| 409 if (NULL == codec) { | 487 if (NULL == codec) { |
| 410 gMissingCodecs.push_back().set(srcPath); | 488 gMissingCodecs.push_back().set(srcPath); |
| 411 return; | 489 return; |
| 412 } | 490 } |
| 413 | 491 |
| 414 SkAutoTDelete<SkImageDecoder> ad(codec); | 492 SkAutoTDelete<SkImageDecoder> ad(codec); |
| 415 | 493 |
| 416 codec->setSkipWritingZeroes(FLAGS_skip); | 494 codec->setSkipWritingZeroes(FLAGS_skip); |
| 417 codec->setSampleSize(FLAGS_sampleSize); | 495 codec->setSampleSize(FLAGS_sampleSize); |
| 418 stream.rewind(); | 496 stream.rewind(); |
| 419 | 497 |
| 420 // Create a string representing just the filename itself, for use in json ex pectations. | 498 // Create a string representing just the filename itself, for use in json ex pectations. |
| 421 SkString basename = SkOSPath::SkBasename(srcPath); | 499 SkString basename = SkOSPath::SkBasename(srcPath); |
| 500 // Replace '_' with '-', so that the names can fit gm_json.py's IMAGE_FILENA ME_PATTERN | |
| 501 replace_char(&basename, '_', '-'); | |
| 502 // Replace '.' with '-', so the output filename can still retain the origina l file extension, | |
| 503 // but still end up with only one '.', which denotes the actual extension of the final file. | |
| 504 replace_char(&basename, '.', '-'); | |
| 422 const char* filename = basename.c_str(); | 505 const char* filename = basename.c_str(); |
| 423 | 506 |
| 424 if (!codec->decode(&stream, &bitmap, gPrefConfig, | 507 if (!codec->decode(&stream, &bitmap, gPrefConfig, |
| 425 SkImageDecoder::kDecodePixels_Mode)) { | 508 SkImageDecoder::kDecodePixels_Mode)) { |
| 426 if (NULL != gJsonExpectations.get()) { | 509 if (NULL != gJsonExpectations.get()) { |
| 427 skiagm::Expectations jsExpectations = gJsonExpectations->get(filenam e); | 510 const SkString name_config = create_json_key(filename); |
| 511 skiagm::Expectations jsExpectations = gJsonExpectations->get(name_co nfig.c_str()); | |
| 428 if (jsExpectations.ignoreFailure()) { | 512 if (jsExpectations.ignoreFailure()) { |
| 429 // This is a known failure. | 513 // This is a known failure. |
| 430 gKnownFailures.push_back().appendf( | 514 gKnownFailures.push_back().appendf( |
| 431 "failed to decode %s, which is a known failure.", srcPath); | 515 "failed to decode %s, which is a known failure.", srcPath); |
| 432 return; | 516 return; |
| 433 } | 517 } |
| 434 if (jsExpectations.empty()) { | 518 if (jsExpectations.empty()) { |
| 435 // This is a failure, but it is a new file. Mark it as missing, with | 519 // This is a failure, but it is a new file. Mark it as missing, with |
| 436 // a note that it should be marked failing. | 520 // a note that it should be marked failing. |
| 437 gMissingExpectations.push_back().appendf( | 521 gMissingExpectations.push_back().appendf( |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 455 gDecodeFailures.push_back() = failure; | 539 gDecodeFailures.push_back() = failure; |
| 456 } else { | 540 } else { |
| 457 // Now check that the bounds match: | 541 // Now check that the bounds match: |
| 458 if (dim.width() != bitmap.width() || dim.height() != bitmap.height() ) { | 542 if (dim.width() != bitmap.width() || dim.height() != bitmap.height() ) { |
| 459 SkString failure = SkStringPrintf("bounds do not match for %s", srcPath); | 543 SkString failure = SkStringPrintf("bounds do not match for %s", srcPath); |
| 460 gDecodeFailures.push_back() = failure; | 544 gDecodeFailures.push_back() = failure; |
| 461 } | 545 } |
| 462 } | 546 } |
| 463 } | 547 } |
| 464 | 548 |
| 465 skiagm::GmResultDigest digest(bitmap); | 549 skiagm::BitmapAndDigest bitmapAndDigest(bitmap); |
| 466 if (compare_to_expectations_if_necessary(digest, filename, | 550 if (compare_to_expectations_if_necessary(bitmapAndDigest.fDigest, filename, &gDecodeFailures, |
| 467 &gDecodeFailures, | 551 &gMissingExpectations, &gKnownFailu res)) { |
| 468 &gMissingExpectations, | |
| 469 &gKnownFailures)) { | |
| 470 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(), | 552 gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.widt h(), |
| 471 bitmap.height()); | 553 bitmap.height()); |
| 472 } else if (!FLAGS_mismatchPath.isEmpty()) { | 554 } else if (!FLAGS_mismatchPath.isEmpty()) { |
| 473 SkString outPath; | 555 if (write_bitmap(FLAGS_mismatchPath[0], filename, bitmapAndDigest)) { |
| 474 make_outname(&outPath, FLAGS_mismatchPath[0], srcPath, ".png"); | 556 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 { | 557 } else { |
| 478 gEncodeFailures.push_back().set(outPath); | 558 gEncodeFailures.push_back().set(filename); |
| 479 } | 559 } |
| 480 } | 560 } |
| 481 | 561 |
| 482 // FIXME: This test could be run on windows/mac once we remove their dependence on | 562 // 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 | 563 // getLength. See https://code.google.com/p/skia/issues/detail?id=1570 |
| 484 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) | 564 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) |
| 485 test_stream_without_length(srcPath, codec, digest); | 565 test_stream_without_length(srcPath, codec, bitmapAndDigest.fDigest); |
| 486 #endif | 566 #endif |
| 487 | 567 |
| 488 if (writePath != NULL) { | 568 if (writePath != NULL) { |
| 489 SkString outPath; | 569 if (write_bitmap(writePath->c_str(), filename, bitmapAndDigest)) { |
| 490 make_outname(&outPath, writePath->c_str(), srcPath, ".png"); | 570 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 { | 571 } else { |
| 494 gEncodeFailures.push_back().set(outPath); | 572 gEncodeFailures.push_back().set(filename); |
| 495 } | 573 } |
| 496 } | 574 } |
| 497 | 575 |
| 498 write_expectations(bitmap, filename); | 576 write_expectations(bitmapAndDigest, filename); |
| 499 | 577 |
| 500 if (FLAGS_testSubsetDecoding) { | 578 if (FLAGS_testSubsetDecoding) { |
| 501 SkDEBUGCODE(bool couldRewind =) stream.rewind(); | 579 SkDEBUGCODE(bool couldRewind =) stream.rewind(); |
| 502 SkASSERT(couldRewind); | 580 SkASSERT(couldRewind); |
| 503 int width, height; | 581 int width, height; |
| 504 // Build the tile index for decoding subsets. If the image is 1x1, skip subset | 582 // Build the tile index for decoding subsets. If the image is 1x1, skip subset |
| 505 // decoding since there are no smaller subsets. | 583 // decoding since there are no smaller subsets. |
| 506 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { | 584 if (codec->buildTileIndex(&stream, &width, &height) && width > 1 && heig ht > 1) { |
| 507 SkASSERT(bitmap.width() == width && bitmap.height() == height); | 585 SkASSERT(bitmap.width() == width && bitmap.height() == height); |
| 508 // Call decodeSubset multiple times: | 586 // Call decodeSubset multiple times: |
| 509 SkRandom rand(0); | 587 SkRandom rand(0); |
| 510 for (int i = 0; i < 5; i++) { | 588 for (int i = 0; i < 5; i++) { |
| 511 SkBitmap bitmapFromDecodeSubset; | 589 SkBitmap bitmapFromDecodeSubset; |
| 512 // FIXME: Come up with a more representative set of rectangles. | 590 // FIXME: Come up with a more representative set of rectangles. |
| 513 SkIRect rect = generate_random_rect(&rand, width, height); | 591 SkIRect rect = generate_random_rect(&rand, width, height); |
| 514 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, | 592 SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, |
| 515 rect.fRight, rect.fBottom); | 593 rect.fRight, rect.fBottom); |
| 516 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf ig)) { | 594 if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, gPrefConf ig)) { |
| 517 SkString subsetName = SkStringPrintf("%s_%s", filename, subs etDim.c_str()); | 595 SkString subsetName = SkStringPrintf("%s-%s", filename, subs etDim.c_str()); |
| 518 skiagm::GmResultDigest subsetDigest(bitmapFromDecodeSubset); | 596 skiagm::BitmapAndDigest subsetBitmapAndDigest(bitmapFromDeco deSubset); |
| 519 if (compare_to_expectations_if_necessary(subsetDigest, | 597 if (compare_to_expectations_if_necessary(subsetBitmapAndDige st.fDigest, |
| 520 subsetName.c_str(), | 598 subsetName.c_str(), |
| 521 &gFailedSubsetDecod es, | 599 &gFailedSubsetDecod es, |
| 522 &gMissingSubsetExpe ctations, | 600 &gMissingSubsetExpe ctations, |
| 523 &gKnownSubsetFailur es)) { | 601 &gKnownSubsetFailur es)) { |
| 524 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s", | 602 gSuccessfulSubsetDecodes.push_back().printf("Decoded sub set %s from %s", |
| 525 subsetDim.c_str(), srcPath); | 603 subsetDim.c_str(), srcPath); |
| 526 } else if (!FLAGS_mismatchPath.isEmpty()) { | 604 } else if (!FLAGS_mismatchPath.isEmpty()) { |
| 527 write_subset(FLAGS_mismatchPath[0], filename, subsetDim. c_str(), | 605 write_subset(FLAGS_mismatchPath[0], subsetName, |
| 528 &bitmapFromDecodeSubset, rect, bitmap); | 606 subsetBitmapAndDigest, rect, bitmap); |
| 529 } | 607 } |
| 530 | 608 |
| 531 write_expectations(bitmapFromDecodeSubset, subsetName.c_str( )); | 609 write_expectations(subsetBitmapAndDigest, subsetName.c_str() ); |
| 610 | |
| 532 if (writePath != NULL) { | 611 if (writePath != NULL) { |
| 533 write_subset(writePath->c_str(), filename, subsetDim.c_s tr(), | 612 write_subset(writePath->c_str(), subsetName, |
| 534 &bitmapFromDecodeSubset, rect, bitmap); | 613 subsetBitmapAndDigest, rect, bitmap); |
| 535 } | 614 } |
| 536 } else { | 615 } else { |
| 537 gFailedSubsetDecodes.push_back().printf("Failed to decode re gion %s from %s", | 616 gFailedSubsetDecodes.push_back().printf("Failed to decode re gion %s from %s", |
| 538 subsetDim.c_str(), s rcPath); | 617 subsetDim.c_str(), s rcPath); |
| 539 } | 618 } |
| 540 } | 619 } |
| 541 } | 620 } |
| 542 } | 621 } |
| 543 | 622 |
| 544 // Do not attempt to re-encode A8, since our image encoders do not support e ncoding to A8. | 623 // 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)) { | 659 if (!encoder->encodeStream(&wStream, bitmap, 100)) { |
| 581 gEncodeFailures.push_back().printf("Failed to reencode %s to type '% s'", srcPath, | 660 gEncodeFailures.push_back().printf("Failed to reencode %s to type '% s'", srcPath, |
| 582 suffix_for_type(type)); | 661 suffix_for_type(type)); |
| 583 return; | 662 return; |
| 584 } | 663 } |
| 585 | 664 |
| 586 SkAutoTUnref<SkData> data(wStream.copyToData()); | 665 SkAutoTUnref<SkData> data(wStream.copyToData()); |
| 587 if (writePath != NULL && type != SkImageEncoder::kPNG_Type) { | 666 if (writePath != NULL && type != SkImageEncoder::kPNG_Type) { |
| 588 // Write the encoded data to a file. Do not write to PNG, which was already written. | 667 // Write the encoded data to a file. Do not write to PNG, which was already written. |
| 589 SkString outPath; | 668 SkString outPath; |
| 590 make_outname(&outPath, writePath->c_str(), srcPath, suffix_for_type( type)); | 669 make_outname(&outPath, writePath->c_str(), filename, suffix_for_type (type)); |
| 591 SkFILEWStream file(outPath.c_str()); | 670 SkFILEWStream file(outPath.c_str()); |
| 592 if(file.write(data->data(), data->size())) { | 671 if(file.write(data->data(), data->size())) { |
| 593 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_s tr()); | 672 gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_s tr()); |
| 594 } else { | 673 } else { |
| 595 gEncodeFailures.push_back().printf("Failed to write %s", outPath .c_str()); | 674 gEncodeFailures.push_back().printf("Failed to write %s", outPath .c_str()); |
| 596 } | 675 } |
| 597 } | 676 } |
| 598 // Ensure that the reencoded data can still be decoded. | 677 // Ensure that the reencoded data can still be decoded. |
| 599 SkMemoryStream memStream(data); | 678 SkMemoryStream memStream(data); |
| 600 SkBitmap redecodedBitmap; | 679 SkBitmap redecodedBitmap; |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 749 print_strings("Known failures", gKnownFailures); | 828 print_strings("Known failures", gKnownFailures); |
| 750 | 829 |
| 751 return failed ? -1 : 0; | 830 return failed ? -1 : 0; |
| 752 } | 831 } |
| 753 | 832 |
| 754 #if !defined SK_BUILD_FOR_IOS | 833 #if !defined SK_BUILD_FOR_IOS |
| 755 int main(int argc, char * const argv[]) { | 834 int main(int argc, char * const argv[]) { |
| 756 return tool_main(argc, (char**) argv); | 835 return tool_main(argc, (char**) argv); |
| 757 } | 836 } |
| 758 #endif | 837 #endif |
| OLD | NEW |