[Scummvm-cvs-logs] scummvm master -> 4ee1901706e6e92d8c27f3acb9c4be75eaca5a3f

csnover csnover at users.noreply.github.com
Thu Jun 30 22:10:55 CEST 2016


This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
29f7a66af4 SCI32: Add low resolution constants
a785147d6c SCI32: Document & clean up extra rect argument for frameOut
0310b4dc4d SCI32: Implement engine-accurate screen item list sorting
e89bdf536d SCI32: Clean-up pass on rendering pipeline
4ee1901706 SCI32: Add transparent pic plane type


Commit: 29f7a66af40acdc348e28d5c681f43e1f33bf4de
    https://github.com/scummvm/scummvm/commit/29f7a66af40acdc348e28d5c681f43e1f33bf4de
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-06-30T14:04:56-05:00

Commit Message:
SCI32: Add low resolution constants

In a few places in the graphics system, fixed low-resolution values
are used instead of the game script resolution.

Changed paths:
    engines/sci/graphics/celobj32.cpp
    engines/sci/graphics/celobj32.h
    engines/sci/graphics/screen_item32.cpp



diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index af5ee3a..befa5cd 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -799,8 +799,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 	if (_scaledWidth == 0 || _scaledHeight == 0) {
 		byte sizeFlag = data[5];
 		if (sizeFlag == 0) {
-			_scaledWidth = 320;
-			_scaledHeight = 200;
+			_scaledWidth = kLowResX;
+			_scaledHeight = kLowResY;
 		} else if (sizeFlag == 1) {
 			_scaledWidth = 640;
 			_scaledHeight = 480;
@@ -985,8 +985,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 		_scaledWidth = sizeFlag1;
 		_scaledHeight = sizeFlag2;
 	} else if (sizeFlag1 == 0) {
-		_scaledWidth = 320;
-		_scaledHeight = 200;
+		_scaledWidth = kLowResX;
+		_scaledHeight = kLowResY;
 	} else if (sizeFlag1 == 1) {
 		_scaledWidth = 640;
 		_scaledHeight = 480;
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
index 6e401b3..e405592 100644
--- a/engines/sci/graphics/celobj32.h
+++ b/engines/sci/graphics/celobj32.h
@@ -31,6 +31,20 @@
 namespace Sci {
 typedef Common::Rational Ratio;
 
+// SCI32 has four different coordinate systems:
+// 1. low resolution, 2. game/script resolution,
+// 3. text/bitmap resolution, 4. screen resolution
+//
+// In CelObj, these values are used when there is
+// no baked in resolution of cels.
+//
+// In ScreenItem, it is used when deciding which
+// path to take to calculate dimensions.
+enum {
+	kLowResX = 320,
+	kLowResY = 200
+};
+
 enum CelType {
 	kCelTypeView  = 0,
 	kCelTypePic   = 1,
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index c0b3240..d2673d4 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -273,7 +273,9 @@ void ScreenItem::calcRects(const Plane &plane) {
 		// Cel may use a coordinate system that is not the same size as the
 		// script coordinate system (usually this means high-resolution
 		// pictures with low-resolution scripts)
-		if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) {
+		if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) {
+			// high resolution coordinates
+
 			if (_useInsetRect) {
 				const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth);
 				const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight);
@@ -345,6 +347,8 @@ void ScreenItem::calcRects(const Plane &plane) {
 			_ratioX = scaleX * celToScreenX;
 			_ratioY = scaleY * celToScreenY;
 		} else {
+			// low resolution coordinates
+
 			int displaceX = celObj._displace.x;
 			if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) {
 				displaceX = celObj._width - celObj._displace.x - 1;
@@ -582,7 +586,9 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const {
 		displaceX = celObj._width - displaceX - 1;
 	}
 
-	if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) {
+	if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) {
+		// high resolution coordinates
+
 		if (_useInsetRect) {
 			Ratio scriptToCelX(celObj._scaledWidth, scriptWidth);
 			Ratio scriptToCelY(celObj._scaledHeight, scriptHeight);
@@ -616,6 +622,8 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const {
 		mulinc(nsRect, celToScriptX, celToScriptY);
 		nsRect.translate(_position.x - displaceX, _position.y - displaceY);
 	} else {
+		// low resolution coordinates
+
 		if (!scaleX.isOne() || !scaleY.isOne()) {
 			mulinc(nsRect, scaleX, scaleY);
 			// TODO: This was in the original code, baked into the


Commit: a785147d6c4bf9a9468a60643dd0f8e9f4847400
    https://github.com/scummvm/scummvm/commit/a785147d6c4bf9a9468a60643dd0f8e9f4847400
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-06-30T14:04:57-05:00

Commit Message:
SCI32: Document & clean up extra rect argument for frameOut

This extra rect seems to probably only ever be used by VMD
playback in some SCI2.1 games.

Changed paths:
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/frameout.h



diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index cb77730..4ec91dc 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -492,7 +492,7 @@ void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pi
 #pragma mark -
 #pragma mark Rendering
 
-void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) {
+void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) {
 // TODO: Robot
 //	if (_robot != nullptr) {
 //		_robot.doRobot();
@@ -510,7 +510,7 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect)
 		remapMarkRedraw();
 	}
 
-	calcLists(screenItemLists, eraseLists, rect);
+	calcLists(screenItemLists, eraseLists, eraseRect);
 
 	for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
 		list->sort();
@@ -597,14 +597,17 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL
 	RectList rectlist;
 	Common::Rect outRects[4];
 
+// 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) {
 	int deletedPlaneCount = 0;
 	bool addedToRectList = false;
 	int planeCount = _planes.size();
 	bool foundTransparentPlane = false;
 
-	if (!calcRect.isEmpty()) {
-		addedToRectList = true;
-		rectlist.add(calcRect);
+	if (!eraseRect.isEmpty()) {
+		addedToEraseList = true;
+		rectlist.add(eraseRect);
 	}
 
 	for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) {
@@ -885,8 +888,6 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
 	_showList.add(rect);
 	showBits();
 
-	Common::Rect calcRect(0, 0);
-
 	// NOTE: The original engine allocated these as static arrays of 100
 	// pointers to ScreenItemList / RectList
 	ScreenItemListList screenItemLists;
@@ -899,7 +900,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
 		remapMarkRedraw();
 	}
 
-	calcLists(screenItemLists, eraseLists, calcRect);
+	calcLists(screenItemLists, eraseLists);
 	for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
 		list->sort();
 	}
@@ -959,7 +960,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
 		remapMarkRedraw();
 	}
 
-	calcLists(screenItemLists, eraseLists, calcRect);
+	calcLists(screenItemLists, eraseLists);
 	for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
 		list->sort();
 	}
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 0be5aec..e736872 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -376,8 +376,10 @@ private:
 	 * over the entire screen for rendering the next frame.
 	 * The draw and erase lists in `drawLists` and
 	 * `eraseLists` each represent one plane on the screen.
+	 * The optional `eraseRect` argument allows a specific
+	 * area of the screen to be erased.
 	 */
-	void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect);
+	void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect = Common::Rect());
 
 	/**
 	 * Erases the areas in the given erase list from the
@@ -430,9 +432,10 @@ public:
 	/**
 	 * Updates the internal screen buffer for the next
 	 * frame. If `shouldShowBits` is true, also sends the
-	 * buffer to hardware.
+	 * buffer to hardware. If `eraseRect` is non-empty,
+	 * it is added to the erase list for this frame.
 	 */
-	void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect());
+	void frameOut(const bool shouldShowBits, const Common::Rect &eraseRect = Common::Rect());
 
 	/**
 	 * Modifies the raw pixel data for the next frame with


Commit: 0310b4dc4df8de96f05f63f751e3d915800f1a0c
    https://github.com/scummvm/scummvm/commit/0310b4dc4df8de96f05f63f751e3d915800f1a0c
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-06-30T14:04:57-05:00

Commit Message:
SCI32: Implement engine-accurate screen item list sorting

It seems highly probable that there are later SCI games that use
the "hi res" rendering path, so sorting and unsorting of
ScreenItemLists needs to be accurate.

Changed paths:
    engines/sci/graphics/screen_item32.cpp
    engines/sci/graphics/screen_item32.h



diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index d2673d4..c1644a5 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -657,23 +657,43 @@ ScreenItem *ScreenItemList::findByObject(const reg_t object) const {
 	return *screenItemIt;
 }
 void ScreenItemList::sort() {
-	// TODO: SCI engine used _unsorted as an array of indexes into the
-	// list itself and then performed the same swap operations on the
-	// _unsorted array as the _storage array during sorting, but the
-	// only reason to do this would be if some of the pointers in the
-	// list were replaced so the pointer values themselves couldn’t
-	// simply be recorded and then restored later. It is not yet
-	// verified whether this simplification of the sort/unsort is
-	// safe.
+	if (size() < 2) {
+		return;
+	}
+
 	for (size_type i = 0; i < size(); ++i) {
-		_unsorted[i] = (*this)[i];
+		_unsorted[i] = i;
 	}
 
-	Common::sort(begin(), end(), sortHelper);
+	for (size_type i = size() - 1; i > 0; --i) {
+		bool swap = false;
+
+		for (size_type j = 0; j < i; ++j)  {
+			value_type &a = operator[](j);
+			value_type &b = operator[](j + 1);
+
+			if (a == nullptr || *a > *b) {
+				SWAP(a, b);
+				SWAP(_unsorted[j], _unsorted[j + 1]);
+				swap = true;
+			}
+		}
+
+		if (!swap) {
+			break;
+		}
+	}
 }
 void ScreenItemList::unsort() {
+	if (size() < 2) {
+		return;
+	}
+
 	for (size_type i = 0; i < size(); ++i) {
-		(*this)[i] = _unsorted[i];
+		while (_unsorted[i] != i) {
+			SWAP(operator[](_unsorted[i]), operator[](i));
+			SWAP(_unsorted[_unsorted[i]], _unsorted[i]);
+		}
 	}
 }
 
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index eef2f48..2e44e41 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -236,6 +236,24 @@ public:
 		return false;
 	}
 
+	inline bool operator>(const ScreenItem &other) const {
+		if (_priority > other._priority) {
+			return true;
+		}
+
+		if (_priority == other._priority) {
+			if (_position.y + _z > other._position.y + other._z) {
+				return true;
+			}
+
+			if (_position.y + _z == other._position.y + other._z) {
+				return _object > other._object;
+			}
+		}
+
+		return false;
+	}
+
 	/**
 	 * Calculates the dimensions and scaling parameters for
 	 * the screen item, using the given plane as the parent
@@ -279,12 +297,10 @@ public:
 
 typedef StablePointerArray<ScreenItem, 250> ScreenItemListBase;
 class ScreenItemList : public ScreenItemListBase {
-	inline static bool sortHelper(const ScreenItem *a, const ScreenItem *b) {
-		return *a < *b;
-	}
-public:
-	ScreenItem *_unsorted[250];
+private:
+	size_type _unsorted[250];
 
+public:
 	ScreenItem *findByObject(const reg_t object) const;
 	void sort();
 	void unsort();


Commit: e89bdf536d0c8f7e89840c7225032c79ddb4015f
    https://github.com/scummvm/scummvm/commit/e89bdf536d0c8f7e89840c7225032c79ddb4015f
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-06-30T14:04:57-05:00

Commit Message:
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.

Changed paths:
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/plane32.cpp
    engines/sci/graphics/plane32.h



diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 4ec91dc..62a77da 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 3f0b035..021ac52 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 c93fb5b..20cff40 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


Commit: 4ee1901706e6e92d8c27f3acb9c4be75eaca5a3f
    https://github.com/scummvm/scummvm/commit/4ee1901706e6e92d8c27f3acb9c4be75eaca5a3f
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-06-30T14:04:57-05:00

Commit Message:
SCI32: Add transparent pic plane type

It is not clear if this is ever actually used by game scripts,
though.

Changed paths:
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/plane32.cpp
    engines/sci/graphics/plane32.h



diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 62a77da..fd37020 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -700,6 +700,8 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL
 		const Plane *outerPlane = _planes[outerPlaneIndex];
 		const Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object);
 
+		// NOTE: SSCI only ever checks for kPlaneTypeTransparent here, even
+		// though kPlaneTypeTransparentPicture is also a transparent plane
 		if (outerPlane->_type == kPlaneTypeTransparent) {
 			foundTransparentPlane = true;
 		}
@@ -890,6 +892,8 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL
 		}
 	}
 
+	// NOTE: SSCI only looks for kPlaneTypeTransparent, not
+	// kPlaneTypeTransparentPicture
 	if (foundTransparentPlane) {
 		for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
 			for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) {
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 021ac52..175875c 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -163,11 +163,15 @@ void Plane::printDebugInfo(Console *con) const {
 void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX) {
 
 	uint16 celCount = 1000;
+	bool transparent = true;
 	for (uint16 celNo = 0; celNo < celCount; ++celNo) {
 		CelObjPic *celObj = new CelObjPic(pictureId, celNo);
 		if (celCount == 1000) {
 			celCount = celObj->_celCount;
 		}
+		if (!celObj->_transparent) {
+			transparent = false;
+		}
 
 		ScreenItem *screenItem = new ScreenItem(_object, celObj->_info);
 		screenItem->_pictureId = pictureId;
@@ -184,6 +188,7 @@ void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *p
 		delete screenItem->_celObj;
 		screenItem->_celObj = celObj;
 	}
+	_type = transparent ? kPlaneTypeTransparentPicture : kPlaneTypePicture;
 }
 
 void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) {
@@ -196,7 +201,7 @@ void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position,
 void Plane::changePic() {
 	_pictureChanged = false;
 
-	if (_type != kPlaneTypePicture) {
+	if (_type != kPlaneTypePicture && _type != kPlaneTypeTransparentPicture) {
 		return;
 	}
 
@@ -245,7 +250,10 @@ void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList
 
 	for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
 		for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) {
-			if (planeList[j]->_type != kPlaneTypeTransparent) {
+			if (
+				planeList[j]->_type != kPlaneTypeTransparent &&
+				planeList[j]->_type != kPlaneTypeTransparentPicture
+			) {
 				Common::Rect outRects[4];
 				int splitCount = splitRects(drawList[i]->rect, planeList[j]->_screenRect, outRects);
 				if (splitCount != -1) {
@@ -268,7 +276,10 @@ void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeLi
 
 	for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
 		for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) {
-			if (planeList[j]->_type != kPlaneTypeTransparent) {
+			if (
+				planeList[j]->_type != kPlaneTypeTransparent &&
+				planeList[j]->_type != kPlaneTypeTransparentPicture
+			) {
 				Common::Rect outRects[4];
 				int splitCount = splitRects(*eraseList[i], planeList[j]->_screenRect, outRects);
 				if (splitCount != -1) {
@@ -555,7 +566,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force
 void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const {
 	const RectList::size_type higherEraseCount = higherEraseList.size();
 
-	if (_type == kPlaneTypeTransparent) {
+	if (_type == kPlaneTypeTransparent || _type == kPlaneTypeTransparentPicture) {
 		for (RectList::size_type i = 0; i < higherEraseCount; ++i) {
 			const Common::Rect &r = *higherEraseList[i];
 			const ScreenItemList::size_type screenItemCount = _screenItemList.size();
@@ -727,14 +738,24 @@ void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList
 }
 
 void Plane::setType() {
-	if (_pictureId == kPlanePicOpaque) {
-		_type = kPlaneTypeOpaque;
-	} else if (_pictureId == kPlanePicTransparent) {
-		_type = kPlaneTypeTransparent;
-	} else if (_pictureId == kPlanePicColored) {
+	switch (_pictureId) {
+	case kPlanePicColored:
 		_type = kPlaneTypeColored;
-	} else {
-		_type = kPlaneTypePicture;
+		break;
+	case kPlanePicTransparent:
+		_type = kPlaneTypeTransparent;
+		break;
+	case kPlanePicOpaque:
+		_type = kPlaneTypeOpaque;
+		break;
+	case kPlanePicTransparentPicture:
+		_type = kPlaneTypeTransparentPicture;
+		break;
+	default:
+		if (_type != kPlaneTypeTransparentPicture) {
+			_type = kPlaneTypePicture;
+		}
+		break;
 	}
 }
 
diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h
index 20cff40..acd535e 100644
--- a/engines/sci/graphics/plane32.h
+++ b/engines/sci/graphics/plane32.h
@@ -32,19 +32,21 @@
 
 namespace Sci {
 enum PlaneType {
-	kPlaneTypeColored     = 0,
-	kPlaneTypePicture     = 1,
-	kPlaneTypeTransparent = 2,
-	kPlaneTypeOpaque      = 3
+	kPlaneTypeColored            = 0,
+	kPlaneTypePicture            = 1,
+	kPlaneTypeTransparent        = 2,
+	kPlaneTypeOpaque             = 3,
+	kPlaneTypeTransparentPicture = 4
 };
 
 enum PlanePictureCodes {
-	// NOTE: Any value at or below 65532 means the plane
+	// NOTE: Any value at or below 65531 means the plane
 	// is a kPlaneTypePicture.
-	kPlanePic            = 65532,
-	kPlanePicOpaque      = 65533,
-	kPlanePicTransparent = 65534,
-	kPlanePicColored     = 65535
+	kPlanePic                   = 65531,
+	kPlanePicTransparentPicture = 65532,
+	kPlanePicOpaque             = 65533,
+	kPlanePicTransparent        = 65534,
+	kPlanePicColored            = 65535
 };
 
 #pragma mark -






More information about the Scummvm-git-logs mailing list