/* 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. * * $URL$ * $Id$ * */ #include "common/util.h" #include "graphics/primitives.h" #include "sci/sci.h" #include "sci/engine/state.h" #include "sci/tools.h" #include "sci/gui/gui_font.h" #include "sci/gui/gui_picture.h" #include "sci/gui/gui_view.h" #include "sci/gui/gui_screen.h" #include "sci/gui/gui_palette.h" #include "sci/gui/gui_gfx.h" namespace Sci { SciGuiGfx::SciGuiGfx(EngineState *state, SciGuiScreen *screen, SciGuiPalette *palette) : _s(state), _screen(screen), _palette(palette) { init(); } SciGuiGfx::~SciGuiGfx() { } void SciGuiGfx::init() { _font = NULL; _textFonts = NULL; _textFontsCount = 0; _textColors = NULL; _textColorsCount = 0; // FIXME: _mainPort is never freed // FIXME: _mainPort has no id, and is not known to the WindowManager -- this could lead to problems _mainPort = new GuiPort(0); SetPort(_mainPort); OpenPort(_mainPort); // FIXME: _menuPort is never freed // FIXME: _menuPort has no id, and is not known to the WindowManager -- this could lead to problems _menuPort = new GuiPort(0); OpenPort(_menuPort); SetFont(0); _menuPort->rect = Common::Rect(0, 0, _screen->_width, _screen->_height); _menuRect = Common::Rect(0, 0, _screen->_width, 9); } GuiPort *SciGuiGfx::SetPort(GuiPort *newPort) { GuiPort *oldPort = _curPort; _curPort = newPort; return oldPort; } GuiPort *SciGuiGfx::GetPort(void) { return _curPort; } void SciGuiGfx::SetOrigin(int16 left, int16 top) { _curPort->left = left; _curPort->top = top; } void SciGuiGfx::MoveTo(int16 left, int16 top) { _curPort->curTop = top; _curPort->curLeft = left; } void SciGuiGfx::Move(int16 left, int16 top) { _curPort->curTop += top; _curPort->curLeft += left; } GuiResourceId SciGuiGfx::GetFontId() { return _curPort->fontId; } SciGuiFont *SciGuiGfx::GetFont() { if ((_font == NULL) || (_font->getResourceId() != _curPort->fontId)) _font = new SciGuiFont(_s->resMan, _curPort->fontId); return _font; } void SciGuiGfx::SetFont(GuiResourceId fontId) { if ((_font == NULL) || (_font->getResourceId() != fontId)) _font = new SciGuiFont(_s->resMan, fontId); _curPort->fontId = _font->getResourceId(); _curPort->fontHeight = _font->getHeight(); } void SciGuiGfx::OpenPort(GuiPort *port) { port->fontId = 0; port->fontHeight = 8; GuiPort *tmp = _curPort; _curPort = port; SetFont(port->fontId); _curPort = tmp; port->top = 0; port->left = 0; port->textFace = 0; port->penClr = 0; port->backClr = 255; port->penMode = 0; port->rect = _bounds; } void SciGuiGfx::PenColor(int16 color) { _curPort->penClr = color; } void SciGuiGfx::PenMode(int16 mode) { _curPort->penMode = mode; } void SciGuiGfx::TextFace(int16 textFace) { _curPort->textFace = textFace; } int16 SciGuiGfx::GetPointSize(void) { return _curPort->fontHeight; } void SciGuiGfx::ClearScreen(byte color) { FillRect(_curPort->rect, SCI_SCREEN_MASK_ALL, color, 0, 0); } void SciGuiGfx::InvertRect(const Common::Rect &rect) { int16 oldpenmode = _curPort->penMode; _curPort->penMode = 2; FillRect(rect, 1, _curPort->penClr, _curPort->backClr); _curPort->penMode = oldpenmode; } //----------------------------- void SciGuiGfx::EraseRect(const Common::Rect &rect) { FillRect(rect, 1, _curPort->backClr); } //----------------------------- void SciGuiGfx::PaintRect(const Common::Rect &rect) { FillRect(rect, 1, _curPort->penClr); } void SciGuiGfx::FillRect(const Common::Rect &rect, int16 drawFlags, byte clrPen, byte clrBack, byte bControl) { Common::Rect r = rect; r.clip(_curPort->rect); if (r.isEmpty()) // nothing to fill return; int16 oldPenMode = _curPort->penMode; OffsetRect(r); int16 x, y; byte curVisual; // Doing visual first if (drawFlags & SCI_SCREEN_MASK_VISUAL) { if (oldPenMode == 2) { // invert mode for (y = r.top; y < r.bottom; y++) { for (x = r.left; x < r.right; x++) { curVisual = _screen->getVisual(x, y); if (curVisual == clrPen) { _screen->putPixel(x, y, 1, clrBack, 0, 0); } else if (curVisual == clrBack) { _screen->putPixel(x, y, 1, clrPen, 0, 0); } } } } else { // just fill rect with ClrPen for (y = r.top; y < r.bottom; y++) { for (x = r.left; x < r.right; x++) { _screen->putPixel(x, y, 1, clrPen, 0, 0); } } } } if (drawFlags < 2) return; drawFlags &= SCI_SCREEN_MASK_PRIORITY|SCI_SCREEN_MASK_CONTROL; if (oldPenMode != 2) { for (y = r.top; y < r.bottom; y++) { for (x = r.left; x < r.right; x++) { _screen->putPixel(x, y, drawFlags, 0, clrBack, bControl); } } } else { for (y = r.top; y < r.bottom; y++) { for (x = r.left; x < r.right; x++) { _screen->putPixel(x, y, drawFlags, 0, !_screen->getPriority(x, y), !_screen->getControl(x, y)); } } } } void SciGuiGfx::FrameRect(const Common::Rect &rect) { Common::Rect r; // left r = rect; r.right = rect.left + 1; PaintRect(r); // right r.right = rect.right; r.left = rect.right - 1; PaintRect(r); //top r.left = rect.left; r.bottom = rect.top + 1; PaintRect(r); //bottom r.bottom = rect.bottom; r.top = rect.bottom - 1; PaintRect(r); } void SciGuiGfx::OffsetRect(Common::Rect &r) { r.top += _curPort->top; r.bottom += _curPort->top; r.left += _curPort->left; r.right += _curPort->left; } byte SciGuiGfx::CharHeight(int16 ch) { #if 0 CResFont *res = getResFont(); return res ? res->getCharH(ch) : 0; #endif return 0; } //----------------------------- byte SciGuiGfx::CharWidth(int16 ch) { SciGuiFont *font = GetFont(); return font ? font->getCharWidth(ch) : 0; } void SciGuiGfx::ClearChar(int16 chr) { if (_curPort->penMode != 1) return; Common::Rect rect; rect.top = _curPort->curTop; rect.bottom = rect.top + _curPort->fontHeight; rect.left = _curPort->curLeft; rect.right = rect.left + CharWidth(chr); EraseRect(rect); } void SciGuiGfx::DrawChar(int16 chr) { chr = chr & 0xFF; ClearChar(chr); StdChar(chr); _curPort->curLeft += CharWidth(chr); } void SciGuiGfx::StdChar(int16 chr) { #if 0 CResFont*res = getResFont(); if (res) res->Draw(chr, _curPort->top + _curPort->curTop, _curPort->left + _curPort->curLeft, _vSeg, 320, _curPort->penClr, _curPort->textFace); #endif } void SciGuiGfx::SetTextFonts(int argc, reg_t *argv) { int i; if (_textFonts) { delete _textFonts; } _textFontsCount = argc; _textFonts = new GuiResourceId[argc]; for (i = 0; i < argc; i++) { _textFonts[i] = (GuiResourceId)argv[i].toUint16(); } } void SciGuiGfx::SetTextColors(int argc, reg_t *argv) { int i; if (_textColors) { delete _textColors; } _textColorsCount = argc; _textColors = new uint16[argc]; for (i = 0; i < argc; i++) { _textColors[i] = argv[i].toUint16(); } } // This internal function gets called as soon as a '|' is found in a text // It will process the encountered code and set new font/set color // We only support one-digit codes currently, don't know if multi-digit codes are possible // Returns textcode character count int16 SciGuiGfx::TextCodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor) { const char *textCode = text; int16 textCodeSize = 0; char curCode; unsigned char curCodeParm; // Find the end of the textcode while ((++textCodeSize) && (*text++ != 0x7C)) { } // possible TextCodes: // c -> sets textColor to current port pen color // cX -> sets textColor to _textColors[X-1] curCode = textCode[0]; curCodeParm = textCode[1]; if (isdigit(curCodeParm)) { curCodeParm -= '0'; } else { curCodeParm = 0; } switch (curCode) { case 'c': // set text color if (curCodeParm == 0) { _curPort->penClr = orgPenColor; } else { if (curCodeParm < _textColorsCount) { _curPort->penClr = _textColors[curCodeParm]; } } break; case 'f': if (curCodeParm == 0) { SetFont(orgFontId); } else { if (curCodeParm < _textFontsCount) { SetFont(_textFonts[curCodeParm]); } } break; } return textCodeSize; } // return max # of chars to fit maxwidth with full words int16 SciGuiGfx::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) { char curChar; int16 maxChars = 0, curCharCount = 0; uint16 width = 0; GuiResourceId oldFontId = GetFontId(); int16 oldPenColor = _curPort->penClr; GetFont(); if (!_font) return 0; while (width <= maxWidth) { curChar = *text++; switch (curChar) { case 0x7C: curCharCount++; curCharCount += TextCodeProcessing(text, orgFontId, oldPenColor); continue; case 0xD: curCharCount++; continue; case 0xA: curCharCount++; case 0: SetFont(oldFontId); PenColor(oldPenColor); return curCharCount; case ' ': maxChars = curCharCount + 1; break; } width += _font->getCharWidth(curChar); curCharCount++; } SetFont(oldFontId); PenColor(oldPenColor); return maxChars; } void SciGuiGfx::TextWidth(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight) { unsigned char curChar; GuiResourceId oldFontId = GetFontId(); int16 oldPenColor = _curPort->penClr; textWidth = 0; textHeight = 0; GetFont(); if (_font) { text += from; while (len--) { curChar = *text++; switch (curChar) { case 0x7C: len -= TextCodeProcessing(text, orgFontId, 0); break; case 0x0A: case 0x0D: textHeight = MAX (textHeight, _curPort->fontHeight); break; default: textHeight = MAX (textHeight, _curPort->fontHeight); textWidth += _font->getCharWidth(curChar); } } } SetFont(oldFontId); PenColor(oldPenColor); return; } void SciGuiGfx::StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight) { TextWidth(str, 0, (int16)strlen(str), orgFontId, textWidth, textHeight); } int16 SciGuiGfx::TextSize(Common::Rect &rect, const char *str, GuiResourceId fontId, int16 maxWidth) { GuiResourceId oldFontId = GetFontId(); int16 oldPenColor = _curPort->penClr; int16 charCount; int16 maxTextWidth = 0, textWidth; int16 totalHeight = 0, textHeight; if (fontId != -1) SetFont(fontId); rect.top = rect.left = 0; if (maxWidth < 0) { // force output as single line StringWidth(str, oldFontId, 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*p = str; while (*p) { //if (*p == 0xD || *p == 0xA) { // p++; // continue; //} charCount = GetLongest(p, rect.right, oldFontId); if (charCount == 0) break; TextWidth(p, 0, charCount, oldFontId, textWidth, textHeight); maxTextWidth = MAX(textWidth, maxTextWidth); totalHeight += textHeight; p += charCount; } rect.bottom = totalHeight; rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth); } SetFont(oldFontId); PenColor(oldPenColor); return rect.right; } // returns maximum font height used void SciGuiGfx::DrawText(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor) { int16 curChar, charWidth; Common::Rect rect; GetFont(); if (!_font) return; rect.top = _curPort->curTop; rect.bottom = rect.top + _curPort->fontHeight; text += from; while (len--) { curChar = (*text++); switch (curChar) { case 0x7C: len -= TextCodeProcessing(text, orgFontId, orgPenColor); break; case 0x0A: case 0x0D: case 0: break; default: charWidth = _font->getCharWidth(curChar); // clear char if (_curPort->penMode == 1) { rect.left = _curPort->curLeft; rect.right = rect.left + charWidth; EraseRect(rect); } // CharStd _font->draw(_screen, curChar, _curPort->top + _curPort->curTop, _curPort->left + _curPort->curLeft, _curPort->penClr, _curPort->textFace); _curPort->curLeft += charWidth; } } } // returns maximum font height used void SciGuiGfx::ShowText(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor) { Common::Rect rect; rect.top = _curPort->curTop; rect.bottom = rect.top + GetPointSize(); rect.left = _curPort->curLeft; DrawText(text, from, len, orgFontId, orgPenColor); rect.right = _curPort->curLeft; ShowBits(rect, 1); } // Draws a text in rect. void SciGuiGfx::TextBox(const char *text, int16 bshow, const Common::Rect &rect, int16 align, GuiResourceId fontId) { int16 textWidth, textHeight, charCount, offset; int16 hline = 0; GuiResourceId orgFontId = GetFontId(); int16 orgPenColor = _curPort->penClr; if (fontId != -1) SetFont(fontId); while (*text) { // if (*text == 0xD || *text == 0xA) { // text++; // continue; // } charCount = GetLongest(text, rect.width(), orgFontId); if (charCount == 0) break; TextWidth(text, 0, charCount, orgFontId, textWidth, textHeight); switch (align) { case -1: // right-aligned offset = rect.width() - textWidth; break; case 1: // center text offset = (rect.width() - textWidth) / 2; break; default: // left-aligned offset = 0; } MoveTo(rect.left + offset, rect.top + hline); if (bshow) { ShowText(text, 0, charCount, orgFontId, orgPenColor); } else { DrawText(text, 0, charCount, orgFontId, orgPenColor); } hline += textHeight; text += charCount; } SetFont(orgFontId); PenColor(orgPenColor); } // Update (part of) screen void SciGuiGfx::ShowBits(const Common::Rect &r, uint16 flags) { Common::Rect rect(r.left, r.top, r.right, r.bottom); rect.clip(_curPort->rect); if (rect.isEmpty()) // nothing to show return; OffsetRect(rect); assert((flags&0x8000) == 0); _screen->copyToScreen(); // _system->copyRectToScreen(GetSegment(flags) + _baseTable[rect.top] + rect.left, 320, rect.left, rect.top, rect.width(), rect.height()); // _system->updateScreen(); } GuiMemoryHandle SciGuiGfx::SaveBits(const Common::Rect &rect, byte screenMask) { GuiMemoryHandle memoryId; byte *memoryPtr; int size; Common::Rect r(rect.left, rect.top, rect.right, rect.bottom); r.clip(_curPort->rect); if (r.isEmpty()) // nothing to save return NULL_REG; OffsetRect(r); //local port coords to screen coords // now actually ask _screen how much space it will need for saving size = _screen->getBitsDataSize(r, screenMask); memoryId = kalloc(_s->_segMan, "SaveBits()", size); memoryPtr = kmem(_s->_segMan, memoryId); _screen->saveBits(r, screenMask, memoryPtr); return memoryId; } void SciGuiGfx::RestoreBits(GuiMemoryHandle memoryHandle) { byte *memoryPtr = kmem(_s->_segMan, memoryHandle);; if (memoryPtr) { _screen->restoreBits(memoryPtr); kfree(_s->_segMan, memoryHandle); } } // Sierra's Bresenham line drawing // WARNING: Do not just blindly replace this with Graphics::drawLine(), as it seems to create issues with flood fill void SciGuiGfx::drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte priority, byte control) { //set_drawing_flag byte drawMask = _screen->getDrawingMask(color, priority, control); // offseting the line left += _curPort->left; right += _curPort->left; top += _curPort->top; bottom += _curPort->top; // horizontal line if (top == bottom) { if (right < left) SWAP(right, left); for (int i = left; i <= right; i++) _screen->putPixel(i, top, drawMask, color, priority, control); return; } // vertical line if (left == right) { if (top > bottom) SWAP(top, bottom); for (int i = top; i <= bottom; i++) _screen->putPixel(left, i, drawMask, color, priority, control); return; } // sloped line - draw with Bresenham algorithm int dy = bottom - top; int dx = right - left; int stepy = dy < 0 ? -1 : 1; int stepx = dx < 0 ? -1 : 1; dy = ABS(dy) << 1; dx = ABS(dx) << 1; // setting the 1st and last pixel _screen->putPixel(left, top, drawMask, color, priority, control); _screen->putPixel(right, bottom, drawMask, color, priority, control); // drawing the line if (dx > dy) { // going horizontal int fraction = dy - (dx >> 1); while (left != right) { if (fraction >= 0) { top += stepy; fraction -= dx; } left += stepx; fraction += dy; _screen->putPixel(left, top, drawMask, color, priority, control); } } else { // going vertical int fraction = dx - (dy >> 1); while (top != bottom) { if (fraction >= 0) { left += stepx; fraction -= dy; } top += stepy; fraction += dx; _screen->putPixel(left, top, drawMask, color, priority, control); } } //g_sci->eventMgr->waitUntil(5); //ShowBits(&_rThePort->rect,6); } // Bitmap for drawing sierra circles static const byte s_patternCircles[8][30] = { { 0x01 }, { 0x4C, 0x02 }, { 0xCE, 0xF7, 0x7D, 0x0E }, { 0x1C, 0x3E, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x00 }, { 0x38, 0xF8, 0xF3, 0xDF, 0x7F, 0xFF, 0xFD, 0xF7, 0x9F, 0x3F, 0x38 }, { 0x70, 0xC0, 0x1F, 0xFE, 0xE3, 0x3F, 0xFF, 0xF7, 0x7F, 0xFF, 0xE7, 0x3F, 0xFE, 0xC3, 0x1F, 0xF8, 0x00 }, { 0xF0, 0x01, 0xFF, 0xE1, 0xFF, 0xF8, 0x3F, 0xFF, 0xDF, 0xFF, 0xF7, 0xFF, 0xFD, 0x7F, 0xFF, 0x9F, 0xFF, 0xE3, 0xFF, 0xF0, 0x1F, 0xF0, 0x01 }, { 0xE0, 0x03, 0xF8, 0x0F, 0xFC, 0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, 0x0F, 0xE0, 0x03 } // { 0x01 }; // { 0x03, 0x03, 0x03 }, // { 0x02, 0x07, 0x07, 0x07, 0x02 }, // { 0x06, 0x06, 0x0F, 0x0F, 0x0F, 0x06, 0x06 }, // { 0x04, 0x0E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x04 }, // { 0x0C, 0x1E, 0x1E, 0x1E, 0x3F, 0x3F, 0x3F, 0x1E, 0x1E, 0x1E, 0x0C }, // { 0x1C, 0x3E, 0x3E, 0x3E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x3E, 0x3E, 0x3E, 0x1C }, // { 0x18, 0x3C, 0x7E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0x7E, 0x3C, 0x18 } }; // TODO: perhaps this is a better way to set the s_patternTextures array below? // in that case one would need to adjust bits of secondary table. Bit 256 is ignored by original interpreter #if 0 static const byte patternTextures[32 * 2] = { 0x04, 0x29, 0x40, 0x24, 0x09, 0x41, 0x25, 0x45, 0x41, 0x90, 0x50, 0x44, 0x48, 0x08, 0x42, 0x28, 0x89, 0x52, 0x89, 0x88, 0x10, 0x48, 0xA4, 0x08, 0x44, 0x15, 0x28, 0x24, 0x00, 0x0A, 0x24, 0x20, // Now the table is actually duplicated, so we won't need to wrap around 0x04, 0x29, 0x40, 0x24, 0x09, 0x41, 0x25, 0x45, 0x41, 0x90, 0x50, 0x44, 0x48, 0x08, 0x42, 0x28, 0x89, 0x52, 0x89, 0x88, 0x10, 0x48, 0xA4, 0x08, 0x44, 0x15, 0x28, 0x24, 0x00, 0x0A, 0x24, 0x20, }; #endif // This table is bitwise upwards (from bit0 to bit7), sierras original table went down the bits (bit7 to bit0) // this was done to simplify things, so we can just run through the table w/o worrying too much about clipping static const bool s_patternTextures[32 * 8 * 2] = { false, false, true, false, false, false, false, false, // 0x04 true, false, false, true, false, true, false, false, // 0x29 false, false, false, false, false, false, true, false, // 0x40 false, false, true, false, false, true, false, false, // 0x24 true, false, false, true, false, false, false, false, // 0x09 true, false, false, false, false, false, true, false, // 0x41 true, false, true, false, false, true, false, false, // 0x25 true, false, true, false, false, false, true, false, // 0x45 true, false, false, false, false, false, true, false, // 0x41 false, false, false, false, true, false, false, true, // 0x90 false, false, false, false, true, false, true, false, // 0x50 false, false, true, false, false, false, true, false, // 0x44 false, false, false, true, false, false, true, false, // 0x48 false, false, false, true, false, false, false, false, // 0x08 false, true, false, false, false, false, true, false, // 0x42 false, false, false, true, false, true, false, false, // 0x28 true, false, false, true, false, false, false, true, // 0x89 false, true, false, false, true, false, true, false, // 0x52 true, false, false, true, false, false, false, true, // 0x89 false, false, false, true, false, false, false, true, // 0x88 false, false, false, false, true, false, false, false, // 0x10 false, false, false, true, false, false, true, false, // 0x48 false, false, true, false, false, true, false, true, // 0xA4 false, false, false, true, false, false, false, false, // 0x08 false, false, true, false, false, false, true, false, // 0x44 true, false, true, false, true, false, false, false, // 0x15 false, false, false, true, false, true, false, false, // 0x28 false, false, true, false, false, true, false, false, // 0x24 false, false, false, false, false, false, false, false, // 0x00 false, true, false, true, false, false, false, false, // 0x0A false, false, true, false, false, true, false, false, // 0x24 false, false, false, false, false, true, false, // 0x20 (last bit is not mentioned cause original interpreter also ignores that bit) // Now the table is actually duplicated, so we won't need to wrap around false, false, true, false, false, false, false, false, // 0x04 true, false, false, true, false, true, false, false, // 0x29 false, false, false, false, false, false, true, false, // 0x40 false, false, true, false, false, true, false, false, // 0x24 true, false, false, true, false, false, false, false, // 0x09 true, false, false, false, false, false, true, false, // 0x41 true, false, true, false, false, true, false, false, // 0x25 true, false, true, false, false, false, true, false, // 0x45 true, false, false, false, false, false, true, false, // 0x41 false, false, false, false, true, false, false, true, // 0x90 false, false, false, false, true, false, true, false, // 0x50 false, false, true, false, false, false, true, false, // 0x44 false, false, false, true, false, false, true, false, // 0x48 false, false, false, true, false, false, false, false, // 0x08 false, true, false, false, false, false, true, false, // 0x42 false, false, false, true, false, true, false, false, // 0x28 true, false, false, true, false, false, false, true, // 0x89 false, true, false, false, true, false, true, false, // 0x52 true, false, false, true, false, false, false, true, // 0x89 false, false, false, true, false, false, false, true, // 0x88 false, false, false, false, true, false, false, false, // 0x10 false, false, false, true, false, false, true, false, // 0x48 false, false, true, false, false, true, false, true, // 0xA4 false, false, false, true, false, false, false, false, // 0x08 false, false, true, false, false, false, true, false, // 0x44 true, false, true, false, true, false, false, false, // 0x15 false, false, false, true, false, true, false, false, // 0x28 false, false, true, false, false, true, false, false, // 0x24 false, false, false, false, false, false, false, false, // 0x00 false, true, false, true, false, false, false, false, // 0x0A false, false, true, false, false, true, false, false, // 0x24 false, false, false, false, false, true, false, // 0x20 (last bit is not mentioned cause original interpreter also ignores that bit) }; // Bit offsets into pattern_textures static const byte s_patternTextureOffset[128] = { 0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d, 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1 }; void SciGuiGfx::Draw_Box(Common::Rect box, byte color, byte prio, byte control) { byte flag = _screen->getDrawingMask(color, prio, control); int y, x; for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { _screen->putPixel(x, y, flag, color, prio, control); } } } void SciGuiGfx::Draw_TexturedBox(Common::Rect box, byte color, byte prio, byte control, byte texture) { byte flag = _screen->getDrawingMask(color, prio, control); const bool *textureData = &s_patternTextures[s_patternTextureOffset[texture]]; int y, x; for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (*textureData) { _screen->putPixel(x, y, flag, color, prio, control); } textureData++; } } } void SciGuiGfx::Draw_Circle(Common::Rect box, byte size, byte color, byte prio, byte control) { byte flag = _screen->getDrawingMask(color, prio, control); byte *circleData = (byte *)&s_patternCircles[size]; byte bitmap = *circleData; byte bitNo = 0; int y, x; for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (bitmap & 1) { _screen->putPixel(x, y, flag, color, prio, control); } bitNo++; if (bitNo == 8) { circleData++; bitmap = *circleData; bitNo = 0; } else { bitmap = bitmap >> 1; } } } } void SciGuiGfx::Draw_TexturedCircle(Common::Rect box, byte size, byte color, byte prio, byte control, byte texture) { byte flag = _screen->getDrawingMask(color, prio, control); byte *circleData = (byte *)&s_patternCircles[size]; byte bitmap = *circleData; byte bitNo = 0; const bool *textureData = &s_patternTextures[s_patternTextureOffset[texture]]; int y, x; for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (bitmap & 1) { if (*textureData) { _screen->putPixel(x, y, flag, color, prio, control); } textureData++; } bitNo++; if (bitNo == 8) { circleData++; bitmap = *circleData; bitNo = 0; } else { bitmap = bitmap >> 1; } } } } void SciGuiGfx::Draw_Pattern(int16 x, int16 y, byte color, byte priority, byte control, byte code, byte texture) { byte size = code & SCI_PATTERN_CODE_PENSIZE; Common::Rect rect; // We need to adjust the given coordinates, because the ones given us do not define upper left but somewhat middle y -= size; if (y < 0) y = 0; x -= size; if (x < 0) x = 0; rect.top = y + _curPort->top; rect.left = x + _curPort->left; rect.setHeight((size*2)+1); rect.setWidth((size*2)+2); if (code & SCI_PATTERN_CODE_RECTANGLE) { // Rectangle if (code & SCI_PATTERN_CODE_USE_TEXTURE) { Draw_TexturedBox(rect, color, priority, control, texture); } else { Draw_Box(rect, color, priority, control); } } else { // Circle if (code & SCI_PATTERN_CODE_USE_TEXTURE) { Draw_TexturedCircle(rect, size, color, priority, control, texture); } else { Draw_Circle(rect, size, color, priority, control); } } } void SciGuiGfx::Draw_String(const char *text) { GuiResourceId orgFontId = GetFontId(); int16 orgPenColor = _curPort->penClr; DrawText(text, 0, strlen(text), orgFontId, orgPenColor); SetFont(orgFontId); PenColor(orgPenColor); } // Do not replace w/ some generic code. This algo really needs to behave exactly as the one from sierra void SciGuiGfx::Pic_Fill(int16 x, int16 y, byte color, byte prio, byte control) { Common::Stack stack; Common::Point p, p1; byte flag = _screen->getDrawingMask(color, prio, control), fmatch; p.x = x + _curPort->left; p.y = y + _curPort->top; stack.push(p); // parameters check //if ((flag & 2 && prio == 0) || (flag & 3 && control == 0)) // return; byte t_col = _screen->getVisual(p.x, p.y); byte t_pri = _screen->getPriority(p.x, p.y); byte t_con = _screen->getControl(p.x, p.y); int16 w, e, a_set, b_set; // if in 1st point priority,control or color is already set to target, clear the flag if (flag & 1 && t_col == color) flag ^= 1; if (flag & 2 && t_pri == prio) flag ^= 2; if (flag & 4 && t_con == control) flag ^= 4; if (flag == 0)// nothing to fill return; // hard borders for filling int l = _curPort->rect.left + _curPort->left; int t = _curPort->rect.top + _curPort->top; int r = _curPort->rect.right + _curPort->left - 1; int b = _curPort->rect.bottom + _curPort->top - 1; while (stack.size()) { p = stack.pop(); if ((fmatch = _screen->isFillMatch(p.x, p.y, flag, t_col, t_pri, t_con)) == 0) // already filled continue; _screen->putPixel(p.x, p.y, flag, color, prio, control); w = p.x; e = p.x; // moving west and east pointers as long as there is a matching color to fill while (w > l && (fmatch == _screen->isFillMatch(w - 1, p.y, flag, t_col, t_pri, t_con))) _screen->putPixel(--w, p.y, fmatch, color, prio, control); while (e < r && (fmatch == _screen->isFillMatch(e + 1, p.y, flag, t_col, t_pri, t_con))) _screen->putPixel(++e, p.y, fmatch, color, prio, control); // checking lines above and below for possible flood targets a_set = b_set = 0; while (w <= e) { if (p.y > t && (fmatch == _screen->isFillMatch(w, p.y - 1, flag, t_col, t_pri, t_con))) { // one line above if (a_set == 0) { p1.x = w; p1.y = p.y - 1; stack.push(p1); a_set = 1; } } else a_set = 0; if (p.y < b && (fmatch == _screen->isFillMatch(w, p.y + 1, flag, t_col, t_pri, t_con))) { // one line below if (b_set == 0) { p1.x = w; p1.y = p.y + 1; stack.push(p1); b_set = 1; } } else b_set = 0; w++; } } } void SciGuiGfx::drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId) { SciGuiPicture *picture; picture = new SciGuiPicture(_s, this, _screen, _palette, pictureId); // do we add to a picture? if not -> clear screen if (!addToFlag) { if (_s->resMan->isVGA()) ClearScreen(0); else ClearScreen(15); } picture->draw(animationNr, mirroredFlag, addToFlag, paletteId); } void SciGuiGfx::drawCel(GuiResourceId viewId, GuiViewLoopNo loopNo, GuiViewCelNo celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo) { SciGuiView *view = new SciGuiView(_s->resMan, _screen, _palette, viewId); Common::Rect rect(0, 0); Common::Rect clipRect(0, 0); if (view) { rect.left = leftPos; rect.top = topPos; rect.right = rect.left + view->getWidth(loopNo, celNo); rect.bottom = rect.top + view->getHeight(loopNo, celNo); clipRect = rect; clipRect.clip(_curPort->rect); if (clipRect.isEmpty()) // nothing to draw return; Common::Rect clipRectTranslated = clipRect; OffsetRect(clipRectTranslated); view->draw(rect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo); //if (_picNotValid == 0) // _gfx->ShowBits(rect, 1); } } int16 SciGuiGfx::onControl(uint16 screenMask, Common::Rect rect) { Common::Rect outRect(rect.left, rect.top, rect.right, rect.bottom); int16 x, y; int16 result = 0; outRect.clip(_curPort->rect); if (outRect.isEmpty()) // nothing to control return 0; OffsetRect(outRect); if (screenMask & SCI_SCREEN_MASK_PRIORITY) { for (y = outRect.top; y < outRect.bottom; y++) { for (x = outRect.left; x < outRect.right; x++) { result |= 1 << _screen->getPriority(x, y); } } } else { for (y = outRect.top; y < outRect.bottom; y++) { for (x = outRect.left; x < outRect.right; x++) { result |= 1 << _screen->getControl(x, y); } } } return result; } static inline int sign_extend_byte(int value) { if (value & 0x80) return value - 256; else return value; } void SciGuiGfx::AnimateDisposeLastCast() { // FIXME //if (!_lastCast->first.isNull()) //_lastCast->DeleteList(); } enum { SCI_ANIMATE_SIGNAL_FROZEN = 0x0100 }; void SciGuiGfx::AnimateInvoke(List *list, int argc, reg_t *argv) { SegManager *segMan = _s->_segMan; reg_t curAddress = list->first; Node *curNode = _s->_segMan->lookupNode(curAddress); reg_t curObject; uint16 signal; while (curNode) { curObject = curNode->value; signal = GET_SEL32V(curObject, signal); if (!(signal & SCI_ANIMATE_SIGNAL_FROZEN)) { invoke_selector(_s, curObject, _s->_kernel->_selectorCache.doit, kContinueOnInvalidSelector, argv, argc, __FILE__, __LINE__, 0); // Lookup node again, since the nodetable it was in may have been reallocated curNode = _s->_segMan->lookupNode(curAddress); } curAddress = curNode->succ; curNode = _s->_segMan->lookupNode(curAddress); } } void SciGuiGfx::AnimateFill() { } Common::List *SciGuiGfx::AnimateMakeSortedList(List *list) { SegManager *segMan = _s->_segMan; Common::List *sortedList = new Common::List; GuiAnimateList listHelper; reg_t curAddress = list->first; Node *curNode = _s->_segMan->lookupNode(curAddress); reg_t curObject; // First convert the given List to Common::List while (curNode) { curObject = curNode->value; listHelper.address = curAddress; listHelper.y = (int16)GET_SEL32V(curObject, y); listHelper.z = (int16)GET_SEL32V(curObject, z); sortedList->push_back(listHelper); curAddress = curNode->succ; curNode = _s->_segMan->lookupNode(curAddress); } // Now do a bubble sort on this Common::List if (sortedList->size() < 2) return sortedList; sortedList->begin(); // Common::List::iterator iter; // for (iter = s->_executionStack.begin(); // iter != s->_executionStack.end(); ++iter) { // ExecStack &es = *iter; return sortedList; } void SciGuiGfx::AnimateUpdate() { } void SciGuiGfx::AnimateDrawCels() { } void SciGuiGfx::AnimateRestoreAndDelete() { } void SciGuiGfx::SetNowSeen(reg_t objectReference) { SegManager *segMan = _s->_segMan; SciGuiView *view = NULL; Common::Rect celRect(0, 0); GuiResourceId viewId = (GuiResourceId)GET_SEL32V(objectReference, view); GuiViewLoopNo loopNo = sign_extend_byte((GuiViewLoopNo)GET_SEL32V(objectReference, loop)); GuiViewCelNo celNo = sign_extend_byte((GuiViewCelNo)GET_SEL32V(objectReference, cel)); int16 x = (int16)GET_SEL32V(objectReference, x); int16 y = (int16)GET_SEL32V(objectReference, y); int16 z = 0; if (_s->_kernel->_selectorCache.z > -1) { z = (int16)GET_SEL32V(objectReference, z); } // now get cel rectangle view = new SciGuiView(_s->resMan, _screen, _palette, viewId); view->getCelRect(loopNo, celNo, x, y, z, &celRect); // TODO: sometimes loop is negative. Check what it means if (lookup_selector(_s->_segMan, objectReference, _s->_kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { PUT_SEL32V(objectReference, nsLeft, celRect.left); PUT_SEL32V(objectReference, nsRight, celRect.right); PUT_SEL32V(objectReference, nsTop, celRect.top); PUT_SEL32V(objectReference, nsBottom, celRect.bottom); } } } // End of namespace Sci