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