[Scummvm-git-logs] scummvm master -> 8af01f9be595d3bf8bafbe29110a35cf18df9453
sev-
noreply at scummvm.org
Tue Apr 28 17:52:23 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
8af01f9be5 DIRECTOR: DT: Add Image and text viewer window
Commit: 8af01f9be595d3bf8bafbe29110a35cf18df9453
https://github.com/scummvm/scummvm/commit/8af01f9be595d3bf8bafbe29110a35cf18df9453
Author: ramyak-sharma (76775021+ramyak-sharma at users.noreply.github.com)
Date: 2026-04-28T19:52:19+02:00
Commit Message:
DIRECTOR: DT: Add Image and text viewer window
Changed paths:
engines/director/debugger/debugtools.cpp
engines/director/debugger/dt-castdetails.cpp
engines/director/debugger/dt-internal.h
diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index a5f297dc42f..733d3c04d9f 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -754,6 +754,14 @@ void setTheme(int themeIndex) {
}
}
+// helper to draw Image Viewer
+void openImageViewer(ImGuiImage image, const Common::String &text, const Common::String &title) {
+ _state->_imageViewerState.image = image;
+ _state->_imageViewerState.text = text;
+ _state->_imageViewerState.title = title;
+ _state->_w.imageViewer = true;
+}
+
static void showSettings() {
if (!_state->_w.settings)
return;
@@ -913,6 +921,7 @@ void onImGuiRender() {
showChannels();
showCast();
showCastDetails();
+ showImageViewer();
showFuncList();
showScore();
showSearchBar();
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index 71f4226fc17..d275b5e460c 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -153,10 +153,184 @@ void drawBitmapCMprops(BitmapCastMember *member) {
}
}
+void showImageViewer() {
+ if (!_state->_w.imageViewer)
+ return;
+
+ auto state = &_state->_imageViewerState;
+
+ ImGuiImage imgID = state->image;
+ const Common::String &text = state->text;
+ Common::String title = state->title;
+
+ if (!imgID.id) {
+ _state->_w.imageViewer = false;
+ return;
+ }
+
+ // Recompute cache ig required
+ if (text != state->cachedRaw) {
+ state->cachedRaw = text;
+ state->cachedNormalized = text;
+ state->cachedNormalized.replace('\r', '\n');
+
+ size_t needed = state->cachedNormalized.size() + 1;
+
+ if (needed > state->bufferSize) {
+ free(state->buffer);
+ state->buffer = (char *)malloc(needed);
+ state->bufferSize = needed;
+ }
+
+ memcpy(state->buffer, state->cachedNormalized.c_str(), needed);
+ }
+
+ static float zoom = 1.0f;
+ static bool fitToWindow = true;
+ static ImVec2 offset(0.0f, 0.0f);
+
+ const char *windowTitle = title.empty() ? "Image Viewer" : title.c_str();
+
+ if (ImGui::Begin(windowTitle, &_state->_w.imageViewer,
+ ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
+
+ // Toolbar
+ if (ImGui::Button("Fit")) {
+ fitToWindow = true;
+ offset = ImVec2(0.0f, 0.0f);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("-")) {
+ zoom = MAX(0.1f, zoom - 0.1f);
+ fitToWindow = false;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("+")) {
+ zoom = MIN(10.0f, zoom + 0.1f);
+ fitToWindow = false;
+ }
+ ImGui::SameLine();
+ ImGui::Text("| %d x %d |", imgID.width, imgID.height);
+
+ if (!state->cachedNormalized.empty()) {
+ ImGui::SameLine();
+ if (ImGui::Button("Copy text"))
+ ImGui::SetClipboardText(state->cachedNormalized.c_str());
+ }
+
+ ImGui::Separator();
+
+ // Tabs
+ if (ImGui::BeginTabBar("##imageViewerTabs")) {
+
+ // view tab
+ if (ImGui::BeginTabItem("View")) {
+
+ ImVec2 canvasSize = ImGui::GetContentRegionAvail();
+
+ ImGui::BeginChild("##imageCanvas", canvasSize, false,
+ ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
+
+ ImVec2 size = { imgID.width * zoom, imgID.height * zoom };
+
+ if (fitToWindow && imgID.width > 0 && imgID.height > 0) {
+ float scaleX = canvasSize.x / (float)imgID.width;
+ float scaleY = canvasSize.y / (float)imgID.height;
+ zoom = MIN(scaleX, scaleY);
+ size = { imgID.width * zoom, imgID.height * zoom };
+ }
+
+ ImVec2 pos = ImGui::GetCursorPos();
+ ImVec2 imgPos = pos + ImVec2(
+ offset.x + (canvasSize.x - size.x) * 0.5f,
+ offset.y + (canvasSize.y - size.y) * 0.5f
+ );
+
+ ImGui::SetCursorPos(imgPos);
+
+ ImGui::GetWindowDrawList()->AddRect(
+ ImGui::GetCursorScreenPos(),
+ ImGui::GetCursorScreenPos() + size,
+ 0xFFFFFFFF
+ );
+
+ ImGui::Image(imgID.id, size);
+
+ ImGui::SetCursorPos(pos);
+ ImGui::InvisibleButton("##canvas", canvasSize);
+
+ if (ImGui::IsItemHovered()) {
+ ImGuiIO &io = ImGui::GetIO();
+ float speed = 30.0f;
+
+ // Ctrl + wheel -> zoom
+ if (io.KeyCtrl && io.MouseWheel != 0.0f) {
+ zoom = CLIP(zoom + io.MouseWheel * 0.1f, 0.1f, 10.0f);
+ fitToWindow = false;
+ } else {
+ // Vertical scroll -> vertical pan
+ if (io.MouseWheel != 0.0f && !io.KeyShift) {
+ offset.y += io.MouseWheel * speed;
+ fitToWindow = false;
+ }
+
+ // Horizontal scroll -> Shift + wheel
+ if (io.KeyShift && io.MouseWheel != 0.0f) {
+ offset.x += io.MouseWheel * speed;
+ fitToWindow = false;
+ }
+
+ // Trackpad / horizontal wheel
+ if (io.MouseWheelH != 0.0f) {
+ offset.x += io.MouseWheelH * speed;
+ fitToWindow = false;
+ }
+ }
+ }
+
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
+ offset.x += ImGui::GetIO().MouseDelta.x;
+ offset.y += ImGui::GetIO().MouseDelta.y;
+ fitToWindow = false;
+ }
+
+ ImGui::EndChild();
+ ImGui::EndTabItem();
+ }
+
+ // text tab
+ if (!state->cachedNormalized.empty() && ImGui::BeginTabItem("Text")) {
+
+ ImGui::BeginChild("##textPanel", ImGui::GetContentRegionAvail(), false,
+ ImGuiWindowFlags_HorizontalScrollbar);
+
+ ImGui::InputTextMultiline(
+ "##text",
+ state->buffer,
+ state->bufferSize,
+ ImGui::GetContentRegionAvail(),
+ ImGuiInputTextFlags_ReadOnly
+ );
+
+ ImGui::EndChild();
+ ImGui::EndTabItem();
+ }
+
+ ImGui::EndTabBar();
+ }
+ }
+
+ ImGui::End();
+}
+
void drawTextCMprops(TextCastMember *member) {
assert(member != nullptr);
if (ImGui::BeginTabItem("Text")) {
+ if (ImGui::Button("View")) {
+ openImageViewer(getTextID(member), member->getText());
+ }
+
if (ImGui::CollapsingHeader("Text Properties")) {
if (ImGui::BeginTable("##TextProperties", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index cad554ca13f..cf5e1d0c981 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -98,6 +98,7 @@ typedef struct ImGuiWindows {
bool watchedVars = false;
bool executionContext = false;
bool search = false;
+ bool imageViewer = false;
} ImGuiWindows;
@@ -254,6 +255,21 @@ typedef struct ImGuiState {
uint32 _lastTimeRefreshed = 0;
} _vars;
+ struct {
+ ImGuiImage image;
+ Common::String text; // empty = no text panel
+ Common::String title; // optional title
+
+ // cached normalized text
+ Common::String cachedRaw;
+ Common::String cachedNormalized;
+
+ // reusable buffer
+ char *buffer = nullptr;
+ size_t bufferSize = 0;
+
+ } _imageViewerState;
+
ImGuiWindows _w;
ImGuiWindows _savedW;
bool _wasHidden = false;
@@ -335,6 +351,7 @@ ImColor brightenColor(const ImColor &color, float factor);
Window *windowListCombo(Common::String *target);
Common::String formatHandlerName(int scriptId, int castId, Common::String handlerName, ScriptType scriptType, bool childScript);
void setTheme(int themeIndex);
+void openImageViewer(ImGuiImage image, const Common::String &text = "", const Common::String &title = "");
// helper to draw thin rectangles for table grid
inline void addThinRect(ImDrawList *dl, ImVec2 min, ImVec2 max, ImU32 col, float thickness = 0.1f) {
@@ -345,6 +362,7 @@ inline void addThinRect(ImDrawList *dl, ImVec2 min, ImVec2 max, ImU32 col, float
}
void showCast(); // dt-cast.cpp
+void showImageViewer(); // dt-castdetails.cpp
void showCastDetails(); // dt-castdetails.cpp
void showControlPanel();// dt-controlpanel.cpp
More information about the Scummvm-git-logs
mailing list