Reland "Deserialize MultiPictureDocument based SKP files (with image sharing proc) in wasm debugger."
This is a reland of 7635013ad1
Original change's description:
> Deserialize MultiPictureDocument based SKP files (with image sharing proc) in wasm debugger.
>
> Change-Id: I73affae3cd05a2aa6ac1c75c8e049d352bbf3a85
> Bug: 9176
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217135
> Commit-Queue: Nathaniel Nifong <nifong@google.com>
> Reviewed-by: Derek Sollenberger <djsollen@google.com>
> Reviewed-by: Kevin Lubick <kjlubick@google.com>
Bug: 9176
Change-Id: Ifef1ff45ac0013ba3015f88c7ecd75527b28b604
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222505
Commit-Queue: Nathaniel Nifong <nifong@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
This commit is contained in:
parent
87bda3dabe
commit
0426c38daa
2
BUILD.gn
2
BUILD.gn
@ -1057,6 +1057,7 @@ if (target_cpu == "wasm") {
|
||||
public_configs = [ ":skia_public" ]
|
||||
|
||||
sources = [
|
||||
"tools/SkSharingProc.cpp",
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
"tools/debugger/DrawCommand.cpp",
|
||||
@ -1597,6 +1598,7 @@ if (skia_enable_tools) {
|
||||
"tools/LsanSuppressions.cpp",
|
||||
"tools/ProcStats.cpp",
|
||||
"tools/Resources.cpp",
|
||||
"tools/SkSharingProc.cpp",
|
||||
"tools/ToolUtils.cpp",
|
||||
"tools/UrlDataManager.cpp",
|
||||
"tools/debugger/DebugCanvas.cpp",
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "include/core/SkPicture.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "src/utils/SkJSONWriter.h"
|
||||
#include "src/utils/SkMultiPictureDocument.h"
|
||||
#include "tools/SkSharingProc.h"
|
||||
#include "tools/UrlDataManager.h"
|
||||
#include "tools/debugger/DebugCanvas.h"
|
||||
|
||||
@ -26,6 +28,10 @@
|
||||
|
||||
using JSColor = int32_t;
|
||||
|
||||
// file signature for SkMultiPictureDocument
|
||||
// TODO(nifong): make public and include from SkMultiPictureDocument.h
|
||||
static constexpr char kMultiMagic[] = "Skia Multi-Picture Doc\n\n";
|
||||
|
||||
struct SimpleImageInfo {
|
||||
int width;
|
||||
int height;
|
||||
@ -52,45 +58,61 @@ class SkpDebugPlayer {
|
||||
* pointer we're expecting.
|
||||
*/
|
||||
void loadSkp(uintptr_t cptr, int length) {
|
||||
const auto* data = reinterpret_cast<const uint8_t*>(cptr);
|
||||
// note overloaded = operator that actually does a move
|
||||
fPicture = SkPicture::MakeFromData(data, length);
|
||||
if (!fPicture) {
|
||||
SkDebugf("Unable to parse SKP file.\n");
|
||||
return;
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(cptr);
|
||||
char magic[8];
|
||||
// Both traditional and multi-frame skp files have a magic word
|
||||
SkMemoryStream stream(data, length);
|
||||
SkDebugf("make stream at %p, with %d bytes\n",data, length);
|
||||
// Why -1? I think it's got to do with using a constexpr, just a guess.
|
||||
const size_t magicsize = sizeof(kMultiMagic) - 1;
|
||||
if (memcmp(data, kMultiMagic, magicsize) == 0) {
|
||||
SkDebugf("Try reading as a multi-frame skp\n");
|
||||
loadMultiFrame(&stream);
|
||||
} else {
|
||||
SkDebugf("Try reading as single-frame skp\n");
|
||||
frames.push_back(loadSingleFrame(&stream));
|
||||
}
|
||||
SkDebugf("Parsed SKP file.\n");
|
||||
// Make debug canvas using bounds from SkPicture
|
||||
fBounds = fPicture->cullRect().roundOut();
|
||||
fDebugCanvas.reset(new DebugCanvas(fBounds));
|
||||
SkDebugf("DebugCanvas created.\n");
|
||||
|
||||
// Only draw picture to the debug canvas once.
|
||||
fDebugCanvas->drawPicture(fPicture);
|
||||
SkDebugf("Added picture with %d commands.\n", fDebugCanvas->getSize());
|
||||
}
|
||||
|
||||
/* drawTo asks the debug canvas to draw from the beginning of the picture
|
||||
* to the given command and flush the canvas.
|
||||
*/
|
||||
void drawTo(SkSurface* surface, int32_t index) {
|
||||
fDebugCanvas->drawTo(surface->getCanvas(), index);
|
||||
int cmdlen = frames[fp]->getSize();
|
||||
if (cmdlen == 0) {
|
||||
SkDebugf("Zero commands to execute");
|
||||
return;
|
||||
}
|
||||
if (index >= cmdlen) {
|
||||
SkDebugf("Constrained command index (%d) within this frame's length (%d)\n", index, cmdlen);
|
||||
index = cmdlen-1;
|
||||
}
|
||||
frames[fp]->drawTo(surface->getCanvas(), index);
|
||||
surface->getCanvas()->flush();
|
||||
}
|
||||
|
||||
const SkIRect& getBounds() { return fBounds; }
|
||||
|
||||
void setOverdrawVis(bool on) { fDebugCanvas->setOverdrawViz(on); }
|
||||
void setOverdrawVis(bool on) {
|
||||
frames[fp]->setOverdrawViz(on);
|
||||
}
|
||||
void setGpuOpBounds(bool on) {
|
||||
fDebugCanvas->setDrawGpuOpBounds(on);
|
||||
frames[fp]->setDrawGpuOpBounds(on);
|
||||
}
|
||||
void setClipVizColor(JSColor color) {
|
||||
fDebugCanvas->setClipVizColor(SkColor(color));
|
||||
frames[fp]->setClipVizColor(SkColor(color));
|
||||
}
|
||||
void deleteCommand(int index) {
|
||||
frames[fp]->deleteDrawCommandAt(index);
|
||||
}
|
||||
int getSize() const { return fDebugCanvas->getSize(); }
|
||||
void deleteCommand(int index) { fDebugCanvas->deleteDrawCommandAt(index); }
|
||||
void setCommandVisibility(int index, bool visible) {
|
||||
fDebugCanvas->toggleCommand(index, visible);
|
||||
frames[fp]->toggleCommand(index, visible);
|
||||
}
|
||||
int getSize() const {
|
||||
return frames[fp]->getSize();
|
||||
}
|
||||
int getFrameCount() const {
|
||||
return frames.size();
|
||||
}
|
||||
|
||||
// Return the command list in JSON representation as a string
|
||||
@ -101,7 +123,7 @@ class SkpDebugPlayer {
|
||||
// this will be prepended to any links that are created in the json command list.
|
||||
UrlDataManager udm(SkString("/"));
|
||||
writer.beginObject(); // root
|
||||
fDebugCanvas->toJSON(writer, udm, getSize(), surface->getCanvas());
|
||||
frames[fp]->toJSON(writer, udm, getSize(), surface->getCanvas());
|
||||
writer.endObject(); // root
|
||||
writer.flush();
|
||||
auto skdata = stream.detachAsData();
|
||||
@ -113,8 +135,8 @@ class SkpDebugPlayer {
|
||||
|
||||
// Gets the clip and matrix of the last command drawn
|
||||
std::string lastCommandInfo() {
|
||||
SkMatrix vm = fDebugCanvas->getCurrentMatrix();
|
||||
SkIRect clip = fDebugCanvas->getCurrentClip();
|
||||
SkMatrix vm = frames[fp]->getCurrentMatrix();
|
||||
SkIRect clip = frames[fp]->getCurrentClip();
|
||||
|
||||
SkDynamicMemoryWStream stream;
|
||||
SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast);
|
||||
@ -135,10 +157,81 @@ class SkpDebugPlayer {
|
||||
return std::string(data_view);
|
||||
}
|
||||
|
||||
void changeFrame(int index) {
|
||||
fp = index;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<DebugCanvas> fDebugCanvas;
|
||||
sk_sp<SkPicture> fPicture;
|
||||
SkIRect fBounds;
|
||||
|
||||
// Loads a single frame (traditional) skp file from the provided data stream and returns
|
||||
// a newly allocated DebugCanvas initialized with the SkPicture that was in the file.
|
||||
std::unique_ptr<DebugCanvas> loadSingleFrame(SkMemoryStream* stream) {
|
||||
// note overloaded = operator that actually does a move
|
||||
sk_sp<SkPicture> picture = SkPicture::MakeFromStream(stream);
|
||||
if (!picture) {
|
||||
SkDebugf("Unable to deserialze frame.\n");
|
||||
return nullptr;
|
||||
}
|
||||
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");
|
||||
|
||||
// Only draw picture to the debug canvas once.
|
||||
debugDanvas->drawPicture(picture);
|
||||
SkDebugf("Added picture with %d commands.\n", debugDanvas->getSize());
|
||||
return debugDanvas;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
int page_count = SkMultiPictureDocumentReadPageCount(stream);
|
||||
if (!page_count) {
|
||||
SkDebugf("Not a MultiPictureDocument");
|
||||
return;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// A vector of DebugCanvas, each one initialized to a frame of the animation.
|
||||
std::vector<std::unique_ptr<DebugCanvas>> frames;
|
||||
// The index of the current frame (into the vector above)
|
||||
int fp = 0;
|
||||
// The width and height of the animation. (in practice the bounds of the last loaded frame)
|
||||
SkIRect fBounds;
|
||||
// SKP version of loaded file.
|
||||
uint32_t fFileVersion;
|
||||
};
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
@ -218,7 +311,9 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
||||
.function("setCommandVisibility", &SkpDebugPlayer::setCommandVisibility)
|
||||
.function("setGpuOpBounds", &SkpDebugPlayer::setGpuOpBounds)
|
||||
.function("jsonCommandList", &SkpDebugPlayer::jsonCommandList, allow_raw_pointers())
|
||||
.function("lastCommandInfo", &SkpDebugPlayer::lastCommandInfo);
|
||||
.function("lastCommandInfo", &SkpDebugPlayer::lastCommandInfo)
|
||||
.function("changeFrame", &SkpDebugPlayer::changeFrame)
|
||||
.function("getFrameCount", &SkpDebugPlayer::getFrameCount);
|
||||
|
||||
// Structs used as arguments or returns to the functions above
|
||||
value_object<SkIRect>("SkIRect")
|
||||
|
@ -152,6 +152,7 @@ tests_sources = [
|
||||
"$_tests/MessageBusTest.cpp",
|
||||
"$_tests/MetaDataTest.cpp",
|
||||
"$_tests/MipMapTest.cpp",
|
||||
"$_tests/MultiSkpTest.cpp",
|
||||
"$_tests/NonlinearBlendingTest.cpp",
|
||||
"$_tests/OctoBoundsTest.cpp",
|
||||
"$_tests/OSPathTest.cpp",
|
||||
|
@ -453,6 +453,7 @@ DM_SRCS_ALL = struct(
|
||||
"experimental/svg/model/*.h",
|
||||
"gm/*.cpp",
|
||||
"gm/*.h",
|
||||
"src/utils/SkMultiPictureDocument.cpp",
|
||||
"src/xml/*.cpp",
|
||||
"tests/*.cpp",
|
||||
"tests/*.h",
|
||||
@ -499,6 +500,7 @@ DM_SRCS_ALL = struct(
|
||||
"tools/gpu/**/*.h",
|
||||
"tools/random_parse_path.cpp",
|
||||
"tools/random_parse_path.h",
|
||||
"tools/SkSharingProc.cpp",
|
||||
"tools/ToolUtils.cpp",
|
||||
"tools/ToolUtils.h",
|
||||
"tools/timer/*.cpp",
|
||||
|
@ -80,7 +80,8 @@ struct MultiPictureDocument final : public SkDocument {
|
||||
SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
|
||||
for (const sk_sp<SkPicture>& page : fPages) {
|
||||
c->drawPicture(page);
|
||||
c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr);
|
||||
// Annotations must include some data.
|
||||
c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, SkData::MakeWithCString("X"));
|
||||
}
|
||||
sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
|
||||
p->serialize(wStream, &fProcs);
|
||||
@ -195,7 +196,8 @@ bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
|
||||
// PagerCanvas::onDrawAnnotation().
|
||||
picture->playback(&canvas);
|
||||
if (canvas.fIndex != dstArrayCount) {
|
||||
SkDEBUGF("Malformed SkMultiPictureDocument\n");
|
||||
SkDEBUGF("Malformed SkMultiPictureDocument: canvas.fIndex=%d dstArrayCount=%d\n",
|
||||
canvas.fIndex, dstArrayCount);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
199
tests/MultiSkpTest.cpp
Normal file
199
tests/MultiSkpTest.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
* This test confirms that a multi skp can be serialize and deserailzied without error.
|
||||
*/
|
||||
|
||||
#include "include/core/SkDocument.h"
|
||||
#include "include/core/SkPicture.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "include/core/SkTextBlob.h"
|
||||
#include "include/core/SkFont.h"
|
||||
#include "src/core/SkRecord.h"
|
||||
#include "src/core/SkRecorder.h"
|
||||
#include "src/utils/SkMultiPictureDocument.h"
|
||||
#include "tools/SkSharingProc.h"
|
||||
#include "tests/Test.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class RecordVisitor {
|
||||
// An SkRecord visitor that remembers the name of the last visited command.
|
||||
public:
|
||||
SkString name;
|
||||
|
||||
explicit RecordVisitor() {}
|
||||
|
||||
template <typename T>
|
||||
void operator()(const T& command) {
|
||||
name = SkString(NameOf(command));
|
||||
}
|
||||
|
||||
SkString lastCommandName() {
|
||||
return name;
|
||||
}
|
||||
private:
|
||||
template <typename T>
|
||||
static const char* NameOf(const T&) {
|
||||
#define CASE(U) case SkRecords::U##_Type: return #U;
|
||||
switch (T::kType) { SK_RECORD_TYPES(CASE) }
|
||||
#undef CASE
|
||||
return "Unknown T";
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Compare record tested with record expected. Assert op sequence is the same (comparing types)
|
||||
// frame_num is only used for error message.
|
||||
static void compareRecords(const SkRecord& tested, const SkRecord& expected,
|
||||
int frame_num, skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, tested.count() == expected.count(),
|
||||
"Found %d commands in frame %d, expected %d", tested.count(), frame_num, expected.count());
|
||||
|
||||
RecordVisitor rv;
|
||||
for (int i = 0; i < tested.count(); i++) {
|
||||
tested.visit(i, rv);
|
||||
const SkString testCommandName = rv.lastCommandName();
|
||||
expected.visit(i, rv);
|
||||
const SkString expectedCommandName = rv.lastCommandName();
|
||||
REPORTER_ASSERT(reporter, testCommandName == expectedCommandName,
|
||||
"Unexpected command type '%s' in frame %d, op %d. Expected '%s'",
|
||||
testCommandName.c_str(), frame_num, i, expectedCommandName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_something(SkCanvas* canvas, int seed, sk_sp<SkImage> image) {
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(seed);
|
||||
paint.setColor(SK_ColorRED);
|
||||
|
||||
SkRect rect = SkRect::MakeXYWH(50+seed, 50+seed, 4*seed, 60);
|
||||
canvas->drawRect(rect, paint);
|
||||
|
||||
SkRRect oval;
|
||||
oval.setOval(rect);
|
||||
oval.offset(40, 60+seed);
|
||||
paint.setColor(SK_ColorBLUE);
|
||||
canvas->drawRRect(oval, paint);
|
||||
|
||||
paint.setColor(SK_ColorCYAN);
|
||||
canvas->drawCircle(180, 50, 5*seed, paint);
|
||||
|
||||
rect.offset(80, 0);
|
||||
paint.setColor(SK_ColorYELLOW);
|
||||
canvas->drawRoundRect(rect, 10, 10, paint);
|
||||
|
||||
SkPath path;
|
||||
path.cubicTo(768, 0, -512, 256, 256, 256);
|
||||
paint.setColor(SK_ColorGREEN);
|
||||
canvas->drawPath(path, paint);
|
||||
|
||||
canvas->drawImage(image, 128-seed, 128, &paint);
|
||||
|
||||
if (seed % 2 == 0) {
|
||||
SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
|
||||
canvas->drawImageRect(image, rect2, &paint);
|
||||
}
|
||||
|
||||
SkPaint paint2;
|
||||
auto text = SkTextBlob::MakeFromString(
|
||||
SkStringPrintf("Frame %d", seed).c_str(), SkFont(nullptr, 2+seed));
|
||||
canvas->drawTextBlob(text.get(), 50, 25, paint2);
|
||||
}
|
||||
|
||||
// Test serialization and deserialization of multi skp.
|
||||
DEF_TEST(Serialize_and_deserialize_multi_skp, reporter) {
|
||||
// Create the stream we will serialize into.
|
||||
SkDynamicMemoryWStream stream;
|
||||
|
||||
// Create the image sharing proc.
|
||||
SkSharingSerialContext ctx;
|
||||
SkSerialProcs procs;
|
||||
procs.fImageProc = SkSharingSerialContext::serializeImage;
|
||||
procs.fImageCtx = &ctx;
|
||||
|
||||
// Create the mulit picture document used for recording frames.
|
||||
sk_sp<SkDocument> multipic = SkMakeMultiPictureDocument(&stream, &procs);
|
||||
|
||||
static const int NUM_FRAMES = 12;
|
||||
static const int WIDTH = 256;
|
||||
static const int HEIGHT = 256;
|
||||
|
||||
// Make an image to be used in a later step.
|
||||
auto surface(SkSurface::MakeRasterN32Premul(100, 100));
|
||||
surface->getCanvas()->clear(SK_ColorGREEN);
|
||||
sk_sp<SkImage> image(surface->makeImageSnapshot());
|
||||
REPORTER_ASSERT(reporter, image);
|
||||
|
||||
// Create frames, recording them to multipic.
|
||||
SkRecord expectedRecords[NUM_FRAMES];
|
||||
for (int i=0; i<NUM_FRAMES; i++) {
|
||||
SkCanvas* pictureCanvas = multipic->beginPage(WIDTH, HEIGHT);
|
||||
draw_something(pictureCanvas, i, image);
|
||||
multipic->endPage();
|
||||
// Also record the same commands to separate SkRecords for later comparison
|
||||
SkRecorder canvas(&expectedRecords[i], WIDTH, HEIGHT);
|
||||
draw_something(&canvas, i, image);
|
||||
}
|
||||
// Finalize
|
||||
multipic->close();
|
||||
|
||||
// Confirm written data is at least as large as the magic word
|
||||
std::unique_ptr<SkStreamAsset> writtenStream = stream.detachAsStream();
|
||||
REPORTER_ASSERT(reporter, writtenStream->getLength() > 24,
|
||||
"Written data length too short (%d)", writtenStream->getLength());
|
||||
SkDebugf("Multi Frame file size = %d\n", writtenStream->getLength());
|
||||
|
||||
// Set up deserialization
|
||||
SkSharingDeserialContext deserialContext;
|
||||
SkDeserialProcs dprocs;
|
||||
dprocs.fImageProc = SkSharingDeserialContext::deserializeImage;
|
||||
dprocs.fImageCtx = &deserialContext;
|
||||
|
||||
// Confirm data is a MultiPictureDocument
|
||||
int frame_count = SkMultiPictureDocumentReadPageCount(writtenStream.get());
|
||||
REPORTER_ASSERT(reporter, frame_count == NUM_FRAMES,
|
||||
"Expected %d frames, got %d. \n 0 frames may indicate the written file was not a "
|
||||
"MultiPictureDocument.", NUM_FRAMES, frame_count);
|
||||
|
||||
// Deserailize
|
||||
std::vector<SkDocumentPage> frames(frame_count);
|
||||
REPORTER_ASSERT(reporter,
|
||||
SkMultiPictureDocumentRead(writtenStream.get(), frames.data(), frame_count, &dprocs),
|
||||
"Failed while reading MultiPictureDocument");
|
||||
|
||||
// Examine each frame.
|
||||
SkRecorder resultRecorder(nullptr, 1, 1);
|
||||
int i=0;
|
||||
for (const auto& frame : frames) {
|
||||
SkRect bounds = frame.fPicture->cullRect();
|
||||
REPORTER_ASSERT(reporter, bounds.width() == WIDTH,
|
||||
"Page width: expected (%d) got (%d)", WIDTH, bounds.width());
|
||||
REPORTER_ASSERT(reporter, bounds.height() == HEIGHT,
|
||||
"Page height: expected (%d) got (%d)", HEIGHT, bounds.height());
|
||||
// confirm contents of picture match what we drew.
|
||||
// There are several ways of doing this, an ideal comparison would not break in the same
|
||||
// way at the same time as the code under test (no serialization), and would involve only
|
||||
// minimal transformation of frame.fPicture, minimizing the chance that a detected fault lies
|
||||
// in the test itself. The comparions also would not be an overly sensitive change detector,
|
||||
// so that it doesn't break every time someone submits code (no golden file)
|
||||
|
||||
// Extract the SkRecord from the deserialized picture using playback (instead of a mess of
|
||||
// friend classes to grab the private record inside frame.fPicture
|
||||
SkRecord record;
|
||||
// This picture mode is necessary so that we record the command contents of frame.fPicture
|
||||
// not just a 'DrawPicture' command.
|
||||
resultRecorder.reset(&record, bounds, SkRecorder::Playback_DrawPictureMode, nullptr);
|
||||
frame.fPicture->playback(&resultRecorder);
|
||||
// Compare the record to the expected one
|
||||
compareRecords(record, expectedRecords[i], i, reporter);
|
||||
i++;
|
||||
}
|
||||
}
|
48
tools/SkSharingProc.cpp
Normal file
48
tools/SkSharingProc.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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/SkSharingProc.h"
|
||||
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkSerialProcs.h"
|
||||
|
||||
sk_sp<SkData> SkSharingSerialContext::serializeImage(SkImage* img, void* ctx) {
|
||||
SkSharingSerialContext* context = reinterpret_cast<SkSharingSerialContext*>(ctx);
|
||||
uint32_t id = img->uniqueID(); // get this process's id for the image. these are not hashes.
|
||||
// find out if we have already serialized this, and if so, what its in-file id is.
|
||||
auto iter = context->fImageMap.find(id);
|
||||
if (iter == context->fImageMap.end()) {
|
||||
// When not present, add its id to the map and return its usual serialized form.
|
||||
context->fImageMap[id] = context->fImageMap.size();
|
||||
return img->encodeToData();
|
||||
}
|
||||
uint32_t fid = context->fImageMap[id];
|
||||
// if present, return only the in-file id we registered the first time we serialized it.
|
||||
return SkData::MakeWithCopy(&fid, sizeof(fid));
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkSharingDeserialContext::deserializeImage(
|
||||
const void* data, size_t length, void* ctx) {
|
||||
SkSharingDeserialContext* context = reinterpret_cast<SkSharingDeserialContext*>(ctx);
|
||||
uint32_t fid;
|
||||
// If the data is an image fid, look up an already deserialized image from our map
|
||||
if (length == sizeof(fid)) {
|
||||
memcpy(&fid, data, sizeof(fid));
|
||||
if (fid >= context->fImages.size()) {
|
||||
SkDebugf("We do not have the data for image %d.\n", fid);
|
||||
return nullptr;
|
||||
}
|
||||
return context->fImages[fid];
|
||||
}
|
||||
// Otherwise, the data is an image, deserialise it, store it in our map at its fid.
|
||||
// TODO(nifong): make DeserialProcs accept sk_sp<SkData> so we don't have to copy this.
|
||||
sk_sp<SkData> dataView = SkData::MakeWithCopy(data, length);
|
||||
const sk_sp<SkImage> image = SkImage::MakeFromEncoded(std::move(dataView));
|
||||
context->fImages.push_back(image);
|
||||
return image;
|
||||
}
|
42
tools/SkSharingProc.h
Normal file
42
tools/SkSharingProc.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 SkSharingProc_DEFINED
|
||||
#define SkSharingProc_DEFINED
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkSerialProcs.h"
|
||||
|
||||
struct SkSharingSerialContext {
|
||||
// A map from the ids from SkImage::uniqueID() to ids used within the file
|
||||
std::unordered_map<uint32_t, uint32_t> fImageMap;
|
||||
|
||||
// A serial proc that shares images between subpictures
|
||||
// To use this, create an instance of SkSerialProcs and populate it this way.
|
||||
// The client must retain ownership of the context.
|
||||
// auto ctx = std::make_unique<SkSharingSerialContext>()
|
||||
// SkSerialProcs procs;
|
||||
// procs.fImageProc = SkSharingSerialContext::serializeImage;
|
||||
// procs.fImageCtx = ctx.get();
|
||||
static sk_sp<SkData> serializeImage(SkImage* img, void* ctx);
|
||||
};
|
||||
|
||||
struct SkSharingDeserialContext {
|
||||
// a list of unique images in the order they were encountered in the file
|
||||
// Subsequent occurrences of an image refer to it by it's index in this list.
|
||||
std::vector<sk_sp<SkImage>> fImages;
|
||||
|
||||
// A deserial proc that can interpret id's in place of images as references to previous images.
|
||||
// Can also deserialize a SKP where all images are inlined (it's backwards compatible)
|
||||
static sk_sp<SkImage> deserializeImage(const void* data, size_t length, void* ctx);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user