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