[Scummvm-git-logs] scummvm master -> 73d2d99ad61cfa926ea60d63b19955ffc6a96b45

Scorpeg noreply at scummvm.org
Sun Oct 4 15:12:26 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:
73d2d99ad6 PHOENIXVR: Better subtitle handling


Commit: 73d2d99ad61cfa926ea60d63b19955ffc6a96b45
    https://github.com/scummvm/scummvm/commit/73d2d99ad61cfa926ea60d63b19955ffc6a96b45
Author: Scorp (scorp at mrs.mn)
Date: 2026-10-04T18:12:13+03:00

Commit Message:
PHOENIXVR: Better subtitle handling

Changed paths:
  A engines/phoenixvr/subtitles.cpp
  A engines/phoenixvr/subtitles.h
    engines/phoenixvr/credits.pl
    engines/phoenixvr/module.mk
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/credits.pl b/engines/phoenixvr/credits.pl
index bfdcc50b4ba..d37790df65a 100644
--- a/engines/phoenixvr/credits.pl
+++ b/engines/phoenixvr/credits.pl
@@ -1,5 +1,5 @@
 begin_section("PhoenixVR");
 	add_person("Vladimir Menshakov", "whoozle", "");
-	add_person("Hermann Noll", "Helco", "Help with 3D VR projections");
 	add_person("", "Scorp", "Dracula 1/2 and Messenger support, Engine improvements");
+	add_person("Hermann Noll", "Helco", "Help with 3D VR projections");
 end_section();
diff --git a/engines/phoenixvr/module.mk b/engines/phoenixvr/module.mk
index 6d3ef3e84ff..16bd09064e3 100644
--- a/engines/phoenixvr/module.mk
+++ b/engines/phoenixvr/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS = \
 	script.o \
 	script_v1.o \
 	script_v2.o \
+	subtitles.o \
 	variables.o \
 	vr.o
 
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 8c77f56f0b6..c784f3ca99e 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -51,10 +51,10 @@
 #include "phoenixvr/pakf.h"
 #include "phoenixvr/region_set.h"
 #include "phoenixvr/script.h"
+#include "phoenixvr/subtitles.h"
 #include "phoenixvr/vr.h"
 #include "video/4xm_decoder.h"
 #include "video/smk_decoder.h"
