[Scummvm-git-logs] scummvm master -> 39e4fdb98c27f97ffffcfba8b6579697a4c809d2

peterkohaut peterkohaut at users.noreply.github.com
Tue Jul 16 22:05:05 CEST 2019


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ffbfe90afa BLADERUNNER: Ajdust font interface to ScummVM's one
a767a6800d BLADERUNNER: Added TTF & UTF8 support for subtitles
39e4fdb98c BLADERUNNER: Use ScummVM word wrapping algorithm


Commit: ffbfe90afad5c1b6a24705e94235172d2d38b032
    https://github.com/scummvm/scummvm/commit/ffbfe90afad5c1b6a24705e94235172d2d38b032
Author: Peter Kohaut (peter.kohaut at gmail.com)
Date: 2019-07-16T21:32:34+02:00

Commit Message:
BLADERUNNER: Ajdust font interface to ScummVM's one

Changed paths:
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/debugger.cpp
    engines/bladerunner/dialogue_menu.cpp
    engines/bladerunner/font.cpp
    engines/bladerunner/font.h
    engines/bladerunner/subtitles.cpp
    engines/bladerunner/ui/end_credits.cpp
    engines/bladerunner/ui/esper.cpp
    engines/bladerunner/ui/kia.cpp
    engines/bladerunner/ui/kia_section_clues.cpp
    engines/bladerunner/ui/kia_section_crimes.cpp
    engines/bladerunner/ui/kia_section_diagnostic.cpp
    engines/bladerunner/ui/kia_section_pogo.cpp
    engines/bladerunner/ui/kia_section_save.cpp
    engines/bladerunner/ui/kia_section_settings.cpp
    engines/bladerunner/ui/kia_section_suspects.cpp
    engines/bladerunner/ui/scores.cpp
    engines/bladerunner/ui/ui_image_picker.cpp
    engines/bladerunner/ui/ui_input_box.cpp
    engines/bladerunner/ui/ui_scroll_box.cpp


diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index ecb4048..24554c0 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -629,9 +629,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 
 	_scores = new Scores(this);
 
-	_mainFont = new Font(this);
-	_mainFont->open("KIA6PT.FON", 640, 480, -1, 0, _surfaceFront.format.RGBToColor(72, 72, 104));
-	_mainFont->setSpacing(1, 0);
+	_mainFont = Font::load(this, "KIA6PT.FON", 1, false);
 
 	for (int i = 0; i != 43; ++i) {
 		Shape *shape = new Shape(this);
@@ -738,11 +736,8 @@ void BladeRunnerEngine::shutdown() {
 	}
 	_shapes.clear();
 
-	if (_mainFont) {
-		_mainFont->close();
-		delete _mainFont;
-		_mainFont = nullptr;
-	}
+	delete _mainFont;
+	_mainFont = nullptr;
 
 	delete _scores;
 	_scores = nullptr;
diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp
index e497de3..8021045 100644
--- a/engines/bladerunner/debugger.cpp
+++ b/engines/bladerunner/debugger.cpp
@@ -2221,7 +2221,7 @@ void Debugger::drawSceneObjects() {
 					color = _vm->_surfaceFront.format.RGBToColor(255, 0, 0);
 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
-					_vm->_mainFont->drawColor(_vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors), _vm->_surfaceFront, pos.x, pos.y, color);
+					_vm->_mainFont->drawString(&_vm->_surfaceFront, _vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors), pos.x, pos.y, _vm->_surfaceFront.w, color);
 				}
 				break;
 			case kSceneObjectTypeItem:
@@ -2233,7 +2233,7 @@ void Debugger::drawSceneObjects() {
 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
 					sprintf(itemText, "item %i", sceneObject->id - kSceneObjectOffsetItems);
 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
-					_vm->_mainFont->drawColor(itemText, _vm->_surfaceFront, pos.x, pos.y, color);
+					_vm->_mainFont->drawString(&_vm->_surfaceFront, itemText, pos.x, pos.y, _vm->_surfaceFront.w, color);
 				}
 				break;
 			case kSceneObjectTypeObject:
@@ -2246,7 +2246,7 @@ void Debugger::drawSceneObjects() {
 					}
 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
-					_vm->_mainFont->drawColor(_vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects), _vm->_surfaceFront, pos.x, pos.y, color);
+					_vm->_mainFont->drawString(&_vm->_surfaceFront, _vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects), pos.x, pos.y, _vm->_surfaceFront.w, color);
 				}
 				break;
 			}
@@ -2283,7 +2283,7 @@ void Debugger::drawLights() {
 
 			_vm->_surfaceFront.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color);
 
-			_vm->_mainFont->drawColor(light->_name, _vm->_surfaceFront, posOriginT.x, posOriginT.y, color);
+			_vm->_mainFont->drawString(&_vm->_surfaceFront, light->_name, posOriginT.x, posOriginT.y, _vm->_surfaceFront.w, color);
 		}
 	}
 }
@@ -2319,7 +2319,7 @@ void Debugger::drawFogs() {
 			// TODO: draw line only for cone fogs, draw boxes or circles for the other types
 			_vm->_surfaceFront.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color);
 
-			_vm->_mainFont->drawColor(fog->_name, _vm->_surfaceFront, posOriginT.x, posOriginT.y, color);
+			_vm->_mainFont->drawString(&_vm->_surfaceFront, fog->_name, posOriginT.x, posOriginT.y, _vm->_surfaceFront.w, color);
 		}
 		fog = fog->_next;
 	}
@@ -2371,7 +2371,7 @@ void Debugger::drawWaypoints() {
 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
 				char waypointText[40];
 				sprintf(waypointText, "waypoint %i", i);
-				_vm->_mainFont->drawColor(waypointText, _vm->_surfaceFront, spos.x, spos.y, color);
+				_vm->_mainFont->drawString(&_vm->_surfaceFront, waypointText, spos.x, spos.y, _vm->_surfaceFront.w, color);
 			}
 		}
 	}
@@ -2393,7 +2393,7 @@ void Debugger::drawWaypoints() {
 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
 				char coverText[40];
 				sprintf(coverText, "cover %i", i);
-				_vm->_mainFont->drawColor(coverText, _vm->_surfaceFront, spos.x, spos.y, color);
+				_vm->_mainFont->drawString(&_vm->_surfaceFront, coverText, spos.x, spos.y, _vm->_surfaceFront.w, color);
 			}
 		}
 	}
@@ -2415,7 +2415,7 @@ void Debugger::drawWaypoints() {
 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
 				char fleeText[40];
 				sprintf(fleeText, "flee %i", i);
-				_vm->_mainFont->drawColor(fleeText, _vm->_surfaceFront, spos.x, spos.y, color);
+				_vm->_mainFont->drawString(&_vm->_surfaceFront, fleeText, spos.x, spos.y, _vm->_surfaceFront.w, color);
 			}
 		}
 	}
@@ -2433,7 +2433,7 @@ void Debugger::drawWalkboxes() {
 				Vector3 end = _vm->_view->calculateScreenPosition(walkbox->vertices[(j + 1) % walkbox->vertexCount]);
 				_vm->_surfaceFront.drawLine(start.x, start.y, end.x, end.y, _vm->_surfaceFront.format.RGBToColor(255, 255, 0));
 				Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (walkbox->vertices[j] + walkbox->vertices[(j + 1) % walkbox->vertexCount]));
-				_vm->_mainFont->drawColor(walkbox->name, _vm->_surfaceFront, pos.x, pos.y, _vm->_surfaceFront.format.RGBToColor(255, 255, 0));
+				_vm->_mainFont->drawString(&_vm->_surfaceFront, walkbox->name, pos.x, pos.y, _vm->_surfaceFront.w, _vm->_surfaceFront.format.RGBToColor(255, 255, 0));
 			}
 		}
 	}
diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp
index 9d09b5f..352303c 100644
--- a/engines/bladerunner/dialogue_menu.cpp
+++ b/engines/bladerunner/dialogue_menu.cpp
@@ -381,7 +381,7 @@ void DialogueMenu::draw(Graphics::Surface &s) {
 		_shapes[1].draw(s, x1, y);
 		_shapes[4].draw(s, x2, y);
 		uint16 color = s.format.RGBToColor((_items[i].colorIntensity / 2) * (256 / 32), (_items[i].colorIntensity / 2) * (256 / 32), _items[i].colorIntensity * (256 / 32));
-		_vm->_mainFont->drawColor(_items[i].text, s, x, y, color);
+		_vm->_mainFont->drawString(&s, _items[i].text, x, y, s.w, color);
 		y += kLineHeight;
 	}
 	for (; x != x2; ++x) {
@@ -407,7 +407,7 @@ const char *DialogueMenu::getText(int id) const {
 void DialogueMenu::calculatePosition(int unusedX, int unusedY) {
 	_maxItemWidth = 0;
 	for (int i = 0; i != _listSize; ++i) {
-		_maxItemWidth = MAX(_maxItemWidth, _vm->_mainFont->getTextWidth(_items[i].text));
+		_maxItemWidth = MAX(_maxItemWidth, _vm->_mainFont->getStringWidth(_items[i].text));
 	}
 	_maxItemWidth += 2;
 
diff --git a/engines/bladerunner/font.cpp b/engines/bladerunner/font.cpp
index 703495c..2512f86 100644
--- a/engines/bladerunner/font.cpp
+++ b/engines/bladerunner/font.cpp
@@ -28,8 +28,7 @@
 
 namespace BladeRunner {
 
-Font::Font(BladeRunnerEngine *vm) {
-	_vm = vm;
+Font::Font() {
 	reset();
 }
 
@@ -37,45 +36,43 @@ Font::~Font() {
 	close();
 }
 
-bool Font::open(const Common::String &fileName, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color) {
-	reset();
-
-	_screenWidth = screenWidth;
-	_screenHeight = screenHeight;
-	_spacing1 = spacing1;
-	_spacing2 = spacing2;
-	_defaultColor = color;
-	_color = color;
+Font* Font::load(BladeRunnerEngine *vm, const Common::String &fileName, int spacing, bool useFontColor) {
+	Font *font = new Font();
+	font->_spacing = spacing;
+	font->_useFontColor = useFontColor;
 
-	Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->getResourceStream(fileName));
+	Common::ScopedPtr<Common::SeekableReadStream> stream(vm->getResourceStream(fileName));
 	if (!stream) {
 		warning("Font::open failed to open '%s'", fileName.c_str());
-		return false;
+		delete font;
+		return nullptr;
 	}
 
-	_characterCount = stream->readUint32LE();
-	_maxWidth = stream->readUint32LE();
-	_maxHeight = stream->readUint32LE();
-	_dataSize = stream->readUint32LE();
-	_data = new uint16[_dataSize];
-	if (!_data) {
+	font->_characterCount = stream->readUint32LE();
+	font->_maxWidth = stream->readUint32LE();
+	font->_maxHeight = stream->readUint32LE();
+	font->_dataSize = stream->readUint32LE();
+	font->_data = new uint16[font->_dataSize];
+	if (!font->_data) {
 		warning("Font::open failed to allocate font buffer");
-		return false;
+		delete font;
+		return nullptr;
 	}
 
-	for (int i = 0; i < _characterCount; i++) {
-		_characters[i].x = stream->readUint32LE();
-		_characters[i].y = stream->readUint32LE();
-		_characters[i].width = stream->readUint32LE();
-		_characters[i].height = stream->readUint32LE();
-		_characters[i].dataOffset = stream->readUint32LE();
+	font->_characters.resize(font->_characterCount);
+	for (uint32 i = 0; i < font->_characterCount; i++) {
+		font->_characters[i].x = stream->readUint32LE();
+		font->_characters[i].y = stream->readUint32LE();
+		font->_characters[i].width = stream->readUint32LE();
+		font->_characters[i].height = stream->readUint32LE();
+		font->_characters[i].dataOffset = stream->readUint32LE();
 	}
 
-	for (int i = 0; i < _dataSize; i++) {
-		_data[i] = stream->readUint16LE();
+	for (int i = 0; i < font->_dataSize; i++) {
+		font->_data[i] = stream->readUint16LE();
 	}
 
-	return true;
+	return font;
 }
 
 void Font::close() {
@@ -85,68 +82,6 @@ void Font::close() {
 	reset();
 }
 
-void Font::setSpacing(int spacing1, int spacing2) {
-	if (_data) {
-		_spacing1 = spacing1;
-		_spacing2 = spacing2;
-	}
-}
-
-void Font::setColor(uint16 color) {
-	_color = color;
-}
-
-void Font::draw(const Common::String &text, Graphics::Surface &surface, int x, int y) const {
-	if (!_data) {
-		return;
-	}
-
-	x = CLIP(x, 0, _screenWidth - getTextWidth(text) + 1);
-	y = CLIP(y, 0, _screenHeight - _maxHeight);
-
-	const uint8 *character = (const uint8 *)text.c_str();
-	while (*character != 0) {
-		drawCharacter(*character, surface, x, y);
-		x += _spacing1 + _characters[*character + 1].width;
-		character++;
-	}
-
-}
-
-void Font::drawColor(const Common::String &text, Graphics::Surface &surface, int x, int y, uint16 color) {
-	setColor(color);
-	draw(text, surface, x, y);
-}
-
-void Font::drawNumber(int num, Graphics::Surface &surface, int x, int y) const {
-	char buffer[20];
-
-	snprintf(buffer, 20, "%d", num);
-
-	draw(buffer, surface, x, y);
-}
-
-int Font::getTextWidth(const Common::String &text) const {
-	const uint8 *character = (const uint8 *)text.c_str();
-
-	if (!_data) {
-		return 0;
-	}
-	int totalWidth = 0;
-	if (*character == 0) {
-		return 0;
-	}
-	while (*character != 0) {
-		totalWidth += _spacing1 + _characters[*character + 1].width;
-		character++;
-	}
-	return totalWidth - _spacing1;
-}
-
-int Font::getTextHeight(const Common::String &text) const {
-	return _maxHeight;
-}
-
 void Font::reset() {
 	_maxWidth = 0;
 	_maxHeight = 0;
@@ -155,26 +90,40 @@ void Font::reset() {
 	_dataSize = 0;
 	_screenWidth = 0;
 	_screenHeight = 0;
-	_spacing1 = 0;
-	_spacing2 = 0;
-	_color = screenPixelFormat().RGBToColor(255, 255, 255);
+	_spacing = 0;
+	_useFontColor = false;
 	_intersperse = 0;
 
-	memset(_characters, 0, 256 * sizeof(Character));
+	_characters.clear();
+}
+
+int Font::getFontHeight() const {
+	return _maxHeight;
+}
+
+int Font::getMaxCharWidth() const {
+	return _maxWidth;
+}
+
+int Font::getCharWidth(uint32 chr) const {
+	if (chr >= _characterCount) {
+		return 0;
+	}
+	return _characters[chr + 1].width + _spacing;
 }
 
-void Font::drawCharacter(const uint8 character, Graphics::Surface &surface, int x, int y) const {
-	uint8 characterIndex = character + 1;
-	if (x < 0 || x >= _screenWidth || y < 0 || y >= _screenHeight || !_data || characterIndex >= _characterCount) {
+void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	uint32 characterIndex = chr + 1;
+	if (x < 0 || x >= dst->w || y < 0 || y >= dst->h || !_data || characterIndex >= _characterCount) {
 		return;
 	}
 
-	uint16 *dstPtr = (uint16 *)surface.getBasePtr(x + _characters[characterIndex].x, y + _characters[characterIndex].y);
+	uint16 *dstPtr = (uint16 *)dst->getBasePtr(x + _characters[characterIndex].x, y + _characters[characterIndex].y);
 	uint16 *srcPtr = &_data[_characters[characterIndex].dataOffset];
 	int width = _characters[characterIndex].width;
 	int height = _characters[characterIndex].height;
 	if (_intersperse && y & 1) {
-		dstPtr += surface.pitch / 2;
+		dstPtr += dst->pitch / 2;
 	}
 
 	int endY = height + y - 1;
@@ -191,28 +140,28 @@ void Font::drawCharacter(const uint8 character, Graphics::Surface &surface, int
 		return;
 	}
 
-	while (currentY <= endY && currentY < _screenHeight) {
+	while (currentY <= endY && currentY < dst->h) {
 		int currentX = x;
 		int endX = width + x - 1;
-		while (currentX <= endX && currentX < _screenWidth) {
+		while (currentX <= endX && currentX < dst->w) {
 			uint8 a, r, g, b;
 			gameDataPixelFormat().colorToARGB(*srcPtr, a, r, g, b);
-			if (!a) {
-				if (_color == _defaultColor) {
+			if (!a) { // Alpha is inversed
+				if (_useFontColor) {
 					// Ignore the alpha in the output as it is inversed in the input
-					*dstPtr = surface.format.RGBToColor(r, g, b);
+					*dstPtr = dst->format.RGBToColor(r, g, b);
 				} else {
-					*dstPtr = _color;
+					*dstPtr = (uint16)color;
 				}
 			}
 			dstPtr++;
 			srcPtr++;
 			currentX++;
 		}
-		dstPtr += surface.pitch / 2 - width;
+		dstPtr += dst->pitch / 2 - width;
 		if (_intersperse) {
 			srcPtr += width;
-			dstPtr += surface.pitch / 2;
+			dstPtr += dst->pitch / 2;
 			currentY++;
 		}
 		currentY++;
diff --git a/engines/bladerunner/font.h b/engines/bladerunner/font.h
index ba5b327..38fd305 100644
--- a/engines/bladerunner/font.h
+++ b/engines/bladerunner/font.h
@@ -23,8 +23,11 @@
 #ifndef BLADERUNNER_FONT_H
 #define BLADERUNNER_FONT_H
 
+#include "common/array.h"
 #include "common/str.h"
 
+#include "graphics/font.h"
+
 namespace Graphics {
 struct Surface;
 }
@@ -33,7 +36,7 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 
-class Font {
+class Font : public Graphics::Font {
 	struct Character {
 		int x;
 		int y;
@@ -42,43 +45,33 @@ class Font {
 		int dataOffset;
 	};
 
-	BladeRunnerEngine *_vm;
-
-	int           _characterCount;
-	int           _maxWidth;
-	int           _maxHeight;
-	Character     _characters[256];
-	int           _dataSize;
-	uint16       *_data;
-	int           _screenWidth;
-	int           _screenHeight;
-	int           _spacing1;
-	int           _spacing2;
-	uint16        _defaultColor;
-	uint16        _color;
-	int           _intersperse;
+	uint32                   _characterCount;
+	int                      _maxWidth;
+	int                      _maxHeight;
+	Common::Array<Character> _characters;
+	int                      _dataSize;
+	uint16                  *_data;
+	int                      _screenWidth;
+	int                      _screenHeight;
+	int                      _spacing;
+	bool                     _useFontColor;
+	int                      _intersperse;
 
 public:
-	Font(BladeRunnerEngine *vm);
 	~Font();
 
-	bool open(const Common::String &fileName, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color);
-	void close();
-
-	void setSpacing(int spacing1, int spacing2);
-	void setColor(uint16 color);
+	static Font* load(BladeRunnerEngine *vm, const Common::String &fileName, int spacing, bool useFontColor);
 
-	void draw(const Common::String &text, Graphics::Surface &surface, int x, int y) const;
-	void drawColor(const Common::String &text, Graphics::Surface &surface, int x, int y, uint16 color);
-	void drawNumber(int num, Graphics::Surface &surface, int x, int y) const;
-
-	int getTextWidth(const Common::String &text) const;
-	int getTextHeight(const Common::String &text) const;
+	int getFontHeight() const;
+	int getMaxCharWidth() const;
+	int getCharWidth(uint32 chr) const;
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const;
 
 private:
+	Font();
 	void reset();
-
-	void drawCharacter(const uint8 character, Graphics::Surface &surface, int x, int y) const;
+	void close();
+	// void drawCharacter(const uint8 character, Graphics::Surface &surface, int x, int y) const;
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/subtitles.cpp b/engines/bladerunner/subtitles.cpp
index 80e56f3..f7c9923 100644
--- a/engines/bladerunner/subtitles.cpp
+++ b/engines/bladerunner/subtitles.cpp
@@ -119,7 +119,6 @@ Subtitles::~Subtitles() {
 	}
 
 	if (_subsFont != nullptr) {
-		_subsFont->close();
 		delete _subsFont;
 		_subsFont = nullptr;
 	}
@@ -151,11 +150,10 @@ void Subtitles::init(void) {
 	// Done - Loading text resources
 	//
 	// Initializing/Loading Subtitles Fonts
-	_subsFont = new Font(_vm);
+	_subsFont = Font::load(_vm, SUBTITLES_FONT_FILENAME_EXTERNAL, -1, true);
 	// Use TAHOMA18.FON (is corrupted in places)
 	// 10PT or TAHOMA24 or KIA6PT  have all caps glyphs (and also are too big or too small) so they are not appropriate.
-	if (_subsFont ->open(SUBTITLES_FONT_FILENAME_EXTERNAL, 640, 480, -1, 0, 0)) { // Color setting does not seem to affect the TAHOMA fonts or does it affect the black outline since we give 0 here?
-		_subsFont->setSpacing(-1, 0);
+	if (_subsFont) { // Color setting does not seem to affect the TAHOMA fonts or does it affect the black outline since we give 0 here?
 		_subsFontsLoaded = true;
 	} else {
 		_subsFontsLoaded = false;
@@ -168,7 +166,7 @@ void Subtitles::init(void) {
 	//  debug("Max height %d", _subsFont->getTextHeight(""));
 	if (_subsFontsLoaded) {
 		for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
-			_subtitleLineScreenY[i] = 479 - kSubtitlesBottomYOffsetPx - ((kMaxNumOfSubtitlesLines - i) * (_subsFont->getTextHeight("") + 1));
+			_subtitleLineScreenY[i] = 479 - kSubtitlesBottomYOffsetPx - ((kMaxNumOfSubtitlesLines - i) * (_subsFont->getFontHeight() + 1));
 		}
 	}
 
@@ -209,8 +207,7 @@ int Subtitles::getIdxForSubsTreName(const Common::String &treName) const {
 	for (int i = 0; i < kMaxTextResourceEntries; ++i) {
 		if (!strcmp(SUBTITLES_FILENAME_PREFIXES[i], "WSTLGO") || !strcmp(SUBTITLES_FILENAME_PREFIXES[i], "BRLOGO")) {
 			tmpConstructedFileName = Common::String(SUBTITLES_FILENAME_PREFIXES[i]) + "_E"; // Only English versions of these exist
-		}
-		else {
+		} else {
 			tmpConstructedFileName = Common::String(SUBTITLES_FILENAME_PREFIXES[i]) + "_" + _vm->_languageCode;
 		}
 		if (tmpConstructedFileName == treName) {
@@ -392,7 +389,7 @@ void Subtitles::draw(Graphics::Surface &s) {
 	}
 
 	for (int i = 0, j = startingLineFromTop; i < _currentSubtitleLines; ++i, ++j) {
-		_subsFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i], _subtitleLineScreenY[j]);
+		_subsFont->drawString(&s, _subtitleLineQuote[i], _subtitleLineScreenX[i], _subtitleLineScreenY[j], s.w, 0);
 	}
 }
 
@@ -423,7 +420,7 @@ void Subtitles::draw(Graphics::Surface &s) {
 void Subtitles::calculatePosition() {
 
 	// wOrig is in pixels, origQuoteNumOfChars is num of chars in string
-	int wOrig = _subsFont->getTextWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
+	int wOrig = _subsFont->getStringWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
 	int origQuoteNumOfChars = _currentSubtitleTextFull.size();
 	int tmpCharIndex = 0;
 	bool drawSingleLineQuote = false;
@@ -469,7 +466,7 @@ void Subtitles::calculatePosition() {
 		//
 		// Check widths and set starting X positions per line
 		for (int k = 0; k < _currentSubtitleLines; ++k) {
-			tmpLineWidth[k] = _subsFont->getTextWidth(_subtitleLineQuote[k]) + 2;
+			tmpLineWidth[k] = _subsFont->getStringWidth(_subtitleLineQuote[k]) + 2;
 			_subtitleLineScreenX[k] = (639 - tmpLineWidth[k]) / 2;
 			_subtitleLineScreenX[k] = CLIP(_subtitleLineScreenX[k], 0, 639 - tmpLineWidth[k]);
 		}
@@ -499,7 +496,7 @@ void Subtitles::calculatePosition() {
 					}
 					_subtitleLineQuote[0] += '\0';
 //                    debug(" Line 0 quote %s", _subtitleLineQuote[0].c_str());
-					tmpLineWidth[0] = _subsFont->getTextWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
+					tmpLineWidth[0] = _subsFont->getStringWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
 					if (tmpLineWidth[0] > kMaxWidthPerLineToAutoSplitThresholdPx && linesToSplitInto < kMaxNumOfSubtitlesLines) {
 						// we exceed max width so we reset process by trying to split into more lines
 						continue; // re-try the For-loop with increased linesToSplitInto by 1
@@ -524,7 +521,7 @@ void Subtitles::calculatePosition() {
 						//
 						// Check widths and set starting X positions per line
 						for (int j = 0; j < _currentSubtitleLines; ++j) {
-							tmpLineWidth[j] = _subsFont->getTextWidth(_subtitleLineQuote[j]) + 2;
+							tmpLineWidth[j] = _subsFont->getStringWidth(_subtitleLineQuote[j]) + 2;
 							_subtitleLineScreenX[j] = (639 - tmpLineWidth[j]) / 2;
 							_subtitleLineScreenX[j] = CLIP(_subtitleLineScreenX[j], 0, 639 - tmpLineWidth[j]);
 						}
@@ -586,7 +583,6 @@ void Subtitles::reset() {
 	}
 
 	if (_subsFont != nullptr) {
-		_subsFont->close();
 		delete _subsFont;
 		_subsFont = nullptr;
 	}
diff --git a/engines/bladerunner/ui/end_credits.cpp b/engines/bladerunner/ui/end_credits.cpp
index cab3aa2..08cce2e 100644
--- a/engines/bladerunner/ui/end_credits.cpp
+++ b/engines/bladerunner/ui/end_credits.cpp
@@ -55,13 +55,8 @@ void EndCredits::show() {
 
 	_vm->_music->play(_vm->_gameInfo->getMusicTrack(kMusicCredits), 100, 0, 2, -1, 0, 3);
 
-	Font *fontBig = new Font(_vm);
-	fontBig->open("TAHOMA24.FON", 640, 480, -1, 0, 0);
-	fontBig->setSpacing(1, 0);
-
-	Font *fontSmall = new Font(_vm);
-	fontSmall->open("TAHOMA18.FON", 640, 480, -1, 0, 0);
-	fontSmall->setSpacing(1, 0);
+	Font *fontBig = Font::load(_vm, "TAHOMA24.FON", 1, true);
+	Font *fontSmall = Font::load(_vm, "TAHOMA18.FON", 1, true);
 
 	TextResource *textResource = new TextResource(_vm);
 	textResource->open("ENDCRED");
@@ -141,10 +136,10 @@ void EndCredits::show() {
 				if (font == fontBig) {
 					x = 280;
 				} else {
-					x = 270 - font->getTextWidth(s);
+					x = 270 - font->getStringWidth(s);
 				}
 
-				font->draw(s, _vm->_surfaceFront, x, y);
+				font->drawString(&_vm->_surfaceFront, s, x, y, _vm->_surfaceFront.w, 0);
 			}
 		}
 
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
index 355865f..7913362 100644
--- a/engines/bladerunner/ui/esper.cpp
+++ b/engines/bladerunner/ui/esper.cpp
@@ -101,8 +101,6 @@ void ESPER::open(Graphics::Surface *surface) {
 
 	_viewportNext = _viewport;
 
-	_vm->_mainFont->setColor(surface->format.RGBToColor(0, 0, 248));
-
 	_shapeButton = new Shape(_vm);
 	if (!_shapeButton->open("ESPBUTTN.SHP", 0)) {
 		return;
@@ -1026,13 +1024,13 @@ void ESPER::drawVideoFrame(Graphics::Surface &surface) {
 
 void ESPER::drawTextCoords(Graphics::Surface &surface) {
 	if (_vm->_language == Common::RU_RUS) {
-		_vm->_mainFont->drawColor(Common::String::format("gh %04.0f", _zoom / _zoomMin * 2.0f  ), surface, 155, 364, surface.format.RGBToColor(0, 0, 255));
-		_vm->_mainFont->drawColor(Common::String::format("dh %04d",   12 * _viewport.top  +  98), surface, 260, 364, surface.format.RGBToColor(0, 0, 255));
-		_vm->_mainFont->drawColor(Common::String::format("uh %04d",   12 * _viewport.left + 167), surface, 364, 364, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("gh %04.0f", _zoom / _zoomMin * 2.0f  ), 155, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("dh %04d",   12 * _viewport.top  +  98), 260, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("uh %04d",   12 * _viewport.left + 167), 364, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
 	} else {
-		_vm->_mainFont->drawColor(Common::String::format("ZM %04.0f", _zoom / _zoomMin * 2.0f  ), surface, 155, 364, surface.format.RGBToColor(0, 0, 255));
-		_vm->_mainFont->drawColor(Common::String::format("NS %04d",   12 * _viewport.top  +  98), surface, 260, 364, surface.format.RGBToColor(0, 0, 255));
-		_vm->_mainFont->drawColor(Common::String::format("EW %04d",   12 * _viewport.left + 167), surface, 364, 364, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("ZM %04.0f", _zoom / _zoomMin * 2.0f  ), 155, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("NS %04d",   12 * _viewport.top  +  98), 260, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
+		_vm->_mainFont->drawString(&surface, Common::String::format("EW %04d",   12 * _viewport.left + 167), 364, 364, surface.w, surface.format.RGBToColor(0, 0, 255));
 	}
 }
 
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index 9f7cac8..666a8bd 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -310,7 +310,7 @@ void KIA::tick() {
 	}
 	if (_currentSectionId != kKIASectionQuit && _transitionId != 14) {
 		if (_vm->_settings->getDifficulty() > kGameDifficultyEasy) {
-			_vm->_mainFont->drawColor(Common::String::format("%04d", _vm->_gameVars[kVariableChinyen]), _vm->_surfaceFront, 580, 341, _vm->_surfaceFront.format.RGBToColor(80, 96, 136));
+			_vm->_mainFont->drawString(&_vm->_surfaceFront, Common::String::format("%04d", _vm->_gameVars[kVariableChinyen]), 580, 341, _vm->_surfaceFront.w, _vm->_surfaceFront.format.RGBToColor(80, 96, 136));
 		} else {
 			_shapes->get(39)->draw(_vm->_surfaceFront, 583, 342);
 		}
@@ -370,7 +370,7 @@ void KIA::tick() {
 			_shapes->get(47)->draw(_vm->_surfaceFront, 182, 446);
 		}
 	}
-	_vm->_mainFont->drawColor("1.00", _vm->_surfaceFront, 438, 471, _vm->_surfaceFront.format.RGBToColor(56, 56, 56)); // 1.01 is DVD version, but only cd handling routines were changed, no game logic
+	_vm->_mainFont->drawString(&_vm->_surfaceFront, "1.00", 438, 471, _vm->_surfaceFront.w, _vm->_surfaceFront.format.RGBToColor(56, 56, 56)); // 1.01 is DVD version, but only cd handling routines were changed, no game logic
 	if (!_transitionId) {
 		_buttons->drawTooltip(_vm->_surfaceFront, mouse.x, mouse.y);
 	}
diff --git a/engines/bladerunner/ui/kia_section_clues.cpp b/engines/bladerunner/ui/kia_section_clues.cpp
index 0557402..d4437f7 100644
--- a/engines/bladerunner/ui/kia_section_clues.cpp
+++ b/engines/bladerunner/ui/kia_section_clues.cpp
@@ -120,10 +120,10 @@ void KIASectionClues::close() {
 void KIASectionClues::draw(Graphics::Surface &surface) {
 	_uiContainer->draw(surface);
 
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(0), surface, 300, 162, surface.format.RGBToColor(232, 240, 255));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(2), surface, 440, 426, surface.format.RGBToColor(80, 96, 136));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(1), surface, 440, 442, surface.format.RGBToColor(80, 96, 136));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(4), surface, 440, 458, surface.format.RGBToColor(80, 96, 136));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(0), 300, 162, surface.w, surface.format.RGBToColor(232, 240, 255));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(2), 440, 426, surface.w, surface.format.RGBToColor(80, 96, 136));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(1), 440, 442, surface.w, surface.format.RGBToColor(80, 96, 136));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(4), 440, 458, surface.w, surface.format.RGBToColor(80, 96, 136));
 
 	int clueId = _cluesScrollBox->getSelectedLineData();
 	if (clueId != -1) {
@@ -135,7 +135,7 @@ void KIASectionClues::draw(Graphics::Surface &surface) {
 		} else {
 			text.clear();
 		}
-		_vm->_mainFont->drawColor(text, surface, 490, 426, surface.format.RGBToColor(136, 168, 255));
+		_vm->_mainFont->drawString(&surface, text, 490, 426, surface.w, surface.format.RGBToColor(136, 168, 255));
 
 		int crimeId = _vm->_crimesDatabase->getCrime(clueId);
 		if (crimeId != -1) {
@@ -143,7 +143,7 @@ void KIASectionClues::draw(Graphics::Surface &surface) {
 		} else {
 			text.clear();
 		}
-		_vm->_mainFont->drawColor(text, surface, 490, 442, surface.format.RGBToColor(136, 168, 255));
+		_vm->_mainFont->drawString(&surface, text, 490, 442, surface.w, surface.format.RGBToColor(136, 168, 255));
 
 		int assetType = _vm->_crimesDatabase->getAssetType(clueId);
 		if (assetType != -1) {
@@ -151,17 +151,17 @@ void KIASectionClues::draw(Graphics::Surface &surface) {
 		} else {
 			text.clear();
 		}
-		_vm->_mainFont->drawColor(text, surface, 490, 458, surface.format.RGBToColor(136, 168, 255));
+		_vm->_mainFont->drawString(&surface, text, 490, 458, surface.w, surface.format.RGBToColor(136, 168, 255));
 	}
 
 	_buttons->draw(surface);
 	_buttons->drawTooltip(surface, _mouseX, _mouseY);
 
 	if (_debugNop) {
-		_vm->_mainFont->drawColor(Common::String::format("Debug display: %s", _vm->_textActorNames->getText(_debugNop)), surface, 120, 132, surface.format.RGBToColor(255, 255, 0));
+		_vm->_mainFont->drawString(&surface, Common::String::format("Debug display: %s", _vm->_textActorNames->getText(_debugNop)), 120, 132, surface.w, surface.format.RGBToColor(255, 255, 0));
 	}
 	if (_debugIntangible) {
-		_vm->_mainFont->drawColor("Debug Mode: Showing intangible clues.", surface, 220, 105, surface.format.RGBToColor(255, 255, 0));
+		_vm->_mainFont->drawString(&surface, "Debug Mode: Showing intangible clues.", 220, 105, surface.w, surface.format.RGBToColor(255, 255, 0));
 	}
 }
 
diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp
index 9dbe090..6369a8f 100644
--- a/engines/bladerunner/ui/kia_section_crimes.cpp
+++ b/engines/bladerunner/ui/kia_section_crimes.cpp
@@ -142,7 +142,7 @@ void KIASectionCrimes::draw(Graphics::Surface &surface) {
 	}
 	if (_suspectPhotoShapeId == 14 || _suspectPhotoShapeId == 13) {
 		text = _vm->_textKIA->getText(49);
-		_vm->_mainFont->drawColor(text, surface, 201 - _vm->_mainFont->getTextWidth(text) / 2, 218, surface.format.RGBToColor(255, 255, 255));
+		_vm->_mainFont->drawString(&surface, text, 201 - _vm->_mainFont->getStringWidth(text) / 2, 218, surface.w, surface.format.RGBToColor(255, 255, 255));
 	}
 
 	surface.fillRect(Common::Rect(120, 134, 250, 145), 0);
@@ -158,7 +158,7 @@ void KIASectionCrimes::draw(Graphics::Surface &surface) {
 		text = _vm->_textCrimes->getText(_crimeSelected);
 	}
 
-	_vm->_mainFont->drawColor(text, surface, 185 - _vm->_mainFont->getTextWidth(text) / 2, 136, surface.format.RGBToColor(136, 168, 255));
+	_vm->_mainFont->drawString(&surface, text, 185 - _vm->_mainFont->getStringWidth(text) / 2, 136, surface.w, surface.format.RGBToColor(136, 168, 255));
 
 	surface.fillRect(Common::Rect(136, 304, 266, 315), 0);
 	surface.hLine(136, 303, 266, surface.format.RGBToColor(48, 40, 40));
@@ -182,7 +182,7 @@ void KIASectionCrimes::draw(Graphics::Surface &surface) {
 			text = generatedText;
 		}
 	}
-	_vm->_mainFont->drawColor(text, surface, 201 - _vm->_mainFont->getTextWidth(text) / 2, 306, surface.format.RGBToColor(136, 168, 255));
+	_vm->_mainFont->drawString(&surface, text, 201 - _vm->_mainFont->getStringWidth(text) / 2, 306, surface.w, surface.format.RGBToColor(136, 168, 255));
 
 	_uiContainer->draw(surface);
 	_buttons->draw(surface);
diff --git a/engines/bladerunner/ui/kia_section_diagnostic.cpp b/engines/bladerunner/ui/kia_section_diagnostic.cpp
index 9c1f57d..41ff43c 100644
--- a/engines/bladerunner/ui/kia_section_diagnostic.cpp
+++ b/engines/bladerunner/ui/kia_section_diagnostic.cpp
@@ -85,7 +85,7 @@ void KIASectionDiagnostic::draw(Graphics::Surface &surface) {
 
 			const char *text = _text->getText(i);
 			if (text) {
-				_vm->_mainFont->drawColor(text, surface, 320 - _vm->_mainFont->getTextWidth(text) / 2, y, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
+				_vm->_mainFont->drawString(&surface, text, 320 - _vm->_mainFont->getStringWidth(text) / 2, y, surface.w, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
 			}
 		}
 	}
diff --git a/engines/bladerunner/ui/kia_section_pogo.cpp b/engines/bladerunner/ui/kia_section_pogo.cpp
index 7b6022c..c5a3251 100644
--- a/engines/bladerunner/ui/kia_section_pogo.cpp
+++ b/engines/bladerunner/ui/kia_section_pogo.cpp
@@ -259,7 +259,7 @@ void KIASectionPogo::draw(Graphics::Surface &surface) {
 	}
 
 	const char *title = "We 3 coders give special thanks to:";
-	_vm->_mainFont->drawColor(title, surface, 313 - _vm->_mainFont->getTextWidth(title) / 2, 143, surface.format.RGBToColor(240, 232, 192));
+	_vm->_mainFont->drawString(&surface, title, 313 - _vm->_mainFont->getStringWidth(title) / 2, 143, surface.w, surface.format.RGBToColor(240, 232, 192));
 
 	int y = 158;
 	for (int i = 0; i < kLineCount; ++i) {
@@ -269,7 +269,7 @@ void KIASectionPogo::draw(Graphics::Surface &surface) {
 			} else {
 				_lineTexts[i] = _strings[_stringIndex];
 				_lineTimeouts[i] = 63;
-				_lineOffsets[i] = _vm->_rnd.getRandomNumberRng(0, 306 - _vm->_mainFont->getTextWidth(_lineTexts[i])) + 155;
+				_lineOffsets[i] = _vm->_rnd.getRandomNumberRng(0, 306 - _vm->_mainFont->getStringWidth(_lineTexts[i])) + 155;
 
 				_stringIndex = (_stringIndex + 1) % kStringCount;
 			}
@@ -281,7 +281,7 @@ void KIASectionPogo::draw(Graphics::Surface &surface) {
 				colorIndex = 63 - colorIndex;
 			}
 			colorIndex /= 2;
-			_vm->_mainFont->drawColor(_lineTexts[i], surface, _lineOffsets[i], y, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
+			_vm->_mainFont->drawString(&surface, _lineTexts[i], _lineOffsets[i], y, surface.w, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
 		}
 		y += 10;
 	}
diff --git a/engines/bladerunner/ui/kia_section_save.cpp b/engines/bladerunner/ui/kia_section_save.cpp
index daa92b4..b0f35b9 100644
--- a/engines/bladerunner/ui/kia_section_save.cpp
+++ b/engines/bladerunner/ui/kia_section_save.cpp
@@ -149,36 +149,36 @@ void KIASectionSave::draw(Graphics::Surface &surface) {
 
 	if (_state == kStateNormal) {
 		const char *textChooseSlot = _vm->_textOptions->getText(24); // Choose a slot ...
-		int textChooseSlotWidth = _vm->_mainFont->getTextWidth(textChooseSlot);
-		_vm->_mainFont->drawColor(textChooseSlot, surface, 308 - textChooseSlotWidth / 2, 143, surface.format.RGBToColor(240, 232, 192));
+		int textChooseSlotWidth = _vm->_mainFont->getStringWidth(textChooseSlot);
+		_vm->_mainFont->drawString(&surface, textChooseSlot, 308 - textChooseSlotWidth / 2, 143, surface.w, surface.format.RGBToColor(240, 232, 192));
 
 		// Original game shows warnings/error here, but we don't have any
 
 		const char *textTypeName = _vm->_textOptions->getText(24); // Type a name ...
-		int textTypeNameWidth = _vm->_mainFont->getTextWidth(textTypeName);
-		_vm->_mainFont->drawColor(textTypeName, surface, 308 - textTypeNameWidth / 2, 352, surface.format.RGBToColor(240, 232, 192));
+		int textTypeNameWidth = _vm->_mainFont->getStringWidth(textTypeName);
+		_vm->_mainFont->drawString(&surface, textTypeName, 308 - textTypeNameWidth / 2, 352, surface.w, surface.format.RGBToColor(240, 232, 192));
 
 		_uiContainer->draw(surface);
 	} else if (_state == kStateOverwrite) {
 		surface.fillRect(Common::Rect(155, 230, 462, 239), surface.format.RGBToColor(80, 56, 32));
 
 		const Common::String &saveName = _saveList[_selectedLineId].getDescription();
-		int saveNameWidth = _vm->_mainFont->getTextWidth(saveName);
-		_vm->_mainFont->drawColor(saveName, surface, 308 - saveNameWidth / 2, 230, surface.format.RGBToColor(232, 208, 136));
+		int saveNameWidth = _vm->_mainFont->getStringWidth(saveName);
+		_vm->_mainFont->drawString(&surface, saveName, 308 - saveNameWidth / 2, 230, surface.w, surface.format.RGBToColor(232, 208, 136));
 
 		const char *textOverwrite = _vm->_textOptions->getText(35); // Overwrite previously saved game?
-		int textOverwriteWidth = _vm->_mainFont->getTextWidth(textOverwrite);
-		_vm->_mainFont->drawColor(textOverwrite, surface, 308 - textOverwriteWidth / 2, 240, surface.format.RGBToColor(240, 232, 192));
+		int textOverwriteWidth = _vm->_mainFont->getStringWidth(textOverwrite);
+		_vm->_mainFont->drawString(&surface, textOverwrite, 308 - textOverwriteWidth / 2, 240, surface.w, surface.format.RGBToColor(240, 232, 192));
 	} else if (_state == kStateDelete) {
 		surface.fillRect(Common::Rect(155, 230, 462, 239), surface.format.RGBToColor(80, 56, 32));
 
 		const Common::String &saveName = _saveList[_selectedLineId].getDescription();
-		int saveNameWidth = _vm->_mainFont->getTextWidth(saveName); // Delete this game?
-		_vm->_mainFont->drawColor(saveName, surface, 308 - saveNameWidth / 2, 230, surface.format.RGBToColor(232, 208, 136));
+		int saveNameWidth = _vm->_mainFont->getStringWidth(saveName); // Delete this game?
+		_vm->_mainFont->drawString(&surface, saveName, 308 - saveNameWidth / 2, 230, surface.w, surface.format.RGBToColor(232, 208, 136));
 
 		const char *textDelete = _vm->_textOptions->getText(40);
-		int textDeleteWidth = _vm->_mainFont->getTextWidth(textDelete);
-		_vm->_mainFont->drawColor(textDelete, surface, 308 - textDeleteWidth / 2, 240, surface.format.RGBToColor(240, 232, 192));
+		int textDeleteWidth = _vm->_mainFont->getStringWidth(textDelete);
+		_vm->_mainFont->drawString(&surface, textDelete, 308 - textDeleteWidth / 2, 240, surface.w, surface.format.RGBToColor(240, 232, 192));
 	}
 
 	int selectedLineId = _scrollBox->getSelectedLineData();
diff --git a/engines/bladerunner/ui/kia_section_settings.cpp b/engines/bladerunner/ui/kia_section_settings.cpp
index be14372..4a85ed7 100644
--- a/engines/bladerunner/ui/kia_section_settings.cpp
+++ b/engines/bladerunner/ui/kia_section_settings.cpp
@@ -153,49 +153,49 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
 	const char *textLight = _vm->_textOptions->getText(15);
 #endif
 
-	int posConversationChoices = 320 - _vm->_mainFont->getTextWidth(textConversationChoices) / 2;
-	int posMusic = 320 - _vm->_mainFont->getTextWidth(textMusic) / 2;
-	int posSoundEffects = 320 - _vm->_mainFont->getTextWidth(textSoundEffects) / 2;
-	int posSpeech = 320 - _vm->_mainFont->getTextWidth(textSpeech) / 2;
-	int posSoft = 178 - _vm->_mainFont->getTextWidth(textSoft);
+	int posConversationChoices = 320 - _vm->_mainFont->getStringWidth(textConversationChoices) / 2;
+	int posMusic = 320 - _vm->_mainFont->getStringWidth(textMusic) / 2;
+	int posSoundEffects = 320 - _vm->_mainFont->getStringWidth(textSoundEffects) / 2;
+	int posSpeech = 320 - _vm->_mainFont->getStringWidth(textSpeech) / 2;
+	int posSoft = 178 - _vm->_mainFont->getStringWidth(textSoft);
 #if BLADERUNNER_ORIGINAL_SETTINGS
-	int posAmbientSound = 320 - _vm->_mainFont->getTextWidth(textAmbientSound) / 2;
-	int posGammaCorrection = 320 - _vm->_mainFont->getTextWidth(textGammaCorrection) / 2;
-	int posDark = 178 - _vm->_mainFont->getTextWidth(textDark);
+	int posAmbientSound = 320 - _vm->_mainFont->getStringWidth(textAmbientSound) / 2;
+	int posGammaCorrection = 320 - _vm->_mainFont->getStringWidth(textGammaCorrection) / 2;
+	int posDark = 178 - _vm->_mainFont->getStringWidth(textDark);
 #endif
 
 	_uiContainer->draw(surface);
 	_playerAgendaSelector->draw(surface);
 
-	_vm->_mainFont->drawColor(textConversationChoices, surface, posConversationChoices, 280, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textConversationChoices, posConversationChoices, 280, surface.w, surface.format.RGBToColor(232, 208, 136));
 
-	_vm->_mainFont->drawColor(textMusic, surface, posMusic, 150, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textSoft, surface, posSoft, 161, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLoud, surface, 462, 161, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textMusic, posMusic, 150, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textSoft, posSoft, 161, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLoud, 462, 161, surface.w, surface.format.RGBToColor(216, 184, 112));
 
-	_vm->_mainFont->drawColor(textSoundEffects, surface, posSoundEffects, 175, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textSoft, surface, posSoft, 186, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLoud, surface, 462, 186, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textSoundEffects, posSoundEffects, 175, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textSoft, posSoft, 186, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLoud, 462, 186, surface.w, surface.format.RGBToColor(216, 184, 112));
 
 #if BLADERUNNER_ORIGINAL_SETTINGS
-	_vm->_mainFont->drawColor(textAmbientSound, surface, posAmbientSound, 200, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textSoft, surface, posSoft, 211, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLoud, surface, 462, 211, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textAmbientSound, posAmbientSound, 200, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textSoft, posSoft, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLoud, 462, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
 
-	_vm->_mainFont->drawColor(textSpeech, surface, posSpeech, 225, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textSoft, surface, posSoft, 236, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLoud, surface, 462, 236, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textSpeech, posSpeech, 225, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textSoft, posSoft, 236, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLoud, 462, 236, surface.w, surface.format.RGBToColor(216, 184, 112));
 
-	_vm->_mainFont->drawColor(textGammaCorrection, surface, posGammaCorrection, 250, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textDark, surface, posDark, 261, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLight, surface, 462, 261, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textGammaCorrection, posGammaCorrection, 250, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textDark, posDark, 261, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLight, 462, 261, surface.w, surface.format.RGBToColor(216, 184, 112));
 #else
-	_vm->_mainFont->drawColor(textSpeech, surface, posSpeech, 200, surface.format.RGBToColor(232, 208, 136));
-	_vm->_mainFont->drawColor(textSoft, surface, posSoft, 211, surface.format.RGBToColor(216, 184, 112));
-	_vm->_mainFont->drawColor(textLoud, surface, 462, 211, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textSpeech, posSpeech, 200, surface.w, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textSoft, posSoft, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
+	_vm->_mainFont->drawString(&surface, textLoud, 462, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
 #endif
 
-	_vm->_mainFont->drawColor(textDesignersCut, surface, 192, 365, surface.format.RGBToColor(232, 208, 136));
+	_vm->_mainFont->drawString(&surface, textDesignersCut, 192, 365, surface.w, surface.format.RGBToColor(232, 208, 136));
 
 	if (_vm->_subtitles->isSystemActive()) {
 		// Allow this to be loading as an extra text item in the resource for text options
@@ -220,9 +220,9 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
 		const char *textSubtitles  = strcmp(_vm->_textOptions->getText(42), "") == 0? subtitlesTranslation : _vm->_textOptions->getText(42); // +1 to the max of original index of textOptions which is 41
 
 		if (_vm->_language == Common::RU_RUS) {
-			_vm->_mainFont->drawColor(textSubtitles, surface, 288, 376, surface.format.RGBToColor(232, 208, 136)); // special case for Russian version, put the option in a new line to avoid overlap
+			_vm->_mainFont->drawString(&surface, textSubtitles, 288, 376, surface.w, surface.format.RGBToColor(232, 208, 136)); // special case for Russian version, put the option in a new line to avoid overlap
 		} else {
-			_vm->_mainFont->drawColor(textSubtitles, surface, 323, 365, surface.format.RGBToColor(232, 208, 136)); // moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
+			_vm->_mainFont->drawString(&surface, textSubtitles, 323, 365, surface.w, surface.format.RGBToColor(232, 208, 136)); // moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
 		}
 	}
 
diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp
index 814e343..cfe3fc3 100644
--- a/engines/bladerunner/ui/kia_section_suspects.cpp
+++ b/engines/bladerunner/ui/kia_section_suspects.cpp
@@ -175,7 +175,7 @@ void KIASectionSuspects::draw(Graphics::Surface &surface) {
 	}
 	if (_suspectPhotoShapeId == 14 || _suspectPhotoShapeId == 13) {
 		text = _vm->_textKIA->getText(49);
-		_vm->_mainFont->drawColor(text, surface, 190 - _vm->_mainFont->getTextWidth(text) / 2, 201, surface.format.RGBToColor(255, 255, 255));
+		_vm->_mainFont->drawString(&surface, text, 190 - _vm->_mainFont->getStringWidth(text) / 2, 201, surface.w, surface.format.RGBToColor(255, 255, 255));
 	}
 
 	_whereaboutsCheckBox->setChecked(_whereaboutsFilter);
@@ -186,14 +186,14 @@ void KIASectionSuspects::draw(Graphics::Surface &surface) {
 
 	_uiContainer->draw(surface);
 
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(0),  surface, 300, 162, surface.format.RGBToColor(232, 240, 248));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(46), surface, 142, 248, surface.format.RGBToColor(232, 240, 248));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(47), surface, 142, 308, surface.format.RGBToColor(232, 240, 248));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(14), surface, 154, 319, surface.format.RGBToColor(72, 104, 152));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(15), surface, 154, 329, surface.format.RGBToColor(96, 120, 184));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(16), surface, 154, 339, surface.format.RGBToColor(112, 144, 216));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(17), surface, 154, 349, surface.format.RGBToColor(96, 120, 184));
-	_vm->_mainFont->drawColor(_vm->_textKIA->getText(48), surface, 154, 359, surface.format.RGBToColor(72, 104, 152));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(0),  300, 162, surface.w, surface.format.RGBToColor(232, 240, 248));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(46), 142, 248, surface.w, surface.format.RGBToColor(232, 240, 248));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(47), 142, 308, surface.w, surface.format.RGBToColor(232, 240, 248));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(14), 154, 319, surface.w, surface.format.RGBToColor(72, 104, 152));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(15), 154, 329, surface.w, surface.format.RGBToColor(96, 120, 184));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(16), 154, 339, surface.w, surface.format.RGBToColor(112, 144, 216));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(17), 154, 349, surface.w, surface.format.RGBToColor(96, 120, 184));
+	_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(48), 154, 359, surface.w, surface.format.RGBToColor(72, 104, 152));
 
 
 	surface.fillRect(Common::Rect(120, 134, 250, 145), 0);
@@ -219,7 +219,7 @@ void KIASectionSuspects::draw(Graphics::Surface &surface) {
 		}
 	}
 
-	_vm->_mainFont->drawColor(text, surface, 185 - _vm->_mainFont->getTextWidth(text) / 2, 136, surface.format.RGBToColor(136, 168, 248));
+	_vm->_mainFont->drawString(&surface, text, 185 - _vm->_mainFont->getStringWidth(text) / 2, 136, surface.w, surface.format.RGBToColor(136, 168, 248));
 
 	_buttons->draw(surface);
 	_buttons->drawTooltip(surface, _mouseX, _mouseY);
diff --git a/engines/bladerunner/ui/scores.cpp b/engines/bladerunner/ui/scores.cpp
index 4c4cb22..399e112 100644
--- a/engines/bladerunner/ui/scores.cpp
+++ b/engines/bladerunner/ui/scores.cpp
@@ -64,9 +64,7 @@ void Scores::open() {
 	_txtScorers = new TextResource(_vm);
 	_txtScorers->open("SCORERS");
 
-	_font = new Font(_vm);
-	_font->open("TAHOMA24.FON", 640, 480, -1, 0, 0);
-	_font->setSpacing(1, 0);
+	_font = Font::load(_vm, "TAHOMA24.FON", 1, true);
 
 	fill();
 
@@ -142,20 +140,20 @@ void Scores::tick() {
 	_vm->_surfaceFront.hLine(200, 139, 400, _vm->_surfaceFront.format.RGBToColor(0, 248, 0));
 	_vm->_surfaceFront.hLine(200, 347, 400, _vm->_surfaceFront.format.RGBToColor(0, 0, 248));
 
-	_font->draw(_txtScorers->getText(7), _vm->_surfaceFront, 200, 114);
+	_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(7), 200, 114, _vm->_surfaceFront.w, 0);
 
 	int y = 140;
 
 	for (int i = 0; i < 7; i++) {
-		_font->draw(_txtScorers->getText(_scorers[i]), _vm->_surfaceFront, 220, y);
-		_font->drawNumber(_scores[_scorers[i]], _vm->_surfaceFront, 360, y);
+		_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(_scorers[i]), 220, y, _vm->_surfaceFront.w, 0);
+		_font->drawString(&_vm->_surfaceFront, Common::String::format("%d", _scores[_scorers[i]]), 360, y, _vm->_surfaceFront.w, 0);
 
 		y += 26;
 	}
 
-	_font->draw(_txtScorers->getText(8), _vm->_surfaceFront, 200, 322);
-	_font->draw(_txtScorers->getText(_lastScoreId), _vm->_surfaceFront, 220, 348);
-	_font->drawNumber(_lastScoreValue, _vm->_surfaceFront, 360, 348);
+	_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(8), 200, 322, _vm->_surfaceFront.w, 0);
+	_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(_lastScoreId), 220, 348, _vm->_surfaceFront.w, 0);
+	_font->drawString(&_vm->_surfaceFront, Common::String::format("%d", _lastScoreValue), 360, 348, _vm->_surfaceFront.w, 0);
 
 	_vm->blitToScreen(_vm->_surfaceFront);
 }
diff --git a/engines/bladerunner/ui/ui_image_picker.cpp b/engines/bladerunner/ui/ui_image_picker.cpp
index dfc897e..0b1a0dd 100644
--- a/engines/bladerunner/ui/ui_image_picker.cpp
+++ b/engines/bladerunner/ui/ui_image_picker.cpp
@@ -214,7 +214,7 @@ void UIImagePicker::draw(Graphics::Surface &surface) {
 
 		if (_vm->_debugger->_viewUI) {
 			surface.frameRect(img.rect, surface.format.RGBToColor(255, 255, 255));
-			_vm->_mainFont->drawColor(Common::String::format("%d", i), surface, (img.rect.left + img.rect.right) / 2, (img.rect.top + img.rect.bottom) / 2, surface.format.RGBToColor(255, 255, 255));
+			_vm->_mainFont->drawString(&surface, Common::String::format("%d", i), (img.rect.left + img.rect.right) / 2, (img.rect.top + img.rect.bottom) / 2, surface.w, surface.format.RGBToColor(255, 255, 255));
 		}
 	}
 }
@@ -239,8 +239,8 @@ void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) {
 		return;
 	}
 
-	int width = _vm->_mainFont->getTextWidth(tooltip) + 1;
-	int height = _vm->_mainFont->getTextHeight(tooltip) + 1;
+	int width = _vm->_mainFont->getStringWidth(tooltip) + 1;
+	int height = _vm->_mainFont->getFontHeight() + 1;
 
 	Common::Rect rect;
 	rect.left = x - ((width / 2) + 1);
@@ -267,7 +267,7 @@ void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) {
 
 	surface.fillRect(rect, surface.format.RGBToColor(0, 0, 0));
 	surface.frameRect(rect, surface.format.RGBToColor(255, 255, 255));
-	_vm->_mainFont->drawColor(tooltip, surface, rect.left + 2, rect.top, surface.format.RGBToColor(255, 255, 255));
+	_vm->_mainFont->drawString(&surface, tooltip, rect.left + 2, rect.top, surface.w, surface.format.RGBToColor(255, 255, 255));
 }
 
 bool UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) {
diff --git a/engines/bladerunner/ui/ui_input_box.cpp b/engines/bladerunner/ui/ui_input_box.cpp
index 5b62ada..be12f94 100644
--- a/engines/bladerunner/ui/ui_input_box.cpp
+++ b/engines/bladerunner/ui/ui_input_box.cpp
@@ -53,9 +53,9 @@ void UIInputBox::draw(Graphics::Surface &surface) {
 	}
 
 	int rectHalfWidth = (_rect.right + _rect.left) / 2;
-	int textHalfWidth = _vm->_mainFont->getTextWidth(_text) / 2;
+	int textHalfWidth = _vm->_mainFont->getStringWidth(_text) / 2;
 
-	_vm->_mainFont->drawColor(_text, surface, rectHalfWidth - textHalfWidth, _rect.top, surface.format.RGBToColor(152, 112, 56));
+	_vm->_mainFont->drawString(&surface, _text, rectHalfWidth - textHalfWidth, _rect.top, surface.w, surface.format.RGBToColor(152, 112, 56));
 
 	if (_cursorIsVisible) {
 		surface.vLine(textHalfWidth + rectHalfWidth + 2, _rect.top, _rect.bottom - 1, surface.format.RGBToColor(248, 240, 232));
diff --git a/engines/bladerunner/ui/ui_scroll_box.cpp b/engines/bladerunner/ui/ui_scroll_box.cpp
index 00a9f5f..48796a2 100644
--- a/engines/bladerunner/ui/ui_scroll_box.cpp
+++ b/engines/bladerunner/ui/ui_scroll_box.cpp
@@ -540,10 +540,10 @@ void UIScrollBox::draw(Graphics::Surface &surface) {
 			}
 
 			if (_center) {
-				x = _rect.left + (_rect.width() - _vm->_mainFont->getTextWidth(_lines[i]->text)) / 2;
+				x = _rect.left + (_rect.width() - _vm->_mainFont->getStringWidth(_lines[i]->text)) / 2;
 			}
 
-			_vm->_mainFont->drawColor(_lines[i]->text, surface, x, y, color);
+			_vm->_mainFont->drawString(&surface, _lines[i]->text, x, y, surface.w, color);
 
 			y1 += kLineHeight;
 			y2 += kLineHeight;


Commit: a767a6800d2f76b90b66d7c0dcb96c0514350721
    https://github.com/scummvm/scummvm/commit/a767a6800d2f76b90b66d7c0dcb96c0514350721
Author: Peter Kohaut (peter.kohaut at gmail.com)
Date: 2019-07-16T21:32:34+02:00

Commit Message:
BLADERUNNER: Added TTF & UTF8 support for subtitles

Changed paths:
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/debugger.cpp
    engines/bladerunner/subtitles.cpp
    engines/bladerunner/subtitles.h


diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 24554c0..412ef1d 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -476,6 +476,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 	_surfaceBack.create(640, 480, screenPixelFormat());
 
 	_time = new Time(this);
+
 	// Try to load the SUBTITLES.MIX first, before Startup.MIX
 	// allows overriding any identically named resources (such as the original font files and as a bonus also the TRE files for the UI and dialogue menu)
 	_subtitles = new Subtitles(this);
diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp
index 8021045..cd34aff 100644
--- a/engines/bladerunner/debugger.cpp
+++ b/engines/bladerunner/debugger.cpp
@@ -1295,8 +1295,8 @@ bool Debugger::cmdSubtitle(int argc, const char **argv) {
 			            _vm->_subtitles->getSubtitlesInfo().dateOfCompile.c_str(),
 			            _vm->_subtitles->getSubtitlesInfo().languageMode.c_str(),
 			            _vm->_subtitles->getSubtitlesInfo().credits.c_str());
-			debugPrintf("Subtitles fonts loaded: %s\n",
-			            _vm->_subtitles->isSubsFontsLoaded()? "True":"False");
+			debugPrintf("Subtitles font loaded: %s\n",
+			            _vm->_subtitles->getSubtitlesInfo().fontName.c_str());
 
 		} else if (subtitleText == "reset") {
 			_vm->_subtitles->setGameSubsText("", false);
diff --git a/engines/bladerunner/subtitles.cpp b/engines/bladerunner/subtitles.cpp
index f7c9923..dcd0f21 100644
--- a/engines/bladerunner/subtitles.cpp
+++ b/engines/bladerunner/subtitles.cpp
@@ -25,8 +25,12 @@
 #include "bladerunner/font.h"
 #include "bladerunner/text_resource.h"
 #include "bladerunner/audio_speech.h"
+
 #include "common/debug.h"
 
+#include "graphics/font.h"
+#include "graphics/fonts/ttf.h"
+
 namespace BladeRunner {
 
 /*
@@ -101,7 +105,7 @@ Subtitles::Subtitles(BladeRunnerEngine *vm) {
 	for (int i = 0; i < kMaxTextResourceEntries; i++) {
 		_vqaSubsTextResourceEntries[i] = nullptr;
 	}
-	_subsFont = nullptr;
+	_font = nullptr;
 	reset();
 }
 
@@ -109,26 +113,74 @@ Subtitles::Subtitles(BladeRunnerEngine *vm) {
 * Subtitles Destructor
 */
 Subtitles::~Subtitles() {
-	// delete any resource entries in the _vqaSubsTextResourceEntries table
-	// and close any open text resource files
-	for (int i = 0; i != kMaxTextResourceEntries; ++i) {
-		if (_vqaSubsTextResourceEntries[i] != nullptr) {
-			delete _vqaSubsTextResourceEntries[i];
-			_vqaSubsTextResourceEntries[i] = nullptr;
-		}
-	}
-
-	if (_subsFont != nullptr) {
-		delete _subsFont;
-		_subsFont = nullptr;
-	}
+	reset();
 }
 
 //
 // Init is kept separated from constructor to allow not loading up resources if subtitles system is disabled
 //
 void Subtitles::init(void) {
-	_subtitlesSystemActive = true;
+	// Loading subtitles versioning info if available
+	TextResource *versionTxtResource = new TextResource(_vm);
+	if ( versionTxtResource->open(SUBTITLES_VERSION_TRENAME, false)) {
+		_subtitlesInfo.credits = versionTxtResource->getText((uint32)0);
+		_subtitlesInfo.versionStr = versionTxtResource->getText((uint32)1);
+		_subtitlesInfo.dateOfCompile = versionTxtResource->getText((uint32)2);
+		_subtitlesInfo.languageMode = versionTxtResource->getText((uint32)3);
+		Common::String fontType = versionTxtResource->getText((uint32)4);
+		_subtitlesInfo.fontName = versionTxtResource->getText((uint32)5);
+
+		if (fontType.equalsIgnoreCase("ttf")) {
+			_subtitlesInfo.fontType = Subtitles::kSubtitlesFontTypeTTF;
+		} else {
+			_subtitlesInfo.fontType = Subtitles::kSubtitlesFontTypeInternal;
+		}
+
+		if ( _subtitlesInfo.fontName.empty()) {
+			_subtitlesInfo.fontName = "SUBTLS_E.FON";
+		}
+
+		debug("Subtitles version info: v%s (%s) %s by: %s",
+		       _subtitlesInfo.versionStr.c_str(),
+		       _subtitlesInfo.dateOfCompile.c_str(),
+		       _subtitlesInfo.languageMode.c_str(),
+		       _subtitlesInfo.credits.c_str());
+
+		delete versionTxtResource;
+		versionTxtResource = nullptr;
+	} else {
+		debug("Subtitles version info: N/A");
+	}
+
+	//
+	// Initializing/Loading Subtitles Fonts
+	if (_subtitlesInfo.fontType == Subtitles::kSubtitlesFontTypeInternal) {
+		// Use TAHOMA18.FON (is corrupted in places)
+		// 10PT or TAHOMA24 or KIA6PT  have all caps glyphs (and also are too big or too small) so they are not appropriate.
+		_font = Font::load(_vm, _subtitlesInfo.fontName, -1, true);
+		_useUTF8 = false;
+	} else if (_subtitlesInfo.fontType == Subtitles::kSubtitlesFontTypeTTF) {
+#if defined(USE_FREETYPE2)
+		Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->getResourceStream(_subtitlesInfo.fontName));
+		// Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember("NotoSansCJKsc-Medium.otf"));
+
+		_font = Graphics::loadTTFFont(*stream, 18);
+		_useUTF8 = true;
+#else
+		warning("Subtitles require a TTF font but this ScummVM build doesn't support it.");
+		return;
+#endif
+	}
+
+	if (_font) {
+		debug("Subtitles font '%s' was loaded successfully.", _subtitlesInfo.fontName.c_str());
+	} else {
+		warning("Subtitles font '%s' could not be loaded.", _subtitlesInfo.fontName.c_str());
+		return;
+	}
+	//Done - Initializing/Loading Subtitles Fonts
+	//
+
 	//
 	// Loading text resources
 	for (int i = 0; i < kMaxTextResourceEntries; i++) {
@@ -149,49 +201,16 @@ void Subtitles::init(void) {
 	}
 	// Done - Loading text resources
 	//
-	// Initializing/Loading Subtitles Fonts
-	_subsFont = Font::load(_vm, SUBTITLES_FONT_FILENAME_EXTERNAL, -1, true);
-	// Use TAHOMA18.FON (is corrupted in places)
-	// 10PT or TAHOMA24 or KIA6PT  have all caps glyphs (and also are too big or too small) so they are not appropriate.
-	if (_subsFont) { // Color setting does not seem to affect the TAHOMA fonts or does it affect the black outline since we give 0 here?
-		_subsFontsLoaded = true;
-	} else {
-		_subsFontsLoaded = false;
-	}
 
-	//Done - Initializing/Loading Subtitles Fonts
 	//
 	// calculate the Screen Y position of the subtitle lines
 	// getTextHeight("") returns the maxHeight of the font glyphs regardless of the actual text parameter
 	//  debug("Max height %d", _subsFont->getTextHeight(""));
-	if (_subsFontsLoaded) {
-		for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
-			_subtitleLineScreenY[i] = 479 - kSubtitlesBottomYOffsetPx - ((kMaxNumOfSubtitlesLines - i) * (_subsFont->getFontHeight() + 1));
-		}
+	for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
+		_subtitleLineScreenY[i] = 479 - kSubtitlesBottomYOffsetPx - ((kMaxNumOfSubtitlesLines - i) * (_font->getFontHeight() + 1));
 	}
 
-	// Loading subtitles versioning info if available
-	TextResource *versionTxtResource = new TextResource(_vm);
-	if ( versionTxtResource->open(SUBTITLES_VERSION_TRENAME, false)) {
-		_subtitlesInfo.credits = versionTxtResource->getText((uint32)0);
-		_subtitlesInfo.versionStr = versionTxtResource->getText((uint32)1);
-		_subtitlesInfo.dateOfCompile = versionTxtResource->getText((uint32)2);
-		_subtitlesInfo.languageMode = versionTxtResource->getText((uint32)3);
-		debug("Subtitles version info: v%s (%s) %s by: %s",
-		       _subtitlesInfo.versionStr.c_str(),
-		       _subtitlesInfo.dateOfCompile.c_str(),
-		       _subtitlesInfo.languageMode.c_str(),
-		       _subtitlesInfo.credits.c_str());
-		if (isSubsFontsLoaded()) {
-			debug("Subtitles font was loaded successfully.");
-		} else {
-			debug("Subtitles font could not be loaded.");
-		}
-		delete versionTxtResource;
-		versionTxtResource = nullptr;
-	} else {
-		debug("Subtitles version info: N/A");
-	}
+	_subtitlesSystemActive = true;
 }
 
 Subtitles::SubtitlesInfo Subtitles::getSubtitlesInfo() const {
@@ -368,7 +387,7 @@ void Subtitles::tick(Graphics::Surface &s) {
 * Draw method for drawing the subtitles on the display surface
 */
 void Subtitles::draw(Graphics::Surface &s) {
-	if (!_isVisible || _currentSubtitleTextFull.empty() || !_subsFontsLoaded) {
+	if (!_subtitlesSystemActive || !_isVisible || _currentSubtitleTextFull.empty()) {
 		return;
 	}
 	if (_subtitlesQuoteChanged) {
@@ -389,7 +408,20 @@ void Subtitles::draw(Graphics::Surface &s) {
 	}
 
 	for (int i = 0, j = startingLineFromTop; i < _currentSubtitleLines; ++i, ++j) {
-		_subsFont->drawString(&s, _subtitleLineQuote[i], _subtitleLineScreenX[i], _subtitleLineScreenY[j], s.w, 0);
+		Common::U32String text = _useUTF8 ? convertUtf8ToUtf32(_subtitleLineQuote[i]) : _subtitleLineQuote[i];
+		switch (_subtitlesInfo.fontType) {
+			case Subtitles::kSubtitlesFontTypeInternal:
+				// shadow/outline is part of the font color data
+				_font->drawString(&s, text, _subtitleLineScreenX[i], _subtitleLineScreenY[j], s.w, 0);
+				break;
+			case Subtitles::kSubtitlesFontTypeTTF:
+				_font->drawString(&s, text, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[j] - 1, s.w, s.format.RGBToColor(  0,   0,   0));
+				_font->drawString(&s, text, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[j] - 1, s.w, s.format.RGBToColor(  0,   0,   0));
+				_font->drawString(&s, text, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[j] + 1, s.w, s.format.RGBToColor(  0,   0,   0));
+				_font->drawString(&s, text, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[j] + 1, s.w, s.format.RGBToColor(  0,   0,   0));
+				_font->drawString(&s, text, _subtitleLineScreenX[i]    , _subtitleLineScreenY[j]    , s.w, s.format.RGBToColor(255, 255, 255));
+				break;
+		}
 	}
 }
 
@@ -418,9 +450,8 @@ void Subtitles::draw(Graphics::Surface &s) {
 * TODO: somehow merge with graphics/font.cpp -> wordWrapTextImpl ?
 */
 void Subtitles::calculatePosition() {
-
 	// wOrig is in pixels, origQuoteNumOfChars is num of chars in string
-	int wOrig = _subsFont->getStringWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
+	int wOrig = _font->getStringWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
 	int origQuoteNumOfChars = _currentSubtitleTextFull.size();
 	int tmpCharIndex = 0;
 	bool drawSingleLineQuote = false;
@@ -466,7 +497,7 @@ void Subtitles::calculatePosition() {
 		//
 		// Check widths and set starting X positions per line
 		for (int k = 0; k < _currentSubtitleLines; ++k) {
-			tmpLineWidth[k] = _subsFont->getStringWidth(_subtitleLineQuote[k]) + 2;
+			tmpLineWidth[k] = _font->getStringWidth(_subtitleLineQuote[k]) + 2;
 			_subtitleLineScreenX[k] = (639 - tmpLineWidth[k]) / 2;
 			_subtitleLineScreenX[k] = CLIP(_subtitleLineScreenX[k], 0, 639 - tmpLineWidth[k]);
 		}
@@ -496,7 +527,7 @@ void Subtitles::calculatePosition() {
 					}
 					_subtitleLineQuote[0] += '\0';
 //                    debug(" Line 0 quote %s", _subtitleLineQuote[0].c_str());
-					tmpLineWidth[0] = _subsFont->getStringWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
+					tmpLineWidth[0] = _font->getStringWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
 					if (tmpLineWidth[0] > kMaxWidthPerLineToAutoSplitThresholdPx && linesToSplitInto < kMaxNumOfSubtitlesLines) {
 						// we exceed max width so we reset process by trying to split into more lines
 						continue; // re-try the For-loop with increased linesToSplitInto by 1
@@ -521,7 +552,7 @@ void Subtitles::calculatePosition() {
 						//
 						// Check widths and set starting X positions per line
 						for (int j = 0; j < _currentSubtitleLines; ++j) {
-							tmpLineWidth[j] = _subsFont->getStringWidth(_subtitleLineQuote[j]) + 2;
+							tmpLineWidth[j] = _font->getStringWidth(_subtitleLineQuote[j]) + 2;
 							_subtitleLineScreenX[j] = (639 - tmpLineWidth[j]) / 2;
 							_subtitleLineScreenX[j] = CLIP(_subtitleLineScreenX[j], 0, 639 - tmpLineWidth[j]);
 						}
@@ -582,12 +613,11 @@ void Subtitles::reset() {
 		_gameSubsResourceEntriesFound[i] = false;
 	}
 
-	if (_subsFont != nullptr) {
-		delete _subsFont;
-		_subsFont = nullptr;
+	if (_font != nullptr) {
+		delete _font;
+		_font = nullptr;
 	}
-
-	_subsFontsLoaded = false;
+	_useUTF8 = false;
 }
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/subtitles.h b/engines/bladerunner/subtitles.h
index 674dde4..eb45a35 100644
--- a/engines/bladerunner/subtitles.h
+++ b/engines/bladerunner/subtitles.h
@@ -26,17 +26,15 @@
 #include "bladerunner/bladerunner.h"
 
 #include "common/str.h"
-#include "graphics/surface.h"
-
-#include "common/file.h"
-#include "common/substream.h"
 
+namespace Graphics {
+class Font;
+}
 
 namespace BladeRunner {
 
 class BladeRunnerEngine;
 class TextResource;
-class Font;
 
 class Subtitles {
 	friend class Debugger;
@@ -54,16 +52,25 @@ class Subtitles {
 
 	BladeRunnerEngine *_vm;
 
+	enum SubtitlesFontType {
+		kSubtitlesFontTypeInternal,
+		kSubtitlesFontTypeTTF
+	};
+
 	struct SubtitlesInfo {
-		Common::String versionStr;
-		Common::String dateOfCompile;
-		Common::String languageMode;
-		Common::String credits;
+		Common::String    versionStr;
+		Common::String    dateOfCompile;
+		Common::String    languageMode;
+		Common::String    credits;
+		SubtitlesFontType fontType;
+		Common::String    fontName;
 	};
 
 	SubtitlesInfo  _subtitlesInfo;
-	TextResource *_vqaSubsTextResourceEntries[kMaxTextResourceEntries];
-	Font         *_subsFont;
+	TextResource  *_vqaSubsTextResourceEntries[kMaxTextResourceEntries];
+
+	Graphics::Font *_font;
+	bool            _useUTF8;
 
 	bool           _isVisible;
 	bool           _forceShowWhenNoSpeech;
@@ -76,7 +83,6 @@ class Subtitles {
 	bool           _subtitlesQuoteChanged;
 
 	bool _gameSubsResourceEntriesFound[kMaxTextResourceEntries]; // false if a TRE file did not open successfully
-	bool _subsFontsLoaded;                                       // false if external fonts did not load
 	bool _subtitlesSystemActive;                                 // true if the whole subtitles subsystem should be disabled (due to missing required resources)
 
 public:
@@ -84,7 +90,6 @@ public:
 	~Subtitles();
 
 	bool isSystemActive() const { return _subtitlesSystemActive; }
-	bool isSubsFontsLoaded() const { return _subsFontsLoaded; }
 
 	void init();
 	SubtitlesInfo getSubtitlesInfo() const;


Commit: 39e4fdb98c27f97ffffcfba8b6579697a4c809d2
    https://github.com/scummvm/scummvm/commit/39e4fdb98c27f97ffffcfba8b6579697a4c809d2
Author: Peter Kohaut (peter.kohaut at gmail.com)
Date: 2019-07-16T21:56:33+02:00

Commit Message:
BLADERUNNER: Use ScummVM word wrapping algorithm

Changed paths:
    engines/bladerunner/actor.cpp
    engines/bladerunner/outtake.cpp
    engines/bladerunner/subtitles.cpp
    engines/bladerunner/subtitles.h


diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index 62ffd6f..84febcc 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -1181,7 +1181,7 @@ void Actor::speechPlay(int sentenceId, bool voiceOver) {
 		pan = (75 * (2 *  CLIP<int>(screenPosition.x, 0, 640) - 640)) / 640; // map [0..640] to [-75..75]
 	}
 
-	_vm->_subtitles->getInGameSubsText(_id, sentenceId);
+	_vm->_subtitles->loadInGameSubsText(_id, sentenceId);
 	_vm->_subtitles->show();
 
 	_vm->_audioSpeech->playSpeech(name, pan);
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index 74ac44a..112a120 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -83,7 +83,7 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 		}
 
 		if (frame >= 0) {
-			_vm->_subtitles->getOuttakeSubsText(resNameNoVQASuffix, frame);
+			_vm->_subtitles->loadOuttakeSubsText(resNameNoVQASuffix, frame);
 			_vm->_subtitles->tickOuttakes(_vm->_surfaceFront);
 			_vm->blitToScreen(_vm->_surfaceFront);
 		}
diff --git a/engines/bladerunner/subtitles.cpp b/engines/bladerunner/subtitles.cpp
index dcd0f21..2170638 100644
--- a/engines/bladerunner/subtitles.cpp
+++ b/engines/bladerunner/subtitles.cpp
@@ -62,10 +62,10 @@ const char *Subtitles::SUBTITLES_FONT_FILENAME_EXTERNAL = "SUBTLS_E.FON";
 
 const char *Subtitles::SUBTITLES_VERSION_TRENAME        = "SBTLVERS"; // addon resource file for Subtitles version info - can only be SBTLVERS.TRE
 /*
-* All entries need to have the language code appended (after a '_').
-* And all entries should get the suffix extension ".TRx"; the last letter in extension "TR*" should also be the language code
-* If/When adding new Text Resources here --> Update kMaxTextResourceEntries and also update method getIdxForSubsTreName()
-*/
+ * All entries need to have the language code appended (after a '_').
+ * And all entries should get the suffix extension ".TRx"; the last letter in extension "TR*" should also be the language code
+ * If/When adding new Text Resources here --> Update kMaxTextResourceEntries and also update method getIdxForSubsTreName()
+ */
 const char *Subtitles::SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries] = {
 	"INGQUO",           // 0 // (in-game subtitles, not VQA subtitles)
 	"WSTLGO",           // 1 // all game (language) versions have the English ('E') version of WSTLGO
@@ -96,22 +96,23 @@ const char *Subtitles::SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries] = {
 };
 
 /**
-* Subtitles Constructor
-*/
+ * Subtitles Constructor
+ */
 Subtitles::Subtitles(BladeRunnerEngine *vm) {
 	_vm = vm;
-	_subtitlesSystemActive = false;
-	// Initializing and reseting Subtitles
-	for (int i = 0; i < kMaxTextResourceEntries; i++) {
+	_isSystemActive = false;
+	for (int i = 0; i < kMaxTextResourceEntries; ++i) {
 		_vqaSubsTextResourceEntries[i] = nullptr;
+		_gameSubsResourceEntriesFound[i] = false;
 	}
 	_font = nullptr;
+	_useUTF8 = false;
 	reset();
 }
 
 /**
-* Subtitles Destructor
-*/
+ * Subtitles Destructor
+ */
 Subtitles::~Subtitles() {
 	reset();
 }
@@ -136,8 +137,8 @@ void Subtitles::init(void) {
 			_subtitlesInfo.fontType = Subtitles::kSubtitlesFontTypeInternal;
 		}
 
-		if ( _subtitlesInfo.fontName.empty()) {
-			_subtitlesInfo.fontName = "SUBTLS_E.FON";
+		if (_subtitlesInfo.fontName.empty()) {
+			_subtitlesInfo.fontName = SUBTITLES_FONT_FILENAME_EXTERNAL;
 		}
 
 		debug("Subtitles version info: v%s (%s) %s by: %s",
@@ -162,8 +163,6 @@ void Subtitles::init(void) {
 	} else if (_subtitlesInfo.fontType == Subtitles::kSubtitlesFontTypeTTF) {
 #if defined(USE_FREETYPE2)
 		Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->getResourceStream(_subtitlesInfo.fontName));
-		// Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember("NotoSansCJKsc-Medium.otf"));
-
 		_font = Graphics::loadTTFFont(*stream, 18);
 		_useUTF8 = true;
 #else
@@ -195,22 +194,14 @@ void Subtitles::init(void) {
 			tmpConstructedFileName = Common::String(SUBTITLES_FILENAME_PREFIXES[i]) + "_" + _vm->_languageCode;
 		}
 
-		if ( _vqaSubsTextResourceEntries[i]->open(tmpConstructedFileName, localizedResource)) {
+		if (_vqaSubsTextResourceEntries[i]->open(tmpConstructedFileName, localizedResource)) {
 			_gameSubsResourceEntriesFound[i] = true;
 		}
 	}
 	// Done - Loading text resources
 	//
 
-	//
-	// calculate the Screen Y position of the subtitle lines
-	// getTextHeight("") returns the maxHeight of the font glyphs regardless of the actual text parameter
-	//  debug("Max height %d", _subsFont->getTextHeight(""));
-	for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
-		_subtitleLineScreenY[i] = 479 - kSubtitlesBottomYOffsetPx - ((kMaxNumOfSubtitlesLines - i) * (_font->getFontHeight() + 1));
-	}
-
-	_subtitlesSystemActive = true;
+	_isSystemActive = true;
 }
 
 Subtitles::SubtitlesInfo Subtitles::getSubtitlesInfo() const {
@@ -218,9 +209,8 @@ Subtitles::SubtitlesInfo Subtitles::getSubtitlesInfo() const {
 }
 
 /**
-*
-* Returns the index of the specified Text Resource filename in the SUBTITLES_FILENAME_PREFIXES table
-*/
+ * Returns the index of the specified Text Resource filename in the SUBTITLES_FILENAME_PREFIXES table
+ */
 int Subtitles::getIdxForSubsTreName(const Common::String &treName) const {
 	Common::String tmpConstructedFileName = "";
 	for (int i = 0; i < kMaxTextResourceEntries; ++i) {
@@ -238,94 +228,77 @@ int Subtitles::getIdxForSubsTreName(const Common::String &treName) const {
 }
 
 /**
-* Get the active subtitle text by searching with actor ID and speech ID
-* Use this method for in-game dialogue - Not dialogue during a VQA cutscene
-* Returns the dialogue quote, but also sets the private _currentSubtitleTextFull member
-*/
-const char *Subtitles::getInGameSubsText(int actorId, int speech_id)  {
-	if (!_subtitlesSystemActive) {
-		return "";
+ * Get the active subtitle text by searching with actor ID and speech ID
+ * Use this method for in-game dialogue - Not dialogue during a VQA cutscene
+ */
+void Subtitles::loadInGameSubsText(int actorId, int speech_id)  {
+	if (!_isSystemActive) {
+		return;
 	}
 
 	int32 id = 10000 * actorId + speech_id;
 	if (!_gameSubsResourceEntriesFound[0]) {
-		if (_currentSubtitleTextFull  != "") {
-			_currentSubtitleTextFull = "";
-			_subtitlesQuoteChanged = true;
-		}
-		return "";
+		_currentText.clear();
+		return;
 	}
+
 	// Search in the first TextResource of the _vqaSubsTextResourceEntries table, which is the TextResource for in-game dialogue (i.e. not VQA dialogue)
-	const Common::String &text = _vqaSubsTextResourceEntries[0]->getText((uint32)id);
-	_currentSubtitleTextFull = Common::String(text);
-	_subtitlesQuoteChanged = true;
-	return _currentSubtitleTextFull.c_str();
+	const char *text = _vqaSubsTextResourceEntries[0]->getText((uint32)id);
+	_currentText = _useUTF8 ? Common::convertUtf8ToUtf32(text) : Common::U32String(text);
 }
 
 /**
-* Use this method for dialogue during VQA cutscenes
-* Returns the dialogue quote, but also sets the private _currentSubtitleTextFull member
-*/
-const char *Subtitles::getOuttakeSubsText(const Common::String &outtakesName, int frame) {
-	if (!_subtitlesSystemActive) {
-		return "";
+ * Use this method for dialogue during VQA cutscenes
+ */
+void Subtitles::loadOuttakeSubsText(const Common::String &outtakesName, int frame) {
+	if (!_isSystemActive) {
+		return;
 	}
 
 	int fileIdx = getIdxForSubsTreName(outtakesName);
 	if (fileIdx == -1 || !_gameSubsResourceEntriesFound[fileIdx]) {
-		if (_currentSubtitleTextFull != "") {
-			_currentSubtitleTextFull = "";
-			_subtitlesQuoteChanged = true;
-		}
-		return "";
+		_currentText.clear();
+		return;
 	}
+
 	// Search in the requested TextResource at the fileIdx index of the _vqaSubsTextResourceEntries table for a quote that corresponds to the specified video frame
 	// debug("Number of resource quotes to search: %d, requested frame: %u", _vqaSubsTextResourceEntries[fileIdx]->getCount(), (uint32)frame );
-	const Common::String &text = _vqaSubsTextResourceEntries[fileIdx]->getOuttakeTextByFrame((uint32)frame);
-	//if (text != "") {
-	//    debug("Text = %s", text.c_str());
-	//}
-	if (_currentSubtitleTextFull != Common::String(text)) {
-		_currentSubtitleTextFull = Common::String(text);
-		_subtitlesQuoteChanged = true;
-	}
-	return _currentSubtitleTextFull.c_str();
+	const char *text = _vqaSubsTextResourceEntries[fileIdx]->getOuttakeTextByFrame((uint32)frame);
+	_currentText = _useUTF8 ? Common::convertUtf8ToUtf32(text) : Common::U32String(text);
 }
 
 /**
-* Explicitly set the active subtitle text to be displayed
-* Used for debug purposes mainly.
-*/
+ * Explicitly set the active subtitle text to be displayed
+ * Used for debug purposes mainly.
+ */
 void Subtitles::setGameSubsText(Common::String dbgQuote, bool forceShowWhenNoSpeech) {
-	if (_currentSubtitleTextFull != dbgQuote) {
-		_currentSubtitleTextFull = dbgQuote;
-		_subtitlesQuoteChanged = true;
-		_forceShowWhenNoSpeech = forceShowWhenNoSpeech; // overrides not showing subtitles when no one is speaking
-	}
+	_currentText = _useUTF8 ? Common::convertUtf8ToUtf32(dbgQuote) : dbgQuote;
+	_forceShowWhenNoSpeech = forceShowWhenNoSpeech; // overrides not showing subtitles when no one is speaking
 }
 
 /**
-* Sets the _isVisible member var to true if it's not already set
-* @return true if the member was set now, false if the member was already set
-*/
+ * Sets the _isVisible member var to true if it's not already set
+ * @return true if the member was set now, false if the member was already set
+ */
 bool Subtitles::show() {
-	if (!_subtitlesSystemActive) {
+	if (!_isSystemActive) {
 		return false;
 	}
 
 	if (_isVisible) {
 		return false;
 	}
+
 	_isVisible = true;
 	return true;
 }
 
 /**
-* Clears the _isVisible member var if not already clear.
-* @return true if the member was cleared, false if it was already clear.
-*/
+ * Clears the _isVisible member var if not already clear.
+ * @return true if the member was cleared, false if it was already clear.
+ */
 bool Subtitles::hide() {
-	if (!_subtitlesSystemActive) {
+	if (!_isSystemActive) {
 		return false;
 	}
 
@@ -338,22 +311,22 @@ bool Subtitles::hide() {
 }
 
 /**
-* Checks whether the subtitles should be visible or not
-* @return the value of the _isVisible member boolean var
-*/
+ * Checks whether the subtitles should be visible or not
+ * @return the value of the _isVisible member boolean var
+ */
 bool Subtitles::isVisible() const {
-	return !_subtitlesSystemActive || _isVisible;
+	return !_isSystemActive || _isVisible;
 }
 
 /**
-* Tick method specific for outtakes (VQA videos)
-*/
+ * Tick method specific for outtakes (VQA videos)
+ */
 void Subtitles::tickOuttakes(Graphics::Surface &s) {
-	if (!_subtitlesSystemActive || !_vm->isSubtitlesEnabled()) {
+	if (!_isSystemActive || !_vm->isSubtitlesEnabled()) {
 		return;
 	}
 
-	if (_currentSubtitleTextFull.empty()) {
+	if (_currentText.empty()) {
 		_vm->_subtitles->hide();
 	} else {
 		_vm->_subtitles->show();
@@ -362,241 +335,72 @@ void Subtitles::tickOuttakes(Graphics::Surface &s) {
 	if (!_isVisible) { // keep it as a separate if
 		return;
 	}
+
 	draw(s);
 }
 
 /**
-* Tick method for in-game subtitles -- Not for outtake cutscenes (VQA videos)
-*/
+ * Tick method for in-game subtitles -- Not for outtake cutscenes (VQA videos)
+ */
 void Subtitles::tick(Graphics::Surface &s) {
-	if (!_subtitlesSystemActive || !_vm->isSubtitlesEnabled()) {
+	if (!_isSystemActive || !_vm->isSubtitlesEnabled()) {
 		return;
 	}
 
-	if (!_vm->_audioSpeech->isPlaying() && !_forceShowWhenNoSpeech && _isVisible) {
+	if (_isVisible && !_forceShowWhenNoSpeech && !_vm->_audioSpeech->isPlaying()) {
 		_vm->_subtitles->hide(); // TODO might need a better system. Don't call it always.
 	}
 
-	if (!_isVisible)  { // keep it as a separate if
+	if (!_isVisible) { // keep it as a separate if
 		return;
 	}
+
 	draw(s);
 }
 
 /**
-* Draw method for drawing the subtitles on the display surface
-*/
+ * Draw method for drawing the subtitles on the display surface
+ */
 void Subtitles::draw(Graphics::Surface &s) {
-	if (!_subtitlesSystemActive || !_isVisible || _currentSubtitleTextFull.empty()) {
+	if (!_isSystemActive || !_isVisible || _currentText.empty()) {
 		return;
 	}
-	if (_subtitlesQuoteChanged) {
-		calculatePosition(); // Don't always call calc position, only when quote has changed
-		_subtitlesQuoteChanged = false;
-	}
 
-	// multi-line quotes appear from top to bottom
-	// ie. _subtitleLineQuote[0] is the top-most line
-	// The default available lines for drawing are:
-	// 	(kMaxNumOfSubtitlesLines - kStartFromSubtitleLineFromTop)
-	// And by default we prefer drawing starting from line: kStartFromSubtitleLineFromTop.
-	// However, if we have to draw more lines than the default available
-	// we should then override the default starting line and start from further up instead
-	int startingLineFromTop = kStartFromSubtitleLineFromTop;
-	if (_currentSubtitleLines > kMaxNumOfSubtitlesLines - kStartFromSubtitleLineFromTop) {
-		startingLineFromTop = kMaxNumOfSubtitlesLines  - _currentSubtitleLines;
-	}
+	Common::Array<Common::U32String> lines;
+	_font->wordWrapText(_currentText, kTextMaxWidth, lines);
+
+	int y = s.h - (kMarginBottom + MAX(kPreferedLine, lines.size()) * _font->getFontHeight());
 
-	for (int i = 0, j = startingLineFromTop; i < _currentSubtitleLines; ++i, ++j) {
-		Common::U32String text = _useUTF8 ? convertUtf8ToUtf32(_subtitleLineQuote[i]) : _subtitleLineQuote[i];
+	for (uint i = 0; i < lines.size(); i++, y += _font->getFontHeight()) {
 		switch (_subtitlesInfo.fontType) {
 			case Subtitles::kSubtitlesFontTypeInternal:
 				// shadow/outline is part of the font color data
-				_font->drawString(&s, text, _subtitleLineScreenX[i], _subtitleLineScreenY[j], s.w, 0);
+				_font->drawString(&s, lines[i], 0, y, s.w, 0, Graphics::kTextAlignCenter);
 				break;
 			case Subtitles::kSubtitlesFontTypeTTF:
-				_font->drawString(&s, text, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[j] - 1, s.w, s.format.RGBToColor(  0,   0,   0));
-				_font->drawString(&s, text, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[j] - 1, s.w, s.format.RGBToColor(  0,   0,   0));
-				_font->drawString(&s, text, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[j] + 1, s.w, s.format.RGBToColor(  0,   0,   0));
-				_font->drawString(&s, text, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[j] + 1, s.w, s.format.RGBToColor(  0,   0,   0));
-				_font->drawString(&s, text, _subtitleLineScreenX[i]    , _subtitleLineScreenY[j]    , s.w, s.format.RGBToColor(255, 255, 255));
-				break;
-		}
-	}
-}
-
-/**
-* Calculate the position (X axis - horizontal) where the current active subtitle text should be displayed/drawn
-* This also determines if more than one lines should be drawn and what text goes into each line; splitting into multiple lines is done here
-*
-* The code first prioritizes splitting on the "new line" character.
-*   That is, if the string contains at least one new line character, then line splitting occurs on new line characters exclusively.
-*   The idea is that new line characters are put in the string explicitly by someone who wants specific control over line splitting
-*   and thus they assume the responsibility for the resulting line segment widths (the code won't bother with them in this case).
-*
-* If there are NO "new line" characters, then the code will split lines on a space character (auto-split case).
-*   For this case we only split if the full original line width exceeds a preset width threshold.
-*   If the threshold is exceeded, then we parse the line and calculate how many lines we can split it into (starting from 2 lines)
-*   to get segments smaller than the width threshold and also while maintaining (close to) even width across the resulting line segments.
-*   What's happening here is that we loop dividing the original quote's character total by an increasing target number of line segments,
-*   in order to get an "ideal" length for each segment (for evenness). Then we seek for split points (space character)
-*   past the characters of the "ideal" length points.
-*
-* For the second case (auto-split), we don't account for the special case of a single word larger than max line length
-* (no spaces), as practically this won't ever happen.
-*
-* TODO: simplify this code
-* TODO: maybe calculate auto-split points taking into account on quote pixel width per character and not simply the character count
-* TODO: somehow merge with graphics/font.cpp -> wordWrapTextImpl ?
-*/
-void Subtitles::calculatePosition() {
-	// wOrig is in pixels, origQuoteNumOfChars is num of chars in string
-	int wOrig = _font->getStringWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
-	int origQuoteNumOfChars = _currentSubtitleTextFull.size();
-	int tmpCharIndex = 0;
-	bool drawSingleLineQuote = false;
-
-	const uint8 *textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();
-	int tmpLineWidth[kMaxNumOfSubtitlesLines];
-
-	// initialization of aux variables
-	_currentSubtitleLines = 1;
-	for (int j = 0; j < kMaxNumOfSubtitlesLines; ++j) {
-		_subtitleLineSplitAtCharIndex[j] = 0;
-		_subtitleLineQuote[j] = "";
-		_subtitleLineScreenX[j] = 0;
-		tmpLineWidth[j] = 0;
-	}
+				_font->drawString(&s, lines[i], -1, y    , s.w, s.format.RGBToColor(  0,   0,   0), Graphics::kTextAlignCenter);
+				_font->drawString(&s, lines[i],  0, y - 1, s.w, s.format.RGBToColor(  0,   0,   0), Graphics::kTextAlignCenter);
+				_font->drawString(&s, lines[i],  1, y    , s.w, s.format.RGBToColor(  0,   0,   0), Graphics::kTextAlignCenter);
+				_font->drawString(&s, lines[i],  0, y + 1, s.w, s.format.RGBToColor(  0,   0,   0), Graphics::kTextAlignCenter);
 
-	while (*textCharacters != 0) {
-		// check for new line explicit split case
-		if (_currentSubtitleLines < kMaxNumOfSubtitlesLines
-			&& *textCharacters == '\n'
-			&& tmpCharIndex != 0
-			&& _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] == 0) {
-			_subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] = tmpCharIndex;
-			_currentSubtitleLines += 1;
-		}
-		tmpCharIndex += 1;
-		textCharacters += 1;
-	}
-	_subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] = tmpCharIndex;
-	if (_currentSubtitleLines > 1) { // This means that splitting on new line characters is possible
-		//
-		int j = 0;																// j iterates over the subtitle line segments
-		textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();		// reset pointer to the start of the subtitle quote
-		for (int i = 0; i < origQuoteNumOfChars ; ++i) {						// i iterates over characters in the quote
-			if (j < _currentSubtitleLines && i < _subtitleLineSplitAtCharIndex[j]) {
-				_subtitleLineQuote[j] += textCharacters[i];
-			} else { 															// i is now at a split point of the quote
-				_subtitleLineQuote[j] += '\0';
-				j += 1;															// start next line
-			}
-		}
-		_subtitleLineQuote[j] += '\0';											// the last line should also be NULL terminated
-		//
-		// Check widths and set starting X positions per line
-		for (int k = 0; k < _currentSubtitleLines; ++k) {
-			tmpLineWidth[k] = _font->getStringWidth(_subtitleLineQuote[k]) + 2;
-			_subtitleLineScreenX[k] = (639 - tmpLineWidth[k]) / 2;
-			_subtitleLineScreenX[k] = CLIP(_subtitleLineScreenX[k], 0, 639 - tmpLineWidth[k]);
-		}
-	} else {
-		// Here we initially have _currentSubtitleLines == 1
-		// We check quote for auto-splitting
-		// Auto splitting requires space characters in the quote string (which should be ok for the typical cases)
-		if (wOrig > kMaxWidthPerLineToAutoSplitThresholdPx) { // kMaxWidthPerLineToAutoSplitThresholdPx is a practical chosen width threshold for auto-splitting quotes purposes
-			// Start by splitting in two lines. If the new parts are still too lengthy, re-try by splitting in three lines, etc.
-			for (int linesToSplitInto = 2; linesToSplitInto <= kMaxNumOfSubtitlesLines; ++linesToSplitInto) {
-				// find the first space after the middle
-				_subtitleLineQuote[0] = "";
-				_currentSubtitleLines = 1;
-
-				textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();		// reset pointer to the start of subtitle quote
-				textCharacters += (origQuoteNumOfChars / linesToSplitInto);
-				_subtitleLineSplitAtCharIndex[0] = (origQuoteNumOfChars / linesToSplitInto);
-				while (*textCharacters != 0 && !Common::isSpace(*textCharacters)) {		// seek for a space character
-					_subtitleLineSplitAtCharIndex[0] += 1;
-					textCharacters += 1;
-				}
-//                debug("space character at: %d", _subtitleLineSplitAtCharIndex[0]);
-				if (Common::isSpace(*textCharacters)) { // if we found a space, we store the segment up to this point in the first _subtitleLineQuote entry
-					textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();
-					for (int i = 0; i < _subtitleLineSplitAtCharIndex[0] ; ++i) {
-						_subtitleLineQuote[0] += textCharacters[i];
-					}
-					_subtitleLineQuote[0] += '\0';
-//                    debug(" Line 0 quote %s", _subtitleLineQuote[0].c_str());
-					tmpLineWidth[0] = _font->getStringWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
-					if (tmpLineWidth[0] > kMaxWidthPerLineToAutoSplitThresholdPx && linesToSplitInto < kMaxNumOfSubtitlesLines) {
-						// we exceed max width so we reset process by trying to split into more lines
-						continue; // re-try the For-loop with increased linesToSplitInto by 1
-					} else {
-						// keep current split, proceed with splitting the quote for the rest of the subtitle lines (linesToSplitInto)
-						for (int j = 2; j <= linesToSplitInto; ++j) {
-							textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();	// reset pointer to the start of subtitle quote
-							textCharacters += ((j * origQuoteNumOfChars) / linesToSplitInto);	// move pointer to start of split-seek point for this line segment
-							_subtitleLineSplitAtCharIndex[_currentSubtitleLines] = ((j * origQuoteNumOfChars) / linesToSplitInto);
-							while (*textCharacters != 0 && !Common::isSpace(*textCharacters)) {
-								_subtitleLineSplitAtCharIndex[_currentSubtitleLines] += 1;
-								textCharacters += 1;
-							}
-							textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();	// reset pointer to the start of subtitle quote
-							for (int i = _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] + 1; i < _subtitleLineSplitAtCharIndex[_currentSubtitleLines]; ++i) {
-								_subtitleLineQuote[_currentSubtitleLines] += textCharacters[i];
-							}
-							_subtitleLineQuote[_currentSubtitleLines] +=  '\0';
-//                            debug(" Line %d, space blank at: %d, quote %s", _currentSubtitleLines, _subtitleLineSplitAtCharIndex[_currentSubtitleLines], _subtitleLineQuote[_currentSubtitleLines].c_str());
-							_currentSubtitleLines += 1;
-						}
-						//
-						// Check widths and set starting X positions per line
-						for (int j = 0; j < _currentSubtitleLines; ++j) {
-							tmpLineWidth[j] = _font->getStringWidth(_subtitleLineQuote[j]) + 2;
-							_subtitleLineScreenX[j] = (639 - tmpLineWidth[j]) / 2;
-							_subtitleLineScreenX[j] = CLIP(_subtitleLineScreenX[j], 0, 639 - tmpLineWidth[j]);
-						}
-						break; // end the for-loop on linesToSplitInto
-					}
-				} else {
-					// the line exceeds max width but has no space characters
-					// we treat it as single line quote (it will appear clipped). This won't happen practically though.
-					drawSingleLineQuote = true;
-					break;  // end the for-loop on linesToSplitInto
-				}
-			}
-		} else { // the width of the line is smaller than the max width
-			drawSingleLineQuote = true;
-		}
-		if (drawSingleLineQuote) {
-			_subtitleLineQuote[0] = _currentSubtitleTextFull;
-			_subtitleLineScreenX[0] = (639 - wOrig) / 2;
-			_subtitleLineScreenX[0] = CLIP(_subtitleLineScreenX[0], 0, 639 - wOrig);
+				_font->drawString(&s, lines[i],  0, y    , s.w, s.format.RGBToColor(255, 255, 255), Graphics::kTextAlignCenter);
+				break;
 		}
 	}
-	//debug("calculatePosition: %d %d", w, _screenFirstLineX);
 }
 
 /**
-* Initialize a few basic member vars
-*/
+ * Initialize a few basic member vars
+ */
 void Subtitles::clear() {
 	_isVisible = false;
 	_forceShowWhenNoSpeech = false;
-	_currentSubtitleTextFull = "";
-	for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
-		_subtitleLineQuote[i] = "";
-		_subtitleLineScreenY[i] = 0;
-		_subtitleLineScreenX[i] = 0;
-		_subtitleLineSplitAtCharIndex[i] = 0;
-	}
-	_subtitlesQuoteChanged = true;
-	_currentSubtitleLines = 0;
+	_currentText.clear();
 }
 
 /**
-* Initialize/ reset member vars, close open file descriptors and garbage collect subtitle fonts and text resource
-*/
+ * Initialize/reset member vars, close open file descriptors and garbage collect subtitle fonts and text resource
+ */
 void Subtitles::reset() {
 	clear();
 
@@ -605,7 +409,7 @@ void Subtitles::reset() {
 	_subtitlesInfo.dateOfCompile = "N/A";
 	_subtitlesInfo.languageMode = "N/A";
 
-	for (int i = 0; i != kMaxTextResourceEntries; ++i) {
+	for (int i = 0; i < kMaxTextResourceEntries; ++i) {
 		if (_vqaSubsTextResourceEntries[i] != nullptr) {
 			delete _vqaSubsTextResourceEntries[i];
 			_vqaSubsTextResourceEntries[i] = nullptr;
@@ -617,6 +421,7 @@ void Subtitles::reset() {
 		delete _font;
 		_font = nullptr;
 	}
+
 	_useUTF8 = false;
 }
 
diff --git a/engines/bladerunner/subtitles.h b/engines/bladerunner/subtitles.h
index eb45a35..1de059b 100644
--- a/engines/bladerunner/subtitles.h
+++ b/engines/bladerunner/subtitles.h
@@ -26,6 +26,7 @@
 #include "bladerunner/bladerunner.h"
 
 #include "common/str.h"
+#include "common/ustr.h"
 
 namespace Graphics {
 class Font;
@@ -41,11 +42,10 @@ class Subtitles {
 	//
 	// Subtitles could be in 6 possible languages are EN_ANY, DE_DEU, FR_FRA, IT_ITA, RU_RUS, ES_ESP
 	// with corresponding _vm->_languageCode values: "E", "G", "F", "I", "E", "S" (Russian version is built on top of English one)
-	static const int kMaxNumOfSubtitlesLines = 4;                  // At least one quote in the game requires 4 lines to be displayed correctly
-	static const int kStartFromSubtitleLineFromTop = 2;            // Prefer drawing from this line (the top-most of available subtitle lines index is 0) by default
-	static const int kSubtitlesBottomYOffsetPx = 12;               // In pixels. This is the bottom margin beneath the subtitles space
-	static const int kMaxWidthPerLineToAutoSplitThresholdPx = 610; // In pixels
-	static const int kMaxTextResourceEntries = 1 + 25;             // Support in-game subs (1) and all possible VQAs (25) with spoken dialogue or translatable text
+	static const uint kPreferedLine           = 2;      // Prefer drawing from this line (the bottom-most of available subtitle lines index is 0) by default
+	static const int  kMarginBottom           = 12;     // In pixels. This is the bottom margin beneath the subtitles space
+	static const int  kTextMaxWidth           = 610;    // In pixels
+	static const int  kMaxTextResourceEntries = 1 + 25; // Support in-game subs (1) and all possible VQAs (25) with spoken dialogue or translatable text
 	static const char *SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries];
 	static const char *SUBTITLES_FONT_FILENAME_EXTERNAL;
 	static const char *SUBTITLES_VERSION_TRENAME;
@@ -72,29 +72,23 @@ class Subtitles {
 	Graphics::Font *_font;
 	bool            _useUTF8;
 
-	bool           _isVisible;
-	bool           _forceShowWhenNoSpeech;
-	Common::String _currentSubtitleTextFull;
-	Common::String _subtitleLineQuote[kMaxNumOfSubtitlesLines];
-	int            _subtitleLineScreenY[kMaxNumOfSubtitlesLines];
-	int            _subtitleLineScreenX[kMaxNumOfSubtitlesLines];
-	int            _subtitleLineSplitAtCharIndex[kMaxNumOfSubtitlesLines];
-	int            _currentSubtitleLines;
-	bool           _subtitlesQuoteChanged;
+	bool              _isVisible;
+	bool              _forceShowWhenNoSpeech;
+	Common::U32String _currentText;
 
 	bool _gameSubsResourceEntriesFound[kMaxTextResourceEntries]; // false if a TRE file did not open successfully
-	bool _subtitlesSystemActive;                                 // true if the whole subtitles subsystem should be disabled (due to missing required resources)
+	bool _isSystemActive;                                        // true if the whole subtitles subsystem should be disabled (due to missing required resources)
 
 public:
 	Subtitles(BladeRunnerEngine *vm);
 	~Subtitles();
 
-	bool isSystemActive() const { return _subtitlesSystemActive; }
+	bool isSystemActive() const { return _isSystemActive; }
 
 	void init();
 	SubtitlesInfo getSubtitlesInfo() const;
-	const char *getInGameSubsText(int actorId, int speech_id);						// get the text for actorId, quoteId (in-game subs)
-	const char *getOuttakeSubsText(const Common::String &outtakesName, int frame);	// get the text for this frame if any
+	void loadInGameSubsText(int actorId, int speech_id);                     // get the text for actorId, quoteId (in-game subs)
+	void loadOuttakeSubsText(const Common::String &outtakesName, int frame); // get the text for this frame if any
 
 	void setGameSubsText(Common::String dbgQuote, bool force); // for debugging - explicit set subs text
 	bool show();
@@ -105,7 +99,6 @@ public:
 
 private:
 	void draw(Graphics::Surface &s);
-	void calculatePosition();
 
 	int getIdxForSubsTreName(const Common::String &treName) const;
 





More information about the Scummvm-git-logs mailing list