| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 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 "PictureRenderer.h" | 8 #include "PictureRenderer.h" |
| 9 #include "picture_utils.h" | 9 #include "picture_utils.h" |
| 10 #include "SamplePipeControllers.h" | 10 #include "SamplePipeControllers.h" |
| 11 #include "SkBitmapHasher.h" | 11 #include "SkBitmapHasher.h" |
| 12 #include "SkCanvas.h" | 12 #include "SkCanvas.h" |
| 13 #include "SkData.h" | 13 #include "SkData.h" |
| 14 #include "SkDevice.h" | 14 #include "SkDevice.h" |
| 15 #include "SkDiscardableMemoryPool.h" | 15 #include "SkDiscardableMemoryPool.h" |
| 16 #include "SkGPipe.h" | 16 #include "SkGPipe.h" |
| 17 #if SK_SUPPORT_GPU | 17 #if SK_SUPPORT_GPU |
| 18 #include "gl/GrGLDefines.h" | 18 #include "gl/GrGLDefines.h" |
| 19 #include "SkGpuDevice.h" | 19 #include "SkGpuDevice.h" |
| 20 #endif | 20 #endif |
| 21 #include "SkGraphics.h" | 21 #include "SkGraphics.h" |
| 22 #include "SkImageEncoder.h" | 22 #include "SkImageEncoder.h" |
| 23 #include "SkMaskFilter.h" | 23 #include "SkMaskFilter.h" |
| 24 #include "SkMatrix.h" | 24 #include "SkMatrix.h" |
| 25 #include "SkOSFile.h" |
| 25 #include "SkPicture.h" | 26 #include "SkPicture.h" |
| 26 #include "SkPictureUtils.h" | 27 #include "SkPictureUtils.h" |
| 27 #include "SkPixelRef.h" | 28 #include "SkPixelRef.h" |
| 28 #include "SkQuadTree.h" | 29 #include "SkQuadTree.h" |
| 29 #include "SkQuadTreePicture.h" | 30 #include "SkQuadTreePicture.h" |
| 30 #include "SkRTree.h" | 31 #include "SkRTree.h" |
| 31 #include "SkScalar.h" | 32 #include "SkScalar.h" |
| 32 #include "SkStream.h" | 33 #include "SkStream.h" |
| 33 #include "SkString.h" | 34 #include "SkString.h" |
| 34 #include "SkTemplates.h" | 35 #include "SkTemplates.h" |
| 35 #include "SkTileGridPicture.h" | 36 #include "SkTileGridPicture.h" |
| 36 #include "SkTDArray.h" | 37 #include "SkTDArray.h" |
| 37 #include "SkThreadUtils.h" | 38 #include "SkThreadUtils.h" |
| 38 #include "SkTypes.h" | 39 #include "SkTypes.h" |
| 39 | 40 |
| 40 static inline SkScalar scalar_log2(SkScalar x) { | 41 static inline SkScalar scalar_log2(SkScalar x) { |
| 41 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2)
); | 42 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2)
); |
| 42 | 43 |
| 43 return SkScalarLog(x) * log2_conversion_factor; | 44 return SkScalarLog(x) * log2_conversion_factor; |
| 44 } | 45 } |
| 45 | 46 |
| 46 namespace sk_tools { | 47 namespace sk_tools { |
| 47 | 48 |
| 48 enum { | 49 enum { |
| 49 kDefaultTileWidth = 256, | 50 kDefaultTileWidth = 256, |
| 50 kDefaultTileHeight = 256 | 51 kDefaultTileHeight = 256 |
| 51 }; | 52 }; |
| 52 | 53 |
| 53 /* TODO(epoger): These constants are already maintained in 2 other places: | 54 /* |
| 54 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place
. | 55 * TODO(epoger): Make constant strings consistent instead of mixing hypenated an
d camel-caps. |
| 56 * |
| 57 * TODO(epoger): Similar constants are already maintained in 2 other places: |
| 58 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place. |
| 55 * Figure out a way to share the definitions instead. | 59 * Figure out a way to share the definitions instead. |
| 60 * |
| 61 * Note that, as of https://codereview.chromium.org/226293002 , the JSON |
| 62 * schema used here has started to differ from the one in gm_expectations.cpp . |
| 63 * TODO(epoger): Consider getting GM and render_pictures to use the same JSON |
| 64 * output module. |
| 56 */ | 65 */ |
| 57 const static char kJsonKey_ActualResults[] = "actual-results"; | 66 const static char kJsonKey_ActualResults[] = "actual-results"; |
| 58 const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison"; | 67 const static char kJsonKey_Header[] = "header"; |
| 59 const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5"; | 68 const static char kJsonKey_Header_Type[] = "type"; |
| 69 const static char kJsonKey_Header_Revision[] = "revision"; // unique within Typ
e |
| 70 const static char kJsonKey_Image_ChecksumAlgorithm[] = "checksumAlgorithm"; |
| 71 const static char kJsonKey_Image_ChecksumValue[] = "checksumValue"; |
| 72 const static char kJsonKey_Image_ComparisonResult[] = "comparisonResult"; |
| 73 const static char kJsonKey_Image_Filepath[] = "filepath"; |
| 74 const static char kJsonKey_Source_TiledImages[] = "tiled-images"; |
| 75 const static char kJsonKey_Source_WholeImage[] = "whole-image"; |
| 76 // Values (not keys) that are written out by this JSON generator |
| 77 const static char kJsonValue_Header_Type[] = "ChecksummedImages"; |
| 78 const static int kJsonValue_Header_Revision = 1; |
| 79 const static char kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5[] = "bitmap-
64bitMD5"; |
| 80 const static char kJsonValue_Image_ComparisonResult_NoComparison[] = "no-compari
son"; |
| 60 | 81 |
| 61 void ImageResultsSummary::add(const char *testName, uint64_t hash) { | 82 void ImageResultsSummary::add(const char *sourceName, const char *fileName, uint
64_t hash, |
| 62 Json::Value jsonTypeValuePair; | 83 const int *tileNumber) { |
| 63 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5)); | 84 Json::Value image; |
| 64 jsonTypeValuePair.append(Json::UInt64(hash)); | 85 image[kJsonKey_Image_ChecksumAlgorithm] = kJsonValue_Image_ChecksumAlgorithm
_Bitmap64bitMD5; |
| 65 fActualResultsNoComparison[testName] = jsonTypeValuePair; | 86 image[kJsonKey_Image_ChecksumValue] = Json::UInt64(hash); |
| 87 image[kJsonKey_Image_ComparisonResult] = kJsonValue_Image_ComparisonResult_N
oComparison; |
| 88 image[kJsonKey_Image_Filepath] = fileName; |
| 89 if (NULL == tileNumber) { |
| 90 fActualResults[sourceName][kJsonKey_Source_WholeImage] = image; |
| 91 } else { |
| 92 fActualResults[sourceName][kJsonKey_Source_TiledImages][*tileNumber] = i
mage; |
| 93 } |
| 66 } | 94 } |
| 67 | 95 |
| 68 void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) { | 96 void ImageResultsSummary::add(const char *sourceName, const char *fileName, cons
t SkBitmap& bitmap, |
| 97 const int *tileNumber) { |
| 69 uint64_t hash; | 98 uint64_t hash; |
| 70 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); | 99 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); |
| 71 this->add(testName, hash); | 100 this->add(sourceName, fileName, hash, tileNumber); |
| 72 } | 101 } |
| 73 | 102 |
| 74 void ImageResultsSummary::writeToFile(const char *filename) { | 103 void ImageResultsSummary::writeToFile(const char *filename) { |
| 75 Json::Value actualResults; | 104 Json::Value header; |
| 76 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoCompari
son; | 105 header[kJsonKey_Header_Type] = kJsonValue_Header_Type; |
| 106 header[kJsonKey_Header_Revision] = kJsonValue_Header_Revision; |
| 77 Json::Value root; | 107 Json::Value root; |
| 78 root[kJsonKey_ActualResults] = actualResults; | 108 root[kJsonKey_Header] = header; |
| 109 root[kJsonKey_ActualResults] = fActualResults; |
| 79 std::string jsonStdString = root.toStyledString(); | 110 std::string jsonStdString = root.toStyledString(); |
| 80 SkFILEWStream stream(filename); | 111 SkFILEWStream stream(filename); |
| 81 stream.write(jsonStdString.c_str(), jsonStdString.length()); | 112 stream.write(jsonStdString.c_str(), jsonStdString.length()); |
| 82 } | 113 } |
| 83 | 114 |
| 84 void PictureRenderer::init(SkPicture* pict, const SkString* outputDir, | 115 void PictureRenderer::init(SkPicture* pict, const SkString* outputDir, |
| 85 const SkString* inputFilename, bool useChecksumBasedF
ilenames) { | 116 const SkString* inputFilename, bool useChecksumBasedF
ilenames) { |
| 86 this->CopyString(&fOutputDir, outputDir); | 117 this->CopyString(&fOutputDir, outputDir); |
| 87 this->CopyString(&fInputFilename, inputFilename); | 118 this->CopyString(&fInputFilename, inputFilename); |
| 88 fUseChecksumBasedFilenames = useChecksumBasedFilenames; | 119 fUseChecksumBasedFilenames = useChecksumBasedFilenames; |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 } | 328 } |
| 298 | 329 |
| 299 /** | 330 /** |
| 300 * Write the canvas to the specified path. | 331 * Write the canvas to the specified path. |
| 301 * | 332 * |
| 302 * @param canvas Must be non-null. Canvas to be written to a file. | 333 * @param canvas Must be non-null. Canvas to be written to a file. |
| 303 * @param outputDir If nonempty, write the binary image to a file within this di
rectory. | 334 * @param outputDir If nonempty, write the binary image to a file within this di
rectory. |
| 304 * @param inputFilename If we are writing out a binary image, use this to build
its filename. | 335 * @param inputFilename If we are writing out a binary image, use this to build
its filename. |
| 305 * @param jsonSummaryPtr If not null, add image results to this summary. | 336 * @param jsonSummaryPtr If not null, add image results to this summary. |
| 306 * @param useChecksumBasedFilenames If true, use checksum-based filenames when w
riting to disk. | 337 * @param useChecksumBasedFilenames If true, use checksum-based filenames when w
riting to disk. |
| 307 * @param numberToAppend If not null, append this number to the filename. | 338 * @param tileNumberPtr If not null, which tile number this image contains. |
| 308 * @return bool True if the Canvas is written to a file. | 339 * @return bool True if the Canvas is written to a file. |
| 309 * | 340 * |
| 310 * TODO(epoger): Right now, all canvases must pass through this function in orde
r to be appended | 341 * TODO(epoger): Right now, all canvases must pass through this function in orde
r to be appended |
| 311 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResu
ltsSummary | 342 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResu
ltsSummary |
| 312 * even if --writePath has not been specified (and thus this function is not cal
led). | 343 * even if --writePath has not been specified (and thus this function is not cal
led). |
| 313 * | 344 * |
| 314 * One fix would be to pass in these path elements separately, and allow this fu
nction to be | 345 * One fix would be to pass in these path elements separately, and allow this fu
nction to be |
| 315 * called even if --writePath was not specified... | 346 * called even if --writePath was not specified... |
| 316 * const char *outputDir // NULL if we don't want to write image files to dis
k | 347 * const char *outputDir // NULL if we don't want to write image files to dis
k |
| 317 * const char *filename // name we use within JSON summary, and as the filen
ame within outputDir | 348 * const char *filename // name we use within JSON summary, and as the filen
ame within outputDir |
| 318 * | 349 * |
| 319 * UPDATE: Now that outputDir and inputFilename are passed separately, we should
be able to do that. | 350 * UPDATE: Now that outputDir and inputFilename are passed separately, we should
be able to do that. |
| 320 */ | 351 */ |
| 321 static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& i
nputFilename, | 352 static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& i
nputFilename, |
| 322 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFile
names, | 353 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFile
names, |
| 323 const int* numberToAppend=NULL) { | 354 const int* tileNumberPtr=NULL) { |
| 324 SkASSERT(canvas != NULL); | 355 SkASSERT(canvas != NULL); |
| 325 if (NULL == canvas) { | 356 if (NULL == canvas) { |
| 326 return false; | 357 return false; |
| 327 } | 358 } |
| 328 | 359 |
| 329 SkBitmap bitmap; | 360 SkBitmap bitmap; |
| 330 SkISize size = canvas->getDeviceSize(); | 361 SkISize size = canvas->getDeviceSize(); |
| 331 sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); | 362 sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); |
| 332 | 363 |
| 333 // Make sure we only compute the bitmap hash once (at most). | 364 // Make sure we only compute the bitmap hash once (at most). |
| 334 uint64_t hash; | 365 uint64_t hash; |
| 335 bool generatedHash = false; | 366 bool generatedHash = false; |
| 336 | 367 |
| 337 canvas->readPixels(&bitmap, 0, 0); | 368 canvas->readPixels(&bitmap, 0, 0); |
| 338 sk_tools::force_all_opaque(bitmap); | 369 sk_tools::force_all_opaque(bitmap); |
| 339 | 370 |
| 340 SkString outputFilename(inputFilename); | 371 SkString escapedInputFilename(inputFilename); |
| 341 outputFilename.remove(outputFilename.size() - 4, 4); | 372 replace_char(&escapedInputFilename, '.', '_'); |
| 342 if (NULL != numberToAppend) { | 373 |
| 343 outputFilename.appendf("%i", *numberToAppend); | |
| 344 } | |
| 345 outputFilename.append(".png"); | |
| 346 // TODO(epoger): what about including the config type within outputFilename?
That way, | 374 // TODO(epoger): what about including the config type within outputFilename?
That way, |
| 347 // we could combine results of different config types without conflicting fi
lenames. | 375 // we could combine results of different config types without conflicting fi
lenames. |
| 348 | 376 SkString outputFilename; |
| 349 if (NULL != jsonSummaryPtr) { | 377 const char *outputSubdirPtr = NULL; |
| 378 if (useChecksumBasedFilenames) { |
| 350 SkASSERT(!generatedHash); | 379 SkASSERT(!generatedHash); |
| 351 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); | 380 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); |
| 352 generatedHash = true; | 381 generatedHash = true; |
| 353 | 382 |
| 354 jsonSummaryPtr->add(outputFilename.c_str(), hash); | 383 outputSubdirPtr = escapedInputFilename.c_str(); |
| 384 outputFilename.set(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5); |
| 385 outputFilename.append("_"); |
| 386 outputFilename.appendU64(hash); |
| 387 } else { |
| 388 outputFilename.set(escapedInputFilename); |
| 389 if (NULL != tileNumberPtr) { |
| 390 outputFilename.append("-tile"); |
| 391 outputFilename.appendS32(*tileNumberPtr); |
| 392 } |
| 355 } | 393 } |
| 394 outputFilename.append(".png"); |
| 356 | 395 |
| 357 // Update outputFilename AFTER adding to JSON summary, but BEFORE writing ou
t the image file. | 396 if (NULL != jsonSummaryPtr) { |
| 358 if (useChecksumBasedFilenames) { | |
| 359 if (!generatedHash) { | 397 if (!generatedHash) { |
| 360 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); | 398 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash)); |
| 361 generatedHash = true; | 399 generatedHash = true; |
| 362 } | 400 } |
| 363 outputFilename.set(kJsonKey_Hashtype_Bitmap_64bitMD5); | 401 |
| 364 outputFilename.append("_"); | 402 SkString outputRelativePath; |
| 365 outputFilename.appendU64(hash); | 403 if (outputSubdirPtr) { |
| 366 outputFilename.append(".png"); | 404 outputRelativePath.set(outputSubdirPtr); |
| 405 outputRelativePath.append("/"); // always use "/", even on Windows |
| 406 outputRelativePath.append(outputFilename); |
| 407 } else { |
| 408 outputRelativePath.set(outputFilename); |
| 409 } |
| 410 |
| 411 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(), |
| 412 hash, tileNumberPtr); |
| 367 } | 413 } |
| 368 | 414 |
| 369 SkASSERT(!outputDir.isEmpty()); // TODO(epoger): we want to remove this cons
traint, | 415 SkASSERT(!outputDir.isEmpty()); // TODO(epoger): we want to remove this cons
traint, |
| 370 // as noted above | 416 // as noted above |
| 371 SkString fullPathname; | 417 SkString dirPath; |
| 372 make_filepath(&fullPathname, outputDir, outputFilename); | 418 if (outputSubdirPtr) { |
| 373 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncod
er::kPNG_Type, 100); | 419 dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr); |
| 420 sk_mkdir(dirPath.c_str()); |
| 421 } else { |
| 422 dirPath.set(outputDir); |
| 423 } |
| 424 SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_s
tr()); |
| 425 return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::
kPNG_Type, 100); |
| 374 } | 426 } |
| 375 | 427 |
| 376 ////////////////////////////////////////////////////////////////////////////////
/////////////// | 428 ////////////////////////////////////////////////////////////////////////////////
/////////////// |
| 377 | 429 |
| 378 SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) { | 430 SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) { |
| 379 // defer the canvas setup until the render step | 431 // defer the canvas setup until the render step |
| 380 return NULL; | 432 return NULL; |
| 381 } | 433 } |
| 382 | 434 |
| 383 // the size_t* parameter is deprecated, so we ignore it | 435 // the size_t* parameter is deprecated, so we ignore it |
| 384 static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { | 436 static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { |
| 385 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); | 437 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); |
| 386 } | 438 } |
| 387 | 439 |
| 388 bool RecordPictureRenderer::render(SkBitmap** out) { | 440 bool RecordPictureRenderer::render(SkBitmap** out) { |
| 389 SkAutoTUnref<SkPicture> replayer(this->createPicture()); | 441 SkAutoTUnref<SkPicture> replayer(this->createPicture()); |
| 390 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->ge
tViewHeight(), | 442 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->ge
tViewHeight(), |
| 391 this->recordFlags()); | 443 this->recordFlags()); |
| 392 this->scaleToScaleFactor(recorder); | 444 this->scaleToScaleFactor(recorder); |
| 393 fPicture->draw(recorder); | 445 fPicture->draw(recorder); |
| 394 replayer->endRecording(); | 446 replayer->endRecording(); |
| 395 if (!fOutputDir.isEmpty()) { | 447 if (!fOutputDir.isEmpty()) { |
| 396 // Record the new picture as a new SKP with PNG encoded bitmaps. | 448 // Record the new picture as a new SKP with PNG encoded bitmaps. |
| 397 SkString skpPath; | 449 SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilena
me.c_str()); |
| 398 make_filepath(&skpPath, fOutputDir, fInputFilename); | |
| 399 SkFILEWStream stream(skpPath.c_str()); | 450 SkFILEWStream stream(skpPath.c_str()); |
| 400 replayer->serialize(&stream, &encode_bitmap_to_data); | 451 replayer->serialize(&stream, &encode_bitmap_to_data); |
| 401 return true; | 452 return true; |
| 402 } | 453 } |
| 403 return false; | 454 return false; |
| 404 } | 455 } |
| 405 | 456 |
| 406 SkString RecordPictureRenderer::getConfigNameInternal() { | 457 SkString RecordPictureRenderer::getConfigNameInternal() { |
| 407 return SkString("record"); | 458 return SkString("record"); |
| 408 } | 459 } |
| (...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 995 virtual SkString getConfigNameInternal() SK_OVERRIDE { | 1046 virtual SkString getConfigNameInternal() SK_OVERRIDE { |
| 996 return SkString("picture_clone"); | 1047 return SkString("picture_clone"); |
| 997 } | 1048 } |
| 998 }; | 1049 }; |
| 999 | 1050 |
| 1000 PictureRenderer* CreatePictureCloneRenderer() { | 1051 PictureRenderer* CreatePictureCloneRenderer() { |
| 1001 return SkNEW(PictureCloneRenderer); | 1052 return SkNEW(PictureCloneRenderer); |
| 1002 } | 1053 } |
| 1003 | 1054 |
| 1004 } // namespace sk_tools | 1055 } // namespace sk_tools |
| OLD | NEW |