diff options
author | Colin Snover | 2016-06-30 13:50:10 -0500 |
---|---|---|
committer | Colin Snover | 2016-06-30 14:04:57 -0500 |
commit | e89bdf536d0c8f7e89840c7225032c79ddb4015f (patch) | |
tree | e6568327a6b5f3545a51c8b39ab528a45907eaf5 /engines/sci | |
parent | 0310b4dc4df8de96f05f63f751e3d915800f1a0c (diff) | |
download | scummvm-rg350-e89bdf536d0c8f7e89840c7225032c79ddb4015f.tar.gz scummvm-rg350-e89bdf536d0c8f7e89840c7225032c79ddb4015f.tar.bz2 scummvm-rg350-e89bdf536d0c8f7e89840c7225032c79ddb4015f.zip |
SCI32: Clean-up pass on rendering pipeline
This pass exposed two bugs, which have been fixed:
1. Checks of `_updated` and `_moved` were reversed in some areas,
which lead to rendering bugs. In SQ6 the rendering bugs were
subtle or non-existant, but in e.g. PQ:SWAT the Sierra logo and
title screen animations were totally missing.
2. The renderer formerly kept reading from ScreenItemLists when
new items were added in decrementScreenItemArrayCounts, but
this was determined to be unnecessary.
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 405 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.cpp | 520 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.h | 121 |
3 files changed, 626 insertions, 420 deletions
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 4ec91dc6ad..62a77da5ef 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -553,133 +553,219 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR // } } -// Determine the parts of 'r' that aren't overlapped by 'other'. -// Returns -1 if r and other have no intersection. -// Returns number of returned parts (in outRects) otherwise. -// (In particular, this returns 0 if r is contained in other.) +/** + * Determines the parts of `r` that aren't overlapped by `other`. + * Returns -1 if `r` and `other` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `r` is contained in `other`.) + */ int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) { if (!r.intersects(other)) { return -1; } - int count = 0; + int splitCount = 0; if (r.top < other.top) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.bottom = other.top; r.top = other.top; } if (r.bottom > other.bottom) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.top = other.bottom; r.bottom = other.bottom; } if (r.left < other.left) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.right = other.left; r.left = other.left; } if (r.right > other.right) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.left = other.right; } - return count; + return splitCount; } -void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect) { - RectList rectlist; - Common::Rect outRects[4]; +/** + * Determines the parts of `middleRect` that aren't overlapped + * by `showRect`, optimised for contiguous memory writes. + * Returns -1 if `middleRect` and `showRect` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `middleRect` is contained + * in `other`.) + * + * `middleRect` is modified directly to extend into the upper + * and lower rects. + */ +int splitRectsForRender(Common::Rect &middleRect, const Common::Rect &showRect, Common::Rect(&outRects)[2]) { + if (!middleRect.intersects(showRect)) { + return -1; + } + + const int16 minLeft = MIN(middleRect.left, showRect.left); + const int16 maxRight = MAX(middleRect.right, showRect.right); + + int16 upperLeft, upperTop, upperRight, upperMaxTop; + if (middleRect.top < showRect.top) { + upperLeft = middleRect.left; + upperTop = middleRect.top; + upperRight = middleRect.right; + upperMaxTop = showRect.top; + } + else { + upperLeft = showRect.left; + upperTop = showRect.top; + upperRight = showRect.right; + upperMaxTop = middleRect.top; + } + + int16 lowerLeft, lowerRight, lowerBottom, lowerMinBottom; + if (middleRect.bottom > showRect.bottom) { + lowerLeft = middleRect.left; + lowerRight = middleRect.right; + lowerBottom = middleRect.bottom; + lowerMinBottom = showRect.bottom; + } else { + lowerLeft = showRect.left; + lowerRight = showRect.right; + lowerBottom = showRect.bottom; + lowerMinBottom = middleRect.bottom; + } + + int splitCount = 0; + middleRect.left = minLeft; + middleRect.top = upperMaxTop; + middleRect.right = maxRight; + middleRect.bottom = lowerMinBottom; + + if (upperTop != upperMaxTop) { + Common::Rect &upperRect = outRects[0]; + upperRect.left = upperLeft; + upperRect.top = upperTop; + upperRect.right = upperRight; + upperRect.bottom = upperMaxTop; + + // Merge upper rect into middle rect if possible + if (upperRect.left == middleRect.left && upperRect.right == middleRect.right) { + middleRect.top = upperRect.top; + } else { + ++splitCount; + } + } + + if (lowerBottom != lowerMinBottom) { + Common::Rect &lowerRect = outRects[splitCount]; + lowerRect.left = lowerLeft; + lowerRect.top = lowerMinBottom; + lowerRect.right = lowerRight; + lowerRect.bottom = lowerBottom; + + // Merge lower rect into middle rect if possible + if (lowerRect.left == middleRect.left && lowerRect.right == middleRect.right) { + middleRect.bottom = lowerRect.bottom; + } else { + ++splitCount; + } + } + + assert(splitCount <= 2); + return splitCount; +} // NOTE: The third rectangle parameter is only ever given a non-empty rect // by VMD code, via `frameOut` void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect) { + RectList eraseList; + Common::Rect outRects[4]; int deletedPlaneCount = 0; - bool addedToRectList = false; - int planeCount = _planes.size(); + bool addedToEraseList = false; bool foundTransparentPlane = false; if (!eraseRect.isEmpty()) { addedToEraseList = true; - rectlist.add(eraseRect); + eraseList.add(eraseRect); } + PlaneList::size_type planeCount = _planes.size(); for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) { - Plane *outerPlane = _planes[outerPlaneIndex]; + const Plane *outerPlane = _planes[outerPlaneIndex]; + const Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); if (outerPlane->_type == kPlaneTypeTransparent) { foundTransparentPlane = true; } - Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); - if (outerPlane->_deleted) { - if (visiblePlane != nullptr) { - if (!visiblePlane->_screenRect.isEmpty()) { - addedToRectList = true; - rectlist.add(visiblePlane->_screenRect); - } + if (visiblePlane != nullptr && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); + addedToEraseList = true; } ++deletedPlaneCount; - } else if (visiblePlane != nullptr) { - if (outerPlane->_updated) { - --outerPlane->_updated; - - int splitcount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); - if (splitcount) { - if (splitcount == -1) { - if (!visiblePlane->_screenRect.isEmpty()) { - rectlist.add(visiblePlane->_screenRect); - } + } else if (visiblePlane != nullptr && outerPlane->_moved) { + // _moved will be decremented in the final loop through the planes, + // at the end of this function + + { + const int splitCount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); + if (splitCount) { + if (splitCount == -1 && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); } else { - for (int i = 0; i < splitcount; ++i) { - rectlist.add(outRects[i]); + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } } - - addedToRectList = true; + addedToEraseList = true; } + } - if (!outerPlane->_redrawAllCount) { - int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); - if (splitCount) { - for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); - } - addedToRectList = true; + if (!outerPlane->_redrawAllCount) { + const int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); + if (splitCount) { + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } + addedToEraseList = true; } } } - if (addedToRectList) { - for (RectList::iterator rect = rectlist.begin(); rect != rectlist.end(); ++rect) { - for (int innerPlaneIndex = _planes.size() - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { - Plane *innerPlane = _planes[innerPlaneIndex]; - - if (!innerPlane->_deleted && innerPlane->_type != kPlaneTypeTransparent && innerPlane->_screenRect.intersects(**rect)) { - if (innerPlane->_redrawAllCount == 0) { - eraseLists[innerPlaneIndex].add(innerPlane->_screenRect.findIntersectingRect(**rect)); + if (addedToEraseList) { + for (int rectIndex = 0; rectIndex < eraseList.size(); ++rectIndex) { + const Common::Rect &rect = *eraseList[rectIndex]; + for (int innerPlaneIndex = planeCount - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { + const Plane &innerPlane = *_planes[innerPlaneIndex]; + + if ( + !innerPlane._deleted && + innerPlane._type != kPlaneTypeTransparent && + innerPlane._screenRect.intersects(rect) + ) { + if (!innerPlane._redrawAllCount) { + eraseLists[innerPlaneIndex].add(innerPlane._screenRect.findIntersectingRect(rect)); } - int splitCount = splitRects(**rect, innerPlane->_screenRect, outRects); + const int splitCount = splitRects(rect, innerPlane._screenRect, outRects); for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } - rectlist.erase(rect); + eraseList.erase_at(rectIndex); break; } } } - rectlist.pack(); + eraseList.pack(); } } @@ -691,9 +777,9 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL if (plane->_deleted) { --plane->_deleted; if (plane->_deleted <= 0) { - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - _visiblePlanes.erase(visiblePlaneIt); + const int visiblePlaneIndex = _visiblePlanes.findIndexByObject(plane->_object); + if (visiblePlaneIndex != -1) { + _visiblePlanes.remove_at(visiblePlaneIndex); } _planes.remove_at(planeIndex); @@ -708,107 +794,112 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } + // Some planes may have been deleted, so re-retrieve count planeCount = _planes.size(); - for (int outerIndex = 0; outerIndex < planeCount; ++outerIndex) { + + for (PlaneList::size_type outerIndex = 0; outerIndex < planeCount; ++outerIndex) { // "outer" just refers to the outer loop - Plane *outerPlane = _planes[outerIndex]; - if (outerPlane->_priorityChanged) { - --outerPlane->_priorityChanged; + Plane &outerPlane = *_planes[outerIndex]; + if (outerPlane._priorityChanged) { + --outerPlane._priorityChanged; - Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane->_object); + const Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane._object); if (visibleOuterPlane == nullptr) { - warning("calcLists could not find visible plane for %04x:%04x", PRINT_REG(outerPlane->_object)); + warning("calcLists could not find visible plane for %04x:%04x", PRINT_REG(outerPlane._object)); continue; } - rectlist.add(outerPlane->_screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); + eraseList.add(outerPlane._screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); - for (int innerIndex = planeCount - 1; innerIndex >= 0; --innerIndex) { + for (PlaneList::size_type innerIndex = planeCount - 1; innerIndex >= 0; --innerIndex) { // "inner" just refers to the inner loop - Plane *innerPlane = _planes[innerIndex]; - Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane->_object); - - int rectCount = rectlist.size(); - for (int rectIndex = 0; rectIndex < rectCount; ++rectIndex) { - int splitCount = splitRects(*rectlist[rectIndex], _planes[innerIndex]->_screenRect, outRects); + const Plane &innerPlane = *_planes[innerIndex]; + const Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane._object); + const RectList::size_type rectCount = eraseList.size(); + for (RectList::size_type rectIndex = 0; rectIndex < rectCount; ++rectIndex) { + const int splitCount = splitRects(*eraseList[rectIndex], innerPlane._screenRect, outRects); if (splitCount == 0) { if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); } else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } else if (splitCount != -1) { for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - *rectlist[rectIndex] = outerPlane->_screenRect.findIntersectingRect(innerPlane->_screenRect); - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); - } - else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + *eraseList[rectIndex] = outerPlane._screenRect.findIntersectingRect(innerPlane._screenRect); + + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); + } else { + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } } - rectlist.pack(); + eraseList.pack(); } } } - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - Plane *plane = _planes[planeIndex]; - Plane *visiblePlane = nullptr; + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + Plane &plane = *_planes[planeIndex]; + Plane *visiblePlane = _visiblePlanes.findByObject(plane._object); - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - visiblePlane = *visiblePlaneIt; - } + if (!plane._screenRect.isEmpty()) { + if (plane._redrawAllCount) { + plane.redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + } else { + if (visiblePlane == nullptr) { + error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane._object)); + } - if (plane->_redrawAllCount) { - plane->redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); - } else { - if (visiblePlane == nullptr) { - error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane->_object)); + plane.calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); } + } else { + plane.decrementScreenItemArrayCounts(visiblePlane, false); + } - plane->calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + if (plane._moved) { + // the work for handling moved/resized planes was already done + // earlier in the function, we are just cleaning up now + --plane._moved; } - if (plane->_created) { - _visiblePlanes.add(new Plane(*plane)); - --plane->_created; - } else if (plane->_moved) { - assert(visiblePlaneIt != _visiblePlanes.end()); - **visiblePlaneIt = *plane; - --plane->_moved; + if (plane._created) { + _visiblePlanes.add(new Plane(plane)); + --plane._created; + } else if (plane._updated) { + *visiblePlane = plane; + --plane._updated; } } if (foundTransparentPlane) { - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpEraseRects(drawLists[i], eraseLists[planeIndex]); } } if (_planes[planeIndex]->_type == kPlaneTypeTransparent) { - for (int i = planeIndex - 1; i >= 0; --i) { + for (PlaneList::size_type i = planeIndex - 1; i >= 0; --i) { _planes[i]->filterDownEraseRects(drawLists[i], eraseLists[i], eraseLists[planeIndex]); } @@ -817,7 +908,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpDrawRects(drawLists[i], drawLists[planeIndex]); } @@ -831,17 +922,19 @@ void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) { return; } - for (RectList::const_iterator it = eraseList.begin(); it != eraseList.end(); ++it) { - mergeToShowList(**it, _showList, _overdrawThreshold); - _currentBuffer.fillRect(**it, plane._back); + const RectList::size_type eraseListSize = eraseList.size(); + for (RectList::size_type i = 0; i < eraseListSize; ++i) { + mergeToShowList(*eraseList[i], _showList, _overdrawThreshold); + _currentBuffer.fillRect(*eraseList[i], plane._back); } } void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - DrawItem &drawItem = **it; + const DrawList::size_type drawListSize = screenItemList.size(); + for (DrawList::size_type i = 0; i < drawListSize; ++i) { + const DrawItem &drawItem = *screenItemList[i]; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); - ScreenItem &screenItem = *drawItem.screenItem; + const ScreenItem &screenItem = *drawItem.screenItem; // TODO: Remove // debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; @@ -850,32 +943,61 @@ void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { } void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold) { - Common::Rect merged(drawRect); - - bool didDelete = true; - RectList::size_type count = showList.size(); - while (didDelete && count) { - didDelete = false; - - for (RectList::size_type i = 0; i < count; ++i) { - Common::Rect existing = *showList[i]; - Common::Rect candidate; - candidate.left = MIN(merged.left, existing.left); - candidate.top = MIN(merged.top, existing.top); - candidate.right = MAX(merged.right, existing.right); - candidate.bottom = MAX(merged.bottom, existing.bottom); - - if (candidate.height() * candidate.width() - merged.width() * merged.height() - existing.width() * existing.height() <= overdrawThreshold) { - merged = candidate; - showList.erase_at(i); - didDelete = true; + RectList mergeList; + Common::Rect merged; + mergeList.add(drawRect); + + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + bool didMerge = false; + const Common::Rect &r1 = *mergeList[i]; + if (!r1.isEmpty()) { + for (RectList::size_type j = 0; j < showList.size(); ++j) { + const Common::Rect &r2 = *showList[j]; + if (!r2.isEmpty()) { + merged = r1; + merged.extend(r2); + + int difference = merged.width() * merged.height(); + difference -= r1.width() * r1.height(); + difference -= r2.width() * r2.height(); + if (r1.intersects(r2)) { + const Common::Rect overlap = r1.findIntersectingRect(r2); + difference += overlap.width() * overlap.height(); + } + + if (difference <= overdrawThreshold) { + mergeList.erase_at(i); + showList.erase_at(j); + mergeList.add(merged); + didMerge = true; + break; + } else { + Common::Rect outRects[2]; + int splitCount = splitRectsForRender(*mergeList[i], *showList[j], outRects); + if (splitCount != -1) { + mergeList.add(*mergeList[i]); + mergeList.erase_at(i); + showList.erase_at(j); + didMerge = true; + while (splitCount--) { + mergeList.add(outRects[splitCount]); + } + break; + } + } + } } - } - count = showList.pack(); + if (didMerge) { + showList.pack(); + } + } } - showList.add(merged); + mergeList.pack(); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + showList.add(*mergeList[i]); + } } void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) { @@ -930,6 +1052,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry } } else { for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { + // TODO: Limiting range 72 to 103 is NOT present in every game if (styleRanges[i] == -1 || (styleRanges[i] == 0 && i > 71 && i < 104)) { sourcePalette.colors[i] = nextPalette.colors[i]; sourcePalette.colors[i].used = true; diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 3f0b035738..021ac523b8 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -240,16 +240,17 @@ void Plane::deleteAllPics() { #pragma mark Plane - Rendering void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const { - int index = planeList.findIndexByObject(_object); + const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1; + const PlaneList::size_type planeCount = planeList.size(); for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) { + for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) { if (planeList[j]->_type != kPlaneTypeTransparent) { - Common::Rect ptr[4]; - int count = splitRects(drawList[i]->rect, planeList[j]->_screenRect, ptr); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - drawList.add(drawList[i]->screenItem, ptr[k]); + Common::Rect outRects[4]; + int splitCount = splitRects(drawList[i]->rect, planeList[j]->_screenRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + drawList.add(drawList[i]->screenItem, outRects[splitCount]); } drawList.erase_at(i); @@ -262,17 +263,17 @@ void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList } void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const { - int index = planeList.findIndexByObject(_object); + const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1; + const PlaneList::size_type planeCount = planeList.size(); for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) { + for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) { if (planeList[j]->_type != kPlaneTypeTransparent) { - Common::Rect ptr[4]; - - int count = splitRects(*eraseList[i], planeList[j]->_screenRect, ptr); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - eraseList.add(ptr[k]); + Common::Rect outRects[4]; + int splitCount = splitRects(*eraseList[i], planeList[j]->_screenRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + eraseList.add(outRects[splitCount]); } eraseList.erase_at(i); @@ -285,94 +286,109 @@ void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeLi } void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) { - ScreenItemList::size_type planeItemCount = _screenItemList.size(); - ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + const ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); + + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + // Items can be added to ScreenItemList and we don't want to process + // those new items, but the list also can grow smaller, so we need + // to check that we are still within the upper bound of the list and + // quit if we aren't any more + if (i >= _screenItemList.size()) { + break; + } + + ScreenItem *item = _screenItemList[i]; + if (item == nullptr) { + continue; + } - for (ScreenItemList::size_type i = 0; i < planeItemCount; ++i) { - ScreenItem *vitem = nullptr; // NOTE: The original engine used an array without bounds checking // so could just get the visible screen item directly; we need to // verify that the index is actually within the valid range for // the visible plane before accessing the item to avoid a range // error. + const ScreenItem *visibleItem = nullptr; if (i < visiblePlaneItemCount) { - vitem = visiblePlane._screenItemList[i]; + visibleItem = visiblePlane._screenItemList[i]; } - ScreenItem *item = _screenItemList[i]; - if (i < _screenItemList.size() && item != nullptr) { - if (item->_deleted) { - // add item's rect to erase list - if ( - i < visiblePlane._screenItemList.size() && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - if (g_sci->_gfxRemap32->getRemapCount()) { - mergeToRectList(vitem->_screenRect, eraseList); - } else { - eraseList.add(vitem->_screenRect); - } - } - } else if (item->_created) { - // add item to draw list - item->calcRects(*this); - - if(!item->_screenRect.isEmpty()) { - if (g_sci->_gfxRemap32->getRemapCount()) { - drawList.add(item, item->_screenRect); - mergeToRectList(item->_screenRect, eraseList); - } else { - drawList.add(item, item->_screenRect); - } - } - } else if (item->_updated) { - // add old rect to erase list, new item to draw list - item->calcRects(*this); + // Keep erase rects for this screen item from drawing outside + // of its owner plane + Common::Rect visibleItemScreenRect; + if (visibleItem != nullptr) { + visibleItemScreenRect = visibleItem->_screenRect; + visibleItemScreenRect.clip(_screenRect); + } + + if (item->_deleted) { + // Add item's rect to erase list + if ( + visibleItem != nullptr && + !visibleItemScreenRect.isEmpty() + ) { if (g_sci->_gfxRemap32->getRemapCount()) { - // if item and vitem don't overlap, ... - if (item->_screenRect.isEmpty() || - i >= visiblePlaneItemCount || - vitem == nullptr || - vitem->_screenRect.isEmpty() || - !vitem->_screenRect.intersects(item->_screenRect) - ) { - // add item to draw list, and old rect to erase list - if (!item->_screenRect.isEmpty()) { - drawList.add(item, item->_screenRect); - mergeToRectList(item->_screenRect, eraseList); - } - if ( - i < visiblePlaneItemCount && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - mergeToRectList(vitem->_screenRect, eraseList); - } - } else { - // otherwise, add bounding box of old+new to erase list, - // and item to draw list + mergeToRectList(visibleItemScreenRect, eraseList); + } else { + eraseList.add(visibleItemScreenRect); + } + } + } - // TODO: This was changed from disasm, verify please! - Common::Rect extendedScreenRect = vitem->_screenRect; - extendedScreenRect.extend(item->_screenRect); + if (!item->_created && !item->_updated) { + continue; + } - drawList.add(item, item->_screenRect); - mergeToRectList(extendedScreenRect, eraseList); - } + item->calcRects(*this); + const Common::Rect itemScreenRect(item->_screenRect); + + if (item->_created) { + // Add item to draw list + if(!itemScreenRect.isEmpty()) { + if (g_sci->_gfxRemap32->getRemapCount()) { + drawList.add(item, itemScreenRect); + mergeToRectList(itemScreenRect, eraseList); } else { - // if no active remaps, just add item to draw list and old rect - // to erase list - if (!item->_screenRect.isEmpty()) { - drawList.add(item, item->_screenRect); + drawList.add(item, itemScreenRect); + } + } + } else { + // Add old rect to erase list, new item to draw list + + if (g_sci->_gfxRemap32->getRemapCount()) { + // If item and visibleItem don't overlap... + if (itemScreenRect.isEmpty() || + visibleItem == nullptr || + visibleItemScreenRect.isEmpty() || + !visibleItemScreenRect.intersects(itemScreenRect) + ) { + // ...add item to draw list, and old rect to erase list... + if (!itemScreenRect.isEmpty()) { + drawList.add(item, itemScreenRect); + mergeToRectList(itemScreenRect, eraseList); } - if ( - i < visiblePlaneItemCount && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - eraseList.add(vitem->_screenRect); + if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) { + mergeToRectList(visibleItemScreenRect, eraseList); } + } else { + // ...otherwise, add bounding box of old+new to erase list, + // and item to draw list + Common::Rect extendedScreenRect = visibleItemScreenRect; + extendedScreenRect.extend(itemScreenRect); + + drawList.add(item, itemScreenRect); + mergeToRectList(extendedScreenRect, eraseList); + } + } else { + // If no active remaps, just add item to draw list and old rect + // to erase list + + // TODO: SCI3 update rects for VMD? + if (!itemScreenRect.isEmpty()) { + drawList.add(item, itemScreenRect); + } + if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) { + eraseList.add(visibleItemScreenRect); } } } @@ -385,40 +401,44 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // We store the current size of the drawlist, as we want to loop // over the currently inserted entries later. DrawList::size_type drawListSizePrimary = drawList.size(); + const RectList::size_type eraseListCount = eraseList.size(); - if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? + // TODO: Figure out which games need which rendering method + if (/* TODO: dword_C6288 */ false) { // "high resolution pictures" _screenItemList.sort(); - bool encounteredPic = false; - bool v81 = false; + bool pictureDrawn = false; + bool screenItemDrawn = false; - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - const Common::Rect *rect = eraseList[i]; + for (RectList::size_type i = 0; i < eraseListCount; ++i) { + const Common::Rect &rect = *eraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { ScreenItem *item = _screenItemList[j]; - if (j < _screenItemList.size() && item != nullptr) { - if (rect->intersects(item->_screenRect)) { - const Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); - if (!item->_deleted) { - if (encounteredPic) { - if (item->_celInfo.type == kCelTypePic) { - if (v81 || item->_celInfo.celNo == 0) { - drawList.add(item, intersection); - } - } else { - if (!item->_updated && !item->_created) { - drawList.add(item, intersection); - } - v81 = true; + if (item == nullptr) { + continue; + } + + if (rect.intersects(item->_screenRect)) { + const Common::Rect intersection = rect.findIntersectingRect(item->_screenRect); + if (!item->_deleted) { + if (pictureDrawn) { + if (item->_celInfo.type == kCelTypePic) { + if (screenItemDrawn || item->_celInfo.celNo == 0) { + mergeToDrawList(j, intersection, drawList); } } else { if (!item->_updated && !item->_created) { - drawList.add(item, intersection); - } - if (item->_celInfo.type == kCelTypePic) { - encounteredPic = true; + mergeToDrawList(j, intersection, drawList); } + screenItemDrawn = true; + } + } else { + if (!item->_updated && !item->_created) { + mergeToDrawList(j, intersection, drawList); + } + if (item->_celInfo.type == kCelTypePic) { + pictureDrawn = true; } } } @@ -428,22 +448,23 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList _screenItemList.unsort(); } else { - // add all items overlapping the erase list to the draw list - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { + // Add all items overlapping the erase list to the draw list + for (RectList::size_type i = 0; i < eraseListCount; ++i) { + const Common::Rect &rect = *eraseList[i]; + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { ScreenItem *item = _screenItemList[j]; if ( item != nullptr && !item->_created && !item->_updated && !item->_deleted && - eraseList[i]->intersects(item->_screenRect) + rect.intersects(item->_screenRect) ) { - drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect)); + drawList.add(item, rect.findIntersectingRect(item->_screenRect)); } } } } - if (g_sci->_gfxRemap32->getRemapCount() == 0) { // no remaps active? + if (g_sci->_gfxRemap32->getRemapCount() == 0) { // Add all items that overlap with items in the drawlist and have higher // priority. @@ -451,23 +472,28 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // those that were added because of the erase list in the previous loop, // or those to be added in this loop. for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) { - DrawItem *dli = drawList[i]; + const DrawItem *drawListEntry = nullptr; + if (i < drawList.size()) { + drawListEntry = drawList[i]; + } - for (ScreenItemList::size_type j = 0; j < planeItemCount; ++j) { - ScreenItem *sli = _screenItemList[j]; + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + ScreenItem *newItem = nullptr; + if (j < _screenItemList.size()) { + newItem = _screenItemList[j]; + } if ( - i < drawList.size() && dli != nullptr && - j < _screenItemList.size() && sli != nullptr && - !sli->_created && !sli->_updated && !sli->_deleted + drawListEntry != nullptr && newItem != nullptr && + !newItem->_created && !newItem->_updated && !newItem->_deleted ) { - ScreenItem *item = dli->screenItem; + const ScreenItem *drawnItem = drawListEntry->screenItem; if ( - (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) && - dli->rect.intersects(sli->_screenRect) + (newItem->_priority > drawnItem->_priority || (newItem->_priority == drawnItem->_priority && newItem->_object > drawnItem->_object)) && + drawListEntry->rect.intersects(newItem->_screenRect) ) { - drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); + mergeToDrawList(j, drawListEntry->rect.findIntersectingRect(newItem->_screenRect), drawList); } } } @@ -475,14 +501,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } decrementScreenItemArrayCounts(&visiblePlane, false); - _screenItemList.pack(); - visiblePlane._screenItemList.pack(); } void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate) { - // The size of the screenItemList may change, so it is - // critical to re-check the size on each iteration - for (ScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) { + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { ScreenItem *item = _screenItemList[i]; if (item != nullptr) { @@ -495,7 +518,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force visiblePlane->_screenItemList.findByObject(item->_object) != nullptr ) ) { - *visiblePlane->_screenItemList[i] = *_screenItemList[i]; + *visiblePlane->_screenItemList[i] = *item; } if (item->_updated) { @@ -514,175 +537,180 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force if (item->_deleted) { item->_deleted--; if (!item->_deleted) { - visiblePlane->_screenItemList.erase_at(i); + if (visiblePlane != nullptr && visiblePlane->_screenItemList.findByObject(item->_object) != nullptr) { + visiblePlane->_screenItemList.erase_at(i); + } _screenItemList.erase_at(i); } } } } + + _screenItemList.pack(); + if (visiblePlane != nullptr) { + visiblePlane->_screenItemList.pack(); + } } -void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const { +void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const { + const RectList::size_type higherEraseCount = higherEraseList.size(); + if (_type == kPlaneTypeTransparent) { - for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - const Common::Rect *r = transparentEraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - if (item != nullptr) { - if (r->intersects(item->_screenRect)) { - mergeToDrawList(j, *r, drawList); - } + for (RectList::size_type i = 0; i < higherEraseCount; ++i) { + const Common::Rect &r = *higherEraseList[i]; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } else { - for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - Common::Rect *r = transparentEraseList[i]; - if (r->intersects(_screenRect)) { - r->clip(_screenRect); - mergeToRectList(*r, eraseList); - - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - - if (item != nullptr) { - if (r->intersects(item->_screenRect)) { - mergeToDrawList(j, *r, drawList); - } + for (RectList::size_type i = 0; i < higherEraseCount; ++i) { + Common::Rect r = *higherEraseList[i]; + if (r.intersects(_screenRect)) { + r.clip(_screenRect); + mergeToRectList(r, eraseList); + + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } - Common::Rect ptr[4]; - const Common::Rect *r2 = transparentEraseList[i]; - int count = splitRects(*r2, *r, ptr); - for (int k = count - 1; k >= 0; --k) { - transparentEraseList.add(ptr[k]); + Common::Rect outRects[4]; + const Common::Rect &r2 = *higherEraseList[i]; + int splitCount = splitRects(r2, r, outRects); + while (splitCount--) { + higherEraseList.add(outRects[splitCount]); } - transparentEraseList.erase_at(i); + higherEraseList.erase_at(i); } } - transparentEraseList.pack(); + higherEraseList.pack(); } } -void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const { - for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - const Common::Rect &r = drawList[i]->rect; - - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - if (item != nullptr) { - if (r.intersects(item->_screenRect)) { - mergeToDrawList(j, r, transparentDrawList); - } +void Plane::filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const { + const DrawList::size_type lowerDrawCount = lowerDrawList.size(); + for (DrawList::size_type i = 0; i < lowerDrawCount; ++i) { + const Common::Rect &r = lowerDrawList[i]->rect; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } -void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - const Common::Rect &r = *eraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - - if (item != nullptr) { - if (r.intersects(item->_screenRect)) { - mergeToDrawList(j, r, drawList); - } +void Plane::filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const { + const RectList::size_type lowerEraseCount = lowerEraseList.size(); + for (RectList::size_type i = 0; i < lowerEraseCount; ++i) { + const Common::Rect &r = *lowerEraseList[i]; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const { - RectList rects; - - ScreenItem *item = _screenItemList[index]; - Common::Rect r = item->_screenRect; + RectList mergeList; + ScreenItem &item = *_screenItemList[index]; + Common::Rect r = item._screenRect; r.clip(rect); - rects.add(r); + mergeList.add(r); - for (RectList::size_type i = 0; i < rects.size(); ++i) { - r = *rects[i]; + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + r = *mergeList[i]; - for (DrawList::size_type j = 0; j < drawList.size(); ++j) { - const DrawItem *drawitem = drawList[j]; - if (item->_object == drawitem->screenItem->_object) { - if (drawitem->rect.contains(r)) { - rects.erase_at(i); + const DrawList::size_type drawCount = drawList.size(); + for (DrawList::size_type j = 0; j < drawCount; ++j) { + const DrawItem &drawItem = *drawList[j]; + if (item._object == drawItem.screenItem->_object) { + if (drawItem.rect.contains(r)) { + mergeList.erase_at(i); break; } Common::Rect outRects[4]; - const int count = splitRects(r, drawitem->rect, outRects); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - rects.add(outRects[k]); + int splitCount = splitRects(r, drawItem.rect, outRects); + if (splitCount != -1) { + while (splitCount--) { + mergeList.add(outRects[splitCount]); } - rects.erase_at(i); + mergeList.erase_at(i); // proceed to the next rect - r = *rects[++i]; + r = *mergeList[++i]; } } } } - rects.pack(); + mergeList.pack(); - for (RectList::size_type i = 0; i < rects.size(); ++i) { - drawList.add(item, *rects[i]); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + drawList.add(&item, *mergeList[i]); } } -void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const { - RectList temp; - temp.add(rect); +void Plane::mergeToRectList(const Common::Rect &rect, RectList &eraseList) const { + RectList mergeList; + Common::Rect r; + mergeList.add(rect); - for (RectList::size_type i = 0; i < temp.size(); ++i) { - Common::Rect r = *temp[i]; + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + r = *mergeList[i]; - for (RectList::size_type j = 0; j < rectList.size(); ++j) { - const Common::Rect *innerRect = rectList[j]; - if (innerRect->contains(r)) { - temp.erase_at(i); + const RectList::size_type eraseCount = eraseList.size(); + for (RectList::size_type j = 0; j < eraseCount; ++j) { + const Common::Rect &eraseRect = *eraseList[j]; + if (eraseRect.contains(r)) { + mergeList.erase_at(i); break; } - Common::Rect out[4]; - const int count = splitRects(r, *innerRect, out); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - temp.add(out[k]); + Common::Rect outRects[4]; + int splitCount = splitRects(r, eraseRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + mergeList.add(outRects[splitCount]); } - temp.erase_at(i); + mergeList.erase_at(i); // proceed to the next rect - r = *temp[++i]; + r = *mergeList[++i]; } } } - temp.pack(); + mergeList.pack(); - for (RectList::size_type i = 0; i < temp.size(); ++i) { - rectList.add(*temp[i]); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + eraseList.add(*mergeList[i]); } } void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) { - for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { - if (*screenItemPtr != nullptr) { - ScreenItem &screenItem = **screenItemPtr; - if (!screenItem._deleted) { - screenItem.calcRects(*this); - if (!screenItem._screenRect.isEmpty()) { - drawList.add(&screenItem, screenItem._screenRect); - } + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + ScreenItem *screenItem = _screenItemList[i]; + if (screenItem != nullptr && !screenItem->_deleted) { + screenItem->calcRects(*this); + if (!screenItem->_screenRect.isEmpty()) { + mergeToDrawList(i, screenItem->_screenRect, drawList); } } } @@ -696,10 +724,6 @@ void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList breakDrawListByPlanes(drawList, planeList); --_redrawAllCount; decrementScreenItemArrayCounts(visiblePlane, true); - _screenItemList.pack(); - if (visiblePlane != nullptr) { - visiblePlane->_screenItemList.pack(); - } } void Plane::setType() { @@ -731,10 +755,12 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { _planeRect.right > other->_planeRect.right || _planeRect.bottom > other->_planeRect.bottom ) { + // the plane moved or got larger _redrawAllCount = g_sci->_gfxFrameout->getScreenCount(); - _updated = g_sci->_gfxFrameout->getScreenCount(); + _moved = g_sci->_gfxFrameout->getScreenCount(); } else if (_planeRect != other->_planeRect) { - _updated = g_sci->_gfxFrameout->getScreenCount(); + // the plane got smaller + _moved = g_sci->_gfxFrameout->getScreenCount(); } if (_priority != other->_priority) { @@ -755,7 +781,7 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { _deleted = 0; if (_created == 0) { - _moved = g_sci->_gfxFrameout->getScreenCount(); + _updated = g_sci->_gfxFrameout->getScreenCount(); } convertGameRectToPlaneRect(); @@ -801,18 +827,22 @@ void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool } void Plane::remapMarkRedraw() { - for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { - if (*screenItemPtr != nullptr) { - ScreenItem &screenItem = **screenItemPtr; - if (screenItem.getCelObj()._remap && !screenItem._deleted && !screenItem._created) { - screenItem._updated = g_sci->_gfxFrameout->getScreenCount(); - } + ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + ScreenItem *screenItem = _screenItemList[i]; + if ( + screenItem != nullptr && + !screenItem->_deleted && !screenItem->_created && + screenItem->getCelObj()._remap + ) { + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); } } } #pragma mark - #pragma mark PlaneList + void PlaneList::add(Plane *plane) { for (iterator it = begin(); it != end(); ++it) { if ((*it)->_priority > plane->_priority) { diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index c93fb5b64e..20cff40814 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -62,7 +62,14 @@ public: #pragma mark DrawList struct DrawItem { + /** + * The screen item to draw. + */ ScreenItem *screenItem; + + /** + * The target rectangle of the draw operation. + */ Common::Rect rect; inline bool operator<(const DrawItem &other) const { @@ -189,16 +196,15 @@ public: * not match * - `deleted` is set when the plane is deleted by a * kernel call - * - `moved` is set when the plane is synchronised from - * another plane and is not already in the "created" - * state + * - `moved` is set when the plane has been moved or + * resized */ int _created, _updated, _deleted, _moved; /** * The vanishing point for the plane. Used when - * calculating the correct scaling of the plane's screen - * items according to their position. + * automatically calculating the correct scaling of the + * plane's screen items according to their position. */ Common::Point _vanishingPoint; @@ -358,42 +364,33 @@ public: private: /** * Splits all rects in the given draw list at the edges - * of all non-transparent planes above the current - * plane. + * of all higher-priority, non-transparent, intersecting + * planes. */ void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const; /** - * Splits all rects in the given erase list rects at the - * edges of all non-transparent planes above the current - * plane. + * Splits all rects in the given erase list at the + * edges of higher-priority, non-transparent, + * intersecting planes. */ void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const; /** - * Synchronises changes to screen items from the current - * plane to the visible plane and deletes screen items - * from the current plane that have been marked as - * deleted. If `forceUpdate` is true, all screen items - * on the visible plane will be updated, even if they - * are not marked as having changed. - */ - void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); - - /** - * Merges the screen item from this plane at the given - * index into the given draw list, clipped to the given - * rect. TODO: Finish documenting + * Adds the screen item at `index` into `drawList`, + * ensuring it is only drawn within the bounds of + * `rect`. If an existing draw list entry exists + * for this screen item, it will be modified. + * Otherwise, a new entry will be added. */ void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const; /** - * Adds the given rect into the given rect list, - * merging it with other rects already inside the list, - * if possible, to avoid overdraw. TODO: Finish - * documenting + * Merges `rect` with an existing rect in `eraseList`, + * if possible. Otherwise, adds the rect as a new entry + * to `eraseList`. */ - void mergeToRectList(const Common::Rect &rect, RectList &rectList) const; + void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const; public: /** @@ -406,19 +403,73 @@ public: void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); /** - * TODO: Documentation + * Synchronises changes to screen items from the current + * plane to the visible plane and deletes screen items + * from the current plane that have been marked as + * deleted. If `forceUpdate` is true, all screen items + * on the visible plane will be updated, even if they + * are not marked as having changed. + */ + void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); + + /** + * This method is called from the highest priority plane + * to the lowest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be redrawn because they intersect entries + * in the `higherEraseList`. + * + * If this plane is opaque, all intersecting erase rects + * in `lowerEraseList` are removed, as they would be + * completely overwritten by the contents of this plane. + * + * If this plane is transparent, erase rects from the + * `lowerEraseList` are added to the erase list for this + * plane, so that lower planes. + * + * @param drawList The draw list for this plane. + * @param eraseList The erase list for this plane. + * @param higherEraseList The erase list for a plane + * above this plane. */ - void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const; + void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const; /** - * TODO: Documentation + * This method is called from the lowest priority plane + * to the highest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be drawn because the lower plane is being + * redrawn and potentially transparent screen items + * from this plane would draw over the lower priority + * plane's screen items. + * + * This method applies only to transparent planes. + * + * @param drawList The draw list for this plane. + * @param eraseList The erase list for a plane below + * this plane. */ - void filterUpEraseRects(DrawList &drawList, RectList &eraseList) const; + void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const; /** - * TODO: Documentation + * This method is called from the lowest priority plane + * to the highest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be drawn because the lower plane is being + * redrawn and potentially transparent screen items + * from this plane would draw over the lower priority + * plane's screen items. + * + * This method applies only to transparent planes. + * + * @param drawList The draw list for this plane. + * @param lowerDrawList The draw list for a plane below + * this plane. */ - void filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const; + void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const; /** * Updates all of the plane's non-deleted screen items @@ -442,6 +493,8 @@ private: using PlaneListBase::push_back; public: + typedef int size_type; + // A method for finding the index of a plane inside a // PlaneList is used because entries in the main plane // list and visible plane list of GfxFrameout are |