| /* |
| * Copyright 2025 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/CaptureSlide.h" |
| |
| #include "imgui.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkClipOp.h" |
| #include "include/core/SkData.h" |
| #include "include/core/SkPicture.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkStream.h" |
| #include "include/private/SkDebug.h" |
| #include "src/capture/SkCapture.h" |
| #include "src/core/SkBigPicture.h" |
| #include "src/core/SkRecord.h" |
| #include "src/utils/SkJSONWriter.h" |
| #include "tools/ProcsUtils.h" |
| #include "tools/UrlDataManager.h" |
| #include "tools/debugger/DebugCanvas.h" |
| #include "tools/debugger/DrawCommand.h" |
| |
| #include <string> |
| |
| const char* RecordTypeNames[] = { |
| #define CASE(T) #T, |
| SK_RECORD_TYPES(CASE) |
| #undef CASE |
| }; |
| |
| CaptureSlide::CaptureSlide(const SkString& name, const SkString& path) { |
| auto data = SkData::MakeFromFileName(path.c_str()); |
| fCapture = SkCapture::MakeFromData(data); |
| if (fCapture) { |
| fMetadata = fCapture->getMetadata(); |
| } else { |
| SkDebugf("Couldn't load capture %s", path.c_str()); |
| } |
| } |
| |
| CaptureSlide::~CaptureSlide() {} |
| |
| void drawCommandHistory(const DebugCanvas* debugCanvas) { |
| static int currentCommandIdx = 0; |
| |
| ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_Appearing); |
| if (!ImGui::Begin("Command History")) { |
| ImGui::End(); |
| return; |
| } |
| |
| ImGui::Text("Total Commands: %d", debugCanvas->getSize()); |
| ImGui::Separator(); |
| |
| const char* previewValue = "Select a command..."; |
| if (currentCommandIdx >= 0 && currentCommandIdx < debugCanvas->getSize()) { |
| DrawCommand* cmd = debugCanvas->getDrawCommandAt(currentCommandIdx); |
| previewValue = DrawCommand::GetCommandString(cmd->getOpType()); |
| } |
| |
| if (ImGui::BeginCombo("Select Command", previewValue)) { |
| for (int i = 0; i < debugCanvas->getSize(); ++i) { |
| const bool isSelected = (currentCommandIdx == i); |
| |
| DrawCommand* cmd = debugCanvas->getDrawCommandAt(i); |
| const char* commandName = DrawCommand::GetCommandString(cmd->getOpType()); |
| |
| if (ImGui::Selectable(commandName, isSelected)) { |
| currentCommandIdx = i; |
| } |
| |
| if (isSelected) { |
| ImGui::SetItemDefaultFocus(); |
| } |
| } |
| ImGui::EndCombo(); |
| } |
| |
| if (currentCommandIdx >= 0 && currentCommandIdx < debugCanvas->getSize()) { |
| ImGui::Separator(); |
| ImGui::Text("JSON for Command #%d:", currentCommandIdx); |
| |
| DrawCommand* cmd = debugCanvas->getDrawCommandAt(currentCommandIdx); |
| |
| SkDynamicMemoryWStream stream; |
| SkJSONWriter writer(&stream, ToolUtils::default_serial_procs(), SkJSONWriter::Mode::kPretty); |
| UrlDataManager urlDataManager(SkString("data")); |
| writer.beginObject(); |
| cmd->toJSON(writer, urlDataManager); |
| writer.endObject(); |
| |
| writer.flush(); |
| |
| stream.write8(0); |
| sk_sp<SkData> data = stream.detachAsData(); |
| char* dataString = const_cast<char*>(static_cast<const char*>(data->data())); |
| |
| ImGui::InputTextMultiline("###json_output", |
| dataString, |
| data->size(), |
| ImVec2(-1, -1), |
| ImGuiInputTextFlags_ReadOnly); |
| } |
| ImGui::End(); |
| } |
| |
| void drawMetadata(int currentPictureIndex) { |
| ImGui::Begin("Capture Info"); |
| ImGui::Text("Current Index: %d", currentPictureIndex); |
| ImGui::End(); |
| } |
| |
| void CaptureSlide::draw(SkCanvas* canvas) { |
| if (!fCapture) { |
| return; |
| } |
| |
| auto focusPicture = fCapture->getPicture(fCurrentPictureIndex); |
| |
| auto bounds = focusPicture->cullRect().roundOut(); |
| DebugCanvas debugCanvas(bounds.width(), bounds.height()); |
| focusPicture->playback(&debugCanvas); |
| |
| canvas->clipIRect(bounds, SkClipOp::kIntersect); |
| canvas->drawPicture(fCapture->getPicture(fCurrentPictureIndex)); |
| |
| drawMetadata(fCurrentPictureIndex); |
| |
| drawCommandHistory(&debugCanvas); |
| } |
| |
| bool CaptureSlide::animate(double) { |
| if (fInvalidate) { |
| fInvalidate = false; |
| return true; |
| } |
| return fInvalidate; |
| } |
| |
| void CaptureSlide::load(SkScalar, SkScalar) {} |
| |
| void CaptureSlide::unload() { fCapture.reset(nullptr); } |
| |
| SkISize CaptureSlide::getDimensions() const { return {0, 0}; } |
| |
| bool CaptureSlide::onChar(SkUnichar c) { |
| switch (c) { |
| case 'N': |
| fCurrentPictureIndex = (fCurrentPictureIndex + 1) % fMetadata.numPictures; |
| fInvalidate = true; |
| return true; |
| case 'P': |
| fCurrentPictureIndex = |
| (fCurrentPictureIndex + fMetadata.numPictures - 1) % fMetadata.numPictures; |
| fInvalidate = true; |
| return true; |
| case 'F': |
| ImGui::GetIO().FontGlobalScale = ImGui::GetIO().FontGlobalScale + 0.1f; |
| return true; |
| case 'S': |
| ImGui::GetIO().FontGlobalScale = ImGui::GetIO().FontGlobalScale - 0.1f; |
| return true; |
| } |
| |
| return Slide::onChar(c); |
| } |