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