-#include "video/subtitles.h"
 
 namespace PhoenixVR {
 
@@ -946,7 +946,7 @@ void PhoenixVREngine::playSound(const Common::String &sound, Audio::Mixer::Sound
 	_mixer->playStream(type, &h, Audio::makeWAVStream(stream.release(), DisposeAfterUse::YES), -1, volume, spatial ? 0 : panToBalance(_globalPan));
 	if (loops < 0 || music)
 		_mixer->loopChannel(h);
-	Common::SharedPtr<Video::Subtitles> subtitles;
+	Common::SharedPtr<Subtitles> subtitles;
 	if (!music)
 		subtitles = loadSubtitles(sound);
 
@@ -1001,21 +1001,21 @@ Common::Path PhoenixVREngine::getSubtitlePath(const Common::String &path) const
 	return Common::Path("subtitle").appendComponent(language).appendComponent(filename);
 }
 
-Common::SharedPtr<Video::Subtitles> PhoenixVREngine::loadSubtitles(const Common::String &path) const {
-	Common::SharedPtr<Video::Subtitles> subtitles;
+Common::SharedPtr<Subtitles> PhoenixVREngine::loadSubtitles(const Common::String &path) const {
+	Common::SharedPtr<Subtitles> subtitles;
 	if (!ConfMan.getBool("subtitles"))
 		return subtitles;
 
-	subtitles = Common::SharedPtr<Video::Subtitles>(new Video::Subtitles());
+	subtitles = Common::SharedPtr<Subtitles>(new Subtitles(_screen));
 	subtitles->loadSRTFile(getSubtitlePath(path));
 	if (!subtitles->isLoaded())
-		return Common::SharedPtr<Video::Subtitles>();
+		return Common::SharedPtr<Subtitles>();
 
 	setupSubtitles(*subtitles);
 	return subtitles;
 }
 
-void PhoenixVREngine::setupSubtitles(Video::Subtitles &subtitles) const {
+void PhoenixVREngine::setupSubtitles(Subtitles &subtitles) const {
 	// Subtitle positioning constants (as percentages of screen height)
 	const int HORIZONTAL_MARGIN = 20;
 	const int MIN_BOTTOM_MARGIN = 4;
@@ -1024,19 +1024,19 @@ void PhoenixVREngine::setupSubtitles(Video::Subtitles &subtitles) const {
 	const float SUBTITLE_HEIGHT_PERCENT = 0.2f;
 
 	// Font sizing constants (as percentage of screen height)
-	const int MIN_FONT_SIZE = 18;
-	const float BASE_FONT_SIZE_PERCENT = 1.0f / 36.0f;
+	const int MIN_FONT_SIZE = 16;
+	const float BASE_FONT_SIZE_PERCENT = 1.0f / 48.0f;
 
-	int16 h = g_system->getOverlayHeight();
-	int16 w = g_system->getOverlayWidth();
+	int16 h = _screen->h;
+	int16 w = _screen->w;
 	int bottomMargin = MAX<int>(MIN_BOTTOM_MARGIN, int(h * BOTTOM_MARGIN_PERCENT));
 	int topOffset = MAX<int>(MIN_SUBTITLE_HEIGHT, int(h * SUBTITLE_HEIGHT_PERCENT));
 	int fontSize = MAX<int>(MIN_FONT_SIZE, int(h * BASE_FONT_SIZE_PERCENT));
 
 	subtitles.setBBox(Common::Rect(HORIZONTAL_MARGIN, h - topOffset, w - HORIZONTAL_MARGIN, h - bottomMargin));
 	subtitles.setColor(0xff, 0xff, 0x80);
-	subtitles.setFont("LiberationSans-Regular.ttf", fontSize, Video::Subtitles::kFontStyleRegular);
-	subtitles.setFont("LiberationSans-Italic.ttf", fontSize, Video::Subtitles::kFontStyleItalic);
+	subtitles.setFont("LiberationSans-Regular.ttf", fontSize, Subtitles::kFontStyleRegular);
+	subtitles.setFont("LiberationSans-Italic.ttf", fontSize, Subtitles::kFontStyleItalic);
 }
 
 void PhoenixVREngine::playMovie(const Common::String &movie) {
@@ -1066,14 +1066,10 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
 	dec->start();
 	_currentDecoder = dec.get();
 
-	Common::SharedPtr<Video::Subtitles> subtitles = loadSubtitles(movie);
-	if (subtitles) {
-		g_system->showOverlay(false);
-		g_system->clearOverlay();
-	}
-
+	Common::SharedPtr<Subtitles> subtitles = loadSubtitles(movie);
 	bool playing = true;
 	Common::ScopedPtr<Graphics::Palette> palette;
+	const Graphics::Surface *frame = nullptr;
 	while (!shouldQuit() && playing && !dec->endOfVideo()) {
 		Common::Event event;
 		while (g_system->getEventManager()->pollEvent(event)) {
@@ -1091,28 +1087,24 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
 			}
 		}
 		if (dec->needsUpdate()) {
-			auto *s = dec->decodeNextFrame();
+			frame = dec->decodeNextFrame();
 			if (dec->hasDirtyPalette()) {
 				palette.reset(new Graphics::Palette(dec->getPalette(), 256));
 			}
-			if (s) {
-				if (!s->format.isCLUT8() || palette) {
-					Common::Point dstPos((g_system->getWidth() - s->w) / 2, (g_system->getHeight() - s->h) / 2);
-					_screen->simpleBlitFrom(*s, dstPos, Graphics::FLIP_NONE, false, 0xff, palette.get());
-				}
-			}
+		}
+		if (frame && (!frame->format.isCLUT8() || palette)) {
+			Common::Point dstPos((g_system->getWidth() - frame->w) / 2, (g_system->getHeight() - frame->h) / 2);
+			_screen->simpleBlitFrom(*frame, dstPos, Graphics::FLIP_NONE, false, 0xff, palette.get());
 		}
 
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
 		_frameLimiter.delayBeforeSwap();
 		if (subtitles && !dec->isPaused())
-			subtitles->drawSubtitle(dec->getTime(), false);
+			subtitles->drawSubtitle(dec->getTime());
 		_screen->update();
 		_frameLimiter.startFrame();
 	}
-	if (subtitles)
-		g_system->hideOverlay();
 	_system->lockMouse(_vr.isVR());
 	_currentDecoder = nullptr;
 }
@@ -1932,7 +1924,7 @@ void PhoenixVREngine::drawAudioSubtitles() {
 	for (auto &kv : _sounds) {
 		auto &sound = kv._value;
 		if (sound.subtitles && _mixer->isSoundHandleActive(sound.handle))
-			sound.subtitles->drawSubtitle(_mixer->getElapsedTime(sound.handle).msecs(), false);
+			sound.subtitles->drawSubtitle(_mixer->getElapsedTime(sound.handle).msecs());
 	}
 }
 
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index c6cd3dfdf64..9d9ad428a62 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -54,13 +54,10 @@ namespace Graphics {
 class Font;
 }
 
-namespace Video {
-class Subtitles;
-}
-
 namespace PhoenixVR {
 
 class ARN;
+class Subtitles;
 struct PhoenixVRGameDescription;
 struct GameState;
 
@@ -299,8 +296,8 @@ private:
 	void resetState();
 	const Graphics::Font *getFont(int size, bool bold) const;
 	Common::Path getSubtitlePath(const Common::String &path) const;
-	Common::SharedPtr<Video::Subtitles> loadSubtitles(const Common::String &path) const;
-	void setupSubtitles(Video::Subtitles &subtitles) const;
+	Common::SharedPtr<Subtitles> loadSubtitles(const Common::String &path) const;
+	void setupSubtitles(Subtitles &subtitles) const;
 	void drawAudioSubtitles();
 
 	void processGenericEvents(const Common::Event &event);
@@ -340,7 +337,7 @@ private:
 		float angle;
 		uint8 volume;
 		int loops;
-		Common::SharedPtr<Video::Subtitles> subtitles;
+		Common::SharedPtr<Subtitles> subtitles;
 	};
 	Common::HashMap<Common::String, Sound, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _sounds;
 
diff --git a/engines/phoenixvr/subtitles.cpp b/engines/phoenixvr/subtitles.cpp
new file mode 100644
index 00000000000..adbba5aaf48
--- /dev/null
+++ b/engines/phoenixvr/subtitles.cpp
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "phoenixvr/subtitles.h"
+
+#include "graphics/font.h"
+#include "graphics/managed_surface.h"
+
+namespace PhoenixVR {
+
+Subtitles::Subtitles(Graphics::ManagedSurface *target) : _target(target) {
+}
+
+Subtitles::~Subtitles() {
+	_outlined.free();
+}
+
+void Subtitles::setBBox(const Common::Rect &bbox) {
+	Video::Subtitles::setBBox(bbox);
+	_bbox = bbox;
+	_outlined.create(bbox.width() + 2 * kOutlineWidth, bbox.height() + 2 * kOutlineWidth,
+					 Graphics::PixelFormat::createFormatRGBA32());
+	clearSubtitle();
+}
+
+void Subtitles::setColor(byte r, byte g, byte b) {
+	Video::Subtitles::setColor(r, g, b);
+	_textColor = _outlined.format.ARGBToColor(255, r, g, b);
+}
+
+void Subtitles::clearSubtitle() const {
+	_parts = nullptr;
+	_bounds.setEmpty();
+	_runs.clear();
+}
+
+void Subtitles::drawSubtitleText(const Graphics::Font &font, const Common::U32String &text, int x, int y, int width) const {
+	_runs.push_back(TextRun{&font, text, x, y, width});
+}
+
+void Subtitles::drawSubtitle(uint32 timestamp) {
+	_runs.clear();
+	Video::Subtitles::drawSubtitle(timestamp);
+	if (!_runs.empty())
+		renderOutline();
+	if (!_parts || _parts->empty() || (*_parts)[0].tag == "sfx" || _bounds.isEmpty())
+		return;
+
+	const Common::Point position(_bbox.left - kOutlineWidth + _bounds.left,
+								 _bbox.top - kOutlineWidth + _bounds.top);
+	_target->simpleBlitFrom(_outlined, _bounds, position, Graphics::FLIP_NONE, true);
+}
+
+void Subtitles::renderOutline() {
+	_outlined.fillRect(Common::Rect(_outlined.w, _outlined.h), 0);
+	_bounds.setEmpty();
+	const uint32 black = _outlined.format.ARGBToColor(255, 0, 0, 0);
+
+	for (const auto &run : _runs) {
+		const Common::Rect bounds(run.x, run.y, run.x + run.width + 2 * kOutlineWidth,
+								  run.y + run.font->getFontHeight() + 2 * kOutlineWidth);
+		if (_bounds.isEmpty())
+			_bounds = bounds;
+		else
+			_bounds.extend(bounds);
+		for (int y = -kOutlineWidth; y <= kOutlineWidth; ++y) {
+			for (int x = -kOutlineWidth; x <= kOutlineWidth; ++x) {
+				if ((x || y) && 4 * (x * x + y * y) <= (2 * kOutlineWidth + 1) * (2 * kOutlineWidth + 1))
+					run.font->drawString(&_outlined, run.text, run.x + kOutlineWidth + x, run.y + kOutlineWidth + y,
+										 run.width, black, Graphics::kTextAlignLeft);
+			}
+		}
+	}
+	for (const auto &run : _runs)
+		run.font->drawString(&_outlined, run.text, run.x + kOutlineWidth, run.y + kOutlineWidth,
+							 run.width, _textColor, Graphics::kTextAlignLeft);
+	_bounds.clip(_outlined.w, _outlined.h);
+}
+
+} // namespace PhoenixVR
diff --git a/engines/phoenixvr/subtitles.h b/engines/phoenixvr/subtitles.h
new file mode 100644
index 00000000000..2a0bbb6d832
--- /dev/null
+++ b/engines/phoenixvr/subtitles.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef PHOENIXVR_SUBTITLES_H
+#define PHOENIXVR_SUBTITLES_H
+
+#include "common/path.h"
+#include "video/subtitles.h"
+
+namespace Graphics {
+class ManagedSurface;
+}
+
+namespace PhoenixVR {
+
+class Subtitles : public Video::Subtitles {
+public:
+	explicit Subtitles(Graphics::ManagedSurface *target);
+	~Subtitles() override;
+
+	void setBBox(const Common::Rect &bbox);
+	void setColor(byte r, byte g, byte b);
+	void drawSubtitle(uint32 timestamp);
+	void clearSubtitle() const override;
+
+protected:
+	void drawSubtitleText(const Graphics::Font &font, const Common::U32String &text, int x, int y, int width) const override;
+	void updateSubtitleOverlay() const override {}
+
+private:
+	struct TextRun {
+		const Graphics::Font *font;
+		Common::U32String text;
+		int x, y, width;
+	};
+	void renderOutline();
+
+	static const int kOutlineWidth = 3;
+	Graphics::ManagedSurface *_target;
+	mutable Graphics::Surface _outlined;
+	Common::Rect _bbox;
+	mutable Common::Rect _bounds;
+	mutable Common::Array<TextRun> _runs;
+	uint32 _textColor = 0;
+};
+
+} // namespace PhoenixVR
+
+#endif




More information about the Scummvm-git-logs mailing list