SCI32: Non-titled text bitmap implementation

This implementation is not 100% engine accurate, but it is
more accurate than what was there, and hopefully the differences
between this and the engine code are merely cosmetic.

The known (intentional) differences are:

1. Uses ScummVM rects inside the engine code, converting to/from
   SCI rects on the kernel edges and when scaling
2. Fewer side effects when performing operations that *should*
   have been pure from the start (like text dimension calculation).
   Still not side-effect-free, but at least things like colours
   and alignment do not need to be reset every time a measurement
   is taken, unlike in the actual engine.

Editor controls and some other kBitmap code are temporarily
disabled as a result of changes to GfxText32 until they can be
updated to be engine-accurate.
This commit is contained in:
Colin Snover 2016-02-28 21:19:06 -06:00
parent 2c81602454
commit 3e5adc33a8
9 changed files with 523 additions and 479 deletions

View File

@ -499,6 +499,8 @@ reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
reg_t kMorphOn(EngineState *s, int argc, reg_t *argv);
reg_t kText(EngineState *s, int argc, reg_t *argv);
reg_t kTextSize32(EngineState *s, int argc, reg_t *argv);
reg_t kTextWidth(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kAutoSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
@ -516,6 +518,7 @@ reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv);
reg_t kCelInfo(EngineState *s, int argc, reg_t *argv);
reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv);
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv);
reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv);
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv);
reg_t kFont(EngineState *s, int argc, reg_t *argv);
reg_t kBitmap(EngineState *s, int argc, reg_t *argv);

View File

