diff options
Diffstat (limited to 'engines/sci/graphics/gfx.cpp')
-rw-r--r-- | engines/sci/graphics/gfx.cpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/engines/sci/graphics/gfx.cpp b/engines/sci/graphics/gfx.cpp new file mode 100644 index 0000000000..be4c7d357d --- /dev/null +++ b/engines/sci/graphics/gfx.cpp @@ -0,0 +1,536 @@ +/* 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 "common/stack.h" +#include "graphics/primitives.h" + +#include "sci/sci.h" +#include "sci/engine/state.h" +#include "sci/graphics/gfx.h" +#include "sci/graphics/animate.h" +#include "sci/graphics/font.h" +#include "sci/graphics/picture.h" +#include "sci/graphics/view.h" +#include "sci/graphics/screen.h" +#include "sci/graphics/palette.h" +#include "sci/graphics/text.h" + +namespace Sci { + +Gfx::Gfx(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, Screen *screen, SciPalette *palette) + : _resMan(resMan), _segMan(segMan), _kernel(kernel), _screen(screen), _palette(palette) { +} + +Gfx::~Gfx() { + purgeCache(); + + delete _mainPort; + delete _menuPort; +} + +void Gfx::init(Text *text) { + _text = text; + + // _mainPort is not known to windowmanager, that's okay according to sierra sci + // its not even used currently in our engine + _mainPort = new Port(0); + SetPort(_mainPort); + OpenPort(_mainPort); + + // _menuPort has actually hardcoded id 0xFFFF. Its not meant to be known to windowmanager according to sierra sci + _menuPort = new Port(0xFFFF); + OpenPort(_menuPort); + _text->SetFont(0); + _menuPort->rect = Common::Rect(0, 0, _screen->_width, _screen->_height); + _menuBarRect = Common::Rect(0, 0, _screen->_width, 9); +} + +void Gfx::purgeCache() { + for (ViewCache::iterator iter = _cachedViews.begin(); iter != _cachedViews.end(); ++iter) { + delete iter->_value; + iter->_value = 0; + } + + _cachedViews.clear(); +} + +View *Gfx::getView(GuiResourceId viewNum) { + if (_cachedViews.size() >= MAX_CACHED_VIEWS) + purgeCache(); + + if (!_cachedViews.contains(viewNum)) + _cachedViews[viewNum] = new View(_resMan, _screen, _palette, viewNum); + + return _cachedViews[viewNum]; +} + +Port *Gfx::SetPort(Port *newPort) { + Port *oldPort = _curPort; + _curPort = newPort; + return oldPort; +} + +Port *Gfx::GetPort() { + return _curPort; +} + +void Gfx::SetOrigin(int16 left, int16 top) { + _curPort->left = left; + _curPort->top = top; +} + +void Gfx::MoveTo(int16 left, int16 top) { + _curPort->curTop = top; + _curPort->curLeft = left; +} + +void Gfx::Move(int16 left, int16 top) { + _curPort->curTop += top; + _curPort->curLeft += left; +} + +void Gfx::OpenPort(Port *port) { + port->fontId = 0; + port->fontHeight = 8; + + Port *tmp = _curPort; + _curPort = port; + _text->SetFont(port->fontId); + _curPort = tmp; + + port->top = 0; + port->left = 0; + port->greyedOutput = false; + port->penClr = 0; + port->backClr = 255; + port->penMode = 0; + port->rect = _bounds; +} + +void Gfx::PenColor(int16 color) { + _curPort->penClr = color; +} + +void Gfx::BackColor(int16 color) { + _curPort->backClr = color; +} + +void Gfx::PenMode(int16 mode) { + _curPort->penMode = mode; +} + +void Gfx::TextGreyedOutput(bool state) { + _curPort->greyedOutput = state; +} + +int16 Gfx::GetPointSize() { + return _curPort->fontHeight; +} + +void Gfx::ClearScreen(byte color) { + FillRect(_curPort->rect, SCI_SCREEN_MASK_ALL, color, 0, 0); +} + +void Gfx::InvertRect(const Common::Rect &rect) { + int16 oldpenmode = _curPort->penMode; + _curPort->penMode = 2; + FillRect(rect, 1, _curPort->penClr, _curPort->backClr); + _curPort->penMode = oldpenmode; +} + +void Gfx::EraseRect(const Common::Rect &rect) { + FillRect(rect, 1, _curPort->backClr); +} + +void Gfx::PaintRect(const Common::Rect &rect) { + FillRect(rect, 1, _curPort->penClr); +} + +void Gfx::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 Gfx::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 Gfx::OffsetRect(Common::Rect &r) { + r.top += _curPort->top; + r.bottom += _curPort->top; + r.left += _curPort->left; + r.right += _curPort->left; +} + +void Gfx::OffsetLine(Common::Point &start, Common::Point &end) { + start.x += _curPort->left; + start.y += _curPort->top; + end.x += _curPort->left; + end.y += _curPort->top; +} + +void Gfx::BitsShow(const Common::Rect &rect) { + Common::Rect workerRect(rect.left, rect.top, rect.right, rect.bottom); + workerRect.clip(_curPort->rect); + if (workerRect.isEmpty()) // nothing to show + return; + + OffsetRect(workerRect); + _screen->copyRectToScreen(workerRect); +} + +MemoryHandle Gfx::BitsSave(const Common::Rect &rect, byte screenMask) { + MemoryHandle memoryId; + byte *memoryPtr; + int size; + + Common::Rect workerRect(rect.left, rect.top, rect.right, rect.bottom); + workerRect.clip(_curPort->rect); + if (workerRect.isEmpty()) // nothing to save + return NULL_REG; + + OffsetRect(workerRect); + + // now actually ask _screen how much space it will need for saving + size = _screen->bitsGetDataSize(workerRect, screenMask); + + memoryId = kalloc(_segMan, "SaveBits()", size); + memoryPtr = kmem(_segMan, memoryId); + _screen->bitsSave(workerRect, screenMask, memoryPtr); + return memoryId; +} + +void Gfx::BitsGetRect(MemoryHandle memoryHandle, Common::Rect *destRect) { + byte *memoryPtr = NULL; + + if (!memoryHandle.isNull()) { + memoryPtr = kmem(_segMan, memoryHandle); + + if (memoryPtr) { + _screen->bitsGetRect(memoryPtr, destRect); + } + } +} + +void Gfx::BitsRestore(MemoryHandle memoryHandle) { + byte *memoryPtr = NULL; + + if (!memoryHandle.isNull()) { + memoryPtr = kmem(_segMan, memoryHandle); + + if (memoryPtr) { + _screen->bitsRestore(memoryPtr); + kfree(_segMan, memoryHandle); + } + } +} + +void Gfx::BitsFree(MemoryHandle memoryHandle) { + if (!memoryHandle.isNull()) { + kfree(_segMan, memoryHandle); + } +} + +void Gfx::drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId) { + SciGuiPicture *picture = new SciGuiPicture(_resMan, this, _screen, _palette, pictureId); + + // do we add to a picture? if not -> clear screen with white + if (!addToFlag) + ClearScreen(_screen->_colorWhite); + + picture->draw(animationNr, mirroredFlag, addToFlag, paletteId); + delete picture; +} + +// This one is the only one that updates screen! +void Gfx::drawCel(GuiResourceId viewId, LoopNo loopNo, CelNo celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, int16 origHeight) { + View *view = getView(viewId); + Common::Rect rect; + Common::Rect clipRect; + 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, origHeight); + if (getSciVersion() >= SCI_VERSION_1_1) { + if (!_screen->_picNotValidSci11) + BitsShow(rect); + } else { + if (!_screen->_picNotValid) + BitsShow(rect); + } + } +} + +// This version of drawCel is not supposed to call BitsShow()! +void Gfx::drawCel(GuiResourceId viewId, LoopNo loopNo, CelNo celNo, Common::Rect celRect, byte priority, uint16 paletteNo) { + View *view = getView(viewId); + Common::Rect clipRect; + if (view) { + clipRect = celRect; + clipRect.clip(_curPort->rect); + if (clipRect.isEmpty()) { // nothing to draw + return; + } + + Common::Rect clipRectTranslated = clipRect; + OffsetRect(clipRectTranslated); + view->draw(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo); + } +} + +// This version of drawCel is not supposed to call BitsShow()! +void Gfx::drawCel(View *view, LoopNo loopNo, CelNo celNo, Common::Rect celRect, byte priority, uint16 paletteNo) { + Common::Rect clipRect; + clipRect = celRect; + clipRect.clip(_curPort->rect); + if (clipRect.isEmpty()) // nothing to draw + return; + + Common::Rect clipRectTranslated = clipRect; + OffsetRect(clipRectTranslated); + view->draw(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo); +} + +uint16 Gfx::onControl(uint16 screenMask, Common::Rect rect) { + Common::Rect outRect(rect.left, rect.top, rect.right, rect.bottom); + int16 x, y; + uint16 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 Gfx::PriorityBandsInit(int16 bandCount, int16 top, int16 bottom) { + int16 y; + int32 bandSize; + + if (bandCount != -1) + _priorityBandCount = bandCount; + + _priorityTop = top; + _priorityBottom = bottom; + + // Do NOT modify this algo or optimize it anyhow, sierra sci used int32 for calculating the + // priority bands and by using double or anything rounding WILL destroy the result + bandSize = ((_priorityBottom - _priorityTop) * 2000) / _priorityBandCount; + + memset(_priorityBands, 0, sizeof(byte) * _priorityTop); + for (y = _priorityTop; y < _priorityBottom; y++) + _priorityBands[y] = 1 + (((y - _priorityTop) * 2000) / bandSize); + if (_priorityBandCount == 15) { + // When having 15 priority bands, we actually replace band 15 with band 14, cause the original sci interpreter also + // does it that way as well + y = _priorityBottom; + while (_priorityBands[--y] == _priorityBandCount) + _priorityBands[y]--; + } + // We fill space that is left over with the highest band + for (y = _priorityBottom; y < _screen->_height; y++) + _priorityBands[y] = _priorityBandCount; +} + +void Gfx::PriorityBandsInit(byte *data) { + int i = 0, inx; + byte priority = 0; + + for (inx = 0; inx < 14; inx++) { + priority = *data++; + while (i < priority) + _priorityBands[i++] = inx; + } + while (i < 200) + _priorityBands[i++] = inx; +} + +byte Gfx::CoordinateToPriority(int16 y) { + if (y < _priorityTop) + return _priorityBands[_priorityTop]; + if (y > _priorityBottom) + return _priorityBands[_priorityBottom]; + return _priorityBands[y]; +} + +int16 Gfx::PriorityToCoordinate(byte priority) { + int16 y; + if (priority <= _priorityBandCount) { + for (y = 0; y <= _priorityBottom; y++) + if (_priorityBands[y] == priority) + return y; + } + return _priorityBottom; +} + +bool Gfx::CanBeHereCheckRectList(reg_t checkObject, Common::Rect checkRect, List *list) { + reg_t curAddress = list->first; + Node *curNode = _segMan->lookupNode(curAddress); + reg_t curObject; + uint16 signal; + Common::Rect curRect; + + while (curNode) { + curObject = curNode->value; + if (curObject != checkObject) { + signal = GET_SEL32V(_segMan, curObject, signal); + if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) { + curRect.left = GET_SEL32V(_segMan, curObject, brLeft); + curRect.top = GET_SEL32V(_segMan, curObject, brTop); + curRect.right = GET_SEL32V(_segMan, curObject, brRight); + curRect.bottom = GET_SEL32V(_segMan, curObject, brBottom); + // Check if curRect is within checkRect + if (curRect.right > checkRect.left && curRect.left < checkRect.right && curRect.bottom > checkRect.top && curRect.top < checkRect.bottom) { + return false; + } + } + } + curAddress = curNode->succ; + curNode = _segMan->lookupNode(curAddress); + } + return true; +} + +void Gfx::SetNowSeen(reg_t objectReference) { + View *view = NULL; + Common::Rect celRect(0, 0); + GuiResourceId viewId = (GuiResourceId)GET_SEL32V(_segMan, objectReference, view); + LoopNo loopNo = sign_extend_byte((LoopNo)GET_SEL32V(_segMan, objectReference, loop)); + CelNo celNo = sign_extend_byte((CelNo)GET_SEL32V(_segMan, objectReference, cel)); + int16 x = (int16)GET_SEL32V(_segMan, objectReference, x); + int16 y = (int16)GET_SEL32V(_segMan, objectReference, y); + int16 z = 0; + if (_kernel->_selectorCache.z > -1) + z = (int16)GET_SEL32V(_segMan, objectReference, z); + + // now get cel rectangle + view = getView(viewId); + view->getCelRect(loopNo, celNo, x, y, z, &celRect); + + // TODO: sometimes loop is negative. Check what it means + if (lookup_selector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { + PUT_SEL32V(_segMan, objectReference, nsLeft, celRect.left); + PUT_SEL32V(_segMan, objectReference, nsRight, celRect.right); + PUT_SEL32V(_segMan, objectReference, nsTop, celRect.top); + PUT_SEL32V(_segMan, objectReference, nsBottom, celRect.bottom); + } +} + +} // End of namespace Sci |