[Scummvm-git-logs] scummvm master -> c360fbf218cbb009de6daac0d8e87bb6f9425934
sev-
noreply at scummvm.org
Sun Apr 19 14:24:23 UTC 2026
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
7e55f340c2 DIRECTOR: DT: Add Filmloop score viewer
c360fbf218 JANITORIAL: Move addThinRect to dt-internal.h to avoid duplication
Commit: 7e55f340c2c46d30021e632bdde016eea8e52ff7
https://github.com/scummvm/scummvm/commit/7e55f340c2c46d30021e632bdde016eea8e52ff7
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-04-19T16:24:18+02:00
Commit Message:
DIRECTOR: DT: Add Filmloop score viewer
Changed paths:
engines/director/debugger/debugtools.cpp
engines/director/debugger/dt-cast.cpp
engines/director/debugger/dt-castdetails.cpp
diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index ce8a089aff6..6d3cd7eb807 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -282,7 +282,7 @@ ImGuiImage getImageID(CastMember *castMember) {
bmpMember->load();
Picture *pic = bmpMember->_picture;
- if (!pic)
+ if (!pic || !pic->_surface.getPixels())
return {};
ImTextureID textureID = (ImTextureID)(intptr_t)g_system->getImGuiTexture(pic->_surface, pic->_palette, pic->_paletteColors);
diff --git a/engines/director/debugger/dt-cast.cpp b/engines/director/debugger/dt-cast.cpp
index 4ae6e758e35..9994701e4cd 100644
--- a/engines/director/debugger/dt-cast.cpp
+++ b/engines/director/debugger/dt-cast.cpp
@@ -166,8 +166,7 @@ void showCast() {
continue;
for (auto castMember : *cast->_loadedCast) {
- if (!castMember._value->isLoaded())
- continue;
+ castMember._value->load();
Common::String name(getDisplayName(castMember._value));
if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
@@ -185,6 +184,7 @@ void showCast() {
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
ImVec2(0, 32.f) // match row height
)) {
+ castMember._value->load();
_state->_castDetails._castMember = castMember._value;
_state->_w.castDetails = true;
}
@@ -261,8 +261,7 @@ void showCast() {
continue;
for (auto castMember : *cast->_loadedCast) {
- if (!castMember._value->isLoaded())
- continue;
+ castMember._value->load();
Common::String name(getDisplayName(castMember._value));
if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
@@ -333,6 +332,7 @@ void showCast() {
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)) {
// Cast Member Clicked
+ castMember._value->load();
_state->_castDetails._castMember = castMember._value; // Must set _castMember before making the caseDetails window visible to prevent null castMember
_state->_w.castDetails = true;
}
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index d9aa8d3a04c..d3ef3463e12 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -31,6 +31,10 @@
#include "director/castmember/richtext.h"
#include "director/castmember/shape.h"
#include "director/castmember/bitmap.h"
+#include "director/sprite.h"
+#include "director/castmember/filmloop.h"
+#include "director/frame.h"
+#include "director/score.h"
#include "director/types.h"
@@ -498,6 +502,247 @@ void drawCastProps(Cast *cast) {
}
}
+// Per filmloop curent frame, keyed by cast member pointer
+static Common::HashMap<FilmLoopCastMember *, int> _filmLoopCurrentFrame;
+
+void drawFilmLoopCMprops(FilmLoopCastMember *member) {
+ assert(member != nullptr);
+ if (ImGui::BeginTabItem("Film Loop")) {
+ if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
+ if (ImGui::BeginTable("##FilmLoopProps", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
+ showPropertyBool("looping", member->_looping);
+ showPropertyBool("enableSound", member->_enableSound);
+ showPropertyBool("crop", member->_crop);
+ showPropertyBool("center", member->_center);
+ if (member->_score)
+ showProperty("frameCount", "%d", (int)member->_score->_scoreCache.size());
+ ImGui::EndTable();
+ }
+ }
+
+ Score *score = member->_score;
+ if (!score || score->_scoreCache.empty()) {
+ ImGui::TextDisabled("No frames loaded");
+ ImGui::EndTabItem();
+ return;
+ }
+
+ int numFrames = (int)score->_scoreCache.size();
+
+ // find highest active channel
+ int maxChannel = 0;
+ for (int f = 0; f < numFrames; f++) {
+ Frame *frame = score->_scoreCache[f];
+ if (!frame) continue;
+ for (int ch = 1; ch < (int)frame->_sprites.size(); ch++) {
+ if (frame->_sprites[ch] && frame->_sprites[ch]->_castId.member != 0)
+ maxChannel = MAX(maxChannel, ch);
+ }
+ }
+
+ if (maxChannel == 0) {
+ ImGui::TextDisabled("No sprite data");
+ ImGui::EndTabItem();
+ return;
+ }
+
+ // Initialize current frame for this member if needed
+ if (!_filmLoopCurrentFrame.contains(member))
+ _filmLoopCurrentFrame[member] = 0;
+ int ¤tFrame = _filmLoopCurrentFrame[member];
+
+ const float cellW = 30.0f;
+ const float cellH = 18.0f;
+ const float labelW = 24.0f;
+ const float rulerH = 16.0f;
+ float gridW = labelW + numFrames * cellW;
+ float gridH = rulerH + maxChannel * cellH;
+
+ ImGui::Spacing();
+ ImGui::Text("Score");
+ ImGui::Separator();
+
+ // Frame navigation controls
+ if (ImGui::Button("|<")) currentFrame = 0;
+ ImGui::SameLine();
+ if (ImGui::Button("<") && currentFrame > 0) currentFrame--;
+ ImGui::SameLine();
+ ImGui::Text("Frame %d / %d", currentFrame + 1, numFrames);
+ ImGui::SameLine();
+ if (ImGui::Button(">") && currentFrame < numFrames - 1) currentFrame++;
+ ImGui::SameLine();
+ if (ImGui::Button(">|")) currentFrame = numFrames - 1;
+
+ // Frame preview, show thumbnails of all active sprites in the current frame
+ Frame *previewFrame = score->_scoreCache[currentFrame];
+ Cast *cast = member->getCast();
+ if (previewFrame && cast) {
+ const float thumbSize = 48.0f;
+ bool anySprite = false;
+ for (int ch = 1; ch <= maxChannel; ch++) {
+ if (ch >= (int)previewFrame->_sprites.size()) break;
+ Sprite *sp = previewFrame->_sprites[ch];
+ if (!sp || sp->_castId.member == 0) continue;
+
+ CastMember *cm = cast->getCastMember(sp->_castId.member, true);
+ if (!cm) continue;
+
+ if (!anySprite) {
+ ImGui::Separator();
+ ImGui::Text("Frame preview");
+ anySprite = true;
+ }
+
+ ImGuiImage imgID = getImageID(cm);
+ ImGui::BeginGroup();
+ if (imgID.id) {
+ showImage(imgID, cm->getName().c_str(), thumbSize);
+ } else {
+ // No image -> draw a placeholder box
+ ImVec2 pos = ImGui::GetCursorScreenPos();
+ ImGui::GetWindowDrawList()->AddRect(pos, ImVec2(pos.x + thumbSize, pos.y + thumbSize), _state->theme->borderColor);
+ ImGui::Dummy(ImVec2(thumbSize, thumbSize));
+ }
+ ImGui::Text("ch%d", ch);
+ ImGui::EndGroup();
+ ImGui::SameLine();
+ }
+ if (anySprite)
+ ImGui::NewLine();
+ }
+ ImGui::Separator();
+
+ // Scrollable child region
+ float scrollbarH = ImGui::GetStyle().ScrollbarSize;
+ ImGui::BeginChild("##FilmLoopScoreChild", ImVec2(0, MIN(gridH + scrollbarH + 4.0f, 200.0f)), false, ImGuiWindowFlags_HorizontalScrollbar);
+
+ ImDrawList *dl = ImGui::GetWindowDrawList();
+ ImVec2 origin = ImGui::GetCursorScreenPos();
+
+ // Ruler
+ for (int f = 0; f < numFrames; f++) {
+ float x = origin.x + labelW + f * cellW;
+ float y = origin.y;
+ ImVec2 rMin = ImVec2(x, y);
+ ImVec2 rMax = ImVec2(x + cellW, y + rulerH);
+ ImU32 rulerCol = ((f + 1) % 5 == 0) ? _state->theme->tableDarkColor : _state->theme->tableLightColor;
+ dl->AddRectFilled(rMin, rMax, rulerCol);
+ dl->AddLine(ImVec2(rMin.x, rMin.y), ImVec2(rMax.x, rMin.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(rMax.x, rMin.y), ImVec2(rMax.x, rMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(rMax.x, rMax.y), ImVec2(rMin.x, rMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(rMin.x, rMax.y), ImVec2(rMin.x, rMin.y), _state->theme->borderColor, 0.1f);
+ Common::String label = Common::String::format("%d", f + 1);
+ ImVec2 textSz = ImGui::CalcTextSize(label.c_str());
+ dl->AddText(ImVec2(x + (cellW - textSz.x) * 0.5f, y + (rulerH - textSz.y) * 0.5f), _state->theme->gridTextColor, label.c_str());
+ }
+
+ // Playhead
+ {
+ float px = origin.x + labelW + currentFrame * cellW;
+ dl->AddLine(ImVec2(px, origin.y), ImVec2(px, origin.y + gridH), _state->theme->playhead_color, 2.0f);
+ dl->AddTriangleFilled(
+ ImVec2(px - 5.0f, origin.y),
+ ImVec2(px + 5.0f, origin.y),
+ ImVec2(px, origin.y + 8.0f),
+ _state->theme->playhead_color);
+ }
+
+ // Channel rows
+ for (int ch = 1; ch <= maxChannel; ch++) {
+ float y = origin.y + rulerH + (ch - 1) * cellH;
+
+ // label cells
+ ImVec2 lblMin = ImVec2(origin.x, y);
+ ImVec2 lblMax = ImVec2(origin.x + labelW, y + cellH);
+ dl->AddRectFilled(lblMin, lblMax, _state->theme->tableDarkColor);
+ dl->AddLine(ImVec2(lblMin.x, lblMin.y), ImVec2(lblMax.x, lblMin.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(lblMax.x, lblMin.y), ImVec2(lblMax.x, lblMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(lblMax.x, lblMax.y), ImVec2(lblMin.x, lblMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(lblMin.x, lblMax.y), ImVec2(lblMin.x, lblMin.y), _state->theme->borderColor, 0.1f);
+ Common::String chLabel = Common::String::format("%d", ch);
+ ImVec2 chSz = ImGui::CalcTextSize(chLabel.c_str());
+ dl->AddText(ImVec2(origin.x + (labelW - chSz.x) * 0.5f, y + (cellH - chSz.y) * 0.5f), _state->theme->gridTextColor, chLabel.c_str());
+
+ // Pass 1: cell backgrounds
+ for (int f = 0; f < numFrames; f++) {
+ float x = origin.x + labelW + f * cellW;
+ ImVec2 cMin = ImVec2(x, y);
+ ImVec2 cMax = ImVec2(x + cellW, y + cellH);
+ ImU32 col = ((f + 1) % 5 == 0) ? _state->theme->tableDarkColor : _state->theme->tableLightColor;
+ dl->AddRectFilled(cMin, cMax, col);
+ dl->AddLine(ImVec2(cMin.x, cMin.y), ImVec2(cMax.x, cMin.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(cMax.x, cMin.y), ImVec2(cMax.x, cMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(cMax.x, cMax.y), ImVec2(cMin.x, cMax.y), _state->theme->borderColor, 0.1f);
+ dl->AddLine(ImVec2(cMin.x, cMax.y), ImVec2(cMin.x, cMin.y), _state->theme->borderColor, 0.1f);
+ }
+
+ // Pass 2: span bars
+ int f = 0;
+ while (f < numFrames) {
+ Frame *frame = score->_scoreCache[f];
+ if (!frame || ch >= (int)frame->_sprites.size() || !frame->_sprites[ch] || frame->_sprites[ch]->_castId.member == 0) {
+ f++;
+ continue;
+ }
+
+ // Find span end
+ int spanStart = f;
+ int memberNum = frame->_sprites[ch]->_castId.member;
+ int spanEnd = f;
+ while (spanEnd + 1 < numFrames) {
+ Frame *nf = score->_scoreCache[spanEnd + 1];
+ if (nf && ch < (int)nf->_sprites.size() && nf->_sprites[ch] && nf->_sprites[ch]->_castId.member == memberNum)
+ spanEnd++;
+ else
+ break;
+ }
+
+ float x1 = origin.x + labelW + spanStart * cellW;
+ float x2 = origin.x + labelW + (spanEnd + 1) * cellW;
+ float cy = y + cellH * 0.5f;
+ float pad = 1.0f;
+
+ int colorIdx = memberNum % 6;
+ ImU32 barColor = _state->theme->contColors[colorIdx];
+
+ dl->AddRectFilled(ImVec2(x1, y + pad), ImVec2(x2 - 1.0f, y + cellH - pad), barColor);
+ dl->AddLine(ImVec2(x1 + 6.0f, cy), ImVec2(x2 - 6.0f, cy), _state->theme->gridTextColor, 1.0f);
+ dl->AddCircle(ImVec2(x1 + 4.0f, cy), 3.0f, _state->theme->gridTextColor, 0, 1.5f);
+ dl->AddRect(ImVec2(x2 - 7.0f, cy - 3.0f), ImVec2(x2 - 1.0f, cy + 3.0f), _state->theme->gridTextColor, 0.0f, 0, 1.5f);
+
+ float spanW = x2 - x1 - 8.0f;
+ Common::String label = Common::String::format("%d", memberNum);
+ if (spanW > 4.0f) {
+ float textY = y + (cellH - ImGui::GetTextLineHeight()) * 0.5f;
+ // clipText function is static in dt-score.cpp, so inline a simple clip
+ Common::String clipped = label;
+ if (ImGui::CalcTextSize(clipped.c_str()).x > spanW) clipped = "";
+ if (!clipped.empty())
+ dl->AddText(ImVec2(x1 + 4.0f, textY), _state->theme->gridTextColor, clipped.c_str());
+ }
+
+ // Tooltip
+ ImGui::SetCursorScreenPos(ImVec2(x1, y));
+ ImGui::InvisibleButton(Common::String::format("##fl_%d_%d", ch, spanStart).c_str(), ImVec2(x2 - x1, cellH));
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("Channel %d | Frames %d-%d | Cast member %d", ch, spanStart + 1, spanEnd + 1, memberNum);
+ ImGui::EndTooltip();
+ }
+
+ f = spanEnd + 1;
+ }
+ }
+
+ // Advance the cursor
+ ImGui::SetCursorScreenPos(ImVec2(origin.x, origin.y + gridH));
+ ImGui::Dummy(ImVec2(gridW, 0));
+
+ ImGui::EndChild();
+ ImGui::EndTabItem();
+ }
+}
+
void drawCMTypeProps(CastMember *member) {
assert(member != nullptr);
switch (member->_type) {
@@ -516,9 +761,11 @@ void drawCMTypeProps(CastMember *member) {
case kCastShape:
drawShapeCMprops(static_cast<ShapeCastMember *>(member));
break;
+ case kCastFilmLoop:
+ drawFilmLoopCMprops(static_cast<FilmLoopCastMember *>(member));
+ break;
case kCastTypeAny:
case kCastTypeNull:
- case kCastFilmLoop:
case kCastPalette:
case kCastPicture:
case kCastSound:
Commit: c360fbf218cbb009de6daac0d8e87bb6f9425934
https://github.com/scummvm/scummvm/commit/c360fbf218cbb009de6daac0d8e87bb6f9425934
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-04-19T16:24:18+02:00
Commit Message:
JANITORIAL: Move addThinRect to dt-internal.h to avoid duplication
Changed paths:
engines/director/debugger/dt-castdetails.cpp
engines/director/debugger/dt-internal.h
engines/director/debugger/dt-score.cpp
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index d3ef3463e12..7a990fd2aa4 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -627,10 +627,7 @@ void drawFilmLoopCMprops(FilmLoopCastMember *member) {
ImVec2 rMax = ImVec2(x + cellW, y + rulerH);
ImU32 rulerCol = ((f + 1) % 5 == 0) ? _state->theme->tableDarkColor : _state->theme->tableLightColor;
dl->AddRectFilled(rMin, rMax, rulerCol);
- dl->AddLine(ImVec2(rMin.x, rMin.y), ImVec2(rMax.x, rMin.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(rMax.x, rMin.y), ImVec2(rMax.x, rMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(rMax.x, rMax.y), ImVec2(rMin.x, rMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(rMin.x, rMax.y), ImVec2(rMin.x, rMin.y), _state->theme->borderColor, 0.1f);
+ addThinRect(dl, rMin, rMax, _state->theme->borderColor);
Common::String label = Common::String::format("%d", f + 1);
ImVec2 textSz = ImGui::CalcTextSize(label.c_str());
dl->AddText(ImVec2(x + (cellW - textSz.x) * 0.5f, y + (rulerH - textSz.y) * 0.5f), _state->theme->gridTextColor, label.c_str());
@@ -655,10 +652,7 @@ void drawFilmLoopCMprops(FilmLoopCastMember *member) {
ImVec2 lblMin = ImVec2(origin.x, y);
ImVec2 lblMax = ImVec2(origin.x + labelW, y + cellH);
dl->AddRectFilled(lblMin, lblMax, _state->theme->tableDarkColor);
- dl->AddLine(ImVec2(lblMin.x, lblMin.y), ImVec2(lblMax.x, lblMin.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(lblMax.x, lblMin.y), ImVec2(lblMax.x, lblMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(lblMax.x, lblMax.y), ImVec2(lblMin.x, lblMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(lblMin.x, lblMax.y), ImVec2(lblMin.x, lblMin.y), _state->theme->borderColor, 0.1f);
+ addThinRect(dl, lblMin, lblMax, _state->theme->borderColor);
Common::String chLabel = Common::String::format("%d", ch);
ImVec2 chSz = ImGui::CalcTextSize(chLabel.c_str());
dl->AddText(ImVec2(origin.x + (labelW - chSz.x) * 0.5f, y + (cellH - chSz.y) * 0.5f), _state->theme->gridTextColor, chLabel.c_str());
@@ -670,10 +664,7 @@ void drawFilmLoopCMprops(FilmLoopCastMember *member) {
ImVec2 cMax = ImVec2(x + cellW, y + cellH);
ImU32 col = ((f + 1) % 5 == 0) ? _state->theme->tableDarkColor : _state->theme->tableLightColor;
dl->AddRectFilled(cMin, cMax, col);
- dl->AddLine(ImVec2(cMin.x, cMin.y), ImVec2(cMax.x, cMin.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(cMax.x, cMin.y), ImVec2(cMax.x, cMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(cMax.x, cMax.y), ImVec2(cMin.x, cMax.y), _state->theme->borderColor, 0.1f);
- dl->AddLine(ImVec2(cMin.x, cMax.y), ImVec2(cMin.x, cMin.y), _state->theme->borderColor, 0.1f);
+ addThinRect(dl, cMin, cMax, _state->theme->borderColor);
}
// Pass 2: span bars
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index 1f008597947..91abb490c6a 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -335,6 +335,14 @@ Window *windowListCombo(Common::String *target);
Common::String formatHandlerName(int scriptId, int castId, Common::String handlerName, ScriptType scriptType, bool childScript);
void setTheme(int themeIndex);
+// helper to draw thin rectangles for table grid
+inline void addThinRect(ImDrawList *dl, ImVec2 min, ImVec2 max, ImU32 col, float thickness = 0.1f) {
+ dl->AddLine(ImVec2(min.x, min.y), ImVec2(max.x, min.y), col, thickness); // top
+ dl->AddLine(ImVec2(max.x, min.y), ImVec2(max.x, max.y), col, thickness); // right
+ dl->AddLine(ImVec2(max.x, max.y), ImVec2(min.x, max.y), col, thickness); // bottom
+ dl->AddLine(ImVec2(min.x, max.y), ImVec2(min.x, min.y), col, thickness); // left
+}
+
void showCast(); // dt-cast.cpp
void showCastDetails(); // dt-castdetails.cpp
void showControlPanel();// dt-controlpanel.cpp
diff --git a/engines/director/debugger/dt-score.cpp b/engines/director/debugger/dt-score.cpp
index 831864766b4..7933e870505 100644
--- a/engines/director/debugger/dt-score.cpp
+++ b/engines/director/debugger/dt-score.cpp
@@ -98,13 +98,7 @@ static inline ImU32 U32(const ImVec4 &c) {
return ImGui::GetColorU32(c);
}
-// helper to draw thin rectangles for table grid
-static void addThinRect(ImDrawList *dl, ImVec2 min, ImVec2 max, ImU32 col, float thickness = 0.1f) {
- dl->AddLine(ImVec2(min.x, min.y), ImVec2(max.x, min.y), col, thickness); // top
- dl->AddLine(ImVec2(max.x, min.y), ImVec2(max.x, max.y), col, thickness); // right
- dl->AddLine(ImVec2(max.x, max.y), ImVec2(min.x, max.y), col, thickness); // bottom
- dl->AddLine(ImVec2(min.x, max.y), ImVec2(min.x, min.y), col, thickness); // left
-}
+
// help to draw tool tip with wrapped text
static void setTooltip(const char *fmt, ...) {
More information about the Scummvm-git-logs
mailing list