aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/kernel.h3
-rw-r--r--engines/sci/engine/kernel_tables.h55
-rw-r--r--engines/sci/engine/kgraphics.cpp7
-rw-r--r--engines/sci/engine/kgraphics32.cpp58
-rw-r--r--engines/sci/engine/kstring.cpp13
-rw-r--r--engines/sci/graphics/controls32.cpp3
-rw-r--r--engines/sci/graphics/helpers.h12
-rw-r--r--engines/sci/graphics/text32.cpp715
-rw-r--r--engines/sci/graphics/text32.h98
9 files changed, 504 insertions, 460 deletions
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 92916ecc68..3a6809aec1 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -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);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index fbd0b13c88..d3b2bfee54 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -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
@@ -284,6 +286,20 @@ static const SciKernelMapSubEntry kSave_subops[] = {
};
// 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 },
{ SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", 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",
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 91d241fb79..c0a3be4c11 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -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
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 4d48ae4e99..706edc87c6 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -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
-
- 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());
- }
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- 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()
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 310e38dbd1..f598cf7457 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -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;
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index 04a70d35f3..df518888a9 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -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
/*
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index fbad120374..1d16b49798 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -139,6 +139,18 @@ inline void mul(Common::Rect &rect, const Common::Rational &ratioX, const Common
}
/**
+ * 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.
*/
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index eabc329ee0..e393a1c68f 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -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,436 +173,367 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int size, const uint8
buffer.frameRect(targetRect, color);
}
-// TODO: This is not disassembled
-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;
-
- // Calculate total text height
- while (*txt) {
- charCount = GetLongest(txt, _textRect.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();
-
- byte *pixels = _segMan->getHunkPointer(_bitmap);
- pixels = pixels + READ_SCI11ENDIAN_UINT32(pixels + 28) + _width * _textRect.top + _textRect.left;
-
- // Draw text in buffer
- while (*txt) {
- charCount = GetLongest(txt, _textRect.width(), _font);
- if (charCount == 0)
- break;
- Width(txt, start, charCount, _fontId, textWidth, textHeight, true);
-
- 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;
-
- default:
- warning("Invalid alignment %d used in TextBox()", _alignment);
- }
+void GfxText32::drawChar(const uint8 charIndex) {
+ byte *bitmap = _segMan->getHunkPointer(_bitmap);
+ byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
- 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, pixels, _width, _height);
- curX += _font->getCharWidth(curChar);
- break;
- }
- }
+ _font->drawToBuffer(charIndex, _drawPosition.y, _drawPosition.x, _foreColor, _dimmed, pixels, _width, _height);
+ _drawPosition.x += _font->getCharWidth(charIndex);
+}
- curX = 0;
- curY += _font->getHeight();
- txt += charCount;
- while (*txt == ' ') {
- txt++; // skip over breaking spaces
- }
+uint16 GfxText32::getCharWidth(const uint8 charIndex, const bool doScaling) const {
+ uint16 width = _font->getCharWidth(charIndex);
+ if (doScaling) {
+ width = scaleUpWidth(width);
}
+ return width;
}
-void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
- Common::Rect targetRect = doScaling ? rect : scaleRect(rect);
+void GfxText32::drawTextBox() {
+ if (_text.size() == 0) {
+ return;
+ }
- byte *bitmap = _segMan->getHunkPointer(_bitmap);
- byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
+ 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");
+ }
- // NOTE: There is an extra optimisation within the SCI code to
- // do a single memset if the scaledRect is the same size as
- // the bitmap, not implemented here.
- Buffer buffer(_width, _height, pixels);
- buffer.fillRect(targetRect, _backColor);
-}
+ charIndex = 0;
+ uint nextCharIndex = 0;
+ while (*text != '\0') {
+ _drawPosition.x = _textRect.left;
-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));
+ uint length = getLongest(&nextCharIndex, textRectWidth);
+ int16 textWidth = getTextWidth(charIndex, length);
- Common::String text = _segMan->getString(stringObject);
+ if (_alignment == kTextAlignCenter) {
+ _drawPosition.x += (textRectWidth - textWidth) / 2;
+ } else if (_alignment == kTextAlignRight) {
+ _drawPosition.x += textRectWidth - textWidth;
+ }
- return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+ drawText(charIndex, length);
+ charIndex = nextCharIndex;
+ text = sourceText + charIndex;
+ _drawPosition.y += _font->getHeight();
+ }
}
-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();
- }
+void GfxText32::drawText(const uint index, uint length) {
+ assert(index + length <= _text.size());
- int entrySize = width * height + BITMAP_HEADER_SIZE;
- reg_t memoryId = NULL_REG;
- if (prevHunk.isNull()) {
- memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
+ // 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++;
- // 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
- }
+ if (currentChar == '|') {
+ const char controlChar = *text++;
+ --length;
- 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);
- }
+ if (length == 0) {
+ return;
+ }
- 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;
+ 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);
+ }
}
- }
- curX = 0;
- curY += font->getHeight();
- txt += charCount;
- while (*txt == ' ')
- txt++; // skip over breaking spaces
+ while (length > 0 && *text != '|') {
+ ++text;
+ --length;
+ }
+ } else {
+ drawChar(currentChar);
+ }
}
-
- return memoryId;
}
-void GfxText32::disposeTextBitmap(reg_t hunkId) {
- _segMan->freeHunkEntry(hunkId);
-}
+uint GfxText32::getLongest(uint *charIndex, const int16 width) {
+ assert(width > 0);
-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);
-}
+ uint testLength = 0;
+ uint length = 0;
-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));
+ const uint initialCharIndex = *charIndex;
- drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/
+ // The index of the next word after the last word break
+ uint lastWordBreakIndex = *charIndex;
- // HACK: we pretty much ignore the plane rect and x, y...
- drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId);
-}
+ const char *text = _text.c_str() + *charIndex;
-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;
+ char currentChar;
+ while ((currentChar = *text++) != '\0') {
+ // NOTE: In the original engine, the font, color, and alignment were
+ // reset here to their initial values
- // Negative coordinates indicate that text shouldn't be displayed
- if (x < 0 || y < 0)
- return;
+ // 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;
+ }
- byte *memoryPtr = _segMan->getHunkPointer(hunkId);
+ // 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;
+ }
- 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;
- }
+ // 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;
+ }
- byte *surface = memoryPtr + BITMAP_HEADER_SIZE;
+ // NOTE: In the original engine, the values of _fontId, _foreColor,
+ // and _alignment were stored for use in the return path mentioned
+ // just above here
- 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);
+ // 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;
+ }
- // 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();
- }
+ // In the middle of a line, keep processing
+ ++*charIndex;
+ ++testLength;
- bool translucent = (skipColor == -1 && backColor == -1);
+ // NOTE: In the original engine, the font, color, and alignment were
+ // reset here to their initial values
- 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);
+ // 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;
}
}
-}
-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++;
+ // 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;
}
- 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();
+ // The last word in the line made it wider than the text area, so return
+ // up to the penultimate word
+ *charIndex = lastWordBreakIndex;
+ return length;
}
-void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWidth, int16 &textHeight) {
- Width(str, 0, (int16)strlen(str), fontId, textWidth, textHeight, true);
-}
+int16 GfxText32::getTextWidth(const uint index, uint length) const {
+ int16 width = 0;
+
+ const char *text = _text.c_str() + index;
+
+ 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);
+ }
+ }
-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;
+ // Forward through any more unknown control character data
+ while (length > 0 && currentChar != '|') {
+ ++text;
+ --length;
}
+ } else {
+ width += font->getCharWidth(currentChar);
}
+
+ currentChar = *text++;
+ --length;
}
+
+ return width;
+}
+
+int16 GfxText32::getTextWidth(const Common::String &text, const uint index, const uint length) {
+ _text = text;
+ return scaleUpWidth(getTextWidth(index, length));
}
-int16 GfxText32::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) {
- int16 charCount;
- int16 maxTextWidth = 0, textWidth;
- int16 totalHeight = 0, textHeight;
+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;
- rect.top = rect.left = 0;
- GfxFont *font = _cache->getFont(fontId);
+ _text = text;
- 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
+ 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;
}
- rect.bottom = totalHeight;
- rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth);
+
+ 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();
+ }
+ }
+
+ 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;
}
- rect.right = rect.right * scriptWidth / _scaledWidth;
- rect.bottom = rect.bottom * scriptHeight / _scaledHeight;
+ return result;
+}
+
+void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
+ Common::Rect targetRect = doScaling ? rect : scaleRect(rect);
+
+ byte *bitmap = _segMan->getHunkPointer(_bitmap);
+ byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
+
+ // NOTE: There is an extra optimisation within the SCI code to
+ // do a single memset if the scaledRect is the same size as
+ // the bitmap, not implemented here.
+ Buffer buffer(_width, _height, pixels);
+ buffer.fillRect(targetRect, _backColor);
+}
+
+void GfxText32::disposeTextBitmap(reg_t hunkId) {
+ _segMan->freeHunkEntry(hunkId);
+}
- 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
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 08568e4958..f9a302a65c 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -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