Parse android layer annotations in debugger, play back layers
Bug: skia:9626 Change-Id: I3ae8fa83520690f9af534e9ab0b70834d7890fb0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/256100 Commit-Queue: Nathaniel Nifong <nifong@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
parent
ed58654e39
commit
20b177a9bf
4
BUILD.gn
4
BUILD.gn
@ -1034,6 +1034,7 @@ if (target_cpu == "wasm") {
|
||||
"tools/SkSharingProc.cpp",
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
"tools/debugger/DebugLayerManager.cpp",
|
||||
"tools/debugger/DrawCommand.cpp",
|
||||
"tools/debugger/JsonWriteBuffer.cpp",
|
||||
]
|
||||
@ -1455,6 +1456,7 @@ if (skia_enable_tools) {
|
||||
"tools/ToolUtils.cpp",
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
"tools/debugger/DebugLayerManager.cpp",
|
||||
"tools/debugger/DrawCommand.cpp",
|
||||
"tools/debugger/JsonWriteBuffer.cpp",
|
||||
"tools/fonts/RandomScalerContext.cpp",
|
||||
@ -1998,6 +2000,7 @@ if (skia_enable_tools) {
|
||||
"fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
"tools/debugger/DebugLayerManager.cpp",
|
||||
"tools/debugger/DrawCommand.cpp",
|
||||
"tools/debugger/JsonWriteBuffer.cpp",
|
||||
]
|
||||
@ -2416,6 +2419,7 @@ if (skia_enable_tools) {
|
||||
sources = [
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
"tools/debugger/DebugLayerManager.cpp",
|
||||
"tools/debugger/DrawCommand.cpp",
|
||||
"tools/debugger/JsonWriteBuffer.cpp",
|
||||
"tools/mdbviz/MainWindow.cpp",
|
||||
|
@ -13,7 +13,12 @@
|
||||
#include "tools/SkSharingProc.h"
|
||||
#include "tools/UrlDataManager.h"
|
||||
#include "tools/debugger/DebugCanvas.h"
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/bind.h>
|
||||
|
||||
@ -98,10 +103,20 @@ class SkpDebugPlayer {
|
||||
SkDebugf("Constrained command index (%d) within this frame's length (%d)\n", index, cmdlen);
|
||||
index = cmdlen-1;
|
||||
}
|
||||
auto* canvas = surface->getCanvas();
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
frames[fp]->drawTo(surface->getCanvas(), index);
|
||||
surface->getCanvas()->flush();
|
||||
}
|
||||
|
||||
// Draws to the end of the current frame.
|
||||
void draw(SkSurface* surface) {
|
||||
auto* canvas = surface->getCanvas();
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
frames[fp]->draw(surface->getCanvas());
|
||||
surface->getCanvas()->flush();
|
||||
}
|
||||
|
||||
const SkIRect& getBounds() { return fBounds; }
|
||||
|
||||
// The following three operations apply to every frame because they are overdraw features.
|
||||
@ -201,6 +216,11 @@ class SkpDebugPlayer {
|
||||
return toSimpleImageInfo(fImages[index]->imageInfo(), fImages[index].get());
|
||||
}
|
||||
|
||||
// return a list of layer draw events that happened at the beginning of this frame.
|
||||
std::vector<DebugLayerManager::DrawEventSummary> getLayerDrawEvents() {
|
||||
return fLayerManager->summarizeEvents(fp);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Loads a single frame (traditional) skp file from the provided data stream and returns
|
||||
@ -215,54 +235,57 @@ class SkpDebugPlayer {
|
||||
SkDebugf("Parsed SKP file.\n");
|
||||
// Make debug canvas using bounds from SkPicture
|
||||
fBounds = picture->cullRect().roundOut();
|
||||
std::unique_ptr<DebugCanvas> debugDanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||
SkDebugf("DebugCanvas created.\n");
|
||||
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||
|
||||
// Only draw picture to the debug canvas once.
|
||||
debugDanvas->drawPicture(picture);
|
||||
SkDebugf("Added picture with %d commands.\n", debugDanvas->getSize());
|
||||
return debugDanvas;
|
||||
debugCanvas->drawPicture(picture);
|
||||
return debugCanvas;
|
||||
}
|
||||
|
||||
void loadMultiFrame(SkMemoryStream* stream) {
|
||||
// Attempt to deserialize with an image sharing serial proc.
|
||||
auto deserialContext = std::make_unique<SkSharingDeserialContext>();
|
||||
SkDeserialProcs procs;
|
||||
procs.fImageProc = SkSharingDeserialContext::deserializeImage;
|
||||
procs.fImageCtx = deserialContext.get();
|
||||
|
||||
// Attempt to deserialize with an image sharing serial proc.
|
||||
auto deserialContext = std::make_unique<SkSharingDeserialContext>();
|
||||
SkDeserialProcs procs;
|
||||
procs.fImageProc = SkSharingDeserialContext::deserializeImage;
|
||||
procs.fImageCtx = deserialContext.get();
|
||||
int page_count = SkMultiPictureDocumentReadPageCount(stream);
|
||||
if (!page_count) {
|
||||
SkDebugf("Not a MultiPictureDocument");
|
||||
return;
|
||||
}
|
||||
SkDebugf("Expecting %d frames\n", page_count);
|
||||
|
||||
int page_count = SkMultiPictureDocumentReadPageCount(stream);
|
||||
if (!page_count) {
|
||||
SkDebugf("Not a MultiPictureDocument");
|
||||
return;
|
||||
std::vector<SkDocumentPage> pages(page_count);
|
||||
if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
|
||||
SkDebugf("Reading frames from MultiPictureDocument failed");
|
||||
return;
|
||||
}
|
||||
|
||||
fLayerManager = std::make_unique<DebugLayerManager>();
|
||||
|
||||
int i = 0;
|
||||
for (const auto& page : pages) {
|
||||
i++;
|
||||
// Make debug canvas using bounds from SkPicture
|
||||
fBounds = page.fPicture->cullRect().roundOut();
|
||||
std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||
debugCanvas->setLayerManagerAndFrame(fLayerManager.get(), i);
|
||||
|
||||
// Only draw picture to the debug canvas once.
|
||||
debugCanvas->drawPicture(page.fPicture);
|
||||
|
||||
if (debugCanvas->getSize() <=0 ){
|
||||
SkDebugf("Skipped corrupted frame, had %d commands \n", debugCanvas->getSize());
|
||||
continue;
|
||||
}
|
||||
SkDebugf("Expecting %d frames\n", page_count);
|
||||
|
||||
std::vector<SkDocumentPage> pages(page_count);
|
||||
if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
|
||||
SkDebugf("Reading frames from MultiPictureDocument failed");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& page : pages) {
|
||||
// Make debug canvas using bounds from SkPicture
|
||||
fBounds = page.fPicture->cullRect().roundOut();
|
||||
std::unique_ptr<DebugCanvas> debugDanvas = std::make_unique<DebugCanvas>(fBounds);
|
||||
// Only draw picture to the debug canvas once.
|
||||
debugDanvas->drawPicture(page.fPicture);
|
||||
SkDebugf("Added picture with %d commands.\n", debugDanvas->getSize());
|
||||
|
||||
if (debugDanvas->getSize() <=0 ){
|
||||
SkDebugf("Skipped corrupted frame, had %d commands \n", debugDanvas->getSize());
|
||||
continue;
|
||||
}
|
||||
debugDanvas->setOverdrawViz(false);
|
||||
debugDanvas->setDrawGpuOpBounds(false);
|
||||
debugDanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
||||
frames.push_back(std::move(debugDanvas));
|
||||
}
|
||||
fImages = deserialContext->fImages;
|
||||
// If you don't set these, they're undefined.
|
||||
debugCanvas->setOverdrawViz(false);
|
||||
debugCanvas->setDrawGpuOpBounds(false);
|
||||
debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
||||
frames.push_back(std::move(debugCanvas));
|
||||
}
|
||||
fImages = deserialContext->fImages;
|
||||
}
|
||||
|
||||
// A vector of DebugCanvas, each one initialized to a frame of the animation.
|
||||
@ -285,6 +308,10 @@ class SkpDebugPlayer {
|
||||
// find anything. TODO(nifong): Unify these two numbering schemes in CollatingCanvas.
|
||||
UrlDataManager udm;
|
||||
|
||||
// A structure holding the picture information needed to draw any layers used in an mskp file
|
||||
// individual frames hold a pointer to it, store draw events, and request images from it.
|
||||
// it is stateful and is set to the current frame at all times.
|
||||
std::unique_ptr<DebugLayerManager> fLayerManager;
|
||||
};
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
@ -356,6 +383,7 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
||||
.constructor<>()
|
||||
.function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
|
||||
.function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers())
|
||||
.function("draw", &SkpDebugPlayer::draw, allow_raw_pointers())
|
||||
.function("getBounds", &SkpDebugPlayer::getBounds)
|
||||
.function("setOverdrawVis", &SkpDebugPlayer::setOverdrawVis)
|
||||
.function("setClipVizColor", &SkpDebugPlayer::setClipVizColor)
|
||||
@ -369,7 +397,8 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
||||
.function("getFrameCount", &SkpDebugPlayer::getFrameCount)
|
||||
.function("getImageResource", &SkpDebugPlayer::getImageResource)
|
||||
.function("getImageCount", &SkpDebugPlayer::getImageCount)
|
||||
.function("getImageInfo", &SkpDebugPlayer::getImageInfo);
|
||||
.function("getImageInfo", &SkpDebugPlayer::getImageInfo)
|
||||
.function("getLayerDrawEvents", &SkpDebugPlayer::getLayerDrawEvents);
|
||||
|
||||
// Structs used as arguments or returns to the functions above
|
||||
value_object<SkIRect>("SkIRect")
|
||||
@ -377,6 +406,15 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
||||
.field("fTop", &SkIRect::fTop)
|
||||
.field("fRight", &SkIRect::fRight)
|
||||
.field("fBottom", &SkIRect::fBottom);
|
||||
// emscripten provided the following convenience function for binding vector<T>
|
||||
// https://emscripten.org/docs/api_reference/bind.h.html#_CPPv415register_vectorPKc
|
||||
register_vector<DebugLayerManager::DrawEventSummary>("VectorDrawEventSummary");
|
||||
value_object<DebugLayerManager::DrawEventSummary>("DebugLayerManager::DrawEventSummary")
|
||||
.field("nodeId", &DebugLayerManager::DrawEventSummary::nodeId)
|
||||
.field("fullRedraw", &DebugLayerManager::DrawEventSummary::fullRedraw)
|
||||
.field("commandCount", &DebugLayerManager::DrawEventSummary::commandCount)
|
||||
.field("layerWidth", &DebugLayerManager::DrawEventSummary::layerWidth)
|
||||
.field("layerHeight", &DebugLayerManager::DrawEventSummary::layerHeight);
|
||||
|
||||
// Symbols needed by cpu.js to perform surface creation and flushing.
|
||||
enum_<SkColorType>("ColorType")
|
||||
|
@ -50,6 +50,7 @@ tests_sources = [
|
||||
"$_tests/ColorTest.cpp",
|
||||
"$_tests/CopySurfaceTest.cpp",
|
||||
"$_tests/CubicMapTest.cpp",
|
||||
"$_tests/DebugLayerManagerTest.cpp",
|
||||
"$_tests/DashPathEffectTest.cpp",
|
||||
"$_tests/DataRefTest.cpp",
|
||||
"$_tests/DefaultPathRendererTest.cpp",
|
||||
|
84
tests/DebugLayerManagerTest.cpp
Normal file
84
tests/DebugLayerManagerTest.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkPicture.h"
|
||||
#include "include/core/SkPictureRecorder.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
|
||||
// Adds one full update, one partial update, and requests one image a few frames later.
|
||||
static void test_basic(skiatest::Reporter* reporter) {
|
||||
// prepare supporting objects
|
||||
int layerWidth = 100;
|
||||
int layerHeight = 100;
|
||||
|
||||
// make a picture that fully updates the layer
|
||||
SkPictureRecorder rec;
|
||||
SkCanvas* canvas = rec.beginRecording(layerWidth, layerHeight);
|
||||
canvas->clear(0x00000000);
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorBLUE);
|
||||
canvas->drawOval(SkRect::MakeLTRB(1,1,99,99), paint);
|
||||
canvas->flush();
|
||||
auto picture1 = rec.finishRecordingAsPicture();
|
||||
SkIRect dirtyRectFull = SkIRect::MakeLTRB(0, 0, layerWidth, layerHeight);
|
||||
|
||||
// make a second picture that acts as a partial update.
|
||||
SkPictureRecorder rec2;
|
||||
canvas = rec2.beginRecording(layerWidth, layerHeight);
|
||||
paint.setColor(SK_ColorGREEN);
|
||||
canvas->drawOval(SkRect::MakeLTRB(40,40,60,60), paint);
|
||||
canvas->flush();
|
||||
auto picture2 = rec2.finishRecordingAsPicture();
|
||||
SkIRect dirtyRectPartial = SkIRect::MakeLTRB(40,40,60,60);
|
||||
|
||||
int node = 2;
|
||||
|
||||
// create and exercise DebugLayerManager
|
||||
DebugLayerManager dlm;
|
||||
dlm.storeSkPicture(node, 0, picture1, dirtyRectFull);
|
||||
dlm.storeSkPicture(node, 10, picture2, dirtyRectPartial);
|
||||
auto frames = dlm.listFramesForNode(node);
|
||||
|
||||
// Confirm the layer manager stored these at the right places.
|
||||
REPORTER_ASSERT(reporter, frames.size() == 2);
|
||||
REPORTER_ASSERT(reporter, frames[0] == 0);
|
||||
REPORTER_ASSERT(reporter, frames[1] == 10);
|
||||
|
||||
SkPixmap pixmap;
|
||||
// request an image of the layer between the two updates.
|
||||
for (int i=0; i<10; i++) {
|
||||
auto image = dlm.getLayerAsImage(node, i);
|
||||
REPORTER_ASSERT(reporter, image->width() == layerWidth);
|
||||
REPORTER_ASSERT(reporter, image->height() == layerHeight);
|
||||
// confirm center is blue, proving only first pic was drawn.
|
||||
image->peekPixels(&pixmap);
|
||||
SkColor paintColor = pixmap.getColor(50, 50);
|
||||
REPORTER_ASSERT(reporter, paintColor == SK_ColorBLUE);
|
||||
}
|
||||
|
||||
// For any images after the second draw, confirm the center is green, but the area just outside
|
||||
// that smaller circle is still blue, proving dlm drew both pictures.
|
||||
for (int i=10; i<12; i++) {
|
||||
auto image = dlm.getLayerAsImage(node, i);
|
||||
REPORTER_ASSERT(reporter, image->width() == layerWidth);
|
||||
REPORTER_ASSERT(reporter, image->height() == layerHeight);
|
||||
image->peekPixels(&pixmap);
|
||||
auto innerColor = pixmap.getColor(50, 50);
|
||||
REPORTER_ASSERT(reporter, innerColor == SK_ColorGREEN);
|
||||
auto outerColor = pixmap.getColor(10, 50);
|
||||
REPORTER_ASSERT(reporter, outerColor == SK_ColorBLUE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
DEF_TEST(DebugLayerManagerTest, reporter) {
|
||||
test_basic(reporter);
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
#include "src/core/SkRectPriv.h"
|
||||
#include "src/utils/SkJSONWriter.h"
|
||||
#include "tools/debugger/DebugCanvas.h"
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
#include "tools/debugger/DrawCommand.h"
|
||||
|
||||
#include "include/gpu/GrContext.h"
|
||||
@ -20,11 +21,19 @@
|
||||
#include "src/gpu/GrContextPriv.h"
|
||||
#include "src/gpu/GrRenderTargetContext.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#define SKDEBUGCANVAS_VERSION 1
|
||||
#define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version"
|
||||
#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands"
|
||||
#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail"
|
||||
|
||||
namespace {
|
||||
// Constants used in Annotations by Android for keeping track of layers
|
||||
static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
|
||||
static constexpr char kSurfaceID[] = "SurfaceID";
|
||||
} // namespace
|
||||
|
||||
class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
|
||||
public:
|
||||
DebugPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) {}
|
||||
@ -53,7 +62,9 @@ DebugCanvas::DebugCanvas(int width, int height)
|
||||
: INHERITED(width, height)
|
||||
, fOverdrawViz(false)
|
||||
, fClipVizColor(SK_ColorTRANSPARENT)
|
||||
, fDrawGpuOpBounds(false) {
|
||||
, fDrawGpuOpBounds(false)
|
||||
, fnextDrawPictureLayerId(-1)
|
||||
, fnextDrawImageRectLayerId(-1) {
|
||||
// SkPicturePlayback uses the base-class' quickReject calls to cull clipped
|
||||
// operations. This can lead to problems in the debugger which expects all
|
||||
// the operations in the captured skp to appear in the debug canvas. To
|
||||
@ -73,7 +84,9 @@ DebugCanvas::DebugCanvas(int width, int height)
|
||||
this->INHERITED::onClipRect(large, kReplace_SkClipOp, kHard_ClipEdgeStyle);
|
||||
}
|
||||
|
||||
DebugCanvas::DebugCanvas(SkIRect bounds) { DebugCanvas(bounds.width(), bounds.height()); }
|
||||
DebugCanvas::DebugCanvas(SkIRect bounds) {
|
||||
DebugCanvas(bounds.width(), bounds.height());
|
||||
}
|
||||
|
||||
DebugCanvas::~DebugCanvas() { fCommandVector.deleteAll(); }
|
||||
|
||||
@ -94,7 +107,6 @@ void DebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) {
|
||||
SkRect windowRect = SkRect::MakeWH(SkIntToScalar(originalCanvas->getBaseLayerSize().width()),
|
||||
SkIntToScalar(originalCanvas->getBaseLayerSize().height()));
|
||||
|
||||
originalCanvas->clear(SK_ColorTRANSPARENT);
|
||||
originalCanvas->resetMatrix();
|
||||
if (!windowRect.isEmpty()) {
|
||||
originalCanvas->clipRect(windowRect, kReplace_SkClipOp);
|
||||
@ -307,6 +319,23 @@ void DebugCanvas::didConcat(const SkMatrix& matrix) {
|
||||
}
|
||||
|
||||
void DebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
|
||||
// Parse layer-releated annotations added in SkiaPipeline.cpp and RenderNodeDrawable.cpp
|
||||
// the format of the annotations is <Indicator|RenderNodeId>
|
||||
SkTArray<SkString> tokens;
|
||||
SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
|
||||
if (tokens.size() == 2) {
|
||||
if (tokens[0].equals(kOffscreenLayerDraw)) {
|
||||
// Indicates that the next drawPicture command contains the SkPicture to render the node
|
||||
// at this id in an offscreen buffer.
|
||||
fnextDrawPictureLayerId = std::stoi(tokens[1].c_str());
|
||||
fnextDrawPictureDirtyRect = rect.roundOut();
|
||||
return; // don't record it
|
||||
} else if (tokens[0].equals(kSurfaceID)) {
|
||||
// Indicates that the following drawImageRect should draw the offscreen buffer.
|
||||
fnextDrawImageRectLayerId = std::stoi(tokens[1].c_str());
|
||||
return; // don't record it
|
||||
}
|
||||
}
|
||||
this->addDrawCommand(new DrawAnnotationCommand(rect, key, sk_ref_sp(value)));
|
||||
}
|
||||
|
||||
@ -359,7 +388,22 @@ void DebugCanvas::onDrawImageRect(const SkImage* image,
|
||||
const SkRect& dst,
|
||||
const SkPaint* paint,
|
||||
SrcRectConstraint constraint) {
|
||||
this->addDrawCommand(new DrawImageRectCommand(image, src, dst, paint, constraint));
|
||||
if (fnextDrawImageRectLayerId != -1 && fLayerManager) {
|
||||
// This drawImageRect command would have drawn the offscreen buffer for a layer.
|
||||
// On Android, we recorded an SkPicture of the commands that drew to the layer.
|
||||
// To render the layer as it would have looked on the frame this DebugCanvas draws, we need
|
||||
// to call fLayerManager->getLayerAsImage(id). This must be done just before
|
||||
// drawTo(command), since it depends on the index into the layer's commands
|
||||
// (managed by fLayerManager)
|
||||
// Instead of adding a DrawImageRectCommand, we need a deferred command, that when
|
||||
// executed, will call drawImageRect(fLayerManager->getLayerAsImage())
|
||||
this->addDrawCommand(new DrawImageRectLayerCommand(
|
||||
fLayerManager, fnextDrawImageRectLayerId, fFrame, src, dst, paint, constraint));
|
||||
} else {
|
||||
this->addDrawCommand(new DrawImageRectCommand(image, src, dst, paint, constraint));
|
||||
}
|
||||
// Reset expectation so next drawImageRect is not special.
|
||||
fnextDrawImageRectLayerId = -1;
|
||||
}
|
||||
|
||||
void DebugCanvas::onDrawImageNine(const SkImage* image,
|
||||
@ -400,10 +444,16 @@ void DebugCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
|
||||
void DebugCanvas::onDrawPicture(const SkPicture* picture,
|
||||
const SkMatrix* matrix,
|
||||
const SkPaint* paint) {
|
||||
this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint));
|
||||
SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
|
||||
picture->playback(this);
|
||||
this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
|
||||
if (fnextDrawPictureLayerId != -1 && fLayerManager) {
|
||||
fLayerManager->storeSkPicture(fnextDrawPictureLayerId, fFrame, sk_ref_sp(picture),
|
||||
fnextDrawPictureDirtyRect);
|
||||
} else {
|
||||
this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint));
|
||||
SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
|
||||
picture->playback(this);
|
||||
this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
|
||||
}
|
||||
fnextDrawPictureLayerId = -1;
|
||||
}
|
||||
|
||||
void DebugCanvas::onDrawPoints(PointMode mode,
|
||||
|
@ -16,11 +16,13 @@
|
||||
#include "include/pathops/SkPathOps.h"
|
||||
#include "include/private/SkTArray.h"
|
||||
#include "tools/UrlDataManager.h"
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
#include "tools/debugger/DrawCommand.h"
|
||||
|
||||
class GrAuditTrail;
|
||||
class SkNWayCanvas;
|
||||
class SkPicture;
|
||||
class DebugLayerManager;
|
||||
|
||||
class DebugCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
|
||||
public:
|
||||
@ -30,6 +32,21 @@ public:
|
||||
|
||||
~DebugCanvas() override;
|
||||
|
||||
/**
|
||||
* Provide a DebugLayerManager for mskp files containing layer information
|
||||
* when set this DebugCanvas will attempt to parse layer info from annotations.
|
||||
* it will store layer pictures to the layer manager, and interpret some drawImageRects
|
||||
* as layer draws, deferring to the layer manager for images.
|
||||
* Provide a frame number that will be passed to all layer manager functions to identify this
|
||||
* DebugCanvas.
|
||||
*
|
||||
* Used only in wasm debugger animations.
|
||||
*/
|
||||
void setLayerManagerAndFrame(DebugLayerManager* lm, int frame) {
|
||||
fLayerManager = lm;
|
||||
fFrame = frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable overdraw visualization
|
||||
*/
|
||||
@ -54,6 +71,8 @@ public:
|
||||
|
||||
/**
|
||||
Executes the draw calls up to the specified index.
|
||||
Does not clear the canvas to transparent black first,
|
||||
if needed, caller should do that first.
|
||||
@param canvas The canvas being drawn to
|
||||
@param index The index of the final command being executed
|
||||
@param m an optional Mth gpu op to highlight, or -1
|
||||
@ -207,6 +226,17 @@ private:
|
||||
SkColor fClipVizColor;
|
||||
bool fDrawGpuOpBounds;
|
||||
|
||||
// When not negative, indicates the render node id of the layer represented by the next
|
||||
// drawPicture call.
|
||||
int fnextDrawPictureLayerId = -1;
|
||||
int fnextDrawImageRectLayerId = -1;
|
||||
SkIRect fnextDrawPictureDirtyRect;
|
||||
// may be null, in which case layer annotations are ignored.
|
||||
DebugLayerManager* fLayerManager = nullptr;
|
||||
// May be set when DebugCanvas is used in playing back an animation.
|
||||
// Only used for passing to fLayerManager to identify itself.
|
||||
int fFrame = -1;
|
||||
|
||||
/**
|
||||
Adds the command to the class' vector of commands.
|
||||
@param command The draw command for execution
|
||||
|
142
tools/debugger/DebugLayerManager.cpp
Normal file
142
tools/debugger/DebugLayerManager.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkPicture.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/private/SkTHash.h"
|
||||
#include "tools/debugger/DebugCanvas.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
void DebugLayerManager::setCommand(int nodeId, int frame, int command) {
|
||||
auto* drawEvent = fDraws.find({frame, nodeId});
|
||||
if (!drawEvent) {
|
||||
SkDebugf("Could not set command playhead for event {%d, %d}, it is not tracked by"
|
||||
"DebugLayerManager.\n", frame, nodeId);
|
||||
return;
|
||||
}
|
||||
const int count = drawEvent->debugCanvas->getSize();
|
||||
drawEvent->command = command < count ? command : count - 1;
|
||||
// Invalidate stored images that depended on this combination of node and frame.
|
||||
// actually this does all of the events for this nodeId, but close enough.
|
||||
auto relevantFrames = listFramesForNode(nodeId);
|
||||
for (const auto& frame : relevantFrames) {
|
||||
fDraws[{frame, nodeId}].image = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugLayerManager::storeSkPicture(int nodeId, int frame, sk_sp<SkPicture> picture,
|
||||
SkIRect dirty) {
|
||||
const LayerKey k = {frame, nodeId};
|
||||
|
||||
// Make debug canvas using bounds from SkPicture. This will be equal to whatever width and
|
||||
// height were passed into SkPictureRecorder::beginRecording(w, h) which is the layer bounds.
|
||||
const auto& layerBounds = picture->cullRect().roundOut();
|
||||
auto debugCanvas = std::make_unique<DebugCanvas>(layerBounds);
|
||||
// Must be set or they end up undefined due to cosmic rays, bad luck, etc.
|
||||
debugCanvas->setOverdrawViz(false);
|
||||
debugCanvas->setDrawGpuOpBounds(false);
|
||||
debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
|
||||
// Setting this allows a layer to contain another layer. TODO(nifong): write a test for this.
|
||||
debugCanvas->setLayerManagerAndFrame(this, frame);
|
||||
// Only draw picture to the debug canvas once.
|
||||
debugCanvas->drawPicture(picture);
|
||||
int numCommands = debugCanvas->getSize();
|
||||
|
||||
DrawEvent event = {
|
||||
frame == 0 || dirty==layerBounds, // fullRedraw
|
||||
nullptr, // image
|
||||
std::move(debugCanvas), // debugCanvas
|
||||
numCommands-1, // command
|
||||
{layerBounds.height(), layerBounds.width()}, // layerBounds
|
||||
};
|
||||
|
||||
fDraws.set(k, std::move(event));
|
||||
keys.push_back(k);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> DebugLayerManager::getLayerAsImage(const int nodeId, const int frame) {
|
||||
// What is the last frame having an SkPicture for this layer? call it frame N
|
||||
// have cached image of it? if so, return it.
|
||||
// if not, draw it at frame N by the following method:
|
||||
// The picture at frame N could have been a full redraw, or it could have been clipped to a
|
||||
// dirty region. In order to know what the layer looked like on this frame, we must draw every
|
||||
// picture starting with the last full redraw, up to the last one before the current frame, since
|
||||
// any of those previous draws could be showing through.
|
||||
|
||||
// list of frames this node was updated on.
|
||||
auto relevantFrames = listFramesForNode(nodeId);
|
||||
// find largest one not greater than `frame`.
|
||||
uint32_t i = relevantFrames.size()-1;
|
||||
while (relevantFrames[i] > frame) { i--; }
|
||||
const int frameN = relevantFrames[i];
|
||||
// Fetch the draw event
|
||||
auto& drawEvent = fDraws[{frameN, nodeId}];
|
||||
// if an image of this is cached, return it.
|
||||
if (drawEvent.image) {
|
||||
return drawEvent.image;
|
||||
}
|
||||
// when it's not cached, we'll have to render it in an offscreen surface.
|
||||
// start at the last full redraw. (pick up counting backwards from above)
|
||||
while (i>0 && !(fDraws[{relevantFrames[i], nodeId}].fullRedraw)) { i--; }
|
||||
// The correct layer bounds can be obtained from any drawEvent on this layer.
|
||||
// the color type and alpha type are chosen here to match wasm-skp-debugger/cpu.js which was
|
||||
// chosen to match the capabilities of HTML canvas, which this ultimately has to be drawn into.
|
||||
// TODO(nifong): introduce a method of letting the user choose the backend for this.
|
||||
auto surface = SkSurface::MakeRaster(SkImageInfo::Make(drawEvent.layerBounds,
|
||||
kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, nullptr));
|
||||
// draw everything from the last full redraw up to the current frame.
|
||||
// other frames drawn are partial, meaning they were clipped to not completely cover the layer.
|
||||
// count back up with i
|
||||
auto* canvas = surface->getCanvas();
|
||||
for (; i<relevantFrames.size() && relevantFrames[i]<=frameN; i++) {
|
||||
auto& evt = fDraws[{relevantFrames[i], nodeId}];
|
||||
evt.debugCanvas->drawTo(canvas, evt.command);
|
||||
canvas->flush();
|
||||
}
|
||||
drawEvent.image = surface->makeImageSnapshot();
|
||||
return drawEvent.image;
|
||||
}
|
||||
|
||||
std::vector<DebugLayerManager::DrawEventSummary> DebugLayerManager::summarizeEvents(int frame) const {
|
||||
std::vector<DrawEventSummary> result;
|
||||
for (const auto& node : listNodesForFrame(frame)) {
|
||||
auto* evt = fDraws.find({frame, node});
|
||||
if (!evt) { continue; }
|
||||
result.push_back({
|
||||
node, evt->fullRedraw, evt->debugCanvas->getSize(),
|
||||
evt->layerBounds.width(), evt->layerBounds.height()
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int> DebugLayerManager::listNodesForFrame(int frame) const {
|
||||
std::vector<int> result;
|
||||
for (const auto& key : keys) {
|
||||
if (key.frame == frame) {
|
||||
result.push_back(key.nodeId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int> DebugLayerManager::listFramesForNode(int nodeId) const {
|
||||
std::vector<int> result;
|
||||
for (const auto& key : keys) {
|
||||
if (key.nodeId == nodeId) {
|
||||
result.push_back(key.frame);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
126
tools/debugger/DebugLayerManager.h
Normal file
126
tools/debugger/DebugLayerManager.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef DEBUGLAYERMANAGER_H_
|
||||
#define DEBUGLAYERMANAGER_H_
|
||||
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/private/SkTHash.h"
|
||||
#include "tools/debugger/DebugCanvas.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// A class to assist in playing back and debugging an mskp file containing offscreen layer commands.
|
||||
|
||||
// Holds SkPictures necessary to draw layers in one or more DebugCanvases. During
|
||||
// recording of the mskp file on Android, each layer had a RenderNode id, which is recorded with
|
||||
// the layer's draw commands.
|
||||
// Creates one surface (cpu only for now) for each layer, and renders
|
||||
// pictures to it up to the requested command using a DebugCanvas.
|
||||
|
||||
// Animations are expected to, but may not always use a layer on more than frame.
|
||||
// the layer may be drawn to more than once, and each different draw is saved for reconstructing the
|
||||
// layer as it was on any frame. Draws may be partial, meaning their commands were clipped to not
|
||||
// cover the entire layer.
|
||||
|
||||
// Clients may ask for a rendering of a given layer by it's RenderNode id and frame, and
|
||||
// this class will return a rendering of how it looked on that frame.
|
||||
// returning an SkImage snapshot of the internally managed surface.
|
||||
|
||||
class DebugCanvas;
|
||||
|
||||
class DebugLayerManager {
|
||||
public:
|
||||
DebugLayerManager() {}
|
||||
|
||||
// Store an SkPicture under a given nodeId (and under the currently set frame number)
|
||||
// `dirty` is the recorded rect that was used to call androidFramework_setDeviceClipRestriction
|
||||
// when the layer was drawn.
|
||||
void storeSkPicture(int nodeId, int frame, sk_sp<SkPicture> picture, SkIRect dirty);
|
||||
|
||||
// Set's the command playback head for a given picture/draw event.
|
||||
void setCommand(int nodeId, int frame, int command);
|
||||
|
||||
// getLayerAsImage draws the given layer as it would have looked on frame and returns an image.
|
||||
// Though each picture can be played back in as many ways as there are commands, we will let
|
||||
// that be determined by the user who sets an independent playhead for each draw event, tracked
|
||||
// here, so it stays how they left it.
|
||||
// For example: Say we are drawing a layer at frame 10.
|
||||
// Frame 0: Layer was completely redrawn. By default we draw it to it's last command. We always
|
||||
// save the result by (nodeId, frame)
|
||||
// Frame 5: Layer was partially redrawn, and the user has inspected this draw event, leaving
|
||||
// its command playhead at command 50/100. We have drew this at the time and save how
|
||||
// the result looked (all of the commands at frame 0, then half of the commands in the
|
||||
// partial draw at frame 5)
|
||||
// Frame 10: Another partial redraw, un-altered, drawn on top of the result from frame 5. We
|
||||
// return this as the image of how the layer should look on frame 10
|
||||
// Frame 15: A full redraw
|
||||
//
|
||||
// If the user then comes along and moves the command playhead of the picture at frame 0,
|
||||
// we invalidate the stored images for 0, 5, and 10, but we can leave 15 alone if we have it.
|
||||
//
|
||||
// Which leaves us with one less degree of freedom to think about when implementing this
|
||||
// function: We can assume there is only one way to play back a given picture. :)
|
||||
//
|
||||
// The reason the public version of this function doesn't let you specify the frame, is that
|
||||
// I expect DebugCanvas to call it, which doesn't know which frame it's rendering. The code in
|
||||
// debugger_bindings.cpp does know, which it why I'm having it set the frame via setFrame(int)
|
||||
sk_sp<SkImage> getLayerAsImage(const int nodeId, const int frame);
|
||||
|
||||
// Mean to be bindable by emscripted and returned to the javascript side
|
||||
struct DrawEventSummary {
|
||||
int nodeId;
|
||||
bool fullRedraw;
|
||||
int commandCount;
|
||||
int layerWidth;
|
||||
int layerHeight;
|
||||
};
|
||||
// Return a list summarizing the layer draw events on the current frame.
|
||||
std::vector<DrawEventSummary> summarizeEvents(int frame) const;
|
||||
|
||||
// Return the list of node ids which have DrawEvents on the given frame
|
||||
std::vector<int> listNodesForFrame(int frame) const;
|
||||
// Return the list of frames on which the given node had DrawEvents.
|
||||
std::vector<int> listFramesForNode(int nodeId) const;
|
||||
|
||||
private:
|
||||
// This class is basically a map from (frame, node) to draw-event
|
||||
// during recording, at the beginning of any frame, one or more layers could have been drawn on.
|
||||
// every draw event was recorded, and when reading the mskp file they are stored and organized
|
||||
// here.
|
||||
|
||||
struct LayerKey{
|
||||
int frame; // frame of animation on which this event was recorded.
|
||||
int nodeId; // the render node id of the layer which was drawn to.
|
||||
|
||||
bool operator==(const LayerKey& b) const {
|
||||
return this->frame==b.frame && this->nodeId==b.nodeId;
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawEvent {
|
||||
// true the pic's clip equals the layer bounds.
|
||||
bool fullRedraw;
|
||||
// the saved result of how the layer looks on this frame.
|
||||
// null if we don't have it.
|
||||
sk_sp<SkImage> image;
|
||||
// A debug canvas used for drawing this picture.
|
||||
// the SkPicture itself isn't saved, since it's in the DebugCanvas.
|
||||
std::unique_ptr<DebugCanvas> debugCanvas;
|
||||
// the command index where the debugCanvas was left off.
|
||||
int command;
|
||||
// the size of the layer this drew into. redundant between multiple DrawEvents on the same
|
||||
// layer but helpful.
|
||||
SkISize layerBounds;
|
||||
};
|
||||
|
||||
SkTHashMap<LayerKey, DrawEvent> fDraws;
|
||||
// The list of all keys in the map above (it has no keys() method)
|
||||
std::vector<LayerKey> keys;
|
||||
};
|
||||
|
||||
#endif
|
@ -28,6 +28,7 @@
|
||||
#include "src/core/SkRectPriv.h"
|
||||
#include "src/core/SkTextBlobPriv.h"
|
||||
#include "src/core/SkWriteBuffer.h"
|
||||
#include "tools/debugger/DebugLayerManager.h"
|
||||
#include "tools/debugger/JsonWriteBuffer.h"
|
||||
|
||||
#define DEBUGCANVAS_ATTRIBUTE_COMMAND "command"
|
||||
@ -125,6 +126,7 @@
|
||||
#define DEBUGCANVAS_ATTRIBUTE_AMBIENTCOLOR "ambientColor"
|
||||
#define DEBUGCANVAS_ATTRIBUTE_SPOTCOLOR "spotColor"
|
||||
#define DEBUGCANVAS_ATTRIBUTE_LIGHTRADIUS "lightRadius"
|
||||
#define DEBUGCANVAS_ATTRIBUTE_LAYERNODEID "layerNodeId"
|
||||
|
||||
#define DEBUGCANVAS_VERB_MOVE "move"
|
||||
#define DEBUGCANVAS_VERB_LINE "line"
|
||||
@ -225,6 +227,7 @@ const char* DrawCommand::GetCommandString(OpType type) {
|
||||
case kDrawImageLattice_OpType: return "DrawImageLattice";
|
||||
case kDrawImageNine_OpType: return "DrawImageNine";
|
||||
case kDrawImageRect_OpType: return "DrawImageRect";
|
||||
case kDrawImageRectLayer_OpType: return "DrawImageRectLayer";
|
||||
case kDrawOval_OpType: return "DrawOval";
|
||||
case kDrawPaint_OpType: return "DrawPaint";
|
||||
case kDrawPatch_OpType: return "DrawPatch";
|
||||
@ -1448,6 +1451,65 @@ void DrawImageRectCommand::toJSON(SkJSONWriter& writer, UrlDataManager& urlDataM
|
||||
writer.appendString(DEBUGCANVAS_ATTRIBUTE_SHORTDESC, str_append(&desc, fDst)->c_str());
|
||||
}
|
||||
|
||||
DrawImageRectLayerCommand::DrawImageRectLayerCommand(DebugLayerManager* layerManager,
|
||||
const int nodeId,
|
||||
const int frame,
|
||||
const SkRect* src,
|
||||
const SkRect& dst,
|
||||
const SkPaint* paint,
|
||||
SkCanvas::SrcRectConstraint constraint)
|
||||
: INHERITED(kDrawImageRectLayer_OpType)
|
||||
, fLayerManager(layerManager)
|
||||
, fNodeId(nodeId)
|
||||
, fFrame(frame)
|
||||
, fSrc(src)
|
||||
, fDst(dst)
|
||||
, fPaint(paint)
|
||||
, fConstraint(constraint) {}
|
||||
|
||||
void DrawImageRectLayerCommand::execute(SkCanvas* canvas) const {
|
||||
sk_sp<SkImage> snapshot = fLayerManager->getLayerAsImage(fNodeId, fFrame);
|
||||
canvas->legacy_drawImageRect(
|
||||
snapshot.get(), fSrc.getMaybeNull(), fDst, fPaint.getMaybeNull(), fConstraint);
|
||||
}
|
||||
|
||||
bool DrawImageRectLayerCommand::render(SkCanvas* canvas) const {
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
canvas->clear(0xFFFFFFFF);
|
||||
|
||||
xlate_and_scale_to_bounds(canvas, fDst);
|
||||
|
||||
this->execute(canvas);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawImageRectLayerCommand::toJSON(SkJSONWriter& writer, UrlDataManager& urlDataManager) const {
|
||||
INHERITED::toJSON(writer, urlDataManager);
|
||||
|
||||
// Don't append an image attribute here, the image can be rendered in as many different ways
|
||||
// as there are commands in the layer, at least. the urlDataManager would save each one under
|
||||
// a different URL.
|
||||
// Append the node id, and the layer inspector of the debugger will know what to do with it.
|
||||
writer.appendS64(DEBUGCANVAS_ATTRIBUTE_LAYERNODEID, fNodeId);
|
||||
|
||||
if (fSrc.isValid()) {
|
||||
writer.appendName(DEBUGCANVAS_ATTRIBUTE_SRC);
|
||||
MakeJsonRect(writer, *fSrc);
|
||||
}
|
||||
writer.appendName(DEBUGCANVAS_ATTRIBUTE_DST);
|
||||
MakeJsonRect(writer, fDst);
|
||||
if (fPaint.isValid()) {
|
||||
writer.appendName(DEBUGCANVAS_ATTRIBUTE_PAINT);
|
||||
MakeJsonPaint(writer, *fPaint, urlDataManager);
|
||||
}
|
||||
if (fConstraint == SkCanvas::kStrict_SrcRectConstraint) {
|
||||
writer.appendBool(DEBUGCANVAS_ATTRIBUTE_STRICT, true);
|
||||
}
|
||||
|
||||
SkString desc;
|
||||
writer.appendString(DEBUGCANVAS_ATTRIBUTE_SHORTDESC, str_append(&desc, fDst)->c_str());
|
||||
}
|
||||
|
||||
DrawImageNineCommand::DrawImageNineCommand(const SkImage* image,
|
||||
const SkIRect& center,
|
||||
const SkRect& dst,
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "src/utils/SkJSONWriter.h"
|
||||
#include "tools/UrlDataManager.h"
|
||||
|
||||
class DebugLayerManager;
|
||||
|
||||
class DrawCommand {
|
||||
public:
|
||||
enum OpType {
|
||||
@ -43,6 +45,7 @@ public:
|
||||
kDrawImageLattice_OpType,
|
||||
kDrawImageNine_OpType,
|
||||
kDrawImageRect_OpType,
|
||||
kDrawImageRectLayer_OpType, // unique to DebugCanvas
|
||||
kDrawOval_OpType,
|
||||
kDrawArc_OpType,
|
||||
kDrawPaint_OpType,
|
||||
@ -369,6 +372,34 @@ private:
|
||||
typedef DrawCommand INHERITED;
|
||||
};
|
||||
|
||||
// Command for resolving the deferred SkImage representing an android layer
|
||||
// Functions like DrawImageRect except it uses the saved UrlDataManager to resolve the image
|
||||
// at the time execute() is called.
|
||||
class DrawImageRectLayerCommand : public DrawCommand {
|
||||
public:
|
||||
DrawImageRectLayerCommand(DebugLayerManager* layerManager,
|
||||
const int nodeId,
|
||||
const int frame,
|
||||
const SkRect* src,
|
||||
const SkRect& dst,
|
||||
const SkPaint* paint,
|
||||
SkCanvas::SrcRectConstraint constraint);
|
||||
void execute(SkCanvas* canvas) const override;
|
||||
bool render(SkCanvas* canvas) const override;
|
||||
void toJSON(SkJSONWriter& writer, UrlDataManager& urlDataManager) const override;
|
||||
|
||||
private:
|
||||
DebugLayerManager* fLayerManager;
|
||||
int fNodeId;
|
||||
int fFrame;
|
||||
SkTLazy<SkRect> fSrc;
|
||||
SkRect fDst;
|
||||
SkTLazy<SkPaint> fPaint;
|
||||
SkCanvas::SrcRectConstraint fConstraint;
|
||||
|
||||
typedef DrawCommand INHERITED;
|
||||
};
|
||||
|
||||
class DrawOvalCommand : public DrawCommand {
|
||||
public:
|
||||
DrawOvalCommand(const SkRect& oval, const SkPaint& paint);
|
||||
|
@ -71,7 +71,9 @@ SkCanvas* Request::getCanvas() {
|
||||
|
||||
sk_sp<SkData> Request::drawToPng(int n, int m) {
|
||||
//fDebugCanvas->setOverdrawViz(true);
|
||||
fDebugCanvas->drawTo(this->getCanvas(), n, m);
|
||||
auto* canvas = this->getCanvas();
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
fDebugCanvas->drawTo(canvas, n, m);
|
||||
//fDebugCanvas->setOverdrawViz(false);
|
||||
return writeCanvasToPng(this->getCanvas());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user