[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