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 |