Revert "Revert "Add mskp player, use in viewer slide""
This reverts commit 0d174586c46923098eca1684e21a7049d5b62116.
Use SkTLazy instead of std::optional (C++17 library feature)
Bug: skia:11900
Change-Id: Ia41caa9322d812f9ba6644dd14ede7d0015cf8b3
Cq-Include-Trybots: luci.skia.skia.primary:Housekeeper-PerCommit-CreateDockerImage_Skia_Release,Build-Debian10-Clang-x86_64-Release-CMake
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/402642
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index d352489..ed045cf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1679,6 +1679,11 @@
"trim string")) {
data_sources += [ "skps" ]
}
+ if ("True" == exec_script("//gn/checkdir.py",
+ [ rebase_path("mskps", root_build_dir) ],
+ "trim string")) {
+ data_sources += [ "mskps" ]
+ }
}
} else {
# !is_ios
@@ -1877,6 +1882,8 @@
"tools/DDLTileHelper.cpp",
"tools/DDLTileHelper.h",
"tools/LsanSuppressions.cpp",
+ "tools/MSKPPlayer.cpp",
+ "tools/MSKPPlayer.h",
"tools/ProcStats.cpp",
"tools/ProcStats.h",
"tools/Resources.cpp",
@@ -2708,6 +2715,8 @@
"tools/viewer/ImGuiLayer.h",
"tools/viewer/ImageSlide.cpp",
"tools/viewer/ImageSlide.h",
+ "tools/viewer/MSKPSlide.cpp",
+ "tools/viewer/MSKPSlide.h",
"tools/viewer/ParticlesSlide.cpp",
"tools/viewer/ParticlesSlide.h",
"tools/viewer/SKPSlide.cpp",
diff --git a/src/utils/SkMultiPictureDocument.cpp b/src/utils/SkMultiPictureDocument.cpp
index 471a2b3..c9a15fd 100644
--- a/src/utils/SkMultiPictureDocument.cpp
+++ b/src/utils/SkMultiPictureDocument.cpp
@@ -199,6 +199,9 @@
}
auto picture = SkPicture::MakeFromStream(stream, procs);
+ if (!picture) {
+ return false;
+ }
PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
// Must call playback(), not drawPicture() to reach
diff --git a/tools/MSKPPlayer.cpp b/tools/MSKPPlayer.cpp
new file mode 100644
index 0000000..89bae48
--- /dev/null
+++ b/tools/MSKPPlayer.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/MSKPPlayer.h"
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkCanvasVirtualEnforcer.h"
+#include "include/core/SkPicture.h"
+#include "include/core/SkPictureRecorder.h"
+#include "include/core/SkSurface.h"
+#include "include/private/SkTArray.h"
+#include "include/utils/SkNoDrawCanvas.h"
+#include "src/core/SkCanvasPriv.h"
+#include "src/core/SkTLazy.h"
+#include "src/utils/SkMultiPictureDocument.h"
+#include "tools/SkSharingProc.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Base Cmd struct.
+struct MSKPPlayer::Cmd {
+ virtual ~Cmd() = default;
+ virtual void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const = 0;
+};
+
+// Draws a SkPicture.
+struct MSKPPlayer::PicCmd : Cmd {
+ sk_sp<SkPicture> fContent;
+
+ void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override {
+ canvas->drawPicture(fContent.get());
+ }
+};
+
+// Draws another layer. Stores the ID of the layer to draw and what command index on that
+// layer should be current when the layer is drawn. The layer contents are updated to the
+// stored command index before the layer is drawn.
+struct MSKPPlayer::DrawLayerCmd : Cmd {
+ int fLayerId;
+ size_t fLayerCmdCnt;
+ SkRect fSrcRect;
+ SkRect fDstRect;
+ SkSamplingOptions fSampling;
+ SkCanvas::SrcRectConstraint fConstraint;
+ SkTLazy<SkPaint> fPaint;
+
+ void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override;
+};
+
+void MSKPPlayer::DrawLayerCmd::draw(SkCanvas* canvas,
+ const LayerMap& layerMap,
+ LayerStateMap* layerStateMap) const {
+ const Layer& layer = layerMap.at(fLayerId);
+ LayerState* layerState = &(*layerStateMap)[fLayerId];
+ if (!layerState->fSurface) {
+ layerState->fCurrCmd = 0;
+ // Assume layer has same surface props and info as this (mskp doesn't currently record this
+ // data).
+ SkSurfaceProps props;
+ canvas->getProps(&props);
+ layerState->fSurface =
+ canvas->makeSurface(canvas->imageInfo().makeDimensions(layer.fDimensions), &props);
+ if (!layerState->fSurface) {
+ SkDebugf("Couldn't create surface for layer");
+ return;
+ }
+ }
+ size_t cmd = layerState->fCurrCmd;
+ if (cmd > fLayerCmdCnt) {
+ // If the layer contains contents from later commands then replay from the beginning.
+ cmd = 0;
+ }
+ SkCanvas* layerCanvas = layerState->fSurface->getCanvas();
+ for (; cmd < fLayerCmdCnt; ++cmd) {
+ layer.fCmds[cmd]->draw(layerCanvas, layerMap, layerStateMap);
+ }
+ const SkPaint* paint = fPaint.isValid() ? fPaint.get() : nullptr;
+ canvas->drawImageRect(layerState->fSurface->makeImageSnapshot(),
+ fSrcRect,
+ fDstRect,
+ fSampling,
+ paint,
+ fConstraint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MSKPPlayer::CmdRecordCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
+public:
+ CmdRecordCanvas(Layer* dst, LayerMap* offscreenLayers)
+ : fDst(dst), fOffscreenLayers(offscreenLayers) {
+ fRecorder.beginRecording(SkRect::Make(dst->fDimensions));
+ }
+ ~CmdRecordCanvas() override { this->recordPicCmd(); }
+
+protected:
+ void onDrawPaint(const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawPaint(paint);
+ }
+
+ void onDrawBehind(const SkPaint& paint) override {
+ SkCanvasPriv::DrawBehind(fRecorder.getRecordingCanvas(), paint);
+ }
+
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawRect(rect, paint);
+ }
+
+ void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawRRect(rrect, paint);
+ }
+
+ void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawDRRect(outer, inner, paint);
+ }
+
+ void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawOval(rect, paint);
+ }
+
+ void onDrawArc(const SkRect& rect,
+ SkScalar startAngle,
+ SkScalar sweepAngle,
+ bool useCenter,
+ const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawArc(rect, startAngle, sweepAngle, useCenter, paint);
+ }
+
+ void onDrawPath(const SkPath& path, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawPath(path, paint);
+ }
+
+ void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawRegion(region, paint);
+ }
+
+ void onDrawTextBlob(const SkTextBlob* blob,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawTextBlob(blob, x, y, paint);
+ }
+
+ void onDrawPatch(const SkPoint cubics[12],
+ const SkColor colors[4],
+ const SkPoint texCoords[4],
+ SkBlendMode mode,
+ const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawPatch(cubics, colors, texCoords, mode, paint);
+ }
+
+ void onDrawPoints(SkCanvas::PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) override {
+ fRecorder.getRecordingCanvas()->drawPoints(mode, count, pts, paint);
+ }
+
+ void onDrawImage2(const SkImage* image,
+ SkScalar dx,
+ SkScalar dy,
+ const SkSamplingOptions& sampling,
+ const SkPaint* paint) override {
+ fRecorder.getRecordingCanvas()->drawImage(image, dx, dy, sampling, paint);
+ }
+
+ void onDrawImageRect2(const SkImage* image,
+ const SkRect& src,
+ const SkRect& dst,
+ const SkSamplingOptions& sampling,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) override {
+ if (fNextDrawImageFromLayerID != -1) {
+ this->recordPicCmd();
+ auto drawLayer = std::make_unique<DrawLayerCmd>();
+ drawLayer->fLayerId = fNextDrawImageFromLayerID;
+ drawLayer->fLayerCmdCnt = fOffscreenLayers->at(fNextDrawImageFromLayerID).fCmds.size();
+ drawLayer->fSrcRect = src;
+ drawLayer->fDstRect = dst;
+ drawLayer->fSampling = sampling;
+ drawLayer->fConstraint = constraint;
+ if (paint) {
+ drawLayer->fPaint.init(*paint);
+ }
+ fDst->fCmds.push_back(std::move(drawLayer));
+ fNextDrawImageFromLayerID = -1;
+ return;
+ }
+ fRecorder.getRecordingCanvas()->drawImageRect(image, src, dst, sampling, paint, constraint);
+ }
+
+ void onDrawImageLattice2(const SkImage* image,
+ const Lattice& lattice,
+ const SkRect& dst,
+ SkFilterMode mode,
+ const SkPaint* paint) override {
+ fRecorder.getRecordingCanvas()->drawImageLattice(image, lattice, dst, mode, paint);
+ }
+
+ void onDrawAtlas2(const SkImage* image,
+ const SkRSXform rsxForms[],
+ const SkRect src[],
+ const SkColor colors[],
+ int count,
+ SkBlendMode mode,
+ const SkSamplingOptions& sampling,
+ const SkRect* cull,
+ const SkPaint* paint) override {
+ fRecorder.getRecordingCanvas()->drawAtlas(image,
+ rsxForms,
+ src,
+ colors,
+ count,
+ mode,
+ sampling,
+ cull,
+ paint);
+ }
+
+ void onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[],
+ int count,
+ const SkPoint dstClips[],
+ const SkMatrix preViewMatrices[],
+ const SkSamplingOptions& sampling,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) override {
+ fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAImageSet(imageSet,
+ count,
+ dstClips,
+ preViewMatrices,
+ sampling,
+ paint,
+ constraint);
+ }
+
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+ void onDrawEdgeAAQuad(const SkRect& rect,
+ const SkPoint clip[4],
+ SkCanvas::QuadAAFlags aaFlags,
+ const SkColor4f& color,
+ SkBlendMode mode) override {}
+#else
+ void onDrawEdgeAAQuad(const SkRect& rect,
+ const SkPoint clip[4],
+ SkCanvas::QuadAAFlags aaFlags,
+ const SkColor4f& color,
+ SkBlendMode mode) override {
+ fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAQuad(rect,
+ clip,
+ aaFlags,
+ color,
+ mode);
+ }
+#endif
+
+ void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
+ static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
+ static constexpr char kSurfaceID[] = "SurfaceID";
+ 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
+ // to the layer identified by the ID. 'rect' indicates the dirty area to update
+ // (and indicates the layer size if this command is introducing a new layer id).
+ fNextDrawToLayerID = std::stoi(tokens[1].c_str());
+ if (fOffscreenLayers->find(fNextDrawToLayerID) == fOffscreenLayers->end()) {
+ SkASSERT(rect.left() == 0 && rect.top() == 0);
+ SkISize size = {SkScalarCeilToInt(rect.right()),
+ SkScalarCeilToInt(rect.bottom())};
+ (*fOffscreenLayers)[fNextDrawToLayerID].fDimensions = size;
+ }
+ // The next draw picture will notice that fNextDrawLayerID is set and redirect
+ // the picture to the offscreen layer.
+ return;
+ } else if (tokens[0].equals(kSurfaceID)) {
+ // Indicates that the following drawImageRect should draw an offscreen layer
+ // to this layer.
+ fNextDrawImageFromLayerID = std::stoi(tokens[1].c_str());
+ return;
+ }
+ }
+ }
+
+ void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
+ fRecorder.getRecordingCanvas()->private_draw_shadow_rec(path, rec);
+ }
+
+ void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+ fRecorder.getRecordingCanvas()->drawDrawable(drawable, matrix);
+ }
+
+ void onDrawPicture(const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint) override {
+ if (fNextDrawToLayerID != -1) {
+ SkASSERT(!matrix);
+ SkASSERT(!paint);
+ CmdRecordCanvas sc(&fOffscreenLayers->at(fNextDrawToLayerID), fOffscreenLayers);
+ picture->playback(&sc);
+ fNextDrawToLayerID = -1;
+ return;
+ }
+ if (paint) {
+ this->saveLayer(nullptr, paint);
+ }
+ if (matrix) {
+ this->save();
+ this->concat(*matrix);
+ }
+
+ picture->playback(this);
+
+ if (matrix) {
+ this->restore();
+ }
+ if (paint) {
+ this->restore();
+ }
+ fRecorder.getRecordingCanvas()->drawPicture(picture, matrix, paint);
+ }
+
+private:
+ void recordPicCmd() {
+ auto cmd = std::make_unique<PicCmd>();
+ cmd->fContent = fRecorder.finishRecordingAsPicture();
+ if (cmd->fContent) {
+ fDst->fCmds.push_back(std::move(cmd));
+ }
+ // Set up to accumulate again.
+ fRecorder.beginRecording(SkRect::Make(fDst->fDimensions));
+ }
+
+ SkPictureRecorder fRecorder; // accumulates draws until we draw an offscreen into this layer.
+ Layer* fDst = nullptr;
+ int fNextDrawToLayerID = -1;
+ int fNextDrawImageFromLayerID = -1;
+ LayerMap* fOffscreenLayers = nullptr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+std::unique_ptr<MSKPPlayer> MSKPPlayer::Make(SkStreamSeekable* stream) {
+ auto deserialContext = std::make_unique<SkSharingDeserialContext>();
+ SkDeserialProcs procs;
+ procs.fImageProc = SkSharingDeserialContext::deserializeImage;
+ procs.fImageCtx = deserialContext.get();
+
+ int pageCount = SkMultiPictureDocumentReadPageCount(stream);
+ if (!pageCount) {
+ return nullptr;
+ }
+ std::vector<SkDocumentPage> pages(pageCount);
+ if (!SkMultiPictureDocumentRead(stream, pages.data(), pageCount, &procs)) {
+ return nullptr;
+ }
+ std::unique_ptr<MSKPPlayer> result(new MSKPPlayer);
+ result->fRootLayers.reserve(pages.size());
+ for (const auto& page : pages) {
+ SkISize dims = {SkScalarCeilToInt(page.fSize.width()),
+ SkScalarCeilToInt(page.fSize.height())};
+ result->fRootLayers.emplace_back();
+ result->fRootLayers.back().fDimensions = dims;
+ result->fMaxDimensions.fWidth = std::max(dims.width() , result->fMaxDimensions.width() );
+ result->fMaxDimensions.fHeight = std::max(dims.height(), result->fMaxDimensions.height());
+ CmdRecordCanvas sc(&result->fRootLayers.back(), &result->fOffscreenLayers);
+ page.fPicture->playback(&sc);
+ }
+ return result;
+}
+
+MSKPPlayer::~MSKPPlayer() = default;
+
+SkISize MSKPPlayer::frameDimensions(int i) const {
+ if (i < 0 || i >= this->numFrames()) {
+ return {-1, -1};
+ }
+ return fRootLayers[i].fDimensions;
+}
+
+bool MSKPPlayer::playFrame(SkCanvas* canvas, int i) {
+ if (i < 0 || i >= this->numFrames()) {
+ return false;
+ }
+
+ // Find the first offscreen layer that has a valid surface. If it's recording context
+ // differs from the passed canvas's then reset all the layers. Playback will
+ // automatically allocate new surfaces for offscreen layers as they're encountered.
+ for (const auto& ols : fOffscreenLayerStates) {
+ const LayerState& state = ols.second;
+ if (state.fSurface) {
+ if (state.fSurface->recordingContext() != canvas->recordingContext()) {
+ this->resetLayers();
+ }
+ break;
+ }
+ }
+
+ // Replay all the commands for this frame to the caller's canvas.
+ const Layer& layer = fRootLayers[i];
+ for (const auto& cmd : layer.fCmds) {
+ cmd->draw(canvas, fOffscreenLayers, &fOffscreenLayerStates);
+ }
+ return true;
+}
+
+void MSKPPlayer::resetLayers() { fOffscreenLayerStates.clear(); }
diff --git a/tools/MSKPPlayer.h b/tools/MSKPPlayer.h
new file mode 100644
index 0000000..6c77574
--- /dev/null
+++ b/tools/MSKPPlayer.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef MSKPPlayer_DEFINED
+#define MSKPPlayer_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
+
+#include <unordered_map>
+#include <vector>
+
+class SkCanvas;
+class SkStreamSeekable;
+class SkSurface;
+
+/**
+ * Plays frames/pages of a MSKP to a canvas. This class uses the term "frame" as though the MSKP
+ * contains an animation, though it could indeed contain pages of a static document.
+ */
+class MSKPPlayer {
+public:
+ ~MSKPPlayer();
+
+ /** Make a player from a MSKP stream, or null if stream can't be read as MSKP. */
+ static std::unique_ptr<MSKPPlayer> Make(SkStreamSeekable* stream);
+
+ /** Maximum width and height across all frames. */
+ SkISize maxDimensions() const { return fMaxDimensions; }
+
+ /** Total number of frames. */
+ int numFrames() const { return static_cast<int>(fRootLayers.size()); }
+
+ /** Size of an individual frame. */
+ SkISize frameDimensions(int i) const;
+
+ /**
+ * Plays a frame into the passed canvas. Frames can be randomly accessed. Offscreen layers are
+ * incrementally updated from their current state to the state required for the frame
+ * (redrawing from scratch if their current state is ahead of the passed frame index).
+ */
+ bool playFrame(SkCanvas* canvas, int i);
+
+ /** Destroys any cached offscreen layers. */
+ void resetLayers();
+
+private:
+ MSKPPlayer() = default;
+ // noncopyable, nonmoveable.
+ MSKPPlayer(const MSKPPlayer&) = delete;
+ MSKPPlayer(MSKPPlayer&&) = delete;
+ MSKPPlayer& operator=(const MSKPPlayer&) = delete;
+ MSKPPlayer& operator=(MSKPPlayer&&) = delete;
+
+ // Cmds are used to draw content to the frame root layer and to offscreen layers.
+ struct Cmd;
+ // Draws a SkPicture.
+ struct PicCmd;
+ // Draws another layer. Stores the ID of the layer to draw and what command index on that
+ // layer should be current when the layer is drawn. The layer contents are updated to the
+ // stored command index before the layer is drawn.
+ struct DrawLayerCmd;
+
+ // The commands for a root/offscreen layer and dimensions of the layer.
+ struct Layer {
+ Layer() = default;
+ Layer(Layer&&) = default;
+ SkISize fDimensions;
+ std::vector<std::unique_ptr<Cmd>> fCmds;
+ };
+
+ // Playback state of layer: the last command index drawn to it and the SkSurface with contents.
+ struct LayerState {
+ size_t fCurrCmd = -1;
+ sk_sp<SkSurface> fSurface;
+ };
+
+ // MSKP layer ID -> Layer
+ using LayerMap = std::unordered_map<int, Layer>;
+ // MSKP layer ID -> LayerState
+ using LayerStateMap = std::unordered_map<int, LayerState>;
+
+ /**
+ * A SkCanvas that consumes the SkPicture and records Cmds into a Layer. It will spawn
+ * additional Layers and record nested SkPictures into those using additional CmdRecordCanvas
+ * CmdRecordCanvas instances. It needs access to fOffscreenLayers to create and update Layer
+ * structs for offscreen layers.
+ */
+ class CmdRecordCanvas;
+
+ SkISize fMaxDimensions = {0, 0}; // Max dimensions across all frames.
+ LayerMap fOffscreenLayers; // All the offscreen layers for all frames.
+ LayerStateMap fOffscreenLayerStates; // Current surfaces and command idx for offscreen
+ // layers
+ std::vector<Layer> fRootLayers; // One root layer for each frame.
+};
+
+#endif
diff --git a/tools/viewer/MSKPSlide.cpp b/tools/viewer/MSKPSlide.cpp
new file mode 100644
index 0000000..d8019b2
--- /dev/null
+++ b/tools/viewer/MSKPSlide.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/viewer/MSKPSlide.h"
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkStream.h"
+#include "src/core/SkOSFile.h"
+
+MSKPSlide::MSKPSlide(const SkString& name, const SkString& path)
+ : MSKPSlide(name, SkStream::MakeFromFile(path.c_str())) {}
+
+MSKPSlide::MSKPSlide(const SkString& name, std::unique_ptr<SkStreamSeekable> stream)
+ : fStream(std::move(stream)) {
+ fName = name;
+}
+
+SkISize MSKPSlide::getDimensions() const {
+ return fPlayer ? fPlayer->maxDimensions() : SkISize{0, 0};
+}
+
+void MSKPSlide::draw(SkCanvas* canvas) {
+ if (fPlayer) {
+ fPlayer->playFrame(canvas, fFrame);
+ }
+}
+
+bool MSKPSlide::animate(double nanos) {
+ if (!fPlayer) {
+ return false;
+ }
+ double elapsed = nanos - fLastFrameTime;
+ double frameTime = 1E9/fFPS;
+ int framesToAdvance = elapsed/frameTime;
+ fFrame = (fFrame + framesToAdvance)%fPlayer->numFrames();
+ // Instead of just adding elapsed, note the time when this frame should have begun.
+ fLastFrameTime += framesToAdvance*frameTime;
+ return framesToAdvance%fPlayer->numFrames() != 0;
+}
+
+void MSKPSlide::load(SkScalar, SkScalar) {
+ if (!fStream) {
+ SkDebugf("No skp stream for slide %s.\n", fName.c_str());
+ return;
+ }
+ fStream->rewind();
+ fPlayer = MSKPPlayer::Make(fStream.get());
+ if (!fPlayer) {
+ SkDebugf("Could parse MSKP from stream for slide %s.\n", fName.c_str());
+ return;
+ }
+}
+
+void MSKPSlide::unload() { fPlayer.reset(); }
+
+void MSKPSlide::gpuTeardown() { fPlayer->resetLayers(); }
+
diff --git a/tools/viewer/MSKPSlide.h b/tools/viewer/MSKPSlide.h
new file mode 100644
index 0000000..41b3c9c
--- /dev/null
+++ b/tools/viewer/MSKPSlide.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef MSKPSlide_DEFINED
+#define MSKPSlide_DEFINED
+
+#include "tools/MSKPPlayer.h"
+#include "tools/viewer/Slide.h"
+
+class SkStreamSeekable;
+
+class MSKPSlide : public Slide {
+public:
+ MSKPSlide(const SkString& name, const SkString& path);
+ MSKPSlide(const SkString& name, std::unique_ptr<SkStreamSeekable>);
+
+ SkISize getDimensions() const override;
+
+ void draw(SkCanvas* canvas) override;
+ bool animate(double nanos) override;
+ void load(SkScalar winWidth, SkScalar winHeight) override;
+ void unload() override;
+ void gpuTeardown() override;
+
+private:
+ std::unique_ptr<SkStreamSeekable> fStream;
+ std::unique_ptr<MSKPPlayer> fPlayer;
+ int fFrame = 0;
+ int fFPS = 15; // TODO: make this adjustable. This happens to work well for calendar.mskp
+ double fLastFrameTime = 0;
+
+ using INHERITED = Slide;
+};
+
+#endif
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index c30615c..82a005e 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -5,6 +5,8 @@
* found in the LICENSE file.
*/
+#include "tools/viewer/Viewer.h"
+
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkGraphics.h"
@@ -43,13 +45,13 @@
#include "tools/viewer/BisectSlide.h"
#include "tools/viewer/GMSlide.h"
#include "tools/viewer/ImageSlide.h"
+#include "tools/viewer/MSKPSlide.h"
#include "tools/viewer/ParticlesSlide.h"
#include "tools/viewer/SKPSlide.h"
#include "tools/viewer/SampleSlide.h"
#include "tools/viewer/SkSLSlide.h"
#include "tools/viewer/SlideDir.h"
#include "tools/viewer/SvgSlide.h"
-#include "tools/viewer/Viewer.h"
#include <cstdlib>
#include <map>
@@ -150,19 +152,18 @@
"it is skipped unless some list entry starts with ~");
#if defined(SK_BUILD_FOR_ANDROID)
- static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
- static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
- static DEFINE_string(lotties, "/data/local/tmp/lotties",
- "Directory to read (Bodymovin) jsons from.");
- static DEFINE_string(rives, "/data/local/tmp/rives",
- "Directory to read Rive (Flare) files from.");
+# define PATH_PREFIX "/data/local/tmp/"
#else
- static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
- static DEFINE_string(skps, "skps", "Directory to read skps from.");
- static DEFINE_string(lotties, "lotties", "Directory to read (Bodymovin) jsons from.");
- static DEFINE_string(rives, "rives", "Directory to read Rive (Flare) files from.");
+# define PATH_PREFIX ""
#endif
+static DEFINE_string(jpgs , PATH_PREFIX "jpgs" , "Directory to read jpgs from.");
+static DEFINE_string(skps , PATH_PREFIX "skps" , "Directory to read skps from.");
+static DEFINE_string(mskps , PATH_PREFIX "mskps" , "Directory to read mskps from.");
+static DEFINE_string(lotties, PATH_PREFIX "lotties", "Directory to read (Bodymovin) jsons from.");
+static DEFINE_string(rives , PATH_PREFIX "rives" , "Directory to read Rive (Flare) files from.");
+#undef PATH_PREFIX
+
static DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file.");
static DEFINE_int_2(threads, j, -1,
@@ -735,6 +736,10 @@
const CommandLineFlags::StringArray& fFlags;
const SlideFactory fFactory;
} gExternalSlidesInfo[] = {
+ { ".mskp", "mskp-dir", FLAGS_mskps,
+ [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
+ return sk_make_sp<MSKPSlide>(name, path);}
+ },
{ ".skp", "skp-dir", FLAGS_skps,
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
return sk_make_sp<SKPSlide>(name, path);}