| Index: tools/PictureRenderer.cpp
|
| diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
|
| index c53bd08265a6e477215c7a9658c0c277d561dbd5..88ddc7513748161bbeed1b804b88b06c663f3ca3 100644
|
| --- a/tools/PictureRenderer.cpp
|
| +++ b/tools/PictureRenderer.cpp
|
| @@ -8,6 +8,7 @@
|
| #include "PictureRenderer.h"
|
| #include "picture_utils.h"
|
| #include "SamplePipeControllers.h"
|
| +#include "SkBitmapHasher.h"
|
| #include "SkCanvas.h"
|
| #include "SkData.h"
|
| #include "SkDevice.h"
|
| @@ -40,6 +41,39 @@ enum {
|
| kDefaultTileHeight = 256
|
| };
|
|
|
| +/* TODO(epoger): These constants are already maintained in 2 other places:
|
| + * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
|
| + * Figure out a way to share the definitions instead.
|
| + */
|
| +const static char kJsonKey_ActualResults[] = "actual-results";
|
| +const static char kJsonKey_ActualResults_Failed[] = "failed";
|
| +const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
|
| +const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
|
| +const static char kJsonKey_ActualResults_Succeeded[] = "succeeded";
|
| +const static char kJsonKey_ExpectedResults[] = "expected-results";
|
| +const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
|
| +const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
|
| +const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
|
| +
|
| +void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
|
| + uint64_t hash;
|
| + SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
|
| + Json::Value jsonTypeValuePair;
|
| + jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
|
| + jsonTypeValuePair.append(Json::UInt64(hash));
|
| + fActualResultsNoComparison[testName] = jsonTypeValuePair;
|
| +}
|
| +
|
| +void ImageResultsSummary::writeToFile(const char *filename) {
|
| + Json::Value actualResults;
|
| + actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
|
| + Json::Value root;
|
| + root[kJsonKey_ActualResults] = actualResults;
|
| + std::string jsonStdString = root.toStyledString();
|
| + SkFILEWStream stream(filename);
|
| + stream.write(jsonStdString.c_str(), jsonStdString.length());
|
| +}
|
| +
|
| void PictureRenderer::init(SkPicture* pict) {
|
| SkASSERT(NULL == fPicture);
|
| SkASSERT(NULL == fCanvas.get());
|
| @@ -217,14 +251,28 @@ uint32_t PictureRenderer::recordFlags() {
|
| * @param canvas Must be non-null. Canvas to be written to a file.
|
| * @param path Path for the file to be written. Should have no extension; write() will append
|
| * an appropriate one. Passed in by value so it can be modified.
|
| + * @param jsonSummaryPtr If not null, add image results to this summary.
|
| * @return bool True if the Canvas is written to a file.
|
| + *
|
| + * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
|
| + * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
|
| + * even if --writePath has not been specified (and thus this function is not called).
|
| + *
|
| + * One fix would be to pass in these path elements separately, and allow this function to be
|
| + * called even if --writePath was not specified...
|
| + * const char *outputDir // NULL if we don't want to write image files to disk
|
| + * const char *filename // name we use within JSON summary, and as the filename within outputDir
|
| */
|
| -static bool write(SkCanvas* canvas, SkString path) {
|
| +static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) {
|
| SkASSERT(canvas != NULL);
|
| if (NULL == canvas) {
|
| return false;
|
| }
|
|
|
| + SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above
|
| + SkString fullPathname(*path);
|
| + fullPathname.append(".png");
|
| +
|
| SkBitmap bitmap;
|
| SkISize size = canvas->getDeviceSize();
|
| sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
|
| @@ -232,22 +280,33 @@ static bool write(SkCanvas* canvas, SkString path) {
|
| canvas->readPixels(&bitmap, 0, 0);
|
| sk_tools::force_all_opaque(bitmap);
|
|
|
| - // Since path is passed in by value, it is okay to modify it.
|
| - path.append(".png");
|
| - return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
|
| + if (NULL != jsonSummaryPtr) {
|
| + // EPOGER: This is a hacky way of constructing the filename associated with the
|
| + // image checksum; we assume that outputDir is not NULL, and we remove outputDir
|
| + // from fullPathname.
|
| + //
|
| + // EPOGER: what about including the config type within hashFilename? That way,
|
| + // we could combine results of different config types without conflicting filenames.
|
| + SkString hashFilename;
|
| + sk_tools::get_basename(&hashFilename, fullPathname);
|
| + jsonSummaryPtr->add(hashFilename.c_str(), bitmap);
|
| + }
|
| +
|
| + return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
|
| }
|
|
|
| /**
|
| - * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
|
| + * If path is non NULL, append number to it, and call write() to write the
|
| * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
|
| */
|
| -static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
|
| +static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number,
|
| + ImageResultsSummary *jsonSummaryPtr) {
|
| if (NULL == path) {
|
| return true;
|
| }
|
| SkString pathWithNumber(*path);
|
| pathWithNumber.appendf("%i", number);
|
| - return write(canvas, pathWithNumber);
|
| + return write(canvas, &pathWithNumber, jsonSummaryPtr);
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////////////////////
|
| @@ -309,7 +368,7 @@ bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
|
| writer.endRecording();
|
| fCanvas->flush();
|
| if (NULL != path) {
|
| - return write(fCanvas, *path);
|
| + return write(fCanvas, path, fJsonSummaryPtr);
|
| }
|
| if (NULL != out) {
|
| *out = SkNEW(SkBitmap);
|
| @@ -340,7 +399,7 @@ bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
|
| fCanvas->drawPicture(*fPicture);
|
| fCanvas->flush();
|
| if (NULL != path) {
|
| - return write(fCanvas, *path);
|
| + return write(fCanvas, path, fJsonSummaryPtr);
|
| }
|
|
|
| if (NULL != out) {
|
| @@ -506,11 +565,22 @@ static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playba
|
|
|
| ///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
| -static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
|
| - int yDst) {
|
| - for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
|
| - for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
|
| - *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
|
| +/**
|
| + * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
|
| + * If the src bitmap is too large to fit within the dst bitmap after the x and y
|
| + * offsets have been applied, any excess will be ignored (so only the top-left portion of the
|
| + * src bitmap will be copied).
|
| + *
|
| + * @param src source bitmap
|
| + * @param dst destination bitmap
|
| + * @param xOffset x-offset within destination bitmap
|
| + * @param yOffset y-offset within destination bitmap
|
| + */
|
| +static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
|
| + int xOffset, int yOffset) {
|
| + for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
|
| + for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
|
| + *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
|
| }
|
| }
|
| }
|
| @@ -545,12 +615,13 @@ bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
|
| for (int i = 0; i < fTileRects.count(); ++i) {
|
| DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
|
| if (NULL != path) {
|
| - success &= writeAppendNumber(fCanvas, path, i);
|
| + success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
|
| }
|
| if (NULL != out) {
|
| if (fCanvas->readPixels(&bitmap, 0, 0)) {
|
| - bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
|
| - SkScalarFloorToInt(fTileRects[i].top()));
|
| + // Add this tile to the entire bitmap.
|
| + bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
|
| + SkScalarFloorToInt(fTileRects[i].top()));
|
| } else {
|
| success = false;
|
| }
|
| @@ -603,7 +674,7 @@ class CloneData : public SkRunnable {
|
|
|
| public:
|
| CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
|
| - SkRunnable* done)
|
| + SkRunnable* done, ImageResultsSummary* jsonSummaryPtr)
|
| : fClone(clone)
|
| , fCanvas(canvas)
|
| , fPath(NULL)
|
| @@ -611,7 +682,8 @@ public:
|
| , fStart(start)
|
| , fEnd(end)
|
| , fSuccess(NULL)
|
| - , fDone(done) {
|
| + , fDone(done)
|
| + , fJsonSummaryPtr(jsonSummaryPtr) {
|
| SkASSERT(fDone != NULL);
|
| }
|
|
|
| @@ -626,7 +698,7 @@ public:
|
|
|
| for (int i = fStart; i < fEnd; i++) {
|
| DrawTileToCanvas(fCanvas, fRects[i], fClone);
|
| - if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
|
| + if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
|
| && fSuccess != NULL) {
|
| *fSuccess = false;
|
| // If one tile fails to write to a file, do not continue drawing the rest.
|
| @@ -635,8 +707,8 @@ public:
|
| if (fBitmap != NULL) {
|
| if (fCanvas->readPixels(&bitmap, 0, 0)) {
|
| SkAutoLockPixels alp(*fBitmap);
|
| - bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
|
| - SkScalarFloorToInt(fRects[i].top()));
|
| + bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
|
| + SkScalarFloorToInt(fRects[i].top()));
|
| } else {
|
| *fSuccess = false;
|
| // If one tile fails to read pixels, do not continue drawing the rest.
|
| @@ -669,6 +741,7 @@ private:
|
| // and only set to false upon failure to write to a PNG.
|
| SkRunnable* fDone;
|
| SkBitmap* fBitmap;
|
| + ImageResultsSummary* fJsonSummaryPtr;
|
| };
|
|
|
| MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
|
| @@ -704,7 +777,8 @@ void MultiCorePictureRenderer::init(SkPicture *pict) {
|
| const int start = i * chunkSize;
|
| const int end = SkMin32(start + chunkSize, fTileRects.count());
|
| fCloneData[i] = SkNEW_ARGS(CloneData,
|
| - (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
|
| + (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
|
| + fJsonSummaryPtr));
|
| }
|
| }
|
|
|
|
|