@ -60,15 +60,17 @@ struct SciKernelMapSubEntry {
#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE
#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
#define SIG_SCI21EARLY_ONLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY
#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3
#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE
#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3
#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE
#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
#define SIG_SCI21EARLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY
#define SIG_UNTIL_SCI21EARLY SCI_VERSION_2, SCI_VERSION_2_1_EARLY
#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE
#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3
#define SIG_SINCE_SCI21MID SCI_VERSION_2_1_MIDDLE, SCI_VERSION_3
#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3
#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
@ -283,6 +285,20 @@ static const SciKernelMapSubEntry kSave_subops[] = {
SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kFont_subops[] = {
{ SIG_SINCE_SCI21MID, 0, MAP_CALL(SetFontHeight), "i", NULL },
{ SIG_SINCE_SCI21MID, 1, MAP_CALL(SetFontRes), "ii", NULL },
SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kText_subops[] = {
{ SIG_SINCE_SCI21MID, 0, MAP_CALL(TextSize32), "r[r0]i(i)(i)", NULL },
{ SIG_SINCE_SCI21MID, 1, MAP_CALL(TextWidth), "ri", NULL },
SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL },
@ -546,10 +562,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, kStrLen_workarounds },
{ MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
{ MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
{ MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
{ MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
{ MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
{ MAP_CALL(TextColors), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL },
{ MAP_CALL(TextFonts), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL },
{ MAP_CALL(TextSize), SIG_SCI16, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
{ MAP_CALL(TextSize), SIG_SCI16, SIGFOR_ALL, "r[r0]i(i)(r0)", NULL, NULL },
{ MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
{ "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
@ -581,6 +597,10 @@ static SciKernelMapEntry s_kernelMap[] = {
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
// TODO: whoever knows his way through those calls, fix the signatures.
{ "TextSize", kTextSize32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "r[r0]i(i)", NULL, NULL },
{ MAP_DUMMY(TextColors), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL },
{ MAP_DUMMY(TextFonts), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL },
{ MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@ -651,7 +671,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(TextWidth), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri", NULL, NULL },
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Kernel Functions
@ -662,7 +682,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
{ MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
{ MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@ -671,8 +691,8 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL },
{ MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "i(.*)", kScrollWindow_subops, NULL },
{ MAP_CALL(SetFontRes), SIG_SCI21EARLY_ONLY, SIGFOR_ALL, "ii", NULL, NULL },
{ MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
{ MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL },
{ MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL },
{ MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL },
{ MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL },
@ -1030,7 +1050,6 @@ static const char *const sci2_default_knames[] = {
/*0x89*/ "TextWidth", // for debugging(?), only in SCI2, not used in any SCI2 game
/*0x8a*/ "PointSize", // for debugging(?), only in SCI2, not used in any SCI2 game
// GK2 Demo (and similar) only kernel functions
/*0x8b*/ "AddLine",
/*0x8c*/ "DeleteLine",
/*0x8d*/ "UpdateLine",

View File

@ -359,12 +359,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
uint16 languageSplitter = 0;
Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep);
#ifdef ENABLE_SCI32
if (g_sci->_gfxText32)
g_sci->_gfxText32->kernelTextSize(splitText.c_str(), font_nr, maxwidth, &textWidth, &textHeight);
else
#endif
g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
// One of the game texts in LB2 German contains loads of spaces in
// its end. We trim the text here, otherwise the graphics code will

View File

@ -197,7 +197,7 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
if (subop == 0) {
TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode));
reg_t out;
return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, 1, &out);
return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true, &out);
} else {
CelInfo32 celInfo;
celInfo.type = kCelTypeView;
@ -214,6 +214,35 @@ reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
reg_t kText(EngineState *s, int argc, reg_t *argv) {
if (!s)
return make_reg(0, getSciVersion());
error("not supposed to call this");
}
reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxText32->setFont(argv[2].toUint16());
reg_t *rect = s->_segMan->derefRegPtr(argv[0], 4);
Common::String text = s->_segMan->getString(argv[1]);
int16 maxWidth = argc > 3 ? argv[3].toSint16() : 0;
bool doScaling = argc > 4 ? argv[4].toSint16() : true;
Common::Rect textRect = g_sci->_gfxText32->getTextSize(text, maxWidth, doScaling);
rect[0] = make_reg(0, textRect.left);
rect[1] = make_reg(0, textRect.top);
rect[2] = make_reg(0, textRect.right - 1);
rect[3] = make_reg(0, textRect.bottom - 1);
return NULL_REG;
}
reg_t kTextWidth(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxText32->setFont(argv[1].toUint16());
Common::String text = s->_segMan->getString(argv[0]);
return make_reg(0, g_sci->_gfxText32->getStringWidth(text));
}
reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 1:
@ -454,18 +483,18 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
#endif
reg_t kFont(EngineState *s, int argc, reg_t *argv) {
// TODO: Handle font settings for SCI2.1
if (!s)
return make_reg(0, getSciVersion());
error("not supposed to call this");
}
switch (argv[0].toUint16()) {
case 1:
g_sci->_gfxText32->_scaledWidth = argv[1].toUint16();
g_sci->_gfxText32->_scaledHeight = argv[2].toUint16();
return NULL_REG;
default:
error("kFont: unknown subop %d", argv[0].toUint16());
}
return s->r_acc;
reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv) {
// TODO: Setting font may have just been for side effect
// of setting the fontHeight on the font manager, in
// which case we could just get the font directly ourselves.
g_sci->_gfxText32->setFont(argv[0].toUint16());
g_sci->_gfxText32->_scaledHeight = (g_sci->_gfxText32->_font->getHeight() * g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight + g_sci->_gfxText32->_scaledHeight - 1) / g_sci->_gfxText32->_scaledHeight;
return NULL_REG;
}
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) {
@ -563,6 +592,10 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
break;
case 4: // add text to bitmap
{
warning("kBitmap(4)");
return NULL_REG;
}
#if 0
// 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
// script 64894
reg_t hunkId = argv[1]; // obtained from kBitmap(0)
@ -613,6 +646,7 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
}
break;
#endif
case 5: // fill with color
{
// 6 params, called e.g. from TextView::init() and TextView::draw()

View File

@ -661,19 +661,6 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
reg_t kText(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0:
return kTextSize(s, argc - 1, argv + 1);
default:
// TODO: Other subops here too, perhaps kTextColors and kTextFonts
warning("kText(%d)", argv[0].toUint16());
break;
}
return s->r_acc;
}
// TODO: there is an unused second argument, happens at least in LSL6 right during the intro
reg_t kStringNew(EngineState *s, int argc, reg_t *argv) {
reg_t stringHandle;

View File

@ -178,11 +178,14 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) {
// Write back string
_segMan->strcpy(textReference, text.c_str());
// Modify the buffer and show it
warning("kernelTexteditChange");
#if 0
_text->createTextBitmap(controlObject, 0, 0, hunkId);
_text->drawTextBitmap(0, 0, nsRect, controlObject);
//texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor
g_system->updateScreen();
#endif
} else {
// TODO: Cursor
/*

View File

@ -138,6 +138,18 @@ inline void mul(Common::Rect &rect, const Common::Rational &ratioX, const Common
rect.bottom = (rect.bottom * ratioY).toInt();
}
/**
* Multiplies a rectangle by two ratios with default
* rounding. Modifies the rect directly. Uses inclusive
* rectangle rounding.
*/
inline void mulinc(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) {
rect.left = (rect.left * ratioX).toInt();
rect.top = (rect.top * ratioY).toInt();
rect.right = ((rect.right - 1) * ratioX).toInt() + 1;
rect.bottom = ((rect.bottom - 1) * ratioY).toInt() + 1;
}
/**
* Multiplies a number by a rational number, rounding up to
* the nearest whole number.

View File

@ -37,7 +37,7 @@
namespace Sci {
#define BITMAP_HEADER_SIZE 46
int16 GfxText32::_defaultFontId = 0;
GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) :
_segMan(segMan),
@ -45,8 +45,21 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) :
_screen(screen),
_scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
_scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
_bitmap(NULL_REG) {}
// Not a typo, the original engine did not initialise height, only width
_width(0),
_text(""),
_field_20(0),
_field_2C(2),
_field_30(0),
_field_34(0),
_field_38(0),
_field_3C(0),
_bitmap(NULL_REG) {
_fontId = _defaultFontId;
_font = _cache->getFont(_defaultFontId);
}
#define BITMAP_HEADER_SIZE 46
void GfxText32::buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const {
WRITE_SCI11ENDIAN_UINT16(bitmap + 0, width);
@ -77,8 +90,6 @@ void GfxText32::buildBitmapHeader(byte *bitmap, const int16 width, const int16 h
WRITE_SCI11ENDIAN_UINT16(bitmap + 38, scaledHeight);
}
int16 GfxText32::_defaultFontId = 0;
reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling, reg_t *outBitmapObject) {
_field_22 = 0;
@ -93,10 +104,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect
_alignment = alignment;
_dimmed = dimmed;
if (fontId != _fontId) {
_fontId = fontId == -1 ? _defaultFontId : fontId;
_font = _cache->getFont(_fontId);
}
setFont(fontId);
if (doScaling) {
int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
@ -107,7 +115,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect
_width = (_width * scaleX).toInt();
_height = (_height * scaleY).toInt();
mul(_textRect, scaleX, scaleY);
mulinc(_textRect, scaleX, scaleY);
}
// _textRect represents where text is drawn inside the
@ -133,8 +141,6 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect
drawTextBox();
debug("Drawing a bitmap %dx%d, scaled %dx%d, border %d, font %d", width, height, _width, _height, _borderColor, _fontId);
*outBitmapObject = _bitmap;
return _bitmap;
}
@ -144,7 +150,17 @@ reg_t GfxText32::createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect,
return NULL_REG;
}
void GfxText32::drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling) {
void GfxText32::setFont(const GuiResourceId fontId) {
// NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font
// table is built on the FontMgr directly; instead, because we already have
// font resources, this code just grabs a font out of GfxCache.
if (fontId != _fontId) {
_fontId = fontId == -1 ? _defaultFontId : fontId;
_font = _cache->getFont(_fontId);
}
}
void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling) {
Common::Rect targetRect = doScaling ? scaleRect(rect) : rect;
byte *bitmap = _segMan->getHunkPointer(_bitmap);
@ -157,85 +173,343 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int size, const uint8
buffer.frameRect(targetRect, color);
}
// TODO: This is not disassembled
void GfxText32::drawChar(const uint8 charIndex) {
byte *bitmap = _segMan->getHunkPointer(_bitmap);
byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
_font->drawToBuffer(charIndex, _drawPosition.y, _drawPosition.x, _foreColor, _dimmed, pixels, _width, _height);
_drawPosition.x += _font->getCharWidth(charIndex);
}
uint16 GfxText32::getCharWidth(const uint8 charIndex, const bool doScaling) const {
uint16 width = _font->getCharWidth(charIndex);
if (doScaling) {
width = scaleUpWidth(width);
}
return width;
}
void GfxText32::drawTextBox() {
int16 charCount = 0;
uint16 curX = 0, curY = 0;
const char *txt = _text.c_str();
int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0;
uint16 start = 0;
if (_text.size() == 0) {
return;
}
// Calculate total text height
while (*txt) {
charCount = GetLongest(txt, _textRect.width(), _font);
if (charCount == 0)
break;
const char *text = _text.c_str();
const char *sourceText = text;
int16 textRectWidth = _textRect.width();
_drawPosition.y = _textRect.top;
uint charIndex = 0;
if (getLongest(&charIndex, textRectWidth) == 0) {
error("DrawTextBox GetLongest=0");
}
Width(txt, 0, (int16)strlen(txt), _fontId, textWidth, textHeight, true);
charIndex = 0;
uint nextCharIndex = 0;
while (*text != '\0') {
_drawPosition.x = _textRect.left;
totalHeight += textHeight;
txt += charCount;
while (*txt == ' ') {
txt++; // skip over breaking spaces
uint length = getLongest(&nextCharIndex, textRectWidth);
int16 textWidth = getTextWidth(charIndex, length);
if (_alignment == kTextAlignCenter) {
_drawPosition.x += (textRectWidth - textWidth) / 2;
} else if (_alignment == kTextAlignRight) {
_drawPosition.x += textRectWidth - textWidth;
}
drawText(charIndex, length);
charIndex = nextCharIndex;
text = sourceText + charIndex;
_drawPosition.y += _font->getHeight();
}
}
void GfxText32::drawText(const uint index, uint length) {
assert(index + length <= _text.size());
// NOTE: This draw loop implementation is somewhat different than the
// implementation in the actual engine, but should be accurate. Primarily
// the changes revolve around eliminating some extra temporaries and
// fixing the logic to match.
const char *text = _text.c_str() + index;
while (length-- > 0) {
char currentChar = *text++;
if (currentChar == '|') {
const char controlChar = *text++;
--length;
if (length == 0) {
return;
}
if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') {
uint16 value = 0;
while (length > 0) {
const char valueChar = *text;
if (valueChar < '0' || valueChar > '9') {
break;
}
++text;
--length;
value = 10 * value + (valueChar - '0');
}
if (length == 0) {
return;
}
if (controlChar == 'a') {
_alignment = (TextAlign)value;
} else if (controlChar == 'c') {
_foreColor = value;
} else if (controlChar == 'f') {
setFont(value);
}
}
while (length > 0 && *text != '|') {
++text;
--length;
}
} else {
drawChar(currentChar);
}
}
}
uint GfxText32::getLongest(uint *charIndex, const int16 width) {
assert(width > 0);
uint testLength = 0;
uint length = 0;
const uint initialCharIndex = *charIndex;
// The index of the next word after the last word break
uint lastWordBreakIndex = *charIndex;
const char *text = _text.c_str() + *charIndex;
char currentChar;
while ((currentChar = *text++) != '\0') {
// NOTE: In the original engine, the font, color, and alignment were
// reset here to their initial values
// The text to render contains a line break; stop at the line break
if (currentChar == '\r' || currentChar == '\n') {
// Skip the rest of the line break if it is a Windows-style
// \r\n or non-standard \n\r
// NOTE: In the original engine, the `text` pointer had not been
// advanced yet so the indexes used to access characters were
// one higher
if (
(currentChar == '\r' && text[0] == '\n') ||
(currentChar == '\n' && text[0] == '\r' && text[1] != '\n')
) {
++*charIndex;
}
// We are at the end of a line but the last word in the line made
// it too wide to fit in the text area; return up to the previous
// word
if (length && getTextWidth(initialCharIndex, testLength) > width) {
*charIndex = lastWordBreakIndex;
return length;
}
// Skip the line break and return all text seen up to now
// NOTE: In original engine, the font, color, and alignment were
// reset, then getTextWidth was called to use its side-effects to
// set font, color, and alignment according to the text from
// `initialCharIndex` to `testLength`
++*charIndex;
return testLength;
} else if (currentChar == ' ') {
// The last word in the line made it too wide to fit in the text area;
// return up to the previous word, then collapse the whitespace
// between that word and its next sibling word into the line break
if (getTextWidth(initialCharIndex, testLength) > width) {
*charIndex = lastWordBreakIndex;
const char *nextChar = _text.c_str() + lastWordBreakIndex;
while (*nextChar++ == ' ') {
++*charIndex;
}
// NOTE: In original engine, the font, color, and alignment were
// set here to the values that were seen at the last space character
return length;
}
// NOTE: In the original engine, the values of _fontId, _foreColor,
// and _alignment were stored for use in the return path mentioned
// just above here
// We found a word break that was within the text area, memorise it
// and continue processing. +1 on the character index because it has
// not been incremented yet so currently points to the word break
// and not the word after the break
length = testLength;
lastWordBreakIndex = *charIndex + 1;
}
// In the middle of a line, keep processing
++*charIndex;
++testLength;
// NOTE: In the original engine, the font, color, and alignment were
// reset here to their initial values
// The text to render contained no word breaks yet but is already too
// wide for the text area; just split the word in half at the point
// where it overflows
if (length == 0 && getTextWidth(initialCharIndex, testLength) > width) {
*charIndex = --testLength + lastWordBreakIndex;
return testLength;
}
}
txt = _text.c_str();
// The complete text to render was a single word, or was narrower than
// the text area, so return the entire line
if (length == 0 || getTextWidth(initialCharIndex, testLength) <= width) {
// NOTE: In original engine, the font, color, and alignment were
// reset, then getTextWidth was called to use its side-effects to
// set font, color, and alignment according to the text from
// `initialCharIndex` to `testLength`
return testLength;
}
byte *pixels = _segMan->getHunkPointer(_bitmap);
pixels = pixels + READ_SCI11ENDIAN_UINT32(pixels + 28) + _width * _textRect.top + _textRect.left;
// The last word in the line made it wider than the text area, so return
// up to the penultimate word
*charIndex = lastWordBreakIndex;
return length;
}
// Draw text in buffer
while (*txt) {
charCount = GetLongest(txt, _textRect.width(), _font);
if (charCount == 0)
break;
Width(txt, start, charCount, _fontId, textWidth, textHeight, true);
int16 GfxText32::getTextWidth(const uint index, uint length) const {
int16 width = 0;
switch (_alignment) {
case kTextAlignRight:
offsetX = _textRect.width() - textWidth;
break;
case kTextAlignCenter:
// Center text both horizontally and vertically
offsetX = (_textRect.width() - textWidth) / 2;
offsetY = (_textRect.height() - totalHeight) / 2;
break;
case kTextAlignLeft:
offsetX = 0;
break;
const char *text = _text.c_str() + index;
default:
warning("Invalid alignment %d used in TextBox()", _alignment);
GfxFont *font = _font;
char currentChar = *text++;
while (length > 0 && currentChar != '\0') {
// Control codes are in the format `|<code><value>|`
if (currentChar == '|') {
// NOTE: Original engine code changed the global state of the
// FontMgr here upon encountering any color, alignment, or
// font control code.
// To avoid requiring all callers to manually restore these
// values on every call, we ignore control codes other than
// font change (since alignment and color do not change the
// width of characters), and simply update the font pointer
// on stack instead of the member property font.
currentChar = *text++;
--length;
if (length > 0 && currentChar == 'f') {
GuiResourceId fontId = 0;
do {
currentChar = *text++;
--length;
fontId = fontId * 10 + currentChar - '0';
} while (length > 0 && currentChar >= '0' && currentChar <= '9');
if (length > 0) {
font = _cache->getFont(fontId);
}
}
// Forward through any more unknown control character data
while (length > 0 && currentChar != '|') {
++text;
--length;
}
} else {
width += font->getCharWidth(currentChar);
}
byte curChar;
currentChar = *text++;
--length;
}
for (int i = 0; i < charCount; i++) {
curChar = txt[i];
return width;
}
switch (curChar) {
case 0x0A:
case 0x0D:
case 0:
break;
case 0x7C:
warning("Code processing isn't implemented in SCI32");
break;
default:
_font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, _foreColor, _dimmed, pixels, _width, _height);
curX += _font->getCharWidth(curChar);
break;
int16 GfxText32::getTextWidth(const Common::String &text, const uint index, const uint length) {
_text = text;
return scaleUpWidth(getTextWidth(index, length));
}
Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, bool doScaling) {
// NOTE: Like most of the text rendering code, this function was pretty
// weird in the original engine. The initial result rectangle was actually
// a 1x1 rectangle (0, 0, 0, 0), which was then "fixed" after the main
// text size loop finished running by subtracting 1 from the right and
// bottom edges.
Common::Rect result;
int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
maxWidth = maxWidth * _scaledWidth / scriptWidth;
_text = text;
if (maxWidth >= 0) {
if (maxWidth == 0) {
// TODO: This was hardcoded to 192, but guessing
// that it was originally 60% of the scriptWidth
// before the compiler took over.
// Verify this by looking at a game that uses a
// scriptWidth other than 320, like LSL7
maxWidth = _scaledWidth * (scriptWidth * 0.6) / scriptWidth;
}
result.right = maxWidth;
int16 textWidth = 0;
if (_text.size() > 0) {
const char *rawText = _text.c_str();
const char *sourceText = rawText;
uint charIndex = 0;
uint nextCharIndex = 0;
while (*rawText != '\0') {
uint length = getLongest(&nextCharIndex, result.width());
textWidth = MAX(textWidth, getTextWidth(charIndex, length));
charIndex = nextCharIndex;
rawText = sourceText + charIndex;
// TODO: Due to getLongest and getTextWidth not having side
// effects, it is possible that the currently loaded font's
// height is wrong for this line if it was changed inline
result.bottom += _font->getHeight();
}
}
curX = 0;
curY += _font->getHeight();
txt += charCount;
while (*txt == ' ') {
txt++; // skip over breaking spaces
if (textWidth < maxWidth) {
result.right = textWidth;
}
} else {
result.right = getTextWidth(0, 10000);
result.bottom = _font->getHeight() + 1;
}
if (doScaling) {
// NOTE: The original code did some more complex stuff for edge
// rounding. The right edge was designed to always round down,
// and the bottom edge was designed to always round up. It seems
// we are able to get away with simpler rounding, but it is
// possible that there are still some edge cases here.
mul(result, Ratio(scriptWidth, _scaledWidth), Ratio(scriptHeight, _scaledHeight));
result.right += 1;
result.bottom += 1;
}
return result;
}
void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
@ -251,342 +525,15 @@ void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
buffer.fillRect(targetRect, _backColor);
}
reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
}
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text));
// The object in the text selector of the item can be either a raw string
// or a Str object. In the latter case, we need to access the object's data
// selector to get the raw string.
if (_segMan->isHeapObject(stringObject))
stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
Common::String text = _segMan->getString(stringObject);
return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
}
reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font));
GfxFont *font = _cache->getFont(fontId);
bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed));
int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode));
uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back));
Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject);
uint16 width = nsRect.width() + 1;
uint16 height = nsRect.height() + 1;
// Limit rectangle dimensions, if requested
if (maxWidth > 0)
width = maxWidth;
if (maxHeight > 0)
height = maxHeight;
// Upscale the coordinates/width if the fonts are already upscaled
if (_screen->fontIsUpscaled()) {
width = width * _screen->getDisplayWidth() / _screen->getWidth();
height = height * _screen->getDisplayHeight() / _screen->getHeight();
}
int entrySize = width * height + BITMAP_HEADER_SIZE;
reg_t memoryId = NULL_REG;
if (prevHunk.isNull()) {
memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
// Scroll text objects have no bitmap selector!
ObjVarRef varp;
if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable)
writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
} else {
memoryId = prevHunk;
}
byte *memoryPtr = _segMan->getHunkPointer(memoryId);
if (prevHunk.isNull())
memset(memoryPtr, 0, BITMAP_HEADER_SIZE);
byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
memset(bitmap, backColor, width * height);
// Save totalWidth, totalHeight
WRITE_SCI11ENDIAN_UINT16(memoryPtr, width);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 2, height);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 4, 0);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 6, 0);
memoryPtr[8] = 0;
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 10, 0);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 20, BITMAP_HEADER_SIZE);
WRITE_SCI11ENDIAN_UINT32(memoryPtr + 28, 46);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 36, width);
WRITE_SCI11ENDIAN_UINT16(memoryPtr + 38, height);
int16 charCount = 0;
uint16 curX = 0, curY = 0;
const char *txt = text.c_str();
int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0;
uint16 start = 0;
// Calculate total text height
while (*txt) {
charCount = GetLongest(txt, width, font);
if (charCount == 0)
break;
Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true);
totalHeight += textHeight;
txt += charCount;
while (*txt == ' ')
txt++; // skip over breaking spaces
}
txt = text.c_str();
// Draw text in buffer
while (*txt) {
charCount = GetLongest(txt, width, font);
if (charCount == 0)
break;
Width(txt, start, charCount, fontId, textWidth, textHeight, true);
switch (alignment) {
case kTextAlignRight:
offsetX = width - textWidth;
break;
case kTextAlignCenter:
// Center text both horizontally and vertically
offsetX = (width - textWidth) / 2;
offsetY = (height - totalHeight) / 2;
break;
case kTextAlignLeft:
offsetX = 0;
break;
default:
warning("Invalid alignment %d used in TextBox()", alignment);
}
byte curChar;
for (int i = 0; i < charCount; i++) {
curChar = txt[i];
switch (curChar) {
case 0x0A:
case 0x0D:
case 0:
break;
case 0x7C:
warning("Code processing isn't implemented in SCI32");
break;
default:
font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
curX += font->getCharWidth(curChar);
break;
}
}
curX = 0;
curY += font->getHeight();
txt += charCount;
while (*txt == ' ')
txt++; // skip over breaking spaces
}
return memoryId;
}
void GfxText32::disposeTextBitmap(reg_t hunkId) {
_segMan->freeHunkEntry(hunkId);
}
void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) {
reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap));
drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);
}
void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) {
/*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane));
Common::Rect planeRect;
planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top));
planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left));
planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom));
planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right));
drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/
// HACK: we pretty much ignore the plane rect and x, y...
drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId);
}
void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) {
int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back));
// Sanity check: Check if the hunk is set. If not, either the game scripts
// didn't set it, or an old saved game has been loaded, where it wasn't set.
if (hunkId.isNull())
return;
// Negative coordinates indicate that text shouldn't be displayed
if (x < 0 || y < 0)
return;
byte *memoryPtr = _segMan->getHunkPointer(hunkId);
if (!memoryPtr) {
// Happens when restoring in some SCI32 games (e.g. SQ6).
// Commented out to reduce console spam
//warning("Attempt to draw an invalid text bitmap");
return;
}
byte *surface = memoryPtr + BITMAP_HEADER_SIZE;
int curByte = 0;
int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip));
uint16 textX = planeRect.left + x;
uint16 textY = planeRect.top + y;
// Get totalWidth, totalHeight
uint16 width = READ_LE_UINT16(memoryPtr);
uint16 height = READ_LE_UINT16(memoryPtr + 2);
// Upscale the coordinates/width if the fonts are already upscaled
if (_screen->fontIsUpscaled()) {
textX = textX * _screen->getDisplayWidth() / _screen->getWidth();
textY = textY * _screen->getDisplayHeight() / _screen->getHeight();
}
bool translucent = (skipColor == -1 && backColor == -1);
for (int curY = 0; curY < height; curY++) {
for (int curX = 0; curX < width; curX++) {
byte pixel = surface[curByte++];
if ((!translucent && pixel != skipColor && pixel != backColor) ||
(translucent && pixel != 0xFF))
_screen->putFontPixel(textY, curX + textX, curY, pixel);
}
}
}
int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
uint16 curChar = 0;
int16 maxChars = 0, curCharCount = 0;
uint16 width = 0;
while (width <= maxWidth) {
curChar = (*(const byte *)text++);
switch (curChar) {
// We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit
// which means, we split text like
// 'Mature, experienced software analyst available.' 0xD 0xA
// 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
// and 0xA '-------' 0xA (which is the official sierra subtitle separator)
// Sierra did it the same way.
case 0xD:
// Check, if 0xA is following, if so include it as well
if ((*(const unsigned char *)text) == 0xA)
curCharCount++;
// it's meant to pass through here
case 0xA:
curCharCount++;
// and it's also meant to pass through here
case 0:
return curCharCount;
case ' ':
maxChars = curCharCount; // return count up to (but not including) breaking space
break;
}
if (width + font->getCharWidth(curChar) > maxWidth)
break;
width += font->getCharWidth(curChar);
curCharCount++;
}
return maxChars;
}
void GfxText32::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
Common::Rect rect(0, 0, 0, 0);
Size(rect, text, font, maxWidth);
*textWidth = rect.width();
*textHeight = rect.height();
}
void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWidth, int16 &textHeight) {
Width(str, 0, (int16)strlen(str), fontId, textWidth, textHeight, true);
}
void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
byte curChar;
textWidth = 0; textHeight = 0;
GfxFont *font = _cache->getFont(fontId);
if (font) {
text += from;
while (len--) {
curChar = (*(const byte *)text++);
switch (curChar) {
case 0x0A:
case 0x0D:
textHeight = MAX<int16> (textHeight, font->getHeight());
break;
case 0x7C:
warning("Code processing isn't implemented in SCI32");
break;
default:
textHeight = MAX<int16> (textHeight, font->getHeight());
textWidth += font->getCharWidth(curChar);
break;
}
}
}
}
int16 GfxText32::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) {
int16 charCount;
int16 maxTextWidth = 0, textWidth;
int16 totalHeight = 0, textHeight;
int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
maxWidth = maxWidth * _scaledWidth / scriptWidth;
rect.top = rect.left = 0;
GfxFont *font = _cache->getFont(fontId);
if (maxWidth < 0) { // force output as single line
StringWidth(text, fontId, textWidth, textHeight);
rect.bottom = textHeight;
rect.right = textWidth;
} else {
// rect.right=found widest line with RTextWidth and GetLongest
// rect.bottom=num. lines * GetPointSize
rect.right = (maxWidth ? maxWidth : 192);
const char *curPos = text;
while (*curPos) {
charCount = GetLongest(curPos, rect.right, font);
if (charCount == 0)
break;
Width(curPos, 0, charCount, fontId, textWidth, textHeight, false);
maxTextWidth = MAX(textWidth, maxTextWidth);
totalHeight += textHeight;
curPos += charCount;
while (*curPos == ' ')
curPos++; // skip over breaking spaces
}
rect.bottom = totalHeight;
rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth);
}
rect.right = rect.right * scriptWidth / _scaledWidth;
rect.bottom = rect.bottom * scriptHeight / _scaledHeight;
return rect.right;
int16 GfxText32::getStringWidth(const Common::String &text) {
// TODO: The fact that this double-scales the text makes it
// seem pretty unlikely that this is ever called in real life
error("Called weirdo getStringWidth (FontMgr::StringWidth)");
return scaleUpWidth(getTextWidth(text, 0, 10000));
}
} // End of namespace Sci

View File

@ -31,7 +31,7 @@ namespace Sci {
enum TextAlign {
kTextAlignLeft = 0,
kTextAlignCenter = 1,
kTextAlignRight = -1
kTextAlignRight = 2
};
/**
@ -43,6 +43,10 @@ enum TextAlign {
*/
class GfxText32 {
private:
SegManager *_segMan;
GfxCache *_cache;
GfxScreen *_screen;
/**
* The resource ID of the default font used by the game.
*
@ -54,6 +58,8 @@ private:
/**
* The width and height of the currently active text
* bitmap, in text-system coordinates.
*
* @note These are unsigned in the actual engine.
*/
int16 _width, _height;
@ -110,29 +116,63 @@ private:
*/
reg_t _bitmap;
int16 _field_20;
/**
* TODO: Document
*/
int _field_22;
int16 _field_22;
int _field_2C, _field_30, _field_34, _field_38;
int16 _field_3C;
/**
* The currently active font resource used to write text
* into the bitmap.
*
* @note SCI engine builds the font table directly
* inside of FontMgr; we use GfxFont instead.
* The position of the text draw cursor.
*/
GfxFont *_font;
Common::Point _drawPosition;
// TODO: This is general for all CelObjMem and should be
// put in a single location, like maybe as a static
// method of CelObjMem?!
void buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const;
void drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling);
void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling);
void drawTextBox();
void erase(const Common::Rect &rect, const bool doScaling);
void drawChar(const uint8 charIndex);
uint16 getCharWidth(const uint8 charIndex, const bool doScaling) const;
void drawText(const uint index, uint length);
inline int scaleUpWidth(int value) const {
const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth;
}
/**
* Gets the length of the longest run of text available
* within the currently loaded text, starting from the
* given `charIndex` and running for up to `maxWidth`
* pixels. Returns the number of characters that can be
* written, and mutates the value pointed to by
* `charIndex` to point to the index of the next
* character to render.
*/
uint getLongest(uint *charIndex, const int16 maxWidth);
/**
* Gets the pixel width of a substring of the currently
* loaded text, without scaling.
*/
int16 getTextWidth(const uint index, uint length) const;
/**
* Gets the pixel width of a substring of the currently
* loaded text, with scaling.
*/
int16 getTextWidth(const Common::String &text, const uint index, const uint length);
inline Common::Rect scaleRect(const Common::Rect &rect) {
Common::Rect scaledRect(rect);
int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
@ -158,32 +198,36 @@ public:
*/
int16 _scaledHeight;
/**
* The currently active font resource used to write text
* into the bitmap.
*
* @note SCI engine builds the font table directly
* inside of FontMgr; we use GfxFont instead.
*/
GfxFont *_font;
reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, reg_t *outBitmapObject);
reg_t createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect, Common::String &text, int16 foreColor, int16 backColor, int font, int16 skipColor, int16 borderColor, bool dimmed, void *unknown1);
#pragma mark -
#pragma mark Old stuff
reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject);
void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y);
void disposeTextBitmap(reg_t hunkId);
int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
/**
* Sets the font to be used for rendering and
* calculation of text dimensions.
*/
void setFont(const GuiResourceId fontId);
private:
reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId);
void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId);
int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
/**
* Retrieves the width and height of a block of text.
*/
Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling);
SegManager *_segMan;
GfxCache *_cache;
GfxScreen *_screen;
/**
* Retrieves the width of a line of text.
*/
int16 getStringWidth(const Common::String &text);
};
} // End of namespace Sci