diff options
Diffstat (limited to 'engines/lab/dispman.cpp')
-rw-r--r-- | engines/lab/dispman.cpp | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/engines/lab/dispman.cpp b/engines/lab/dispman.cpp new file mode 100644 index 0000000000..a7c71f2214 --- /dev/null +++ b/engines/lab/dispman.cpp @@ -0,0 +1,768 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on Labyrinth of Time code with assistance of + * + * Copyright (c) 1993 Terra Nova Development + * Copyright (c) 2004 The Wyrmkeep Entertainment Co. + * + */ + +#include "lab/lab.h" +#include "lab/anim.h" +#include "lab/image.h" +#include "lab/labfun.h" +#include "lab/processroom.h" +#include "lab/resource.h" + +namespace Lab { + +DisplayMan::DisplayMan(LabEngine *vm) : _vm(vm) { + _longWinInFront = false; + _lastMessageLong = false; + _doNotDrawMessage = false; + + _screenBytesPerPage = 65536; + _curapen = 0; + _curBitmap = nullptr; + _displayBuffer = nullptr; + _currentDisplayBuffer = nullptr; + _tempScrollData = nullptr; + FadePalette = nullptr; + + _screenWidth = 0; + _screenHeight = 0; + + for (int i = 0; i < 256 * 3; i++) + _curvgapal[i] = 0; +} + +DisplayMan::~DisplayMan() { + freePict(); +} + +// From readPict.c. Reads in pictures and animations from disk. + +void DisplayMan::loadPict(const char *filename) { + Common::File *bitmapFile = _vm->_resource->openDataFile(filename); + freePict(); + _curBitmap = new byte[bitmapFile->size()]; + bitmapFile->read(_curBitmap, bitmapFile->size()); + delete bitmapFile; +} + +/** + * Reads in a picture into the dest bitmap. + */ +void DisplayMan::readPict(const char *filename, bool playOnce, bool onlyDiffData) { + _vm->_anim->stopDiff(); + + loadPict(filename); + + _vm->_music->updateMusic(); + + if (!_vm->_music->_doNotFilestopSoundEffect) + _vm->_music->stopSoundEffect(); + + _dispBitMap._bytesPerRow = _screenWidth; + _dispBitMap._rows = _screenHeight; + _dispBitMap._flags = BITMAPF_VIDEO; + + _vm->_anim->readDiff(_curBitmap, playOnce, onlyDiffData); +} + +/** + * Reads in a picture into buffer memory. + */ +byte *DisplayMan::readPictToMem(const char *filename, uint16 x, uint16 y) { + _vm->_anim->stopDiff(); + + loadPict(filename); + + _vm->_music->updateMusic(); + + if (!_vm->_music->_doNotFilestopSoundEffect) + _vm->_music->stopSoundEffect(); + + _dispBitMap._bytesPerRow = x; + _dispBitMap._rows = y; + _dispBitMap._flags = BITMAPF_NONE; + _dispBitMap._planes[0] = _curBitmap; + _dispBitMap._planes[1] = _dispBitMap._planes[0] + 0x10000; + _dispBitMap._planes[2] = _dispBitMap._planes[1] + 0x10000; + _dispBitMap._planes[3] = _dispBitMap._planes[2] + 0x10000; + _dispBitMap._planes[4] = _dispBitMap._planes[3] + 0x10000; + + _vm->_anim->readDiff(_curBitmap, true); + + return _curBitmap; +} + +void DisplayMan::freePict() { + delete _curBitmap; + _curBitmap = NULL; +} + +//--------------------------------------------------------------------------- +//------------ Does all the text rendering to the message boxes. ------------ +//--------------------------------------------------------------------------- + +/** + * Extracts the first word from a string. + */ +static void getWord(char *wordBuffer, const char *mainBuffer, uint16 *wordWidth) { + uint16 width = 0; + + while ((mainBuffer[width] != ' ') && mainBuffer[width] && (mainBuffer[width] != '\n')) { + wordBuffer[width] = mainBuffer[width]; + width++; + } + + wordBuffer[width] = 0; + + *wordWidth = width; +} + +/** + * Gets a line of text for flowText; makes sure that its length is less than + * or equal to the maximum width. + */ +void DisplayMan::getLine(TextFont *tf, char *lineBuffer, const char **mainBuffer, uint16 lineWidth) { + uint16 curWidth = 0, wordWidth; + char wordBuffer[100]; + bool doit = true; + + lineWidth += textLength(tf, " ", 1); + + lineBuffer[0] = 0; + + while ((*mainBuffer)[0] && doit) { + getWord(wordBuffer, *mainBuffer, &wordWidth); + strcat(wordBuffer, " "); + + if ((curWidth + textLength(tf, wordBuffer, wordWidth + 1)) <= lineWidth) { + strcat(lineBuffer, wordBuffer); + (*mainBuffer) += wordWidth; + + if ((*mainBuffer)[0] == '\n') + doit = false; + + if ((*mainBuffer)[0]) + (*mainBuffer)++; + + curWidth = textLength(tf, lineBuffer, strlen(lineBuffer)); + } else + doit = false; + } +} + +/** + * Dumps a chunk of text to an arbitrary box; flows it within that box and + * optionally centers it. Returns the number of characters that were + * processed. + * Note: Every individual word MUST be int16 enough to fit on a line, and + * each line less than 255 characters. + */ +uint32 DisplayMan::flowText( + void *font, // the TextAttr pointer + int16 spacing, // How much vertical spacing between the lines + byte pencolor, // pen number to use for text + byte backpen, // the background color + bool fillback, // Whether to fill the background + bool centerh, // Whether to center the text horizontally + bool centerv, // Whether to center the text vertically + bool output, // Whether to output any text + uint16 x1, uint16 y1, // Cords + uint16 x2, uint16 y2, + const char *str) { // The text itself + TextFont *_msgFont = (TextFont *)font; + char linebuffer[256]; + const char *temp; + uint16 numlines, actlines, fontheight, width; + uint16 x, y; + + if (fillback) { + setAPen(backpen); + rectFill(x1, y1, x2, y2); + } + + if (str == NULL) + return 0L; + + setAPen(pencolor); + + fontheight = textHeight(_msgFont) + spacing; + numlines = (y2 - y1 + 1) / fontheight; + width = x2 - x1 + 1; + y = y1; + + if (centerv && output) { + temp = str; + actlines = 0; + + while (temp[0]) { + getLine(_msgFont, linebuffer, &temp, width); + actlines++; + } + + if (actlines <= numlines) + y += ((y2 - y1 + 1) - (actlines * fontheight)) / 2; + } + + temp = str; + + while (numlines && str[0]) { + getLine(_msgFont, linebuffer, &str, width); + + x = x1; + + if (centerh) + x += (width - textLength(_msgFont, linebuffer, strlen(linebuffer))) / 2; + + if (output) + text(_msgFont, x, y, pencolor, linebuffer, strlen(linebuffer)); + + numlines--; + y += fontheight; + } + + return (str - temp); +} + +uint32 DisplayMan::flowTextScaled( + void *font, // the TextAttr pointer + int16 spacing, // How much vertical spacing between the lines + byte penColor, // pen number to use for text + byte backPen, // the background color + bool fillBack, // Whether to fill the background + bool centerX, // Whether to center the text horizontally + bool centerY, // Whether to center the text vertically + bool output, // Whether to output any text + uint16 x1, uint16 y1, // Cords + uint16 x2, uint16 y2, + const char *str) { + return flowText(font, spacing, penColor, backPen, fillBack, centerX, centerY, output, + _vm->_utils->vgaScaleX(x1), _vm->_utils->vgaScaleY(y1), + _vm->_utils->vgaScaleX(x2), _vm->_utils->vgaScaleY(y2), str); +} + +/** + * Calls flowText, but flows it to memory. Same restrictions as flowText. + */ +uint32 DisplayMan::flowTextToMem(Image *destIm, + void *font, // the TextAttr pointer + int16 spacing, // How much vertical spacing between the lines + byte pencolor, // pen number to use for text + byte backpen, // the background color + bool fillback, // Whether to fill the background + bool centerh, // Whether to center the text horizontally + bool centerv, // Whether to center the text vertically + bool output, // Whether to output any text + uint16 x1, uint16 y1, // Cords + uint16 x2, uint16 y2, + const char *str) { // The text itself + uint32 res, vgabyte = _screenBytesPerPage; + byte *tmp = _currentDisplayBuffer; + + _currentDisplayBuffer = destIm->_imageData; + _screenBytesPerPage = (uint32)destIm->_width * (int32)destIm->_height; + + res = flowText(font, spacing, pencolor, backpen, fillback, centerh, centerv, output, x1, y1, x2, y2, str); + + _screenBytesPerPage = vgabyte; + _currentDisplayBuffer = tmp; + + return res; +} + +//----- The control panel stuff ----- + +void DisplayMan::createBox(uint16 y2) { + // Message box area + setAPen(7); + rectFillScaled(4, 154, 315, y2 - 2); + + // Box around message area + setAPen(0); + drawHLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleX(317)); + drawVLine(_vm->_utils->vgaScaleX(317), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleY(y2)); + drawHLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(y2), _vm->_utils->vgaScaleX(317)); + drawVLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleY(y2)); +} + +int32 DisplayMan::longDrawMessage(const char *str) { + char newText[512]; + + if (str == NULL) + return 0; + + _vm->_event->attachGadgetList(NULL); + _vm->_event->mouseHide(); + strcpy(newText, str); + + if (!_longWinInFront) { + _longWinInFront = true; + // Clear Area + setAPen(3); + rectFill(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), _vm->_utils->vgaScaleY(199)); + } + + createBox(198); + _vm->_event->mouseShow(); + + return flowTextScaled(_vm->_msgFont, 0, 1, 7, false, true, true, true, 6, 155, 313, 195, str); +} + +/** + * Draws a message to the message box. + */ +void DisplayMan::drawMessage(const char *str) { + if (_doNotDrawMessage) { + _doNotDrawMessage = false; + return; + } + + if (str) { + if ((textLength(_vm->_msgFont, str, strlen(str)) > _vm->_utils->vgaScaleX(306))) { + longDrawMessage(str); + _lastMessageLong = true; + } else { + if (_longWinInFront) { + _longWinInFront = false; + drawPanel(); + } + + _vm->_event->mouseHide(); + createBox(168); + text(_vm->_msgFont, _vm->_utils->vgaScaleX(7), _vm->_utils->vgaScaleY(155) + _vm->_utils->svgaCord(2), 1, str, strlen(str)); + _vm->_event->mouseShow(); + _lastMessageLong = false; + } + } +} + +/** + * Draws the control panel display. + */ +void DisplayMan::drawPanel() { + _vm->_event->mouseHide(); + + // Clear Area + setAPen(3); + rectFill(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), _vm->_utils->vgaScaleY(199)); + + // First Line + setAPen(0); + drawHLine(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319)); + // Second Line + setAPen(5); + drawHLine(0, _vm->_utils->vgaScaleY(149) + 1 + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319)); + // Gadget Separators + setAPen(0); + // First black line to separate buttons + drawHLine(0, _vm->_utils->vgaScaleY(170), _vm->_utils->vgaScaleX(319)); + + if (!_vm->_alternate) { + setAPen(4); + // The horizontal lines under the black one + drawHLine(0, _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(319)); + drawGadgetList(&_vm->_moveGadgetList); + } else { + if (_vm->getPlatform() != Common::kPlatformWindows) { + // Vertical Black lines + drawVLine(_vm->_utils->vgaScaleX(124), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199)); + drawVLine(_vm->_utils->vgaScaleX(194), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199)); + } else { + // Vertical Black lines + drawVLine(_vm->_utils->vgaScaleX(90), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199)); + drawVLine(_vm->_utils->vgaScaleX(160), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199)); + drawVLine(_vm->_utils->vgaScaleX(230), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199)); + } + + setAPen(4); + // The horizontal lines under the black one + drawHLine(0, _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(122)); + drawHLine(_vm->_utils->vgaScaleX(126), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(192)); + drawHLine(_vm->_utils->vgaScaleX(196), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(319)); + // The vertical high light lines + drawVLine(_vm->_utils->vgaScaleX(1), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + + if (_vm->getPlatform() != Common::kPlatformWindows) { + drawVLine(_vm->_utils->vgaScaleX(126), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + drawVLine(_vm->_utils->vgaScaleX(196), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + } else { + drawVLine(_vm->_utils->vgaScaleX(92), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + drawVLine(_vm->_utils->vgaScaleX(162), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + drawVLine(_vm->_utils->vgaScaleX(232), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198)); + } + + drawGadgetList(&_vm->_invGadgetList); + } + + _vm->_event->mouseShow(); +} + +/** + * Sets up the Labyrinth screens, and opens up the initial windows. + */ +void DisplayMan::setUpScreens() { + createScreen(_vm->_isHiRes); + + Common::File *controlFile = _vm->_resource->openDataFile("P:Control"); + for (uint16 i = 0; i < 20; i++) + _vm->_moveImages[i] = new Image(controlFile); + delete controlFile; + + // Creates the gadgets for the movement control panel + uint16 y = _vm->_utils->vgaScaleY(173) - _vm->_utils->svgaCord(2); + + // The key mapping was only set for the Windows version. + // It's very convenient to have those shortcut, so I added them + // for all versions. (Strangerke) + _vm->_moveGadgetList.push_back(createButton( 1, y, 0, 't', _vm->_moveImages[0], _vm->_moveImages[1])); + _vm->_moveGadgetList.push_back(createButton( 33, y, 1, 'm', _vm->_moveImages[2], _vm->_moveImages[3])); + _vm->_moveGadgetList.push_back(createButton( 65, y, 2, 'o', _vm->_moveImages[4], _vm->_moveImages[5])); + _vm->_moveGadgetList.push_back(createButton( 97, y, 3, 'c', _vm->_moveImages[6], _vm->_moveImages[7])); + _vm->_moveGadgetList.push_back(createButton(129, y, 4, 'l', _vm->_moveImages[8], _vm->_moveImages[9])); + _vm->_moveGadgetList.push_back(createButton(161, y, 5, 'i', _vm->_moveImages[12], _vm->_moveImages[13])); + _vm->_moveGadgetList.push_back(createButton(193, y, 6, VKEY_LTARROW, _vm->_moveImages[14], _vm->_moveImages[15])); + _vm->_moveGadgetList.push_back(createButton(225, y, 7, VKEY_UPARROW, _vm->_moveImages[16], _vm->_moveImages[17])); + _vm->_moveGadgetList.push_back(createButton(257, y, 8, VKEY_RTARROW, _vm->_moveImages[18], _vm->_moveImages[19])); + _vm->_moveGadgetList.push_back(createButton(289, y, 9, 'p', _vm->_moveImages[10], _vm->_moveImages[11])); + + Common::File *invFile = _vm->_resource->openDataFile("P:Inv"); + if (_vm->getPlatform() == Common::kPlatformWindows) { + for (uint16 imgIdx = 0; imgIdx < 10; imgIdx++) + _vm->_invImages[imgIdx] = new Image(invFile); + } else { + for (uint16 imgIdx = 0; imgIdx < 6; imgIdx++) + _vm->_invImages[imgIdx] = new Image(invFile); + } + _vm->_invGadgetList.push_back(createButton( 24, y, 0, 'm', _vm->_invImages[0], _vm->_invImages[1])); + _vm->_invGadgetList.push_back(createButton( 56, y, 1, 'g', _vm->_invImages[2], _vm->_invImages[3])); + _vm->_invGadgetList.push_back(createButton( 94, y, 2, 'u', _vm->_invImages[4], _vm->_invImages[5])); + _vm->_invGadgetList.push_back(createButton(126, y, 3, 'l', _vm->_moveImages[8], _vm->_moveImages[9])); + _vm->_invGadgetList.push_back(createButton(164, y, 4, VKEY_LTARROW, _vm->_moveImages[14], _vm->_moveImages[15])); + _vm->_invGadgetList.push_back(createButton(196, y, 5, VKEY_RTARROW, _vm->_moveImages[18], _vm->_moveImages[19])); + + // The windows version has 2 extra gadgets for breadcrumb trail + // TODO: the game is really hard to play without those, maybe we could add something to enable that. + if (_vm->getPlatform() == Common::kPlatformWindows) { + _vm->_invGadgetList.push_back(createButton(234, y, 6, 'b', _vm->_invImages[6], _vm->_invImages[7])); + _vm->_invGadgetList.push_back(createButton(266, y, 7, 'f', _vm->_invImages[8], _vm->_invImages[9])); + } + + delete invFile; +} + +/** + * Sets the pen number to use on all the drawing operations. + */ +void DisplayMan::setAPen(byte pennum) { + _curapen = pennum; +} + +/** + * Fills in a rectangle. + */ +void DisplayMan::rectFill(uint16 x1, uint16 y1, uint16 x2, uint16 y2) { + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; + + if (x1 + w > _screenWidth) + w = _screenWidth - x1; + + if (y1 + h > _screenHeight) + h = _screenHeight - y1; + + if ((w > 0) && (h > 0)) { + char *d = (char *)getCurrentDrawingBuffer() + y1 * _screenWidth + x1; + + while (h-- > 0) { + char *dd = d; + int ww = w; + + while (ww-- > 0) { + *dd++ = _curapen; + } + + d += _screenWidth; + } + } +} + +void DisplayMan::rectFillScaled(uint16 x1, uint16 y1, uint16 x2, uint16 y2) { + rectFill(_vm->_utils->vgaScaleX(x1), _vm->_utils->vgaScaleY(y1), _vm->_utils->vgaScaleX(x2), _vm->_utils->vgaScaleY(y2)); +} + +/** + * Draws a horizontal line. + */ +void DisplayMan::drawVLine(uint16 x, uint16 y1, uint16 y2) { + rectFill(x, y1, x, y2); +} + +/** + * Draws a vertical line. + */ +void DisplayMan::drawHLine(uint16 x1, uint16 y, uint16 x2) { + rectFill(x1, y, x2, y); +} + +void DisplayMan::screenUpdate() { + g_system->copyRectToScreen(_displayBuffer, _screenWidth, 0, 0, _screenWidth, _screenHeight); + g_system->updateScreen(); + + _vm->_event->processInput(); +} + +/** + * Sets up either a low-res or a high-res 256 color screen. + */ +void DisplayMan::createScreen(bool hiRes) { + if (hiRes) { + _screenWidth = 640; + _screenHeight = 480; + } else { + _screenWidth = 320; + _screenHeight = 200; + } + _screenBytesPerPage = _screenWidth * _screenHeight; + _displayBuffer = new byte[_screenBytesPerPage]; // FIXME: Memory leak! +} + +/** + * Converts an Amiga palette (up to 16 colors) to a VGA palette, then sets + * the VGA palette. + */ +void DisplayMan::setAmigaPal(uint16 *pal, uint16 numColors) { + byte vgaPal[16 * 3]; + uint16 vgaIdx = 0; + + if (numColors > 16) + numColors = 16; + + for (uint16 i = 0; i < numColors; i++) { + vgaPal[vgaIdx++] = (byte)(((pal[i] & 0xf00) >> 8) << 2); + vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x0f0) >> 4) << 2); + vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x00f)) << 2); + } + + writeColorRegs(vgaPal, 0, 16); + _vm->waitTOF(); +} + +/** + * Writes any number of the 256 color registers. + * first: the number of the first color register to write. + * numreg: the number of registers to write + * buf: a char pointer which contains the selected color registers. + * Each value representing a color register occupies 3 bytes in + * the array. The order is red, green then blue. The first byte + * in the array is the red component of the first element selected. + * The length of the buffer is 3 times the number of registers + * selected. + */ +void DisplayMan::writeColorRegs(byte *buf, uint16 first, uint16 numreg) { + byte tmp[256 * 3]; + + for (int i = 0; i < 256 * 3; i++) { + tmp[i] = buf[i] * 4; + } + + g_system->getPaletteManager()->setPalette(tmp, first, numreg); + + memcpy(&(_curvgapal[first * 3]), buf, numreg * 3); +} + +void DisplayMan::setPalette(void *cmap, uint16 numcolors) { + if (memcmp(cmap, _curvgapal, numcolors * 3) != 0) + writeColorRegs((byte *)cmap, 0, numcolors); +} + +/** + * Returns the base address of the current VGA display. + */ +byte *DisplayMan::getCurrentDrawingBuffer() { + if (_currentDisplayBuffer) + return _currentDisplayBuffer; + + return _displayBuffer; +} + +/** + * Overlays a region on the screen using the desired pen color. + */ +void DisplayMan::overlayRect(uint16 pencolor, uint16 x1, uint16 y1, uint16 x2, uint16 y2) { + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; + + if (x1 + w > _screenWidth) + w = _screenWidth - x1; + + if (y1 + h > _screenHeight) + h = _screenHeight - y1; + + if ((w > 0) && (h > 0)) { + char *d = (char *)getCurrentDrawingBuffer() + y1 * _screenWidth + x1; + + while (h-- > 0) { + char *dd = d; + int ww = w; + + if (y1 & 1) { + dd++; + ww--; + } + + while (ww > 0) { + *dd = pencolor; + dd += 2; + ww -= 2; + } + + d += _screenWidth; + y1++; + } + } +} + +/** + * Closes a font and frees all memory associated with it. + */ +void DisplayMan::closeFont(TextFont *tf) { + if (tf) { + if (tf->_data && tf->_dataLength) + delete[] tf->_data; + + delete tf; + } +} + +/** + * Returns the length of a text in the specified font. + */ +uint16 DisplayMan::textLength(TextFont *tf, const char *text, uint16 numchars) { + uint16 length = 0; + + if (tf) { + for (uint16 i = 0; i < numchars; i++) { + length += tf->_widths[(uint)*text]; + text++; + } + } + + return length; +} + +/** + * Returns the height of a specified font. + */ +uint16 DisplayMan::textHeight(TextFont *tf) { + return (tf) ? tf->_height : 0; +} + +/** + * Draws the text to the screen. + */ +void DisplayMan::text(TextFont *tf, uint16 x, uint16 y, uint16 color, const char *text, uint16 numchars) { + byte *VGATop, *VGACur, *VGATemp, *VGATempLine, *cdata; + uint32 RealOffset, SegmentOffset; + int32 templeft, LeftInSegment; + uint16 bwidth, mask, curpage, data; + + VGATop = getCurrentDrawingBuffer(); + + for (uint16 i = 0; i < numchars; i++) { + RealOffset = (_screenWidth * y) + x; + curpage = RealOffset / _screenBytesPerPage; + SegmentOffset = RealOffset - (curpage * _screenBytesPerPage); + LeftInSegment = _screenBytesPerPage - SegmentOffset; + VGACur = VGATop + SegmentOffset; + + if (tf->_widths[(uint)*text]) { + cdata = tf->_data + tf->_offsets[(uint)*text]; + bwidth = *cdata++; + VGATemp = VGACur; + VGATempLine = VGACur; + + for (uint16 rows = 0; rows < tf->_height; rows++) { + VGATemp = VGATempLine; + templeft = LeftInSegment; + + for (uint16 cols = 0; cols < bwidth; cols++) { + data = *cdata++; + + if (data && (templeft >= 8)) { + for (int j = 7; j >= 0; j--) { + if ((1 << j) & data) + *VGATemp = color; + VGATemp++; + } + + templeft -= 8; + } else if (data) { + mask = 0x80; + templeft = LeftInSegment; + + for (uint16 counterb = 0; counterb < 8; counterb++) { + if (templeft <= 0) { + curpage++; + VGATemp = (byte *)(VGATop - templeft); + // Set up VGATempLine for next line + VGATempLine -= _screenBytesPerPage; + // Set up LeftInSegment for next line + LeftInSegment += _screenBytesPerPage + templeft; + templeft += _screenBytesPerPage; + } + + if (mask & data) + *VGATemp = color; + + VGATemp++; + + mask = mask >> 1; + templeft--; + } + } else { + templeft -= 8; + VGATemp += 8; + } + } + + VGATempLine += _screenWidth; + LeftInSegment -= _screenWidth; + + if (LeftInSegment <= 0) { + curpage++; + VGATempLine -= _screenBytesPerPage; + LeftInSegment += _screenBytesPerPage; + } + } + } + + x += tf->_widths[(int)*text]; + text++; + } +} +} // End of namespace Lab |