xref: /third_party/skia/tools/viewer/MSKPSlide.cpp (revision cb93a386)
1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2021 Google LLC
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/viewer/MSKPSlide.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h"
13cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
14cb93a386Sopenharmony_ci#include "imgui.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ciMSKPSlide::MSKPSlide(const SkString& name, const SkString& path)
17cb93a386Sopenharmony_ci        : MSKPSlide(name, SkStream::MakeFromFile(path.c_str())) {}
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ciMSKPSlide::MSKPSlide(const SkString& name, std::unique_ptr<SkStreamSeekable> stream)
20cb93a386Sopenharmony_ci        : fStream(std::move(stream)) {
21cb93a386Sopenharmony_ci    fName = name;
22cb93a386Sopenharmony_ci}
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ciSkISize MSKPSlide::getDimensions() const {
25cb93a386Sopenharmony_ci    return fPlayer ? fPlayer->maxDimensions() : SkISize{0, 0};
26cb93a386Sopenharmony_ci}
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_civoid MSKPSlide::draw(SkCanvas* canvas) {
29cb93a386Sopenharmony_ci    if (!fPlayer) {
30cb93a386Sopenharmony_ci        ImGui::Text("Could not read mskp file %s.\n", fName.c_str());
31cb93a386Sopenharmony_ci        return;
32cb93a386Sopenharmony_ci    }
33cb93a386Sopenharmony_ci    ImGui::Begin("MSKP");
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci    ImGui::BeginGroup();
36cb93a386Sopenharmony_ci    // Play/Pause button
37cb93a386Sopenharmony_ci    if (ImGui::Button(fPaused ? "Play " : "Pause")) {
38cb93a386Sopenharmony_ci        fPaused = !fPaused;
39cb93a386Sopenharmony_ci        if (fPaused) {
40cb93a386Sopenharmony_ci            // This will ensure that when playback is unpaused we start on the current frame.
41cb93a386Sopenharmony_ci            fLastFrameTime = -1;
42cb93a386Sopenharmony_ci        }
43cb93a386Sopenharmony_ci    }
44cb93a386Sopenharmony_ci    // Control the frame rate of MSKP playback
45cb93a386Sopenharmony_ci    ImGui::Text("FPS: ");                   ImGui::SameLine();
46cb93a386Sopenharmony_ci    ImGui::RadioButton(  "1", &fFPS,    1); ImGui::SameLine();
47cb93a386Sopenharmony_ci    ImGui::RadioButton( "15", &fFPS,   15); ImGui::SameLine();
48cb93a386Sopenharmony_ci    ImGui::RadioButton( "30", &fFPS,   30); ImGui::SameLine();
49cb93a386Sopenharmony_ci    ImGui::RadioButton( "60", &fFPS,   60); ImGui::SameLine();
50cb93a386Sopenharmony_ci    ImGui::RadioButton("120", &fFPS,  120); ImGui::SameLine();
51cb93a386Sopenharmony_ci    ImGui::RadioButton("1:1", &fFPS,   -1); // Draw one MSKP frame for each real viewer frame.
52cb93a386Sopenharmony_ci    if (fFPS < 0) {
53cb93a386Sopenharmony_ci        // Like above, will cause onAnimate() to resume at current frame when FPS is changed
54cb93a386Sopenharmony_ci        // back to another frame rate.
55cb93a386Sopenharmony_ci        fLastFrameTime = -1;
56cb93a386Sopenharmony_ci    }
57cb93a386Sopenharmony_ci    // Frame control. Slider and +/- buttons. Ctrl-Click slider to type frame number.
58cb93a386Sopenharmony_ci    ImGui::Text("Frame:");
59cb93a386Sopenharmony_ci    ImGui::SameLine();
60cb93a386Sopenharmony_ci    ImGui::PushButtonRepeat(true);  // Enable click-and-hold for frame arrows.
61cb93a386Sopenharmony_ci    int oldFrame = fFrame;
62cb93a386Sopenharmony_ci    if (ImGui::ArrowButton("-mksp_frame", ImGuiDir_Left)) {
63cb93a386Sopenharmony_ci        fFrame = (fFrame + fPlayer->numFrames() - 1)%fPlayer->numFrames();
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci    ImGui::SameLine();
66cb93a386Sopenharmony_ci    if (ImGui::SliderInt("##msk_frameslider", &fFrame, 0, fPlayer->numFrames()-1, "% 3d")) {
67cb93a386Sopenharmony_ci        fFrame = SkTPin(fFrame, 0, fPlayer->numFrames() - 1);
68cb93a386Sopenharmony_ci    }
69cb93a386Sopenharmony_ci    ImGui::SameLine();
70cb93a386Sopenharmony_ci    if (ImGui::ArrowButton("+mskp_frame", ImGuiDir_Right)) {
71cb93a386Sopenharmony_ci        fFrame = (fFrame + 1)%fPlayer->numFrames();
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci    if (fFrame != oldFrame) {
74cb93a386Sopenharmony_ci        // When manually adjusting frames force layers to redraw.
75cb93a386Sopenharmony_ci        this->redrawLayers();
76cb93a386Sopenharmony_ci    }
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_ci    ImGui::PopButtonRepeat();
79cb93a386Sopenharmony_ci    ImGui::EndGroup();
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci    ImGui::BeginGroup();
82cb93a386Sopenharmony_ci    ImGui::Checkbox("Show Frame Bounds", &fShowFrameBounds);
83cb93a386Sopenharmony_ci    ImGui::SetNextItemWidth(200);
84cb93a386Sopenharmony_ci    ImGui::ColorPicker4("background", fBackgroundColor, ImGuiColorEditFlags_AlphaBar);
85cb93a386Sopenharmony_ci    // ImGui lets user enter out of range values by typing.
86cb93a386Sopenharmony_ci    for (float& component : fBackgroundColor) {
87cb93a386Sopenharmony_ci        component = SkTPin(component, 0.f, 1.f);
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci    ImGui::EndGroup();
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci    // UI for visualizing contents of offscreen layers.
92cb93a386Sopenharmony_ci    ImGui::Text("Offscreen Layers "); ImGui::SameLine();
93cb93a386Sopenharmony_ci    ImGui::Checkbox("List All Layers", &fListAllLayers);
94cb93a386Sopenharmony_ci    ImGui::RadioButton("root", &fDrawLayerID, -1);
95cb93a386Sopenharmony_ci    const std::vector<int>& layerIDs = fListAllLayers ? fAllLayerIDs : fFrameLayerIDs[fFrame];
96cb93a386Sopenharmony_ci    fLayerIDStrings.resize(layerIDs.size());
97cb93a386Sopenharmony_ci    for (size_t i = 0; i < layerIDs.size(); ++i) {
98cb93a386Sopenharmony_ci        fLayerIDStrings[i] = SkStringPrintf("%d", layerIDs[i]);
99cb93a386Sopenharmony_ci        ImGui::RadioButton(fLayerIDStrings[i].c_str(), &fDrawLayerID, layerIDs[i]);
100cb93a386Sopenharmony_ci    }
101cb93a386Sopenharmony_ci    ImGui::End();
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    auto bounds = SkIRect::MakeSize(fPlayer->frameDimensions(fFrame));
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci    if (fShowFrameBounds) {
106cb93a386Sopenharmony_ci        SkPaint boundsPaint;
107cb93a386Sopenharmony_ci        boundsPaint.setStyle(SkPaint::kStroke_Style);
108cb93a386Sopenharmony_ci        boundsPaint.setColor(SK_ColorRED);
109cb93a386Sopenharmony_ci        boundsPaint.setStrokeWidth(0.f);
110cb93a386Sopenharmony_ci        boundsPaint.setAntiAlias(true);
111cb93a386Sopenharmony_ci        // Outset so that at default scale we draw at pixel centers of the rows/cols surrounding the
112cb93a386Sopenharmony_ci        // bounds.
113cb93a386Sopenharmony_ci        canvas->drawRect(SkRect::Make(bounds).makeOutset(0.5f, 0.5f), boundsPaint);
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    canvas->save();
117cb93a386Sopenharmony_ci    if (fDrawLayerID >= 0) {
118cb93a386Sopenharmony_ci        // clip out the root layer content, but still call playFrame so layer contents are updated
119cb93a386Sopenharmony_ci        // to fFrame.
120cb93a386Sopenharmony_ci        bounds = SkIRect::MakeEmpty();
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci    canvas->clipIRect(bounds);
123cb93a386Sopenharmony_ci    canvas->clear(SkColor4f{fBackgroundColor[0],
124cb93a386Sopenharmony_ci                            fBackgroundColor[1],
125cb93a386Sopenharmony_ci                            fBackgroundColor[2],
126cb93a386Sopenharmony_ci                            fBackgroundColor[3]});
127cb93a386Sopenharmony_ci    fPlayer->playFrame(canvas, fFrame);
128cb93a386Sopenharmony_ci    canvas->restore();
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci    if (fDrawLayerID >= 0) {
131cb93a386Sopenharmony_ci        if (sk_sp<SkImage> layerImage = fPlayer->layerSnapshot(fDrawLayerID)) {
132cb93a386Sopenharmony_ci            canvas->save();
133cb93a386Sopenharmony_ci            canvas->clipIRect(SkIRect::MakeSize(layerImage->dimensions()));
134cb93a386Sopenharmony_ci            canvas->clear(SkColor4f{fBackgroundColor[0],
135cb93a386Sopenharmony_ci                                    fBackgroundColor[1],
136cb93a386Sopenharmony_ci                                    fBackgroundColor[2],
137cb93a386Sopenharmony_ci                                    fBackgroundColor[3]});
138cb93a386Sopenharmony_ci            canvas->drawImage(std::move(layerImage), 0, 0);
139cb93a386Sopenharmony_ci            canvas->restore();
140cb93a386Sopenharmony_ci        }
141cb93a386Sopenharmony_ci        return;
142cb93a386Sopenharmony_ci    }
143cb93a386Sopenharmony_ci}
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_cibool MSKPSlide::animate(double nanos) {
146cb93a386Sopenharmony_ci    if (!fPlayer || fPaused) {
147cb93a386Sopenharmony_ci        return false;
148cb93a386Sopenharmony_ci    }
149cb93a386Sopenharmony_ci    if (fLastFrameTime < 0) {
150cb93a386Sopenharmony_ci        // We're coming off being paused or switching from 1:1 mode to steady FPS. Advance 1 frame
151cb93a386Sopenharmony_ci        // and reset the frame time to start accumulating time from now.
152cb93a386Sopenharmony_ci        fFrame = (fFrame + 1)%fPlayer->numFrames();
153cb93a386Sopenharmony_ci        fLastFrameTime = nanos;
154cb93a386Sopenharmony_ci        return this->fPlayer->numFrames() > 1;
155cb93a386Sopenharmony_ci    }
156cb93a386Sopenharmony_ci    if (fFPS < 0) {
157cb93a386Sopenharmony_ci        // 1:1 mode. Always draw the next frame on each animation cycle.
158cb93a386Sopenharmony_ci        fFrame = (fFrame + 1)%fPlayer->numFrames();
159cb93a386Sopenharmony_ci        return this->fPlayer->numFrames() > 1;
160cb93a386Sopenharmony_ci    }
161cb93a386Sopenharmony_ci    double elapsed = nanos - fLastFrameTime;
162cb93a386Sopenharmony_ci    double frameTime = 1E9/fFPS;
163cb93a386Sopenharmony_ci    int framesToAdvance = elapsed/frameTime;
164cb93a386Sopenharmony_ci    fFrame = fFrame + framesToAdvance;
165cb93a386Sopenharmony_ci    if (fFrame >= fPlayer->numFrames()) {
166cb93a386Sopenharmony_ci        this->redrawLayers();
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci    fFrame %= fPlayer->numFrames();
169cb93a386Sopenharmony_ci    // Instead of just adding elapsed, note the time when this frame should have begun.
170cb93a386Sopenharmony_ci    fLastFrameTime += framesToAdvance*frameTime;
171cb93a386Sopenharmony_ci    return framesToAdvance > 0;
172cb93a386Sopenharmony_ci}
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_civoid MSKPSlide::load(SkScalar, SkScalar) {
175cb93a386Sopenharmony_ci    if (!fStream) {
176cb93a386Sopenharmony_ci        return;
177cb93a386Sopenharmony_ci    }
178cb93a386Sopenharmony_ci    fStream->rewind();
179cb93a386Sopenharmony_ci    fPlayer = MSKPPlayer::Make(fStream.get());
180cb93a386Sopenharmony_ci    if (!fPlayer) {
181cb93a386Sopenharmony_ci        return;
182cb93a386Sopenharmony_ci    }
183cb93a386Sopenharmony_ci    fAllLayerIDs = fPlayer->layerIDs();
184cb93a386Sopenharmony_ci    fFrameLayerIDs.clear();
185cb93a386Sopenharmony_ci    fFrameLayerIDs.resize(fPlayer->numFrames());
186cb93a386Sopenharmony_ci    for (int i = 0; i < fPlayer->numFrames(); ++i) {
187cb93a386Sopenharmony_ci        fFrameLayerIDs[i] = fPlayer->layerIDs(i);
188cb93a386Sopenharmony_ci    }
189cb93a386Sopenharmony_ci}
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_civoid MSKPSlide::unload() { fPlayer.reset(); }
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_civoid MSKPSlide::gpuTeardown() { fPlayer->resetLayers(); }
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_civoid MSKPSlide::redrawLayers() {
196cb93a386Sopenharmony_ci    if (fDrawLayerID >= 0) {
197cb93a386Sopenharmony_ci        // Completely reset the layers so that we won't see content from later frames on layers
198cb93a386Sopenharmony_ci        // that haven't been visited from frames 0..fFrames.
199cb93a386Sopenharmony_ci        fPlayer->resetLayers();
200cb93a386Sopenharmony_ci    } else {
201cb93a386Sopenharmony_ci        // Just rewind layers so that we redraw any layer from scratch on the next frame that
202cb93a386Sopenharmony_ci        // updates it. Important for benchmarking/profiling as otherwise if a layer is only
203cb93a386Sopenharmony_ci        // drawn once in the frame sequence then it will never be updated after the first play
204cb93a386Sopenharmony_ci        // through. This doesn't reallocate the layer backing stores.
205cb93a386Sopenharmony_ci        fPlayer->rewindLayers();
206cb93a386Sopenharmony_ci    }
207cb93a386Sopenharmony_ci}
208