aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r--engines/sci/graphics/animate.cpp363
-rw-r--r--engines/sci/graphics/animate.h6
-rw-r--r--engines/sci/graphics/compare.cpp25
-rw-r--r--engines/sci/graphics/cursor.cpp29
-rw-r--r--engines/sci/graphics/frameout.cpp32
-rw-r--r--engines/sci/graphics/frameout.h3
-rw-r--r--engines/sci/graphics/helpers.h18
-rw-r--r--engines/sci/graphics/maciconbar.cpp259
-rw-r--r--engines/sci/graphics/maciconbar.h30
-rw-r--r--engines/sci/graphics/menu.cpp34
-rw-r--r--engines/sci/graphics/paint.cpp3
-rw-r--r--engines/sci/graphics/paint.h1
-rw-r--r--engines/sci/graphics/paint16.cpp36
-rw-r--r--engines/sci/graphics/paint16.h1
-rw-r--r--engines/sci/graphics/paint32.cpp11
-rw-r--r--engines/sci/graphics/paint32.h1
-rw-r--r--engines/sci/graphics/palette.cpp201
-rw-r--r--engines/sci/graphics/palette.h9
-rw-r--r--engines/sci/graphics/picture.cpp202
-rw-r--r--engines/sci/graphics/ports.cpp102
-rw-r--r--engines/sci/graphics/ports.h22
-rw-r--r--engines/sci/graphics/screen.cpp128
-rw-r--r--engines/sci/graphics/screen.h29
-rw-r--r--engines/sci/graphics/text16.cpp11
-rw-r--r--engines/sci/graphics/transitions.cpp30
-rw-r--r--engines/sci/graphics/transitions.h3
-rw-r--r--engines/sci/graphics/view.cpp337
-rw-r--r--engines/sci/graphics/view.h14
28 files changed, 1257 insertions, 683 deletions
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 2e5e94c52a..d8f3fa09b0 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -27,6 +27,7 @@
#include "common/stack.h"
#include "graphics/primitives.h"
+#include "sci/console.h"
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/kernel.h"
@@ -191,11 +192,88 @@ void GfxAnimate::makeSortedList(List *list) {
Common::sort(_list.begin(), _list.end(), sortHelper);
}
-void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) {
- reg_t curObject = entry->object;
+void GfxAnimate::fill(byte &old_picNotValid) {
+ GfxView *view = NULL;
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
+
+ for (it = _list.begin(); it != end; ++it) {
+ // Get the corresponding view
+ view = _cache->getView(it->viewId);
+
+ adjustInvalidCels(view, it);
+ processViewScaling(view, it);
+ setNsRect(view, it);
+
+ //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
+
+ // Calculate current priority according to y-coordinate
+ if (!(it->signal & kSignalFixedPriority)) {
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(priority), it->priority);
+ }
+
+ if (it->signal & kSignalNoUpdate) {
+ if ((it->signal & (kSignalForceUpdate | kSignalViewUpdated))
+ || (it->signal & kSignalHidden && !(it->signal & kSignalRemoveView))
+ || (!(it->signal & kSignalHidden) && it->signal & kSignalRemoveView)
+ || (it->signal & kSignalAlwaysUpdate))
+ old_picNotValid++;
+ it->signal &= ~kSignalStopUpdate;
+ } else {
+ if ((it->signal & kSignalStopUpdate) || (it->signal & kSignalAlwaysUpdate))
+ old_picNotValid++;
+ it->signal &= ~kSignalForceUpdate;
+ }
+ }
+}
+
+void GfxAnimate::adjustInvalidCels(GfxView *view, AnimateList::iterator it) {
+ // adjust loop and cel, if any of those is invalid
+ // this seems to be completely crazy code
+ // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases
+ // later during view processing those are compared unsigned again and then set to maximum count - 1
+ // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37
+ // which has cel set to 3. This cel does not exist and the actual knight is 0
+ // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real
+ // high number, which is negative when considered signed. This actually requires to get fixed to
+ // maximum cel, otherwise the trunk would be closed.
+ int16 viewLoopCount = view->getLoopCount();
+ if (it->loopNo >= viewLoopCount) {
+ it->loopNo = 0;
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(loop), it->loopNo);
+ } else if (it->loopNo < 0) {
+ it->loopNo = viewLoopCount - 1;
+ // not setting selector is right, sierra sci didn't do it during view processing as well
+ }
+ int16 viewCelCount = view->getCelCount(it->loopNo);
+ if (it->celNo >= viewCelCount) {
+ it->celNo = 0;
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(cel), it->celNo);
+ } else if (it->celNo < 0) {
+ it->celNo = viewCelCount - 1;
+ }
+}
+
+void GfxAnimate::processViewScaling(GfxView *view, AnimateList::iterator it) {
+ if (!view->isScaleable()) {
+ // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable
+ // this "feature" was removed in later versions of SCI1.1
+ it->scaleSignal = 0;
+ it->scaleY = it->scaleX = 128;
+ } else {
+ // Process global scaling, if needed
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ applyGlobalScaling(it, view);
+ }
+ }
+ }
+}
+void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) {
// Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
- int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale));
+ int16 maxScale = readSelectorValue(_s->_segMan, entry->object, SELECTOR(maxScale));
int16 celHeight = view->getHeight(entry->loopNo, entry->celNo);
int16 maxCelHeight = (maxScale * celHeight) >> 7;
reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object
@@ -215,120 +293,43 @@ void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view)
entry->scaleX = entry->scaleY;
// and set objects scale selectors
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), entry->scaleX);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY);
+ writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleX), entry->scaleX);
+ writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleY), entry->scaleY);
}
-void GfxAnimate::fill(byte &old_picNotValid) {
- reg_t curObject;
- uint16 signal;
- GfxView *view = NULL;
- AnimateList::iterator it;
- const AnimateList::iterator end = _list.end();
-
- for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- // Get the corresponding view
- view = _cache->getView(it->viewId);
-
- // adjust loop and cel, if any of those is invalid
- // this seems to be completely crazy code
- // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases
- // later during view processing those are compared unsigned again and then set to maximum count - 1
- // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37
- // which has cel set to 3. This cel does not exist and the actual knight is 0
- // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real
- // high number, which is negative when considered signed. This actually requires to get fixed to
- // maximum cel, otherwise the trunk would be closed.
- int16 viewLoopCount = view->getLoopCount();
- if (it->loopNo >= viewLoopCount) {
- it->loopNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), it->loopNo);
- } else if (it->loopNo < 0) {
- it->loopNo = viewLoopCount - 1;
- // not setting selector is right, sierra sci didn't do it during view processing as well
- }
- int16 viewCelCount = view->getCelCount(it->loopNo);
- if (it->celNo >= viewCelCount) {
- it->celNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), it->celNo);
- } else if (it->celNo < 0) {
- it->celNo = viewCelCount - 1;
- }
-
- if (!view->isScaleable()) {
- // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable
- // this "feature" was removed in later versions of SCI1.1
- it->scaleSignal = 0;
- it->scaleY = it->scaleX = 128;
- } else {
- // Process global scaling, if needed
- if (it->scaleSignal & kScaleSignalDoScaling) {
- if (it->scaleSignal & kScaleSignalGlobalScaling) {
- applyGlobalScaling(it, view);
- }
- }
- }
-
- //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
+void GfxAnimate::setNsRect(GfxView *view, AnimateList::iterator it) {
+ bool shouldSetNsRect = true;
- bool setNsRect = true;
-
- // Create rect according to coordinates and given cel
- if (it->scaleSignal & kScaleSignalDoScaling) {
- view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
- // when being scaled, only set nsRect, if object will get drawn
- if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate))
- setNsRect = false;
+ // Create rect according to coordinates and given cel
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
+ // when being scaled, only set nsRect, if object will get drawn
+ if ((it->signal & kSignalHidden) && !(it->signal & kSignalAlwaysUpdate))
+ shouldSetNsRect = false;
+ } else {
+ // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
+ // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
+ if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
+ it->celRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft));
+ it->celRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop));
+ it->celRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight));
+ it->celRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom));
+ view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ shouldSetNsRect = false;
} else {
- // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
- // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
- if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
- it->celRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft));
- it->celRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop));
- it->celRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight));
- it->celRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom));
- view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
- setNsRect = false;
- } else {
- view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
- }
- }
-
- if (setNsRect) {
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom);
- }
-
- // Calculate current priority according to y-coordinate
- if (!(signal & kSignalFixedPriority)) {
- it->priority = _ports->kernelCoordinateToPriority(it->y);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), it->priority);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
}
+ }
- if (signal & kSignalNoUpdate) {
- if (signal & (kSignalForceUpdate | kSignalViewUpdated)
- || (signal & kSignalHidden && !(signal & kSignalRemoveView))
- || (!(signal & kSignalHidden) && signal & kSignalRemoveView)
- || (signal & kSignalAlwaysUpdate))
- old_picNotValid++;
- signal &= ~kSignalStopUpdate;
- } else {
- if (signal & kSignalStopUpdate || signal & kSignalAlwaysUpdate)
- old_picNotValid++;
- signal &= ~kSignalForceUpdate;
- }
- it->signal = signal;
+ if (shouldSetNsRect) {
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom), it->celRect.bottom);
}
}
void GfxAnimate::update() {
- reg_t curObject;
- uint16 signal;
reg_t bitsHandle;
Common::Rect rect;
AnimateList::iterator it;
@@ -336,81 +337,66 @@ void GfxAnimate::update() {
// Remove all no-update cels, if requested
for (it = _list.reverse_begin(); it != end; --it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate) {
- if (!(signal & kSignalRemoveView)) {
- bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits));
+ if (it->signal & kSignalNoUpdate) {
+ if (!(it->signal & kSignalRemoveView)) {
+ bitsHandle = readSelector(_s->_segMan, it->object, SELECTOR(underBits));
if (_screen->_picNotValid != 1) {
_paint16->bitsRestore(bitsHandle);
it->showBitsFlag = true;
} else {
_paint16->bitsFree(bitsHandle);
}
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0);
}
- signal &= ~kSignalForceUpdate;
- if (signal & kSignalViewUpdated)
- signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
- } else if (signal & kSignalStopUpdate) {
- signal &= ~kSignalStopUpdate;
- signal |= kSignalNoUpdate;
+ it->signal &= ~kSignalForceUpdate;
+ if (it->signal & kSignalViewUpdated)
+ it->signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
+ } else if (it->signal & kSignalStopUpdate) {
+ it->signal &= ~kSignalStopUpdate;
+ it->signal |= kSignalNoUpdate;
}
- it->signal = signal;
}
// Draw always-update cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalAlwaysUpdate) {
+ if (it->signal & kSignalAlwaysUpdate) {
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
- if ((signal & kSignalIgnoreActor) == 0) {
+ it->signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
+ if (!(it->signal & kSignalIgnoreActor)) {
rect = it->celRect;
rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
- it->signal = signal;
}
}
// Saving background for all NoUpdate-cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate) {
- if (signal & kSignalHidden) {
- signal |= kSignalRemoveView;
+ if (it->signal & kSignalNoUpdate) {
+ if (it->signal & kSignalHidden) {
+ it->signal |= kSignalRemoveView;
} else {
- signal &= ~kSignalRemoveView;
- if (signal & kSignalIgnoreActor)
+ it->signal &= ~kSignalRemoveView;
+ if (it->signal & kSignalIgnoreActor)
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
else
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
- writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle);
}
- it->signal = signal;
}
}
// Draw NoUpdate cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate && !(signal & kSignalHidden)) {
+ if (it->signal & kSignalNoUpdate && !(it->signal & kSignalHidden)) {
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- if (!(signal & kSignalIgnoreActor)) {
+ if (!(it->signal & kSignalIgnoreActor)) {
rect = it->celRect;
rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
@@ -420,30 +406,23 @@ void GfxAnimate::update() {
}
void GfxAnimate::drawCels() {
- reg_t curObject;
- uint16 signal;
reg_t bitsHandle;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
_lastCastData.clear();
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
+ if (!(it->signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
// Save background
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
- writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle);
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- if (signal & kSignalRemoveView) {
- signal &= ~kSignalRemoveView;
- }
- it->signal = signal;
+ if (it->signal & kSignalRemoveView)
+ it->signal &= ~kSignalRemoveView;
// Remember that entry in lastCast
_lastCastData.push_back(*it);
@@ -452,23 +431,18 @@ void GfxAnimate::drawCels() {
}
void GfxAnimate::updateScreen(byte oldPicNotValid) {
- reg_t curObject;
- uint16 signal;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
Common::Rect lsRect;
Common::Rect workerRect;
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (it->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
- (!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) {
- lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft));
- lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop));
- lsRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight));
- lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom));
+ if (it->showBitsFlag || !(it->signal & (kSignalRemoveView | kSignalNoUpdate) ||
+ (!(it->signal & kSignalRemoveView) && (it->signal & kSignalNoUpdate) && oldPicNotValid))) {
+ lsRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft));
+ lsRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop));
+ lsRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight));
+ lsRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom));
workerRect = lsRect;
workerRect.clip(it->celRect);
@@ -480,17 +454,16 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
_paint16->bitsShow(lsRect);
workerRect = it->celRect;
}
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), it->celRect.left);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), it->celRect.top);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), it->celRect.right);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), it->celRect.bottom);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom), it->celRect.bottom);
// may get used for debugging
//_paint16->frameRect(workerRect);
_paint16->bitsShow(workerRect);
- if (signal & kSignalHidden) {
+ if (it->signal & kSignalHidden)
it->signal |= kSignalRemoveView;
- }
}
}
// use this for debug purposes
@@ -498,37 +471,30 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
}
void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
- reg_t curObject;
- uint16 signal;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
-
// This has to be done in a separate loop. At least in sq1 some .dispose
// modifies FIXEDLOOP flag in signal for another object. In that case we
// would overwrite the new signal with our version of the old signal.
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
// Finally update signal
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(signal), it->signal);
}
for (it = _list.reverse_begin(); it != end; --it) {
- curObject = it->object;
// We read out signal here again, this is not by accident but to ensure
// that we got an up-to-date signal
- signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
+ it->signal = readSelectorValue(_s->_segMan, it->object, SELECTOR(signal));
- if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
- _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits)));
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ if ((it->signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
+ _paint16->bitsRestore(readSelector(_s->_segMan, it->object, SELECTOR(underBits)));
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0);
}
- if (signal & kSignalDisposeMe) {
+ if (it->signal & kSignalDisposeMe) {
// Call .delete_ method of that object
- invokeSelector(_s, curObject, SELECTOR(delete_), argc, argv, 0);
+ invokeSelector(_s, it->object, SELECTOR(delete_), argc, argv, 0);
}
}
}
@@ -591,7 +557,7 @@ void GfxAnimate::addToPicDrawCels() {
// draw corresponding cel
_paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
- if ((it->signal & kSignalIgnoreActor) == 0) {
+ if (!(it->signal & kSignalIgnoreActor)) {
it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1);
_paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
@@ -665,10 +631,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
// beginUpdate()/endUpdate() were introduced SCI1.
// Calling those for SCI0 will work most of the time but breaks minor
// stuff like percentage bar of qfg1ega at the character skill screen.
- if (getSciVersion() >= SCI_VERSION_1_EGA)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
_ports->beginUpdate(_ports->_picWind);
update();
- if (getSciVersion() >= SCI_VERSION_1_EGA)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
_ports->endUpdate(_ports->_picWind);
}
@@ -687,6 +653,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
_ports->setPort(oldPort);
// Now trigger speed throttler
+ throttleSpeed();
+}
+
+void GfxAnimate::throttleSpeed() {
switch (_lastCastData.size()) {
case 0:
// No entries drawn -> no speed throttler triggering
@@ -759,4 +729,21 @@ void GfxAnimate::kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 ce
addToPicSetPicNotValid();
}
+void GfxAnimate::printAnimateList(Console *con) {
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
+
+ for (it = _list.begin(); it != end; ++it) {
+ Script *scr = _s->_segMan->getScriptIfLoaded(it->object.segment);
+ int16 scriptNo = scr ? scr->getScriptNumber() : -1;
+
+ con->DebugPrintf("%04x:%04x (%s), script %d, view %d (%d, %d), pal %d, "
+ "at %d, %d, scale %d, %d / %d (z: %d, prio: %d, shown: %d, signal: %d)\n",
+ PRINT_REG(it->object), _s->_segMan->getObjectName(it->object),
+ scriptNo, it->viewId, it->loopNo, it->celNo, it->paletteNo,
+ it->x, it->y, it->scaleX, it->scaleY, it->scaleSignal,
+ it->z, it->priority, it->showBitsFlag, it->signal);
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 44ffdd53af..b2aadcbead 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -77,6 +77,7 @@ struct AnimateEntry {
typedef Common::List<AnimateEntry> AnimateList;
typedef Common::Array<AnimateEntry> AnimateArray;
+class Console;
class GfxCache;
class GfxCursor;
class GfxPorts;
@@ -105,6 +106,7 @@ public:
void reAnimate(Common::Rect rect);
void addToPicDrawCels();
void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
+ void printAnimateList(Console *con);
virtual void kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv);
virtual void kernelAddToPicList(reg_t listReference, int argc, reg_t *argv);
@@ -115,6 +117,10 @@ private:
void addToPicSetPicNotValid();
void animateShowPic();
+ void throttleSpeed();
+ void adjustInvalidCels(GfxView *view, AnimateList::iterator it);
+ void processViewScaling(GfxView *view, AnimateList::iterator it);
+ void setNsRect(GfxView *view, AnimateList::iterator it);
EngineState *_s;
GfxCache *_cache;
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 5f6aef0153..db926f2962 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -81,23 +81,14 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &
curObject = curNode->value;
if (curObject != checkObject) {
signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
- if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) {
+ if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) {
curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
curRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
// Check if curRect is within checkRect
- // TODO: This check is slightly odd, because it means that a rect is not contained
- // in itself. It may very well be that the original SCI engine did it just
- // this way, so it should not be changed lightly. However, somebody should
- // confirm whether the original engine really did it this way. Then, update
- // this comment accordingly, and, if necessary, fix the code.
- if (curRect.right > checkRect.left &&
- curRect.left < checkRect.right &&
- curRect.bottom > checkRect.top &&
- curRect.top < checkRect.bottom) {
+ if (checkRect.contains(curRect))
return curObject;
- }
}
}
curAddress = curNode->succ;
@@ -134,7 +125,7 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
#ifdef ENABLE_SCI32
if (view->isSci2Hires())
- _screen->adjustToUpscaledCoordinates(y, x);
+ view->adjustToUpscaledCoordinates(y, x);
else if (getSciVersion() == SCI_VERSION_2_1)
_coordAdjuster->fromScriptToDisplay(y, x);
#endif
@@ -143,8 +134,8 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
#ifdef ENABLE_SCI32
if (view->isSci2Hires()) {
- _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
- _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ view->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ view->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
} else if (getSciVersion() == SCI_VERSION_2_1) {
_coordAdjuster->fromDisplayToScript(celRect.top, celRect.left);
_coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right);
@@ -232,13 +223,13 @@ void GfxCompare::kernelBaseSetter(reg_t object) {
celRect.bottom = readSelectorValue(_segMan, object, SELECTOR(nsBottom));
} else {
if (tmpView->isSci2Hires())
- _screen->adjustToUpscaledCoordinates(y, x);
+ tmpView->adjustToUpscaledCoordinates(y, x);
tmpView->getCelRect(loopNo, celNo, x, y, z, celRect);
if (tmpView->isSci2Hires()) {
- _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
- _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ tmpView->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ tmpView->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
}
}
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index e78d3e67cb..3b95a5c955 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -26,6 +26,7 @@
#include "common/config-manager.h"
#include "common/events.h"
#include "common/macresman.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "common/util.h"
#include "graphics/cursorman.h"
@@ -38,6 +39,7 @@
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/view.h"
#include "sci/graphics/cursor.h"
+#include "sci/graphics/maciconbar.h"
namespace Sci {
@@ -434,9 +436,17 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
if (_macCursorRemap.empty()) {
// QFG1/Freddy/Hoyle4 use a straight viewNum->cursor ID mapping
- // KQ6 seems to use this mapping for its cursors
- if (g_sci->getGameId() == GID_KQ6)
- viewNum = loopNum * 1000 + celNum;
+ // KQ6 uses this mapping for its cursors
+ if (g_sci->getGameId() == GID_KQ6) {
+ if (viewNum == 990) // Inventory Cursors
+ viewNum = loopNum * 16 + celNum + 2000;
+ else if (viewNum == 998) // Regular Cursors
+ viewNum = celNum + 1000;
+ else // Unknown cursor, ignored
+ return;
+ }
+ if (g_sci->hasMacIconBar())
+ g_sci->_gfxMacIconBar->setInventoryIcon(viewNum);
} else {
// If we do have the list, we'll be using a remap based on what the
// scripts have given us.
@@ -485,10 +495,10 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
cursorBitmap[i * 8 + b] = 0; // Doesn't matter, just is transparent
}
- uint16 hotspotX = READ_BE_UINT16(data);
- uint16 hotspotY = READ_BE_UINT16(data + 2);
+ uint16 hotspotY = READ_BE_UINT16(data);
+ uint16 hotspotX = READ_BE_UINT16(data + 2);
- static const byte cursorPalette[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00 };
+ static const byte cursorPalette[] = { 0x00, 0x00, 0x00, 0xff, 0xff, 0xff };
CursorMan.replaceCursor(cursorBitmap, 16, 16, hotspotX, hotspotY, 0);
CursorMan.replaceCursorPalette(cursorPalette, 1, 2);
@@ -498,11 +508,12 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// Mac crsr cursor
byte *cursorBitmap, *palette;
int width, height, hotspotX, hotspotY, palSize, keycolor;
- Common::MacResManager::convertCrsrCursor(resource->data, resource->size, &cursorBitmap, &width, &height, &hotspotX, &hotspotY, &keycolor, true, &palette, &palSize);
+ Common::MemoryReadStream resStream(resource->data, resource->size);
+ Common::MacResManager::convertCrsrCursor(&resStream, &cursorBitmap, width, height, hotspotX, hotspotY, keycolor, true, &palette, palSize);
CursorMan.replaceCursor(cursorBitmap, width, height, hotspotX, hotspotY, keycolor);
CursorMan.replaceCursorPalette(palette, 0, palSize);
- free(cursorBitmap);
- free(palette);
+ delete[] cursorBitmap;
+ delete[] palette;
}
kernelShow();
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 5b690f289a..dd55b3b060 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -58,6 +58,12 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
GfxFrameout::~GfxFrameout() {
}
+void GfxFrameout::clear() {
+ _screenItems.clear();
+ _planes.clear();
+ _planePictures.clear();
+}
+
void GfxFrameout::kernelAddPlane(reg_t object) {
PlaneEntry newPlane;
@@ -77,10 +83,12 @@ void GfxFrameout::kernelAddPlane(reg_t object) {
}
newPlane.object = object;
- newPlane.pictureId = 0xFFFF;
newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
newPlane.lastPriority = 0xFFFF; // hidden
newPlane.planeOffsetX = 0;
+ newPlane.pictureId = 0xFFFF;
+ newPlane.planePictureMirrored = false;
+ newPlane.planeBack = 0;
_planes.push_back(newPlane);
kernelUpdatePlane(object);
@@ -117,7 +125,10 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
if (it->planeRect.left < 0) {
it->planeOffsetX = -it->planeRect.left;
it->planeRect.left = 0;
+ } else {
+ it->planeOffsetX = 0;
}
+
if (it->planeRect.top < 0)
it->planeRect.top = 0;
// We get bad plane-bottom in sq6
@@ -331,6 +342,8 @@ static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
maxChars = curCharCount; // return count up to (but not including) breaking space
break;
}
+ if (width + font->getCharWidth(curChar) > maxWidth)
+ break;
width += font->getCharWidth(curChar);
curCharCount++;
}
@@ -479,8 +492,8 @@ void GfxFrameout::kernelFrameout() {
if (view->isSci2Hires()) {
int16 dummyX = 0;
- _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
- _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
+ view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
+ view->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
} else if (getSciVersion() == SCI_VERSION_2_1) {
itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight;
itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth;
@@ -497,8 +510,8 @@ void GfxFrameout::kernelFrameout() {
itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1;
itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1;
if (view->isSci2Hires()) {
- _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left);
- _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right);
+ view->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left);
+ view->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right);
}
itemEntry->celRect.translate(itemEntry->x, itemEntry->y);
// TODO: maybe we should clip the cels rect with this, i'm not sure
@@ -514,8 +527,8 @@ void GfxFrameout::kernelFrameout() {
nsRect.translate(it->planeOffsetX, 0);
if (view->isSci2Hires()) {
- _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
- _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
+ view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
+ view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
} else if (getSciVersion() == SCI_VERSION_2_1) {
nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight();
nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth();
@@ -584,6 +597,11 @@ void GfxFrameout::kernelFrameout() {
uint16 startX = itemEntry->x + it->planeRect.left;
uint16 curY = itemEntry->y + it->planeRect.top;
const char *txt = text.c_str();
+ // HACK. The plane sometimes doesn't contain the correct width. This
+ // hack breaks the dialog options when speaking with Grace, but it's
+ // the best we got up to now. This happens because of the unimplemented
+ // kTextWidth function in SCI32.
+ // TODO: Remove this once kTextWidth has been implemented.
uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10;
int16 charCount;
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 93d61ba22e..347ecb9424 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -28,6 +28,8 @@
namespace Sci {
+class GfxPicture;
+
struct PlaneEntry {
reg_t object;
uint16 priority;
@@ -99,6 +101,7 @@ public:
void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX);
void deletePlanePictures(reg_t object);
+ void clear();
private:
SegManager *_segMan;
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index f6cb214a2b..343f3c7e6e 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -45,6 +45,10 @@ typedef int GuiResourceId; // is a resource-number and -1 means no parameter giv
typedef int16 TextAlignment;
+#define PORTS_FIRSTWINDOWID 2
+#define PORTS_FIRSTSCRIPTWINDOWID 3
+
+
struct Port {
uint16 id;
int16 top, left;
@@ -62,6 +66,8 @@ struct Port {
fontHeight(0), fontId(0), greyedOutput(false),
penClr(0), backClr(0xFF), penMode(0), counterTillFree(0) {
}
+
+ bool isWindow() const { return id >= PORTS_FIRSTWINDOWID && id != 0xFFFF; }
};
struct Window : public Port, public Common::Serializable {
@@ -132,12 +138,14 @@ struct PalSchedule {
uint32 schedule;
};
+// Game view types, sorted by the number of colors
enum ViewType {
- kViewUnknown,
- kViewEga,
- kViewVga,
- kViewVga11,
- kViewAmiga
+ kViewUnknown, // uninitialized, or non-SCI
+ kViewEga, // EGA SCI0/SCI1 and Amiga SCI0/SCI1 ECS 16 colors
+ kViewAmiga, // Amiga SCI1 ECS 32 colors
+ kViewAmiga64, // Amiga SCI1 AGA 64 colors (i.e. Longbow)
+ kViewVga, // VGA SCI1 256 colors
+ kViewVga11 // VGA SCI1.1 and newer 256 colors
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index 2ce17ab531..6cf4f269a7 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -27,8 +27,10 @@
#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
+#include "sci/event.h"
#include "sci/graphics/maciconbar.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/screen.h"
#include "common/memstream.h"
#include "common/system.h"
@@ -37,37 +39,189 @@
namespace Sci {
+GfxMacIconBar::GfxMacIconBar() {
+ _lastX = 0;
+
+ if (g_sci->getGameId() == GID_FREDDYPHARKAS)
+ _inventoryIndex = 5;
+ else
+ _inventoryIndex = 4;
+
+ _inventoryIcon = 0;
+ _allDisabled = true;
+}
+
+GfxMacIconBar::~GfxMacIconBar() {
+ if (_inventoryIcon) {
+ _inventoryIcon->free();
+ delete _inventoryIcon;
+ }
+
+ for (uint32 i = 0; i < _iconBarItems.size(); i++) {
+ if (_iconBarItems[i].nonSelectedImage) {
+ _iconBarItems[i].nonSelectedImage->free();
+ delete _iconBarItems[i].nonSelectedImage;
+ }
+
+ if (_iconBarItems[i].selectedImage) {
+ _iconBarItems[i].selectedImage->free();
+ delete _iconBarItems[i].selectedImage;
+ }
+ }
+}
+
void GfxMacIconBar::addIcon(reg_t obj) {
- _iconBarObjects.push_back(obj);
+ IconBarItem item;
+ uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, obj, SELECTOR(iconIndex));
+
+ item.object = obj;
+ item.nonSelectedImage = createImage(iconIndex, false);
+
+ if (iconIndex != _inventoryIndex)
+ item.selectedImage = createImage(iconIndex, true);
+ else
+ item.selectedImage = 0;
+
+ item.enabled = true;
+
+ // Start after the main viewing window and add a two pixel buffer
+ uint16 y = g_sci->_gfxScreen->getHeight() + 2;
+
+ if (item.nonSelectedImage)
+ item.rect = Common::Rect(_lastX, y, MIN<uint32>(_lastX + item.nonSelectedImage->w, 320), y + item.nonSelectedImage->h);
+ else
+ error("Could not find a non-selected image for icon %d", iconIndex);
+
+ _lastX += item.rect.width();
+
+ _iconBarItems.push_back(item);
}
void GfxMacIconBar::drawIcons() {
// Draw the icons to the bottom of the screen
- byte *pal = new byte[256 * 4];
- Graphics::PictDecoder *pict = new Graphics::PictDecoder(Graphics::PixelFormat::createFormatCLUT8());
- uint32 lastX = 0;
+ for (uint32 i = 0; i < _iconBarItems.size(); i++)
+ drawIcon(i, false);
+}
+
+void GfxMacIconBar::drawIcon(uint16 iconIndex, bool selected) {
+ if (iconIndex >= _iconBarItems.size())
+ return;
+
+ Common::Rect rect = _iconBarItems[iconIndex].rect;
+
+ if (isIconEnabled(iconIndex)) {
+ if (selected)
+ drawEnabledImage(_iconBarItems[iconIndex].selectedImage, rect);
+ else
+ drawEnabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect);
+ } else
+ drawDisabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect);
+
+ if ((iconIndex == _inventoryIndex) && _inventoryIcon) {
+ Common::Rect invRect = Common::Rect(0, 0, _inventoryIcon->w, _inventoryIcon->h);
+ invRect.moveTo(rect.left, rect.top);
+ invRect.translate((rect.width() - invRect.width()) / 2, (rect.height() - invRect.height()) / 2);
+
+ if (isIconEnabled(iconIndex))
+ drawEnabledImage(_inventoryIcon, invRect);
+ else
+ drawDisabledImage(_inventoryIcon, invRect);
+ }
+}
+
+void GfxMacIconBar::drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
+ if (surface)
+ g_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height());
+}
+
+void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
+ if (!surface)
+ return;
+
+ // Add a black checkboard pattern to the image before copying it to the screen
+
+ Graphics::Surface newSurf;
+ newSurf.copyFrom(*surface);
+
+ for (int i = 0; i < newSurf.h; i++) {
+ // Start at the next four byte boundary
+ int startX = 3 - ((rect.left + 3) & 3);
+
+ // Start odd rows at two bytes past that (also properly aligned)
+ if ((i + rect.top) & 1)
+ startX = (startX + 2) & 3;
+
+ for (int j = startX; j < newSurf.w; j += 4)
+ *((byte *)newSurf.getBasePtr(j, i)) = 0;
+ }
+
+ g_system->copyRectToScreen((byte *)newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height());
+ newSurf.free();
+}
+
+void GfxMacIconBar::drawSelectedImage(uint16 iconIndex) {
+ assert(iconIndex <= _iconBarItems.size());
+
+ drawEnabledImage(_iconBarItems[iconIndex].selectedImage, _iconBarItems[iconIndex].rect);
+}
- for (uint32 i = 0; i < _iconBarObjects.size(); i++) {
- uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex));
- Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMacIconBarPictN, iconIndex + 1), false);
- if (!res)
- continue;
+bool GfxMacIconBar::isIconEnabled(uint16 iconIndex) const {
+ if (iconIndex >= _iconBarItems.size())
+ return false;
+
+ return !_allDisabled && _iconBarItems[iconIndex].enabled;
+}
+
+void GfxMacIconBar::setIconEnabled(int16 iconIndex, bool enabled) {
+ if (iconIndex < 0)
+ _allDisabled = !enabled;
+ else if (iconIndex < (int)_iconBarItems.size()) {
+ _iconBarItems[iconIndex].enabled = enabled;
+ }
+}
- Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
- Graphics::Surface *surf = pict->decodeImage(stream, pal);
- remapColors(surf, pal);
+void GfxMacIconBar::setInventoryIcon(int16 icon) {
+ Graphics::Surface *surface = 0;
- g_system->copyRectToScreen((byte *)surf->pixels, surf->pitch, lastX, 200, MIN<uint32>(surf->w, 320 - lastX), surf->h);
+ if (icon >= 0)
+ surface = loadPict(ResourceId(kResourceTypeMacPict, icon));
- lastX += surf->w;
- surf->free();
- delete surf;
- delete stream;
+ if (_inventoryIcon) {
+ // Free old inventory icon if we're removing the inventory icon
+ // or setting a new one.
+ if ((icon < 0) || surface) {
+ _inventoryIcon->free();
+ delete _inventoryIcon;
+ _inventoryIcon = 0;
+ }
}
- delete pict;
- delete[] pal;
+ if (surface)
+ _inventoryIcon = surface;
+
+ drawIcon(_inventoryIndex, false);
+}
+
+Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) {
+ Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8());
+ Resource *res = g_sci->getResMan()->findResource(id, false);
+
+ if (!res || res->size == 0)
+ return 0;
+
+ byte palette[256 * 3];
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
+ Graphics::Surface *surface = pictDecoder.decodeImage(stream, palette);
+ remapColors(surface, palette);
+
+ delete stream;
+ return surface;
+}
+
+Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected) {
+ ResourceType type = isSelected ? kResourceTypeMacIconBarPictS : kResourceTypeMacIconBarPictN;
+ return loadPict(ResourceId(type, iconIndex + 1));
}
void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) {
@@ -77,16 +231,67 @@ void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) {
for (uint16 i = 0; i < surf->w * surf->h; i++) {
byte color = *pixels;
- byte r = palette[color * 4];
- byte g = palette[color * 4 + 1];
- byte b = palette[color * 4 + 2];
+ byte r = palette[color * 3];
+ byte g = palette[color * 3 + 1];
+ byte b = palette[color * 3 + 2];
- // For black, make sure the index is 0
- if (r == 0 && g == 0 && b == 0)
- *pixels++ = 0;
- else
- *pixels++ = g_sci->_gfxPalette->kernelFindColor(r, g, b);
+ *pixels++ = g_sci->_gfxPalette->findMacIconBarColor(r, g, b);
+ }
+}
+
+bool GfxMacIconBar::pointOnIcon(uint32 iconIndex, Common::Point point) {
+ return _iconBarItems[iconIndex].rect.contains(point);
+}
+
+reg_t GfxMacIconBar::handleEvents() {
+ // Peek event queue for a mouse button press
+ EventManager *evtMgr = g_sci->getEventManager();
+ SciEvent evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
+
+ // No mouse press found
+ if (evt.type == SCI_EVENT_NONE)
+ return NULL_REG;
+
+ // If the mouse is not over the icon bar, return
+ if (evt.mousePos.y < g_sci->_gfxScreen->getHeight())
+ return NULL_REG;
+
+ // Remove event from queue
+ evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS);
+
+ // Mouse press on the icon bar, check the icon rectangles
+ uint iconNr;
+ for (iconNr = 0; iconNr < _iconBarItems.size(); iconNr++) {
+ if (pointOnIcon(iconNr, evt.mousePos) && isIconEnabled(iconNr))
+ break;
}
+
+ // Mouse press not on an icon
+ if (iconNr == _iconBarItems.size())
+ return NULL_REG;
+
+ drawIcon(iconNr, true);
+ bool isSelected = true;
+
+ // Wait for mouse release
+ while (evt.type != SCI_EVENT_MOUSE_RELEASE) {
+ // Mimic behavior of SSCI when moving mouse with button held down
+ if (isSelected != pointOnIcon(iconNr, evt.mousePos)) {
+ isSelected = !isSelected;
+ drawIcon(iconNr, isSelected);
+ }
+
+ evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_RELEASE);
+ g_system->delayMillis(10);
+ }
+
+ drawIcon(iconNr, false);
+
+ // If user moved away from the icon, we do nothing
+ if (pointOnIcon(iconNr, evt.mousePos))
+ return _iconBarItems[iconNr].object;
+
+ return NULL_REG;
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.h b/engines/sci/graphics/maciconbar.h
index 71e65fcb40..3ac5475147 100644
--- a/engines/sci/graphics/maciconbar.h
+++ b/engines/sci/graphics/maciconbar.h
@@ -38,16 +38,40 @@ namespace Sci {
class GfxMacIconBar {
public:
- GfxMacIconBar() {}
- ~GfxMacIconBar() {}
+ GfxMacIconBar();
+ ~GfxMacIconBar();
void addIcon(reg_t obj);
void drawIcons();
+ void setIconEnabled(int16 index, bool enabled);
+ void setInventoryIcon(int16 icon);
+ reg_t handleEvents();
private:
- Common::Array<reg_t> _iconBarObjects;
+ struct IconBarItem {
+ reg_t object;
+ Graphics::Surface *nonSelectedImage;
+ Graphics::Surface *selectedImage;
+ Common::Rect rect;
+ bool enabled;
+ };
+ Common::Array<IconBarItem> _iconBarItems;
+ uint32 _lastX;
+ uint16 _inventoryIndex;
+ Graphics::Surface *_inventoryIcon;
+ bool _allDisabled;
+
+ Graphics::Surface *loadPict(ResourceId id);
+ Graphics::Surface *createImage(uint32 iconIndex, bool isSelected);
void remapColors(Graphics::Surface *surf, byte *palette);
+
+ void drawIcon(uint16 index, bool selected);
+ void drawSelectedImage(uint16 index);
+ bool isIconEnabled(uint16 index) const;
+ void drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect);
+ void drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect);
+ bool pointOnIcon(uint32 iconIndex, Common::Point point);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 3b9119c52f..50ba77e832 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -399,12 +399,10 @@ void GfxMenu::calculateMenuAndItemWidth() {
reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
int16 keyPress, keyModifier;
- Common::Point mousePosition;
GuiMenuItemList::iterator itemIterator = _itemList.begin();
GuiMenuItemList::iterator itemEnd = _itemList.end();
GuiMenuItemEntry *itemEntry = NULL;
bool forceClaimed = false;
- EngineState *s;
switch (eventType) {
case SCI_EVENT_KEYBOARD:
@@ -438,8 +436,6 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
break;
case SCI_EVENT_SAID:
- // HACK: should be removed as soon as said() is cleaned up
- s = g_sci->getEngineState();
while (itemIterator != itemEnd) {
itemEntry = *itemIterator;
@@ -451,7 +447,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
continue;
}
- if (said(s, saidSpec, 0) != SAID_NO_MATCH)
+ if (said(saidSpec, 0) != SAID_NO_MATCH)
break;
}
itemIterator++;
@@ -460,15 +456,17 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
itemEntry = NULL;
break;
- case SCI_EVENT_MOUSE_PRESS:
- mousePosition = _cursor->getPosition();
+ case SCI_EVENT_MOUSE_PRESS: {
+ Common::Point mousePosition;
+ mousePosition.x = readSelectorValue(_segMan, eventObject, SELECTOR(x));
+ mousePosition.y = readSelectorValue(_segMan, eventObject, SELECTOR(y));
if (mousePosition.y < 10) {
interactiveStart(pauseSound);
itemEntry = interactiveWithMouse();
interactiveEnd(pauseSound);
forceClaimed = true;
}
- break;
+ } break;
}
if (!_menuSaveHandle.isNull()) {
@@ -718,7 +716,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
uint16 newItemId = _curItemId;
GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId);
GuiMenuItemEntry *newItemEntry = curItemEntry;
- Common::Point mousePosition;
// We don't 100% follow Sierra here: we select last item instead of
// selecting first item of first menu every time. Also sierra sci didn't
@@ -796,9 +793,9 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
}
break;
- case SCI_EVENT_MOUSE_PRESS:
- mousePosition = _cursor->getPosition();
- if (_cursor->getPosition().y < 10) {
+ case SCI_EVENT_MOUSE_PRESS: {
+ Common::Point mousePosition = curEvent.mousePos;
+ if (mousePosition.y < 10) {
// Somewhere on the menubar
newMenuId = mouseFindMenuSelection(mousePosition);
if (newMenuId) {
@@ -827,7 +824,8 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
}
newItemId = curItemEntry->id;
}
- break;
+ } break;
+
case SCI_EVENT_NONE:
g_sci->sleep(2500 / 1000);
break;
@@ -843,7 +841,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
SciEvent curEvent;
uint16 newMenuId = 0, newItemId = 0;
uint16 curMenuId = 0, curItemId = 0;
- Common::Point mousePosition = _cursor->getPosition();
bool firstMenuChange = true;
GuiMenuItemEntry *curItemEntry = NULL;
@@ -874,7 +871,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
}
// Find out where mouse is currently pointing to
- mousePosition = _cursor->getPosition();
+ Common::Point mousePosition = curEvent.mousePos;
if (mousePosition.y < 10) {
// Somewhere on the menubar
newMenuId = mouseFindMenuSelection(mousePosition);
@@ -914,6 +911,13 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack
_ports->moveTo(0, 1);
_text16->DrawStatus(text);
_paint16->bitsShow(_ports->_menuBarRect);
+ // Also draw the line under the status bar. Normally, this is never drawn,
+ // but we need it to be drawn because Dr. Brain 1 Mac draws over it when
+ // it displays the icon bar. SSCI used negative rectangles to erase the
+ // area after drawing the icon bar, but this is a much cleaner way of
+ // achieving the same effect.
+ _paint16->fillRect(_ports->_menuLine, 1, 0);
+ _paint16->bitsShow(_ports->_menuLine);
_ports->setPort(oldPort);
}
diff --git a/engines/sci/graphics/paint.cpp b/engines/sci/graphics/paint.cpp
index 50b0534ba7..c347da3c0f 100644
--- a/engines/sci/graphics/paint.cpp
+++ b/engines/sci/graphics/paint.cpp
@@ -43,9 +43,6 @@ GfxPaint::~GfxPaint() {
void GfxPaint::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
}
-void GfxPaint::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
-}
-
void GfxPaint::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
}
diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h
index 994bc4e5e9..a79e8993c2 100644
--- a/engines/sci/graphics/paint.h
+++ b/engines/sci/graphics/paint.h
@@ -36,7 +36,6 @@ public:
virtual ~GfxPaint();
virtual void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
- virtual void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
virtual void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
};
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 0bb3fc85c5..ff738fc3b9 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -121,11 +121,10 @@ void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common:
Common::Rect clipRectTranslated = clipRect;
_ports->offsetRect(clipRectTranslated);
- if (scaleX == 128 && scaleY == 128) {
+ if (scaleX == 128 && scaleY == 128)
view->draw(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo, false);
- } else {
+ else
view->drawScaled(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, scaleX, scaleY);
- }
}
// This is used as replacement for drawCelAndShow() when hires-cels are drawn to
@@ -160,8 +159,8 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c
// adjust curPort to upscaled hires
clipRect = celRect;
curPortRect = _ports->_curPort->rect;
- _screen->adjustToUpscaledCoordinates(curPortRect.top, curPortRect.left);
- _screen->adjustToUpscaledCoordinates(curPortRect.bottom, curPortRect.right);
+ view->adjustToUpscaledCoordinates(curPortRect.top, curPortRect.left);
+ view->adjustToUpscaledCoordinates(curPortRect.bottom, curPortRect.right);
curPortRect.bottom++;
curPortRect.right++;
clipRect.clip(curPortRect);
@@ -171,7 +170,7 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c
clipRectTranslated = clipRect;
if (!upscaledHiresHack) {
curPortPos.x = _ports->_curPort->left; curPortPos.y = _ports->_curPort->top;
- _screen->adjustToUpscaledCoordinates(curPortPos.y, curPortPos.x);
+ view->adjustToUpscaledCoordinates(curPortPos.y, curPortPos.x);
clipRectTranslated.top += curPortPos.y; clipRectTranslated.bottom += curPortPos.y;
clipRectTranslated.left += curPortPos.x; clipRectTranslated.right += curPortPos.x;
}
@@ -278,9 +277,8 @@ void GfxPaint16::fillRect(const Common::Rect &rect, int16 drawFlags, byte color,
}
void GfxPaint16::frameRect(const Common::Rect &rect) {
- Common::Rect r;
+ Common::Rect r = rect;
// left
- r = rect;
r.right = rect.left + 1;
paintRect(r);
// right
@@ -362,7 +360,7 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) {
if (memoryPtr) {
_screen->bitsRestore(memoryPtr);
- _segMan->freeHunkEntry(memoryHandle);
+ bitsFree(memoryHandle);
}
}
}
@@ -534,20 +532,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
case SCI_DISPLAY_RESTOREUNDER:
bitsGetRect(argv[0], &rect);
rect.translate(-_ports->getPort()->left, -_ports->getPort()->top);
- if (g_sci->getGameId() == GID_PQ3 && g_sci->getEngineState()->currentRoomNumber() == 29) {
- // WORKAROUND: PQ3 calls this without calling the associated
- // kDisplay(SCI_DISPLAY_SAVEUNDER) call before. Theoretically,
- // this would result in no rect getting restored. However, we
- // still maintain a pointer from the previous room, resulting
- // in invalidated content being restored on screen, and causing
- // graphics glitches. Thus, we simply don't restore a rect in
- // that room. The correct fix for this would be to erase hunk
- // pointers when changing rooms, but this will suffice for now,
- // as restoring from a totally invalid pointer is very rare.
- // Fixes bug #3037945.
- } else {
- bitsRestore(argv[0]);
- }
+ bitsRestore(argv[0]);
kernelGraphRedrawBox(rect);
// finishing loop
argc = 0;
@@ -583,7 +568,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
// now drawing the text
_text16->Size(rect, text, -1, width);
rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop);
- if (getSciVersion() >= SCI_VERSION_1_LATE) {
+ // Note: This code has been found in SCI1 middle and newer games. It was
+ // previously only for SCI1 late and newer, but the LSL1 interpreter contains
+ // this code.
+ if (getSciVersion() >= SCI_VERSION_1_MIDDLE) {
int16 leftPos = rect.right <= _screen->getWidth() ? 0 : _screen->getWidth() - rect.right;
int16 topPos = rect.bottom <= _screen->getHeight() ? 0 : _screen->getHeight() - rect.bottom;
_ports->move(leftPos, topPos);
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index 4f709fd7c6..69ddf09ea6 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -36,7 +36,6 @@ class GfxPorts;
class GfxScreen;
class GfxPalette;
class Font;
-class SciGuiPicture;
class GfxView;
/**
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index aa3bf8dfb3..69a278eb8b 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -65,17 +65,6 @@ void GfxPaint32::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
delete picture;
}
-// This is "hacked" together, because its only used by debug command
-void GfxPaint32::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
- GfxView *view = _cache->getView(viewId);
- Common::Rect celRect(50, 50, 50, 50);
- Common::Rect translatedRect;
- celRect.bottom += view->getHeight(loopNo, celNo);
- celRect.right += view->getWidth(loopNo, celNo);
- view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false);
- _screen->copyRectToScreen(celRect);
-}
-
void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h
index 3fa9d11d9b..e412bdf1c4 100644
--- a/engines/sci/graphics/paint32.h
+++ b/engines/sci/graphics/paint32.h
@@ -45,7 +45,6 @@ public:
void fillRect(Common::Rect rect, byte color);
void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
- void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
private:
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index b27b5f35a7..0433479a09 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -69,16 +69,39 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMergi
_useMerging = useMerging;
palVaryInit();
+
+ _macClut = 0;
+ loadMacIconBarPalette();
#ifdef ENABLE_SCI32
_clutTable = 0;
#endif
+
+ switch (_resMan->getViewType()) {
+ case kViewEga:
+ _totalScreenColors = 16;
+ break;
+ case kViewAmiga:
+ _totalScreenColors = 32;
+ break;
+ case kViewAmiga64:
+ _totalScreenColors = 64;
+ break;
+ case kViewVga:
+ case kViewVga11:
+ _totalScreenColors = 256;
+ break;
+ default:
+ error("GfxPalette: Unknown view type");
+ }
}
GfxPalette::~GfxPalette() {
if (_palVaryResourceId != -1)
palVaryRemoveTimer();
+ delete[] _macClut;
+
#ifdef ENABLE_SCI32
unloadClut();
#endif
@@ -92,7 +115,7 @@ bool GfxPalette::isMerging() {
void GfxPalette::setDefault() {
if (_resMan->getViewType() == kViewEga)
setEGA();
- else if (_resMan->isAmiga32color())
+ else if (_resMan->getViewType() == kViewAmiga)
setAmiga();
else
kernelSetFromResource(999, true);
@@ -171,39 +194,42 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
// Will try to set amiga palette by using "spal" file. If not found, we return false
bool GfxPalette::setAmiga() {
Common::File file;
- int curColor, byte1, byte2;
if (file.open("spal")) {
- for (curColor = 0; curColor < 32; curColor++) {
- byte1 = file.readByte();
- byte2 = file.readByte();
- if ((byte1 == EOF) || (byte2 == EOF))
+ for (int curColor = 0; curColor < 32; curColor++) {
+ byte byte1 = file.readByte();
+ byte byte2 = file.readByte();
+
+ if (file.eos())
error("Amiga palette file ends prematurely");
+
_sysPalette.colors[curColor].used = 1;
_sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11;
_sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11;
_sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11;
}
- file.close();
+
// Directly set the palette, because setOnScreen() wont do a thing for amiga
- _screen->setPalette(&_sysPalette);
+ copySysPaletteToScreen();
return true;
}
+
return false;
}
// Called from picture class, some amiga sci1 games set half of the palette
void GfxPalette::modifyAmigaPalette(byte *data) {
- int16 curColor, curPos = 0;
- byte byte1, byte2;
- for (curColor = 0; curColor < 16; curColor++) {
- byte1 = data[curPos++];
- byte2 = data[curPos++];
+ int16 curPos = 0;
+
+ for (int curColor = 0; curColor < 16; curColor++) {
+ byte byte1 = data[curPos++];
+ byte byte2 = data[curPos++];
_sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11;
_sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11;
_sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11;
}
- _screen->setPalette(&_sysPalette);
+
+ copySysPaletteToScreen();
}
static byte blendColors(byte c1, byte c2) {
@@ -297,19 +323,20 @@ bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
newPalette->mapping[i] = i;
}
}
+
// We don't update the timestamp for SCI1.1, it's only updated on kDrawPic calls
return paletteChanged;
}
bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
uint16 res;
- int i,j;
bool paletteChanged = false;
- // colors 0 (black) and 255 (white) are not affected by merging
- for (i = 1; i < 255; i++) {
- if (!newPalette->colors[i].used)// color is not used - so skip it
+ for (int i = 1; i < 255; i++) {
+ // skip unused colors
+ if (!newPalette->colors[i].used)
continue;
+
// forced palette merging or dest color is not used yet
if (force || (!_sysPalette.colors[i].used)) {
_sysPalette.colors[i].used = newPalette->colors[i].used;
@@ -322,6 +349,7 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
newPalette->mapping[i] = i;
continue;
}
+
// is the same color already at the same position? -> match it directly w/o lookup
// this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would
// otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes)
@@ -329,14 +357,18 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
newPalette->mapping[i] = i;
continue;
}
+
// check if exact color could be matched
res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b);
if (res & 0x8000) { // exact match was found
newPalette->mapping[i] = res & 0xFF;
continue;
}
+
+ int j = 1;
+
// no exact match - see if there is an unused color
- for (j = 1; j < 256; j++)
+ for (; j < 256; j++) {
if (!_sysPalette.colors[j].used) {
_sysPalette.colors[j].used = newPalette->colors[i].used;
_sysPalette.colors[j].r = newPalette->colors[i].r;
@@ -346,6 +378,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
paletteChanged = true;
break;
}
+ }
+
// if still no luck - set an approximate color
if (j == 256) {
newPalette->mapping[i] = res & 0xFF;
@@ -355,6 +389,7 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
if (!forceRealMerge)
_sysPalette.timestamp = g_system->getMillis() * 60 / 1000;
+
return paletteChanged;
}
@@ -401,14 +436,35 @@ void GfxPalette::getSys(Palette *pal) {
}
void GfxPalette::setOnScreen() {
- // We dont change palette at all times for amiga
- if (_resMan->isAmiga32color())
- return;
- _screen->setPalette(&_sysPalette);
+ copySysPaletteToScreen();
+}
+
+static byte convertMacGammaToSCIGamma(int comp) {
+ return (byte)sqrt(comp * 255.0f);
+}
- // Redraw the Mac SCI1.1 Icon bar every palette change
- if (g_sci->_gfxMacIconBar)
- g_sci->_gfxMacIconBar->drawIcons();
+void GfxPalette::copySysPaletteToScreen() {
+ // just copy palette to system
+ byte bpal[3 * 256];
+
+ // Get current palette, update it and put back
+ g_system->getPaletteManager()->grabPalette(bpal, 0, 256);
+
+ for (int16 i = 0; i < 256; i++) {
+ if (colorIsFromMacClut(i)) {
+ // If we've got a Mac CLUT, override the SCI palette with its non-black colors
+ bpal[i * 3 ] = convertMacGammaToSCIGamma(_macClut[i * 3 ]);
+ bpal[i * 3 + 1] = convertMacGammaToSCIGamma(_macClut[i * 3 + 1]);
+ bpal[i * 3 + 2] = convertMacGammaToSCIGamma(_macClut[i * 3 + 2]);
+ } else if (_sysPalette.colors[i].used != 0) {
+ // Otherwise, copy to the screen
+ bpal[i * 3 ] = CLIP(_sysPalette.colors[i].r * _sysPalette.intensity[i] / 100, 0, 255);
+ bpal[i * 3 + 1] = CLIP(_sysPalette.colors[i].g * _sysPalette.intensity[i] / 100, 0, 255);
+ bpal[i * 3 + 2] = CLIP(_sysPalette.colors[i].b * _sysPalette.intensity[i] / 100, 0, 255);
+ }
+ }
+
+ g_system->getPaletteManager()->setPalette(bpal, 0, 256);
}
bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) {
@@ -420,6 +476,7 @@ bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) {
set(&palette, force);
return true;
}
+
return false;
}
@@ -565,7 +622,16 @@ void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) {
}
void GfxPalette::kernelSyncScreenPalette() {
- _screen->getPalette(&_sysPalette);
+ // just copy palette to system
+ byte bpal[3 * 256];
+
+ // Get current palette, update it and put back
+ g_system->getPaletteManager()->grabPalette(bpal, 0, 256);
+ for (int16 i = 1; i < 255; i++) {
+ _sysPalette.colors[i].r = bpal[i * 3];
+ _sysPalette.colors[i].g = bpal[i * 3 + 1];
+ _sysPalette.colors[i].b = bpal[i * 3 + 2];
+ }
}
// palVary
@@ -795,6 +861,87 @@ void GfxPalette::palVaryProcess(int signal, bool setPalette) {
}
}
+static inline uint getMacColorDiff(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) {
+ // Use the difference of the top 4 bits and add together the differences
+ return ABS((r2 & 0xf0) - (r1 & 0xf0)) + ABS((g2 & 0xf0) - (g1 & 0xf0)) + ABS((b2 & 0xf0) - (b1 & 0xf0));
+}
+
+byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) {
+ // Find the best color for use with the Mac icon bar
+ // Check white, then the palette colors, and then black
+
+ // Try white first
+ byte found = 0xff;
+ uint diff = getMacColorDiff(r, g, b, 0xff, 0xff, 0xff);
+
+ if (diff == 0)
+ return found;
+
+ // Go through the main colors of the CLUT
+ for (uint16 i = 1; i < 255; i++) {
+ if (!colorIsFromMacClut(i))
+ continue;
+
+ uint cdiff = getMacColorDiff(r, g, b, _macClut[i * 3], _macClut[i * 3 + 1], _macClut[i * 3 + 2]);
+
+ if (cdiff == 0)
+ return i;
+ else if (cdiff < diff) {
+ found = i;
+ diff = cdiff;
+ }
+ }
+
+ // Also check black here
+ if (getMacColorDiff(r, g, b, 0, 0, 0) < diff)
+ return 0;
+
+ return found;
+}
+
+void GfxPalette::loadMacIconBarPalette() {
+ if (!g_sci->hasMacIconBar())
+ return;
+
+ Common::SeekableReadStream *clutStream = g_sci->getMacExecutable()->getResource(MKTAG('c','l','u','t'), 150);
+
+ if (!clutStream)
+ error("Could not find clut 150 for the Mac icon bar");
+
+ clutStream->readUint32BE(); // seed
+ clutStream->readUint16BE(); // flags
+ uint16 colorCount = clutStream->readUint16BE() + 1;
+ assert(colorCount == 256);
+
+ _macClut = new byte[256 * 3];
+
+ for (uint16 i = 0; i < colorCount; i++) {
+ clutStream->readUint16BE();
+ _macClut[i * 3 ] = clutStream->readUint16BE() >> 8;
+ _macClut[i * 3 + 1] = clutStream->readUint16BE() >> 8;
+ _macClut[i * 3 + 2] = clutStream->readUint16BE() >> 8;
+ }
+
+ // Adjust bounds on the KQ6 palette
+ // We don't use all of it for the icon bar
+ if (g_sci->getGameId() == GID_KQ6)
+ memset(_macClut + 32 * 3, 0, (256 - 32) * 3);
+
+ // Force black/white
+ _macClut[0x00 * 3 ] = 0;
+ _macClut[0x00 * 3 + 1] = 0;
+ _macClut[0x00 * 3 + 2] = 0;
+ _macClut[0xff * 3 ] = 0xff;
+ _macClut[0xff * 3 + 1] = 0xff;
+ _macClut[0xff * 3 + 2] = 0xff;
+
+ delete clutStream;
+}
+
+bool GfxPalette::colorIsFromMacClut(byte index) {
+ return index != 0 && _macClut && (_macClut[index * 3] != 0 || _macClut[index * 3 + 1] != 0 || _macClut[index * 3 + 1] != 0);
+}
+
#ifdef ENABLE_SCI32
bool GfxPalette::loadClut(uint16 clutId) {
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 84334a4a61..d2e5151d6a 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -54,8 +54,10 @@ public:
bool merge(Palette *pFrom, bool force, bool forceRealMerge);
uint16 matchColor(byte r, byte g, byte b);
void getSys(Palette *pal);
+ uint16 getTotalColorCount() const { return _totalScreenColors; }
void setOnScreen();
+ void copySysPaletteToScreen();
void drewPicture(GuiResourceId pictureId);
@@ -88,6 +90,9 @@ public:
virtual void saveLoadWithSerializer(Common::Serializer &s);
void palVarySaveLoadPalette(Common::Serializer &s, Palette *palette);
+ byte findMacIconBarColor(byte r, byte g, byte b);
+ bool colorIsFromMacClut(byte index);
+
#ifdef ENABLE_SCI32
bool loadClut(uint16 clutId);
byte matchClutColor(uint16 color);
@@ -119,6 +124,10 @@ private:
uint16 _palVaryTicks;
int _palVaryPaused;
int _palVarySignal;
+ uint16 _totalScreenColors;
+
+ void loadMacIconBarPalette();
+ byte *_macClut;
#ifdef ENABLE_SCI32
byte *_clutTable;
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 7e71c1a258..82aae5399f 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -36,6 +36,8 @@
namespace Sci {
+//#define DEBUG_PICTURE_DRAW
+
GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
: _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) {
assert(resourceId != -1);
@@ -222,19 +224,20 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu
}
#endif
+extern void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData);
+
void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) {
byte *celBitmap = NULL;
byte *ptr = NULL;
byte *headerPtr = inbuffer + headerPos;
byte *rlePtr = inbuffer + rlePos;
- byte *literalPtr = inbuffer + literalPos;
int16 displaceX, displaceY;
byte priority = _addToFlag ? _priority : 0;
byte clearColor;
bool compression = true;
- byte curByte, runLength;
+ byte curByte;
int16 y, lastY, x, leftX, rightX;
- int pixelNr, pixelCount;
+ int pixelCount;
uint16 width, height;
#ifdef ENABLE_SCI32
@@ -245,12 +248,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
height = READ_LE_UINT16(headerPtr + 2);
displaceX = (signed char)headerPtr[4];
displaceY = (unsigned char)headerPtr[5];
- if (_resourceType == SCI_PICTURE_TYPE_SCI11) {
+ if (_resourceType == SCI_PICTURE_TYPE_SCI11)
// SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
clearColor = _screen->getColorWhite();
- } else {
+ else
clearColor = headerPtr[6];
- }
#ifdef ENABLE_SCI32
} else {
width = READ_SCI11ENDIAN_UINT16(headerPtr + 0);
@@ -266,90 +268,37 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
if (displaceX || displaceY)
error("unsupported embedded cel-data in picture");
+ // We will unpack cel-data into a temporary buffer and then plot it to screen
+ // That needs to be done cause a mirrored picture may be requested
pixelCount = width * height;
celBitmap = new byte[pixelCount];
if (!celBitmap)
error("Unable to allocate temporary memory for picture drawing");
- if (compression) {
- // We will unpack cel-data into a temporary buffer and then plot it to screen
- // That needs to be done cause a mirrored picture may be requested
- memset(celBitmap, clearColor, pixelCount);
- pixelNr = 0;
- ptr = celBitmap;
- if (literalPos == 0) {
- // decompression for data that has only one stream (vecor embedded view data)
- switch (_resMan->getViewType()) {
- case kViewEga:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte >> 4;
- memset(ptr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- }
- break;
- case kViewVga:
- case kViewVga11:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte & 0x3F;
- switch (curByte & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNr < pixelCount)
- ptr[pixelNr++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(ptr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNr += runLength;
- break;
- }
- }
- break;
- case kViewAmiga:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- if (curByte & 0x07) { // fill with color
- runLength = curByte & 0x07;
- curByte = curByte >> 3;
- while (runLength-- && pixelNr < pixelCount) {
- ptr[pixelNr++] = curByte;
- }
- } else { // fill with transparent
- runLength = curByte >> 3;
- pixelNr += runLength;
- }
- }
- break;
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) {
+ // See GfxView::unpackCel() for why this black/white swap is done
+ // This picture swap is only needed in SCI32, not SCI1.1
+ if (clearColor == 0)
+ clearColor = 0xff;
+ else if (clearColor == 0xff)
+ clearColor = 0;
+ }
- default:
- error("Unsupported picture viewtype");
- }
- } else {
- // decompression for data that has two separate streams (probably SCI 1.1 picture)
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte & 0x3F;
- switch (curByte & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNr < pixelCount)
- ptr[pixelNr++] = *literalPtr++;
- break;
- case 0x80: // fill with color
- memset(ptr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNr += runLength;
- break;
- }
- }
- }
- } else {
+ if (compression)
+ unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, rlePos, literalPos, _resMan->getViewType(), width, false);
+ else
// No compression (some SCI32 pictures)
memcpy(celBitmap, rlePtr, pixelCount);
+
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) {
+ // See GfxView::unpackCel() for why this black/white swap is done
+ // This picture swap is only needed in SCI32, not SCI1.1
+ for (int i = 0; i < pixelCount; i++) {
+ if (celBitmap[i] == 0)
+ celBitmap[i] = 0xff;
+ else if (celBitmap[i] == 0xff)
+ celBitmap[i] = 0;
+ }
}
Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
@@ -377,10 +326,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
sourcePixelSkipPerRow = width - (rightX - leftX);
// Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen
- // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all)
- // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
- // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra
- if (!_addToFlag)
+ // but white and that won't matter because the screen is supposed to be already white. It seems that most (if not all)
+ // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
+ // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra.
+ // SCI32 doesn't use _addToFlag at all.
+ if (!_addToFlag && _resourceType != SCI_PICTURE_TYPE_SCI32)
clearColor = _screen->getColorWhite();
byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY;
@@ -443,6 +393,7 @@ enum {
PIC_OP_OPX = 0xfe,
PIC_OP_TERMINATE = 0xff
};
+
#define PIC_OP_FIRST PIC_OP_SET_COLOR
enum {
@@ -465,6 +416,47 @@ enum {
PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4
};
+#ifdef DEBUG_PICTURE_DRAW
+const char *picOpcodeNames[] = {
+ "Set color",
+ "Disable visual",
+ "Set priority",
+ "Disable priority",
+ "Short patterns",
+ "Medium lines",
+ "Long lines",
+ "Short lines",
+ "Fill",
+ "Set pattern",
+ "Absolute pattern",
+ "Set control",
+ "Disable control",
+ "Medium patterns",
+ "Extended opcode",
+ "Terminate"
+};
+
+const char *picExOpcodeNamesEGA[] = {
+ "Set palette entries",
+ "Set palette",
+ "Mono0",
+ "Mono1",
+ "Mono2",
+ "Mono3",
+ "Mono4",
+ "Embedded view",
+ "Set priority table"
+};
+
+const char *picExOpcodeNamesVGA[] = {
+ "Set palette entries",
+ "Embedded view",
+ "Set palette",
+ "Set priority table (eqdist)",
+ "Set priority table (explicit)"
+};
+#endif
+
#define PIC_EGAPALETTE_COUNT 4
#define PIC_EGAPALETTE_SIZE 40
#define PIC_EGAPALETTE_TOTALSIZE PIC_EGAPALETTE_COUNT*PIC_EGAPALETTE_SIZE
@@ -543,7 +535,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
// Drawing
while (curPos < dataSize) {
- //warning("%X at %d", data[curPos], curPos);
+#ifdef DEBUG_PICTURE_DRAW
+ debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OP_SET_COLOR:
pic_color = data[curPos++];
@@ -681,6 +675,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
case PIC_OP_OPX: // Extended functions
if (isEGA) {
+#ifdef DEBUG_PICTURE_DRAW
+ debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesEGA[data[curPos]], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OPX_EGA_SET_PALETTE_ENTRIES:
while (vectorIsNonOpcode(data[curPos])) {
@@ -726,6 +723,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
error("Unsupported sci1 extended pic-operation %X", pic_op);
}
} else {
+#ifdef DEBUG_PICTURE_DRAW
+ debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesVGA[data[curPos]], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OPX_VGA_SET_PALETTE_ENTRIES:
while (vectorIsNonOpcode(data[curPos])) {
@@ -733,7 +733,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OPX_VGA_SET_PALETTE:
- if (_resMan->isAmiga32color()) {
+ if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64) {
if ((data[curPos] == 0x00) && (data[curPos + 1] == 0x01) && ((data[curPos + 32] & 0xF0) != 0xF0)) {
// Left-Over VGA palette, we simply ignore it
curPos += 256 + 4 + 1024;
@@ -780,7 +780,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
case GID_SQ3:
switch (_resourceId) {
case 154: // SQ3: intro, ship gets sucked in
- _screen->ditherForceMemorial(0xD0);
+ _screen->ditherForceDitheredColor(0xD0);
break;
default:
break;
@@ -866,6 +866,8 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
byte matchedMask, matchMask;
int16 w, e, a_set, b_set;
+ bool isEGA = (_resMan->getViewType() == kViewEga);
+
p.x = x + curPort->left;
p.y = y + curPort->top;
stack.push(p);
@@ -874,6 +876,18 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
byte searchPriority = _screen->getPriority(p.x, p.y);
byte searchControl = _screen->getControl(p.x, p.y);
+ if (isEGA) {
+ // In EGA games a pixel in the framebuffer is only 4 bits. We store
+ // a full byte per pixel to allow undithering, but when comparing
+ // pixels for flood-fill purposes, we should only compare the
+ // visible color of a pixel.
+
+ if ((x ^ y) & 1)
+ searchColor = (searchColor ^ (searchColor >> 4)) & 0x0F;
+ else
+ searchColor = searchColor & 0x0F;
+ }
+
// This logic was taken directly from sierra sci, floodfill will get aborted on various occations
if (screenMask & GFX_SCREEN_MASK_VISUAL) {
if ((color == _screen->getColorWhite()) || (searchColor != _screen->getColorWhite()))
@@ -913,20 +927,20 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
int b = curPort->rect.bottom + curPort->top - 1;
while (stack.size()) {
p = stack.pop();
- if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl)) == 0) // already filled
+ if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled
continue;
_screen->putPixel(p.x, p.y, screenMask, color, priority, 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 && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl)))
+ while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
_screen->putPixel(--w, p.y, screenMask, color, priority, control);
- while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl)))
+ while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
_screen->putPixel(++e, p.y, screenMask, color, priority, control);
// checking lines above and below for possible flood targets
a_set = b_set = 0;
while (w <= e) {
- if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl))) { // one line above
+ if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above
if (a_set == 0) {
p1.x = w;
p1.y = p.y - 1;
@@ -936,7 +950,7 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
} else
a_set = 0;
- if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl))) { // one line below
+ if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below
if (b_set == 0) {
p1.x = w;
p1.y = p.y + 1;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 87f0c3d6a2..cc206bd5b9 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -25,8 +25,10 @@
#include "common/util.h"
+#include "sci/console.h"
#include "sci/sci.h"
#include "sci/engine/features.h"
+#include "sci/engine/gc.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -106,7 +108,10 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te
offTop = 26;
break;
default:
- offTop = 10;
+ // For Mac games running with a height of 190, we do not have a menu bar
+ // so the top offset should be 0.
+ if (_screen->getHeight() == 190)
+ offTop = 0;
break;
}
@@ -243,8 +248,10 @@ void GfxPorts::beginUpdate(Window *wnd) {
PortList::iterator it = _windowList.reverse_begin();
const PortList::iterator end = Common::find(_windowList.begin(), _windowList.end(), wnd);
while (it != end) {
- // FIXME: We also store Port objects in the window list.
- // We should add a check that we really only pass windows here...
+ // We also store Port objects in the window list, but they
+ // shouldn't be encountered during this iteration.
+ assert((*it)->isWindow());
+
updateWindow((Window *)*it);
--it;
}
@@ -260,11 +267,16 @@ void GfxPorts::endUpdate(Window *wnd) {
assert(it != end);
while (++it != end) {
- // FIXME: We also store Port objects in the window list.
- // We should add a check that we really only pass windows here...
+ // We also store Port objects in the window list, but they
+ // shouldn't be encountered during this iteration.
+ assert((*it)->isWindow());
+
updateWindow((Window *)*it);
}
+ if (getSciVersion() < SCI_VERSION_1_EGA_ONLY)
+ g_sci->_gfxPaint16->kernelGraphRedrawBox(_curPort->rect);
+
setPort(oldPort);
}
@@ -297,7 +309,14 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
}
_windowsById[id] = pwnd;
- if (style & SCI_WINDOWMGR_STYLE_TOPMOST)
+
+ // KQ1sci, KQ4, iceman, QfG2 always add windows to the back of the list.
+ // KQ5CD checks style.
+ // Hoyle3-demo also always adds to the back (#3036763).
+ bool forceToBack = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ||
+ (g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo());
+
+ if (!forceToBack && (style & SCI_WINDOWMGR_STYLE_TOPMOST))
_windowList.push_front(pwnd);
else
_windowList.push_back(pwnd);
@@ -308,7 +327,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
// bit of the left dimension in their interpreter. It seems Sierra did it
// for EGA byte alignment (EGA uses 1 byte for 2 pixels) and left it in
// their interpreter even in the newer VGA games.
- r.left = r.left & 0x7FFE;
+ r.left = r.left & 0xFFFE;
if (r.width() > _screen->getWidth()) {
// We get invalid dimensions at least at the end of sq3 (script bug!).
@@ -344,25 +363,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
}
pwnd->dims = r;
- const Common::Rect *wmprect = &_wmgrPort->rect;
+
+ // Clip window, if needed
+ Common::Rect wmprect = _wmgrPort->rect;
+ // Handle a special case for Dr. Brain 1 Mac. When hovering the mouse cursor
+ // over the status line on top, the game scripts try to draw the game's icon
+ // bar above the current port, by specifying a negative window top, so that
+ // the end result will be drawn 10 pixels above the current port. This is a
+ // hack by Sierra, and is only limited to user style windows. Normally, we
+ // should not clip, same as what Sierra does. However, this will result in
+ // having invalid rectangles with negative coordinates. For this reason, we
+ // adjust the containing rectangle instead.
+ if (pwnd->dims.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
+ (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->dims.top >= 0) {
+ // Offset the final rect top by the requested pixels
+ wmprect.top += pwnd->dims.top;
+ }
+
int16 oldtop = pwnd->dims.top;
int16 oldleft = pwnd->dims.left;
- if (wmprect->top > pwnd->dims.top)
- pwnd->dims.moveTo(pwnd->dims.left, wmprect->top);
- if (wmprect->bottom < pwnd->dims.bottom)
- pwnd->dims.moveTo(pwnd->dims.left, wmprect->bottom - pwnd->dims.bottom + pwnd->dims.top);
+ if (wmprect.top > pwnd->dims.top)
+ pwnd->dims.moveTo(pwnd->dims.left, wmprect.top);
+
+ if (wmprect.bottom < pwnd->dims.bottom)
+ pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top);
- if (wmprect->right < pwnd->dims.right)
- pwnd->dims.moveTo(wmprect->right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top);
+ if (wmprect.right < pwnd->dims.right)
+ pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top);
- if (wmprect->left > pwnd->dims.left)
- pwnd->dims.moveTo(wmprect->left, pwnd->dims.top);
+ if (wmprect.left > pwnd->dims.left)
+ pwnd->dims.moveTo(wmprect.left, pwnd->dims.top);
pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop);
+
if (restoreRect == 0)
pwnd->restoreRect = pwnd->dims;
+ if (pwnd->restoreRect.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
+ (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->restoreRect.top >= 0) {
+ // Special case for Dr. Brain 1 Mac (check above), applied to the
+ // restore rectangle.
+ pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top);
+ }
+
if (draw)
drawWindow(pwnd);
setPort((Port *)pwnd);
@@ -575,10 +619,6 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) {
int16 y;
int32 bandSize;
- // This code is for 320x200 games only
- if (_screen->getHeight() != 200)
- return;
-
if (bandCount != -1)
_priorityBandCount = bandCount;
@@ -669,4 +709,26 @@ int16 GfxPorts::kernelPriorityToCoordinate(byte priority) {
return _priorityBottom;
}
+void GfxPorts::processEngineHunkList(WorklistManager &wm) {
+ for (PortList::const_iterator it = _windowList.begin(); it != _windowList.end(); ++it) {
+ if ((*it)->isWindow()) {
+ Window *wnd = ((Window *)*it);
+ wm.push(wnd->hSaved1);
+ wm.push(wnd->hSaved2);
+ }
+ }
+}
+
+void GfxPorts::printWindowList(Console *con) {
+ for (PortList::const_iterator it = _windowList.begin(); it != _windowList.end(); ++it) {
+ if ((*it)->isWindow()) {
+ Window *wnd = ((Window *)*it);
+ con->DebugPrintf("%d: '%s' at %d, %d, (%d, %d, %d, %d), drawn: %d, style: %d\n",
+ wnd->id, wnd->title.c_str(), wnd->left, wnd->top,
+ wnd->rect.left, wnd->rect.top, wnd->rect.right, wnd->rect.bottom,
+ wnd->bDrawn, wnd->wndStyle);
+ }
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index 21c6d31ebd..31ed671daf 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -32,13 +32,10 @@
namespace Sci {
-class SciGui;
class GfxPaint16;
class GfxScreen;
class GfxText16;
-
-#define PORTS_FIRSTWINDOWID 2
-#define PORTS_FIRSTSCRIPTWINDOWID 3
+struct WorklistManager;
// window styles
enum {
@@ -49,6 +46,9 @@ enum {
SCI_WINDOWMGR_STYLE_USER = (1 << 7)
};
+typedef Common::List<Port *> PortList;
+typedef Common::Array<Port *> PortArray;
+
/**
* Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI
* this class also handles adjusting coordinates to a specific port
@@ -103,6 +103,8 @@ public:
void kernelGraphAdjustPriority(int top, int bottom);
byte kernelCoordinateToPriority(int16 y);
int16 kernelPriorityToCoordinate(byte priority);
+ void processEngineHunkList(WorklistManager &wm);
+ void printWindowList(Console *con);
Port *_wmgrPort;
Window *_picWind;
@@ -116,7 +118,11 @@ public:
virtual void saveLoadWithSerializer(Common::Serializer &ser);
private:
- typedef Common::List<Port *> PortList;
+ /** The list of open 'windows' (and ports), in visual order. */
+ PortList _windowList;
+
+ /** The list of all open 'windows' (and ports), ordered by their id. */
+ PortArray _windowsById;
SegManager *_segMan;
GfxPaint16 *_paint16;
@@ -130,12 +136,6 @@ private:
// counts windows that got disposed but are not freed yet
uint16 _freeCounter;
- /** The list of open 'windows' (and ports), in visual order. */
- PortList _windowList;
-
- /** The list of all open 'windows' (and ports), ordered by their id. */
- Common::Array<Port *> _windowsById;
-
Common::Rect _bounds;
// Priority Bands related variables
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 896ac0db22..32f54c7e23 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -32,6 +32,7 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
#include "sci/graphics/screen.h"
+#include "sci/graphics/view.h"
namespace Sci {
@@ -64,7 +65,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_height = 480;
} else {
_width = 320;
- _height = 200;
+ _height = getLowResScreenHeight();
}
// Japanese versions of games use hi-res font on upscaled version of the game.
@@ -113,7 +114,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_unditherState = true;
_fontIsUpscaled = false;
- if (_resMan->isVGA() || (_resMan->isAmiga32color())) {
+ if (_resMan->getViewType() != kViewEga) {
// It is not 100% accurate to set white to be 255 for Amiga 32-color
// games. But 255 is defined as white in our SCI at all times, so it
// doesn't matter.
@@ -132,10 +133,12 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
if (g_sci->hasMacIconBar()) {
// For SCI1.1 Mac games with the custom icon bar, we need to expand the screen
// to accommodate for the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
+ // We add 2 to the height of the icon bar to add a buffer between the screen and the
+ // icon bar (as did the original interpreter).
if (g_sci->getGameId() == GID_KQ6)
- initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320);
+ initGraphics(_displayWidth, _displayHeight + 26 + 2, _displayWidth > 320);
else if (g_sci->getGameId() == GID_FREDDYPHARKAS)
- initGraphics(_displayWidth, _displayHeight + 28, _displayWidth > 320);
+ initGraphics(_displayWidth, _displayHeight + 28 + 2, _displayWidth > 320);
else
error("Unknown SCI1.1 Mac game");
} else
@@ -154,12 +157,14 @@ void GfxScreen::copyToScreen() {
}
void GfxScreen::copyFromScreen(byte *buffer) {
+ // TODO this ignores the pitch
Graphics::Surface *screen = g_system->lockScreen();
memcpy(buffer, screen->pixels, _displayPixels);
g_system->unlockScreen();
}
void GfxScreen::kernelSyncWithFramebuffer() {
+ // TODO this ignores the pitch
Graphics::Surface *screen = g_system->lockScreen();
memcpy(_displayScreen, screen->pixels, _displayPixels);
g_system->unlockScreen();
@@ -351,12 +356,30 @@ byte GfxScreen::getControl(int x, int y) {
return _controlScreen[y * _width + x];
}
-byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con) {
+byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
int offset = y * _width + x;
byte match = 0;
- if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color)
- match |= GFX_SCREEN_MASK_VISUAL;
+ // FIXME:
+ if (screenMask & GFX_SCREEN_MASK_VISUAL) {
+ if (!isEGA) {
+ if (*(_visualScreen + offset) == t_color)
+ match |= GFX_SCREEN_MASK_VISUAL;
+ } else {
+ // In EGA games a pixel in the framebuffer is only 4 bits. We store
+ // a full byte per pixel to allow undithering, but when comparing
+ // pixels for flood-fill purposes, we should only compare the
+ // visible color of a pixel.
+
+ byte c = *(_visualScreen + offset);
+ if ((x ^ y) & 1)
+ c = (c ^ (c >> 4)) & 0x0F;
+ else
+ c = c & 0x0F;
+ if (c == t_color)
+ match |= GFX_SCREEN_MASK_VISUAL;
+ }
+ }
if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri)
match |= GFX_SCREEN_MASK_PRIORITY;
if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con)
@@ -509,34 +532,6 @@ void GfxScreen::bitsRestoreDisplayScreen(Common::Rect rect, byte *&memoryPtr) {
}
}
-void GfxScreen::getPalette(Palette *pal) {
- // just copy palette to system
- byte bpal[4 * 256];
- // Get current palette, update it and put back
- g_system->getPaletteManager()->grabPalette(bpal, 0, 256);
- for (int16 i = 1; i < 255; i++) {
- pal->colors[i].r = bpal[i * 4];
- pal->colors[i].g = bpal[i * 4 + 1];
- pal->colors[i].b = bpal[i * 4 + 2];
- }
-}
-
-void GfxScreen::setPalette(Palette *pal) {
- // just copy palette to system
- byte bpal[4 * 256];
- // Get current palette, update it and put back
- g_system->getPaletteManager()->grabPalette(bpal, 0, 256);
- for (int16 i = 0; i < 256; i++) {
- if (!pal->colors[i].used)
- continue;
- bpal[i * 4] = CLIP(pal->colors[i].r * pal->intensity[i] / 100, 0, 255);
- bpal[i * 4 + 1] = CLIP(pal->colors[i].g * pal->intensity[i] / 100, 0, 255);
- bpal[i * 4 + 2] = CLIP(pal->colors[i].b * pal->intensity[i] / 100, 0, 255);
- bpal[i * 4 + 3] = 100;
- }
- g_system->getPaletteManager()->setPalette(bpal, 0, 256);
-}
-
void GfxScreen::setVerticalShakePos(uint16 shakePos) {
if (!_upscaledHires)
g_system->setShakePos(shakePos);
@@ -591,7 +586,7 @@ void GfxScreen::dither(bool addToFlag) {
}
} else {
if (!addToFlag)
- memset(&_unditherMemorial, 0, sizeof(_unditherMemorial));
+ memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
// Do dithering on visual screen and put decoded but undithered byte onto display-screen
for (y = 0; y < _height; y++) {
for (x = 0; x < _width; x++) {
@@ -599,7 +594,7 @@ void GfxScreen::dither(bool addToFlag) {
if (color & 0xF0) {
color ^= color << 4;
// remember dither combination for cel-undithering
- _unditherMemorial[color]++;
+ _ditheredPicColors[color]++;
// if decoded color wants do dither with black on left side, we turn it around
// otherwise the normal ega color would get used for display
if (color & 0xF0) {
@@ -626,18 +621,17 @@ void GfxScreen::dither(bool addToFlag) {
}
}
-// Force a color combination into memorial
-void GfxScreen::ditherForceMemorial(byte color) {
- _unditherMemorial[color] = 256;
+void GfxScreen::ditherForceDitheredColor(byte color) {
+ _ditheredPicColors[color] = 256;
}
void GfxScreen::debugUnditherSetState(bool flag) {
_unditherState = flag;
}
-int16 *GfxScreen::unditherGetMemorial() {
+int16 *GfxScreen::unditherGetDitheredBgColors() {
if (_unditherState)
- return (int16 *)&_unditherMemorial;
+ return (int16 *)&_ditheredPicColors;
else
return NULL;
}
@@ -702,12 +696,39 @@ void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHei
}
}
-void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x) {
+struct UpScaledAdjust {
+ GfxScreenUpscaledMode gameHiresMode;
+ Sci32ViewNativeResolution viewNativeRes;
+ int numerator;
+ int denominator;
+};
+
+static const UpScaledAdjust s_upscaledAdjustTable[] = {
+ { GFX_SCREEN_UPSCALED_640x480, SCI_VIEW_NATIVERES_640x400, 5, 6 }
+};
+
+void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewNativeRes) {
x *= 2;
y = _upscaledMapping[y];
+
+ for (int i = 0; i < ARRAYSIZE(s_upscaledAdjustTable); i++) {
+ if (s_upscaledAdjustTable[i].gameHiresMode == _upscaledHires &&
+ s_upscaledAdjustTable[i].viewNativeRes == viewNativeRes) {
+ y = (y * s_upscaledAdjustTable[i].numerator) / s_upscaledAdjustTable[i].denominator;
+ break;
+ }
+ }
}
-void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
+void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewNativeRes) {
+ for (int i = 0; i < ARRAYSIZE(s_upscaledAdjustTable); i++) {
+ if (s_upscaledAdjustTable[i].gameHiresMode == _upscaledHires &&
+ s_upscaledAdjustTable[i].viewNativeRes == viewNativeRes) {
+ y = (y * s_upscaledAdjustTable[i].denominator) / s_upscaledAdjustTable[i].numerator;
+ break;
+ }
+ }
+
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_640x400:
x /= 2;
@@ -743,4 +764,23 @@ int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) {
return oldPicNotValid;
}
+uint16 GfxScreen::getLowResScreenHeight() {
+ // Some Mac SCI1/1.1 games only take up 190 rows and do not
+ // have the menu bar.
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
+ switch (g_sci->getGameId()) {
+ case GID_FREDDYPHARKAS:
+ case GID_KQ5:
+ case GID_KQ6:
+ case GID_SQ1:
+ return 190;
+ default:
+ break;
+ }
+ }
+
+ // Everything else is 200
+ return 200;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index b6898b905a..89ad52e0ac 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -28,6 +28,7 @@
#include "sci/sci.h"
#include "sci/graphics/helpers.h"
+#include "sci/graphics/view.h"
#include "graphics/sjis.h"
@@ -51,7 +52,7 @@ enum GfxScreenMasks {
};
enum {
- SCI_SCREEN_UNDITHERMEMORIAL_SIZE = 256
+ DITHERED_BG_COLORS_SIZE = 256
};
/**
@@ -89,35 +90,37 @@ public:
void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) {
drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control);
}
- int getUpscaledHires() const {
+
+ GfxScreenUpscaledMode getUpscaledHires() const {
return _upscaledHires;
}
+
bool getUnditherState() const {
return _unditherState;
}
+
void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color);
byte getVisual(int x, int y);
byte getPriority(int x, int y);
byte getControl(int x, int y);
- byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con);
+ byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
int bitsGetDataSize(Common::Rect rect, byte mask);
void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr);
void bitsGetRect(byte *memoryPtr, Common::Rect *destRect);
void bitsRestore(byte *memoryPtr);
- void getPalette(Palette *pal);
- void setPalette(Palette *pal);
-
void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel = 1);
- void adjustToUpscaledCoordinates(int16 &y, int16 &x);
- void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
+ void adjustToUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE);
+ void adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE);
void dither(bool addToFlag);
- void ditherForceMemorial(byte color);
+
+ // Force a color combination as a dithered color
+ void ditherForceDitheredColor(byte color);
void debugUnditherSetState(bool flag);
- int16 *unditherGetMemorial();
+ int16 *unditherGetDitheredBgColors();
void debugShowMap(int mapNo);
@@ -149,7 +152,7 @@ private:
void setVerticalShakePos(uint16 shakePos);
bool _unditherState;
- int16 _unditherMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
+ int16 _ditheredPicColors[DITHERED_BG_COLORS_SIZE];
// These screens have the real resolution of the game engine (320x200 for
// SCI0/SCI1/SCI11 games, 640x480 for SCI2 games). SCI0 games will be
@@ -175,7 +178,7 @@ private:
// This variable defines, if upscaled hires is active and what upscaled mode
// is used.
- int _upscaledHires;
+ GfxScreenUpscaledMode _upscaledHires;
// This here holds a translation for vertical coordinates between native
// (visual) and actual (display) screen.
@@ -184,6 +187,8 @@ private:
// This defines whether or not the font we're drawing is already scaled
// to the screen size (and we therefore should not upscale it ourselves).
bool _fontIsUpscaled;
+
+ uint16 getLowResScreenHeight();
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 21605ccb8e..6269a58492 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -204,19 +204,24 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
maxChars = curCharCount; // return count up to (but not including) breaking space
break;
}
+ // Sometimes this can go off the screen, like for example bug #3040161.
+ // However, we only perform this for non-Japanese games, as these require
+ // special handling, done after this loop.
+ if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN)
+ break;
width += _font->getCharWidth(curChar);
curCharCount++;
}
+
+ // Text without spaces, probably Kanji/Japanese
if (maxChars == 0) {
- // Text w/o space, supposingly kanji
maxChars = curCharCount;
uint16 nextChar;
// We remove the last char only, if maxWidth was actually equal width
// before adding the last char. Otherwise we won't get the same cutting
- // as in sierra pc98 sci. Note: changing the while() instead will NOT
- // WORK. it would break all sorts of regular sci games.
+ // as in sierra pc98 sci.
if (maxWidth == (width - _font->getCharWidth(curChar))) {
maxChars--;
if (curChar > 0xFF)
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index b7b2bfb38e..fb124055d6 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -39,8 +39,8 @@ namespace Sci {
//#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly)
-GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA)
- : _screen(screen), _palette(palette), _isVGA(isVGA) {
+GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette)
+ : _screen(screen), _palette(palette) {
init();
}
@@ -124,6 +124,7 @@ void GfxTransitions::setup(int16 number, bool blackoutFlag) {
_number = SCI_TRANSITIONS_NONE;
#endif
_blackoutFlag = blackoutFlag;
+ debugC(kDebugLevelGraphics, "Transition %d, blackout %d", number, blackoutFlag);
}
}
@@ -272,8 +273,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
void GfxTransitions::setNewPalette(bool blackoutFlag) {
if (!blackoutFlag)
- if (_isVGA)
- _palette->setOnScreen();
+ _palette->setOnScreen();
}
void GfxTransitions::setNewScreen(bool blackoutFlag) {
@@ -303,7 +303,7 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag
// Note: don't do too many steps in here, otherwise cpu will crap out because of
// the load
void GfxTransitions::fadeOut() {
- byte oldPalette[4 * 256], workPalette[4 * 256];
+ byte oldPalette[3 * 256], workPalette[3 * 256];
int16 stepNr, colorNr;
// Sierra did not fade in/out color 255 for sci1.1, but they used it in
// several pictures (e.g. qfg3 demo/intro), so the fading looked weird
@@ -312,12 +312,18 @@ void GfxTransitions::fadeOut() {
g_system->getPaletteManager()->grabPalette(oldPalette, 0, 256);
for (stepNr = 100; stepNr >= 0; stepNr -= 10) {
- for (colorNr = 1; colorNr < tillColorNr; colorNr++){
- workPalette[colorNr * 4 + 0] = oldPalette[colorNr * 4] * stepNr / 100;
- workPalette[colorNr * 4 + 1] = oldPalette[colorNr * 4 + 1] * stepNr / 100;
- workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100;
+ for (colorNr = 1; colorNr < tillColorNr; colorNr++) {
+ if (_palette->colorIsFromMacClut(colorNr)) {
+ workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3];
+ workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1];
+ workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2];
+ } else {
+ workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3] * stepNr / 100;
+ workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1] * stepNr / 100;
+ workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2] * stepNr / 100;
+ }
}
- g_system->getPaletteManager()->setPalette(workPalette + 4, 1, 254);
+ g_system->getPaletteManager()->setPalette(workPalette + 3, 1, 254);
g_sci->getEngineState()->wait(2);
}
}
@@ -457,7 +463,6 @@ void GfxTransitions::scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int
// Scroll old screen (up/down/left/right) and insert new screen that way - works
// on _picRect area only.
void GfxTransitions::scroll(int16 number) {
- int16 screenWidth, screenHeight;
int16 stepNr = 0;
Common::Rect oldMoveRect = _picRect;
Common::Rect oldScreenRect = _picRect;
@@ -466,7 +471,6 @@ void GfxTransitions::scroll(int16 number) {
uint32 msecCount = 0;
_screen->copyFromScreen(_oldScreen);
- screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight();
switch (number) {
case SCI_TRANSITIONS_SCROLL_LEFT:
@@ -510,7 +514,7 @@ void GfxTransitions::scroll(int16 number) {
newScreenRect.bottom = newScreenRect.top;
newMoveRect.top = newMoveRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
- oldMoveRect.top++; oldScreenRect.top++;
+ oldMoveRect.top++; oldScreenRect.top++;
newScreenRect.bottom++; newMoveRect.top--;
msecCount += 5;
diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h
index 674b7a8173..a8f0ca6f07 100644
--- a/engines/sci/graphics/transitions.h
+++ b/engines/sci/graphics/transitions.h
@@ -65,7 +65,7 @@ class Screen;
*/
class GfxTransitions {
public:
- GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA);
+ GfxTransitions(GfxScreen *screen, GfxPalette *palette);
~GfxTransitions();
void setup(int16 number, bool blackoutFlag);
@@ -97,7 +97,6 @@ private:
GfxScreen *_screen;
GfxPalette *_palette;
- bool _isVGA;
const GfxTransitionTranslateEntry *_translationTable;
int16 _number;
bool _blackoutFlag;
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index a8fc8e7f15..f31cbacb22 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -83,7 +83,7 @@ void GfxView::initData(GuiResourceId resourceId) {
_loopCount = 0;
_embeddedPal = false;
_EGAmapping = NULL;
- _isSci2Hires = false;
+ _sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
_isScaleable = true;
// we adjust inside getCelRect for SCI0EARLY (that version didn't have the +1 when calculating bottom)
@@ -104,9 +104,10 @@ void GfxView::initData(GuiResourceId resourceId) {
}
switch (curViewType) {
- case kViewEga: // View-format SCI0 (and Amiga 16 colors)
+ case kViewEga: // SCI0 (and Amiga 16 colors)
isEGA = true;
- case kViewAmiga: // View-format Amiga (32 colors)
+ case kViewAmiga: // Amiga ECS (32 colors)
+ case kViewAmiga64: // Amiga AGA (64 colors)
case kViewVga: // View-format SCI1
// LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
@@ -132,7 +133,7 @@ void GfxView::initData(GuiResourceId resourceId) {
// SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views
// with broken mapping tables. I guess those games won't use the mapping, so I rather disable it
// for them
- if (getSciVersion() == SCI_VERSION_1_EGA) {
+ if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
_EGAmapping = &_resourceData[palOffset];
for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
@@ -201,8 +202,15 @@ void GfxView::initData(GuiResourceId resourceId) {
assert(headerSize >= 16);
_loopCount = _resourceData[2];
assert(_loopCount);
- _isSci2Hires = _resourceData[5] == 1 ? true : false;
palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8);
+
+ // For SCI32, this is a scale flag
+ if (getSciVersion() >= SCI_VERSION_2) {
+ _sci2ScaleRes = (Sci32ViewNativeResolution)_resourceData[5];
+ if (_screen->getUpscaledHires() == GFX_SCREEN_UPSCALED_DISABLED)
+ _sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
+ }
+
// flags is actually a bit-mask
// it seems it was only used for some early sci1.1 games (or even just laura bow 2)
// later interpreters dont support it at all anymore
@@ -282,10 +290,10 @@ void GfxView::initData(GuiResourceId resourceId) {
}
#ifdef ENABLE_SCI32
// adjust width/height returned to scripts
- if (_isSci2Hires) {
+ if (_sci2ScaleRes != SCI_VIEW_NATIVERES_NONE) {
for (loopNo = 0; loopNo < _loopCount; loopNo++)
for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
- _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight);
+ _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight, _sci2ScaleRes);
} else if (getSciVersion() == SCI_VERSION_2_1) {
for (loopNo = 0; loopNo < _loopCount; loopNo++)
for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
@@ -329,7 +337,7 @@ Palette *GfxView::getPalette() {
}
bool GfxView::isSci2Hires() {
- return _isSci2Hires;
+ return _sci2ScaleRes > SCI_VIEW_NATIVERES_320x200;
}
bool GfxView::isScaleable() {
@@ -346,12 +354,9 @@ void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, C
void GfxView::getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
- int16 adjustY = y - celInfo->height + celInfo->displaceY + 1;
- int16 adjustX = x - ((celInfo->width - 1) >> 1) + celInfo->displaceX;
- outRect.top += adjustY;
- outRect.bottom += adjustY;
- outRect.left += adjustX;
- outRect.right += adjustX;
+ int16 adjustY = y + celInfo->displaceY - celInfo->height + 1;
+ int16 adjustX = x + celInfo->displaceX - ((celInfo->width - 1) >> 1);
+ outRect.translate(adjustX, adjustY);
}
void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
@@ -373,22 +378,157 @@ void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int1
outRect.top = outRect.bottom - scaledHeight;
}
+void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) {
+ byte *outPtr = celBitmap;
+ byte curByte, runLength;
+ byte *rlePtr = inBuffer + rlePos;
+ // The existence of a literal position pointer signifies data with two
+ // separate streams, most likely a SCI1.1 view
+ byte *literalPtr = inBuffer + literalPos;
+ int pixelNr = 0;
+
+ memset(celBitmap, clearColor, pixelCount);
+
+ // View unpacking:
+ //
+ // EGA:
+ // Each byte is like XXXXYYYY (XXXX: 0 - 15, YYYY: 0 - 15)
+ // Set the next XXXX pixels to YYYY
+ //
+ // Amiga:
+ // Each byte is like XXXXXYYY (XXXXX: 0 - 31, YYY: 0 - 7)
+ // - Case A: YYY != 0
+ // Set the next YYY pixels to XXXXX
+ // - Case B: YYY == 0
+ // Skip the next XXXXX pixels (i.e. transparency)
+ //
+ // Amiga 64:
+ // Each byte is like XXYYYYYY (XX: 0 - 3, YYYYYY: 0 - 63)
+ // - Case A: XX != 0
+ // Set the next XX pixels to YYYYYY
+ // - Case B: XX == 0
+ // Skip the next YYYYYY pixels (i.e. transparency)
+ //
+ // VGA:
+ // Each byte is like XXYYYYYY (YYYYY: 0 - 63)
+ // - Case A: XX == 00 (binary)
+ // Copy next YYYYYY bytes as-is
+ // - Case B: XX == 01 (binary)
+ // Same as above, copy YYYYYY + 64 bytes as-is
+ // - Case C: XX == 10 (binary)
+ // Set the next YYYYY pixels to the next byte value
+ // - Case D: XX == 11 (binary)
+ // Skip the next YYYYY pixels (i.e. transparency)
+
+ if (literalPos && isMacSci11ViewData) {
+ // KQ6/Freddy Pharkas use byte lengths, all others use uint16
+ // The SCI devs must have realized that a max of 255 pixels wide
+ // was not very good for 320 or 640 width games.
+ bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS);
+
+ // compression for SCI1.1+ Mac
+ while (pixelNr < pixelCount) {
+ uint32 pixelLine = pixelNr;
+
+ if (hasByteLengths) {
+ pixelNr += *rlePtr++;
+ runLength = *rlePtr++;
+ } else {
+ pixelNr += READ_BE_UINT16(rlePtr);
+ runLength = READ_BE_UINT16(rlePtr + 2);
+ rlePtr += 4;
+ }
+
+ while (runLength-- && pixelNr < pixelCount)
+ outPtr[pixelNr++] = *literalPtr++;
+
+ pixelNr = pixelLine + width;
+ }
+ return;
+ }
+
+ switch (viewType) {
+ case kViewEga:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte >> 4;
+ memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
+ pixelNr += runLength;
+ }
+ break;
+ case kViewAmiga:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ if (curByte & 0x07) { // fill with color
+ runLength = curByte & 0x07;
+ curByte = curByte >> 3;
+ memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
+ } else { // skip the next pixels (transparency)
+ runLength = curByte >> 3;
+ }
+ pixelNr += runLength;
+ }
+ break;
+ case kViewAmiga64:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ if (curByte & 0xC0) { // fill with color
+ runLength = curByte >> 6;
+ memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr));
+ } else { // skip the next pixels (transparency)
+ runLength = curByte & 0x3F;
+ }
+ pixelNr += runLength;
+ }
+ break;
+ case kViewVga:
+ case kViewVga11:
+ // If we have no RLE data, the image is just uncompressed
+ if (rlePos == 0) {
+ memcpy(outPtr, literalPtr, pixelCount);
+ break;
+ }
+
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte & 0x3F;
+
+ switch (curByte & 0xC0) {
+ case 0x40: // copy bytes as is (In copy case, runLength can go up to 127 i.e. pixel & 0x40). Fixes bug #3135872.
+ runLength += 64;
+ case 0x00: // copy bytes as-is
+ if (!literalPos) {
+ memcpy(outPtr + pixelNr, rlePtr, MIN<uint16>(runLength, pixelCount - pixelNr));
+ rlePtr += runLength;
+ } else {
+ memcpy(outPtr + pixelNr, literalPtr, MIN<uint16>(runLength, pixelCount - pixelNr));
+ literalPtr += runLength;
+ }
+ break;
+ case 0x80: // fill with color
+ if (!literalPos)
+ memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
+ else
+ memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
+ break;
+ case 0xC0: // skip the next pixels (transparency)
+ break;
+ }
+
+ pixelNr += runLength;
+ }
+ break;
+ default:
+ error("Unsupported picture viewtype");
+ }
+}
+
void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *rlePtr;
- byte *literalPtr;
- uint32 pixelNo = 0, runLength;
- byte pixel;
if (celInfo->offsetEGA) {
// decompression for EGA views
- literalPtr = _resourceData + _loop[loopNo].cel[celNo].offsetEGA;
- while (pixelNo < pixelCount) {
- pixel = *literalPtr++;
- runLength = pixel >> 4;
- memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- }
+ unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false);
} else {
// We fill the buffer with transparent pixels, so that we can later skip
// over pixels to automatically have them transparent
@@ -411,100 +551,8 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
clearColor = 0;
}
- memset(outPtr, clearColor, pixelCount);
-
- rlePtr = _resourceData + celInfo->offsetRLE;
- if (!celInfo->offsetLiteral) { // no additional literal data
- if (_resMan->isAmiga32color()) {
- // decompression for amiga views
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- if (pixel & 0x07) { // fill with color
- runLength = pixel & 0x07;
- pixel = pixel >> 3;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo++] = pixel;
- }
- } else { // fill with transparent
- runLength = pixel >> 3;
- pixelNo += runLength;
- }
- }
- } else {
- // decompression for data that has just one combined stream
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40)
- runLength += 64;
- case 0x00: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- }
- } else {
- literalPtr = _resourceData + celInfo->offsetLiteral;
- if (celInfo->offsetRLE) {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1) {
- // KQ6/Freddy Pharkas use byte lengths, all others use uint16
- // The SCI devs must have realized that a max of 255 pixels wide
- // was not very good for 320 or 640 width games.
- bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS);
-
- // compression for SCI1.1+ Mac
- while (pixelNo < pixelCount) {
- uint32 pixelLine = pixelNo;
-
- if (hasByteLengths) {
- pixelNo += *rlePtr++;
- runLength = *rlePtr++;
- } else {
- pixelNo += READ_BE_UINT16(rlePtr);
- runLength = READ_BE_UINT16(rlePtr + 2);
- rlePtr += 4;
- }
-
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
-
- pixelNo = pixelLine + celInfo->width;
- }
- } else {
- // decompression for data that has separate rle and literal streams
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- }
- } else {
- // literal stream only, so no compression
- memcpy(outPtr, literalPtr, pixelCount);
- pixelNo = pixelCount;
- }
- }
+ bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1;
+ unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData);
// Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above)
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
@@ -534,9 +582,8 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
// unpack the actual cel bitmap data
unpackCel(loopNo, celNo, pBitmap, pixelCount);
- if (!_resMan->isVGA()) {
+ if (_resMan->getViewType() == kViewEga)
unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
- }
// mirroring the cel if needed
if (_loop[loopNo].mirrorFlag) {
@@ -552,19 +599,15 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
* cel if the dithering in here matches dithering used by the current picture.
*/
void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
- int16 *unditherMemorial = _screen->unditherGetMemorial();
+ int16 *ditheredPicColors = _screen->unditherGetDitheredBgColors();
- // It makes no sense to go further, if no memorial data from current picture
- // is available
- if (!unditherMemorial)
+ // It makes no sense to go further, if there isn't any dithered color data
+ // available for the current picture
+ if (!ditheredPicColors)
return;
- // Makes no sense to process bitmaps that are 3 pixels wide or less
- if (width <= 3)
- return;
-
- // We need at least 2 pixel lines
- if (height < 2)
+ // We need at least a 4x2 bitmap for this algorithm to work
+ if (width < 4 || height < 2)
return;
// If EGA mapping is used for this view, dont do undithering as well
@@ -572,13 +615,13 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
return;
// Walk through the bitmap and remember all combinations of colors
- int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
+ int16 ditheredBitmapColors[DITHERED_BG_COLORS_SIZE];
byte *curPtr;
byte color1, color2;
byte nextColor1, nextColor2;
int16 y, x;
- memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
+ memset(&ditheredBitmapColors, 0, sizeof(ditheredBitmapColors));
// Count all seemingly dithered pixel-combinations as soon as at least 4
// pixels are adjacent and check pixels in the following line as well to
@@ -597,17 +640,17 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4);
nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4;
if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2))
- bitmapMemorial[color1]++;
+ ditheredBitmapColors[color1]++;
}
}
- // Now compare both memorial tables to find out matching
- // dithering-combinations
- bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
+ // Now compare both dither color tables to find out matching dithered color
+ // combinations
+ bool unditherTable[DITHERED_BG_COLORS_SIZE];
byte color, unditherCount = 0;
memset(&unditherTable, false, sizeof(unditherTable));
for (color = 0; color < 255; color++) {
- if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) {
+ if ((ditheredBitmapColors[color] > 5) && (ditheredPicColors[color] > 200)) {
// match found, check if colorKey is contained -> if so, we ignore
// of course
color1 = color & 0x0F; color2 = color >> 4;
@@ -655,10 +698,9 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
int x, y;
- if (_embeddedPal) {
+ if (_embeddedPal)
// Merge view palette in...
_palette->set(&_viewPalette, false);
- }
const int16 width = MIN(clipRect.width(), celWidth);
const int16 height = MIN(clipRect.height(), celHeight);
@@ -679,7 +721,9 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
// UpscaledHires means view is hires and is supposed to
// get drawn onto lowres screen.
// FIXME(?): we can't read priority directly with the
- // hires coordinates. may not be needed at all in kq6
+ // hires coordinates. May not be needed at all in kq6
+ // FIXME: Handle proper aspect ratio. Some GK1 hires images
+ // are in 640x400 instead of 640x480
_screen->putPixelOnDisplay(x2, y2, palette->mapping[color]);
}
}
@@ -718,10 +762,9 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
int16 scaledWidth, scaledHeight;
int pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo;
- if (_embeddedPal) {
+ if (_embeddedPal)
// Merge view palette in...
_palette->set(&_viewPalette, false);
- }
scaledWidth = (celInfo->width * scaleX) >> 7;
scaledHeight = (celInfo->height * scaleY) >> 7;
@@ -788,4 +831,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
}
}
+void GfxView::adjustToUpscaledCoordinates(int16 &y, int16 &x) {
+ _screen->adjustToUpscaledCoordinates(y, x, _sci2ScaleRes);
+}
+
+void GfxView::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
+ _screen->adjustBackUpscaledCoordinates(y, x, _sci2ScaleRes);
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index f785e3c475..36914f916c 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -28,6 +28,13 @@
namespace Sci {
+enum Sci32ViewNativeResolution {
+ SCI_VIEW_NATIVERES_NONE = -1,
+ SCI_VIEW_NATIVERES_320x200 = 0,
+ SCI_VIEW_NATIVERES_640x480 = 1,
+ SCI_VIEW_NATIVERES_640x400 = 2
+};
+
struct CelInfo {
int16 width, height;
int16 scriptWidth, scriptHeight;
@@ -78,6 +85,9 @@ public:
bool isScaleable();
bool isSci2Hires();
+ void adjustToUpscaledCoordinates(int16 &y, int16 &x);
+ void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
+
private:
void initData(GuiResourceId resourceId);
void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount);
@@ -98,8 +108,8 @@ private:
bool _embeddedPal;
Palette _viewPalette;
- // set for SCI2 views in gk1/windows, means that views are hires and should be handled accordingly
- bool _isSci2Hires;
+ // specifies scaling resolution for SCI2 views (see gk1/windows, Wolfgang in room 720)
+ Sci32ViewNativeResolution _sci2ScaleRes;
byte *_EGAmapping;