NANCY: Implement Autotext images

Added support for images inside Autotext. Used extensively
in nancy7.
This commit is contained in:
Kaloyan Chehlarski 2024-01-29 22:47:21 +01:00
parent b43b66dcc6
commit 9a549a88b2
4 changed files with 103 additions and 18 deletions

View File

@ -40,15 +40,19 @@ void Autotext::readData(Common::SeekableReadStream &stream) {
_surfWidth = stream.readUint16LE();
_surfHeight = stream.readUint16LE();
readFilename(stream, _imageName);
Common::Path imageName;
readFilename(stream, imageName);
uint16 numImages = stream.readUint16LE();
if (numImages) {
for (uint i = 0; i < numImages; ++i) {
uint16 line = stream.readUint16LE();
Common::Rect src;
readRect(stream, src);
addImage(line, src);
}
_imageLineIDs.resize(numImages);
_imageSrcs.resize(numImages);
for (uint i = 0; i < numImages; ++i) {
_imageLineIDs[i] = stream.readUint16LE();
readRect(stream, _imageSrcs[i]);
setImageName(imageName);
}
stream.skip((5 - numImages) * (2 + 16));
@ -139,6 +143,14 @@ void Autotext::execute() {
Common::Rect textBounds = surf.getBounds();
textBounds.left += _offset.x;
textBounds.top += _offset.y;
const Font *font = g_nancy->_graphicsManager->getFont(_fontID);
assert(font);
uint d = (font->getFontHeight() + 1) / 2 + 1;
textBounds.top += d + 1;
textBounds.left += d;
drawAllText(textBounds, _fontID, _fontID);
}

View File

@ -52,12 +52,6 @@ protected:
uint16 _surfWidth = 0;
uint16 _surfHeight = 0;
// Data for displaying images inside text; not supported yet
Common::Path _imageName;
Common::Array<uint16> _imageLineIDs;
Common::Array<Common::Rect> _imageSrcs;
bool _useAutotextChunk = false;
// Only one of these is valid

View File

@ -22,6 +22,7 @@
#include "engines/nancy/nancy.h"
#include "engines/nancy/graphics.h"
#include "engines/nancy/resource.h"
#include "engines/nancy/misc/hypertext.h"
@ -48,19 +49,35 @@ void HypertextParser::addTextLine(const Common::String &text) {
_needsTextRedraw = true;
}
void HypertextParser::addImage(uint16 lineID, const Common::Rect &src) {
_imageLineIDs.push_back(lineID);
_imageSrcs.push_back(src);
}
void HypertextParser::setImageName(const Common::Path &name) {
_imageName = name;
}
void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, uint highlightFontID) {
using namespace Common;
const Font *font = nullptr;
const Font *highlightFont = nullptr;
Graphics::ManagedSurface image;
_numDrawnLines = 0;
if (!_imageName.empty()) {
g_nancy->_resource->loadImage(_imageName, image);
}
for (uint lineID = 0; lineID < _textLines.size(); ++lineID) {
Common::String currentLine;
bool hasHotspot = false;
Rect hotspot;
Common::Queue<ColorFontChange> colorTextChanges;
Common::Queue<uint16> newlineTokens;
newlineTokens.push(0);
int curFontID = fontID;
uint numNonSpaceChars = 0;
@ -107,6 +124,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
}
currentLine += '\n';
newlineTokens.push(numNonSpaceChars);
continue;
case 't' :
// Tab
@ -158,6 +176,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
font = g_nancy->_graphicsManager->getFont(curFontID);
highlightFont = g_nancy->_graphicsManager->getFont(highlightFontID);
assert(font && highlightFont);
// Do word wrapping on the text, sans tokens. This assumes
// all text uses fonts of the same width
@ -176,8 +195,41 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
// respect color tokens
uint totalCharsDrawn = 0;
byte colorID = _defaultTextColor;
for (Common::String &line : wrappedLines) {
uint horizontalOffset = 0;
uint numNewlineTokens = 0;
uint horizontalOffset = 0;
for (uint lineNumber = 0; lineNumber < wrappedLines.size(); ++lineNumber) {
Common::String &line = wrappedLines[lineNumber];
horizontalOffset = 0;
// Draw images
if (newlineTokens.front() <= totalCharsDrawn) {
newlineTokens.pop();
for (uint i = 0; i < _imageLineIDs.size(); ++i) {
if (numNewlineTokens == _imageLineIDs[i]) {
// A lot of magic numbers that make sure we draw pixel-perfect. This is a mess for three reasons:
// - The original engine draws strings with a bottom-left anchor, while ScummVM uses top-left
// - The original engine uses inclusive rects, while ScummVM uses non-includive
// - The original engine does some stupid stuff with spacing
// This works correctly in nancy7, but might fail with different games/fonts
if (lineNumber != 0) {
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 1;
}
_fullSurface.blitFrom(image, _imageSrcs[i],
Common::Point( textBounds.left + horizontalOffset + 1,
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
_imageVerticalOffset += _imageSrcs[i].height() - 1;
if (lineNumber == 0) {
_imageVerticalOffset += font->getFontHeight() / 2 - 1;
} else {
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 3;
}
}
}
++numNewlineTokens;
}
// Trim whitespaces (only) at beginning and end of wrapped lines
while (line.lastChar() == ' ') {
@ -220,7 +272,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
font->drawString( &_fullSurface,
stringToDraw,
textBounds.left + horizontalOffset,
textBounds.top + _numDrawnLines * font->getFontHeight(),
textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset,
textBounds.width(),
colorID);
@ -229,7 +281,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
highlightFont->drawString( &_textHighlightSurface,
stringToDraw,
textBounds.left + horizontalOffset,
textBounds.top + _numDrawnLines * highlightFont->getFontHeight(),
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset,
textBounds.width(),
colorID);
}
@ -252,7 +304,25 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint fontID, u
++_numDrawnLines;
// Record the height of the text currently drawn. Used for textbox scrolling
_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight();
_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
}
// Draw the footer image(s)
for (uint i = 0; i < _imageLineIDs.size(); ++i) {
if (numNewlineTokens <= _imageLineIDs[i]) {
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 1;
_fullSurface.blitFrom(image, _imageSrcs[i],
Common::Point( textBounds.left + horizontalOffset + 1,
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
_imageVerticalOffset += _imageSrcs[i].height() - 1;
if (i < _imageLineIDs.size() - 1) {
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 3;
}
_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
}
}
// Add the hotspot to the list

View File

@ -37,13 +37,16 @@ public:
_numDrawnLines(0),
_drawnTextHeight(0),
_needsTextRedraw(false),
_defaultTextColor(0) {}
_defaultTextColor(0),
_imageVerticalOffset(0) {}
virtual ~HypertextParser() {};
protected:
void initSurfaces(uint width, uint height, const struct Graphics::PixelFormat &format, uint32 backgroundColor, uint32 highlightBackgroundColor);
void addTextLine(const Common::String &text);
void addImage(uint16 lineID, const Common::Rect &src);
void setImageName(const Common::Path &name);
void drawAllText(const Common::Rect &textBounds, uint fontID, uint highlightFontID);
virtual void clear();
@ -54,10 +57,16 @@ protected:
uint32 _backgroundColor;
uint32 _highlightBackgroundColor;
uint _defaultTextColor;
uint _imageVerticalOffset;
Common::Array<Common::String> _textLines;
Common::Array<Common::Rect> _hotspots;
// Data for displaying images inside text; used in Hypertext
Common::Path _imageName;
Common::Array<uint16> _imageLineIDs;
Common::Array<Common::Rect> _imageSrcs;
uint16 _numDrawnLines;
uint16 _drawnTextHeight;
bool _needsTextRedraw;