[Scummvm-cvs-logs] scummvm master -> 266a9fdd25c2320684124f6b9f37e698960a6176

csnover csnover at users.noreply.github.com
Mon Mar 7 04:37:04 CET 2016


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

Summary:
1337cd3dec SCI32: Implement kEditText
e8d6ad1d0b SCI32: Fix missing digits in plane item list debug output
3e631ca5e3 SCI32: "Improve" comparison algorithm for planes and screen items
36800b7017 SCI32: Fix memory leaks
56806161a7 SCI32: Make PlaneList definition order match declaration order
266a9fdd25 SCI32: Implement variable size frame drawing


Commit: 1337cd3dec86d64476bc4248e8368848d70b56e5
    https://github.com/scummvm/scummvm/commit/1337cd3dec86d64476bc4248e8368848d70b56e5
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:43-06:00

Commit Message:
SCI32: Implement kEditText

Changed paths:
    engines/sci/engine/kgraphics32.cpp
    engines/sci/engine/selector.cpp
    engines/sci/engine/selector.h
    engines/sci/event.cpp
    engines/sci/event.h
    engines/sci/graphics/controls32.cpp
    engines/sci/graphics/controls32.h
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/frameout.h
    engines/sci/graphics/plane32.cpp
    engines/sci/graphics/plane32.h
    engines/sci/graphics/screen_item32.cpp
    engines/sci/graphics/screen_item32.h
    engines/sci/graphics/text32.cpp
    engines/sci/graphics/text32.h
    engines/sci/sci.cpp



diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 0463b12..8d8b961 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -670,14 +670,9 @@ reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) {
 // but it handles events on its own, using an internal loop, instead of using SCI
 // scripts for event management like kEditControl does. Called by script 64914,
 // DEdit::hilite().
-reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
-	reg_t controlObject = argv[0];
-
-	if (!controlObject.isNull()) {
-		g_sci->_gfxControls32->kernelTexteditChange(controlObject);
-	}
 
-	return s->r_acc;
+reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
+	return g_sci->_gfxControls32->kernelEditText(argv[0]);
 }
 
 reg_t kAddLine(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 1393e96..ac621f5 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -180,6 +180,7 @@ void Kernel::mapSelectors() {
 	FIND_SELECTOR(back);
 	FIND_SELECTOR(skip);
 	FIND_SELECTOR(borderColor);
+	FIND_SELECTOR(width);
 	FIND_SELECTOR(fixPriority);
 	FIND_SELECTOR(mirrored);
 	FIND_SELECTOR(visible);
@@ -192,7 +193,12 @@ void Kernel::mapSelectors() {
 	FIND_SELECTOR(textLeft);
 	FIND_SELECTOR(textBottom);
 	FIND_SELECTOR(textRight);
+	FIND_SELECTOR(title);
+	FIND_SELECTOR(titleFont);
+	FIND_SELECTOR(titleFore);
+	FIND_SELECTOR(titleBack);
 	FIND_SELECTOR(magnifier);
+	FIND_SELECTOR(frameOut);
 	FIND_SELECTOR(casts);
 #endif
 }
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index a8bbbe7..f2d06d1 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -147,6 +147,7 @@ struct SelectorCache {
 	Selector skip;
 	Selector dimmed;
 	Selector borderColor;
+	Selector width;
 
 	Selector fixPriority;
 	Selector mirrored;
@@ -155,9 +156,10 @@ struct SelectorCache {
 	Selector useInsetRect;
 	Selector inTop, inLeft, inBottom, inRight;
 	Selector textTop, textLeft, textBottom, textRight;
+	Selector title, titleFont, titleFore, titleBack;
 
 	Selector magnifier;
-
+	Selector frameOut;
 	Selector casts; // needed for sync'ing screen items/planes with scripts, when our save/restore code is patched in (see GfxFrameout::syncWithScripts)
 #endif
 };
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 3322a27..34f1618 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -29,6 +29,9 @@
 #include "sci/console.h"
 #include "sci/engine/state.h"
 #include "sci/engine/kernel.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
 #include "sci/graphics/screen.h"
 
 namespace Sci {
@@ -133,8 +136,13 @@ static int altify(int ch) {
 }
 
 SciEvent EventManager::getScummVMEvent() {
-	SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
-	SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
+#ifdef ENABLE_SCI32
+	SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+	SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+#else
+	SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+	SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+#endif
 
 	Common::EventManager *em = g_system->getEventManager();
 	Common::Event ev;
@@ -155,7 +163,20 @@ SciEvent EventManager::getScummVMEvent() {
 	// via pollEvent.
 	// We also adjust the position based on the scaling of the screen.
 	Common::Point mousePos = em->getMousePos();
-	g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
+
+#if ENABLE_SCI32
+	if (getSciVersion() >= SCI_VERSION_2) {
+		Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer();
+
+		Common::Point mousePosSci = mousePos;
+		mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight));
+		noEvent.mousePosSci = input.mousePosSci = mousePosSci;
+	} else {
+#endif
+		g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
+#if ENABLE_SCI32
+	}
+#endif
 
 	noEvent.mousePos = input.mousePos = mousePos;
 
@@ -302,6 +323,11 @@ SciEvent EventManager::getScummVMEvent() {
 		input.character = altify(input.character);
 	if (getSciVersion() <= SCI_VERSION_1_MIDDLE && (scummVMKeyFlags & Common::KBD_CTRL) && input.character > 0 && input.character < 27)
 		input.character += 96; // 0x01 -> 'a'
+#ifdef ENABLE_SCI32
+	if (getSciVersion() >= SCI_VERSION_2 && (scummVMKeyFlags & Common::KBD_CTRL) && input.character == 'c') {
+		input.character = SCI_KEY_ETX;
+	}
+#endif
 
 	// If no actual key was pressed (e.g. if only a modifier key was pressed),
 	// ignore the event
@@ -329,7 +355,11 @@ void EventManager::updateScreen() {
 }
 
 SciEvent EventManager::getSciEvent(uint32 mask) {
-	SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
+#ifdef ENABLE_SCI32
+	SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+#else
+	SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+#endif
 
 	EventManager::updateScreen();
 
diff --git a/engines/sci/event.h b/engines/sci/event.h
index 0d0c550..15a94b3 100644
--- a/engines/sci/event.h
+++ b/engines/sci/event.h
@@ -39,11 +39,18 @@ struct SciEvent {
 	uint16 character;
 
 	/**
-	 * The mouse position at the time the event was created.
-	 *
-	 * These are display coordinates!
+	 * The mouse position at the time the event was created,
+	 * in display coordinates.
 	 */
 	Common::Point mousePos;
+
+#ifdef ENABLE_SCI32
+	/**
+	 * The mouse position at the time the event was created,
+	 * in script coordinates.
+	 */
+	Common::Point mousePosSci;
+#endif
 };
 
 /*Values for type*/
@@ -59,6 +66,9 @@ struct SciEvent {
 #define SCI_EVENT_ANY             0x7fff
 
 /* Keycodes of special keys: */
+#ifdef ENABLE_SCI32
+#define SCI_KEY_ETX           3
+#endif
 #define SCI_KEY_ESC          27
 #define SCI_KEY_BACKSPACE     8
 #define SCI_KEY_ENTER        13
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index df51888..fc028f7 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -23,9 +23,11 @@
 #include "common/system.h"
 
 #include "sci/sci.h"
+#include "sci/console.h"
 #include "sci/event.h"
 #include "sci/engine/kernel.h"
 #include "sci/engine/seg_manager.h"
+#include "sci/engine/state.h"
 #include "sci/graphics/cache.h"
 #include "sci/graphics/compare.h"
 #include "sci/graphics/controls32.h"
@@ -34,174 +36,323 @@
 #include "sci/graphics/text32.h"
 
 namespace Sci {
+GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) :
+	_segMan(segMan),
+	_gfxCache(cache),
+	_gfxText32(text),
+	_overwriteMode(false),
+	_nextCursorFlashTick(0) {}
 
-GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text)
-	: _segMan(segMan), _cache(cache), _text(text) {
-}
+GfxControls32::~GfxControls32() {}
 
-GfxControls32::~GfxControls32() {
-}
+reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
+	SegManager *segMan = _segMan;
 
-void GfxControls32::kernelTexteditChange(reg_t controlObject) {
-	SciEvent curEvent;
-	uint16 maxChars = 40;	//readSelectorValue(_segMan, controlObject, SELECTOR(max));	// TODO
-	reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
-	GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font)));
-	Common::String text;
-	uint16 textSize;
-	bool textChanged = false;
-	bool textAddChar = false;
-	Common::Rect rect;
+	TextEditor editor;
+	reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text));
+	editor.text = _segMan->getString(textObject);
+	editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore));
+	editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back));
+	editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip));
+	editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
+	editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width));
+	editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap));
+	editor.cursorCharPosition = 0;
+	editor.cursorIsDrawn = false;
+	editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor));
 
-	if (textReference.isNull())
-		error("kEditControl called on object that doesn't have a text reference");
-	text = _segMan->getString(textReference);
+	reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title));
+
+	int16 titleHeight = 0;
+	GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont));
+	if (!titleObject.isNull()) {
+		GfxFont *titleFont = _gfxCache->getFont(titleFontId);
+		titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1;
+		if (editor.borderColor != -1) {
+			titleHeight += 2;
+		}
+	}
 
-	// TODO: Finish this
-	warning("kEditText ('%s')", text.c_str());
-	return;
+	int16 width = 0;
+	int16 height = titleHeight;
 
-	uint16 cursorPos = 0;
-	//uint16 oldCursorPos = cursorPos;
-	bool captureEvents = true;
-	EventManager* eventMan = g_sci->getEventManager();
+	GfxFont *editorFont = _gfxCache->getFont(editor.fontId);
+	height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1;
+	_gfxText32->setFont(editor.fontId);
+	int16 emSize = _gfxText32->getCharWidth('M', true);
+	width += editor.maxLength * emSize + 1;
+	if (editor.borderColor != -1) {
+		width += 4;
+		height += 2;
+	}
 
-	while (captureEvents) {
-		curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
+	Common::Rect editorPlaneRect(width, height);
+	editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y)));
 
-		if (curEvent.type == SCI_EVENT_NONE) {
-			eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
+	reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane));
+	Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+	if (sourcePlane == nullptr) {
+		error("Could not find plane %04x:%04x", PRINT_REG(planeObj));
+	}
+	editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top);
+
+	editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1);
+	editor.width = width;
+
+	if (editor.bitmap.isNull()) {
+		reg_t out;
+		TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode));
+
+		if (titleObject.isNull()) {
+			bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed));
+			editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true, &out);
 		} else {
-			textSize = text.size();
+			Common::String title = _segMan->getString(titleObject);
+			int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack));
+			int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore));
+			editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true, &out);
+		}
+	}
 
-			switch (curEvent.type) {
-			case SCI_EVENT_MOUSE_PRESS:
-				// TODO: Implement mouse support for cursor change
+	drawCursor(editor);
+
+	Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent);
+	plane->changePic();
+	g_sci->_gfxFrameout->addPlane(*plane);
+
+	CelInfo32 celInfo;
+	celInfo.type = kCelTypeMem;
+	celInfo.bitmap = editor.bitmap;
+
+	ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo());
+	plane->_screenItemList.add(screenItem);
+
+	// frameOut must be called after the screen item is
+	// created, and before it is updated at the end of the
+	// event loop, otherwise it has both created and updated
+	// flags set which crashes the engine (it runs updates
+	// before creations)
+	g_sci->_gfxFrameout->frameOut(true);
+
+	EventManager *eventManager = g_sci->getEventManager();
+	bool clearTextOnInput = true;
+	bool textChanged = false;
+	for (;;) {
+		// We peek here because the last event needs to be allowed to
+		// dispatch a second time to the normal event handling system.
+		// In the actual engine, the event is always consumed and then
+		// the last event just gets posted back to the event manager for
+		// reprocessing, but instead, we only remove the event from the
+		// queue *after* we have determined it is not a defocusing event
+		const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK);
+
+		bool focused = true;
+		// Original engine did not have a QUIT event but we have to handle it
+		if (event.type == SCI_EVENT_QUIT) {
+			return NULL_REG;
+		} else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) {
+			focused = false;
+		} else if (event.type == SCI_EVENT_KEYBOARD) {
+			switch (event.character) {
+			case SCI_KEY_ESC:
+			case SCI_KEY_UP:
+			case SCI_KEY_DOWN:
+			case SCI_KEY_TAB:
+			case SCI_KEY_SHIFT_TAB:
+			case SCI_KEY_ENTER:
+				focused = false;
 				break;
-			case SCI_EVENT_KEYBOARD:
-				switch (curEvent.character) {
-				case SCI_KEY_BACKSPACE:
-					if (cursorPos > 0) {
-						cursorPos--; text.deleteChar(cursorPos);
-						textChanged = true;
-					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_DELETE:
-					if (cursorPos < textSize) {
-						text.deleteChar(cursorPos);
-						textChanged = true;
-					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_HOME: // HOME
-					cursorPos = 0; textChanged = true;
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_END: // END
-					cursorPos = textSize; textChanged = true;
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_LEFT: // LEFT
-					if (cursorPos > 0) {
-						cursorPos--; textChanged = true;
-					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_RIGHT: // RIGHT
-					if (cursorPos + 1 <= textSize) {
-						cursorPos++; textChanged = true;
-					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case 3:	// returned in SCI1 late and newer when Control - C is pressed
-					if (curEvent.modifiers & SCI_KEYMOD_CTRL) {
-						// Control-C erases the whole line
-						cursorPos = 0; text.clear();
-						textChanged = true;
+			}
+		}
+
+		if (!focused) {
+			break;
+		}
+
+		// Consume the event now that we know it is not one of the
+		// defocusing events above
+		eventManager->getSciEvent(SCI_EVENT_ANY);
+
+		// NOTE: In the original engine, the font and bitmap were
+		// reset here on each iteration through the loop, but it
+		// doesn't seem like this should be necessary since
+		// control is not yielded back to the VM until input is
+		// received, which means there is nothing that could modify
+		// the GfxText32's state with a different font in the
+		// meantime
+
+		bool shouldDeleteChar = false;
+		bool shouldRedrawText = false;
+		uint16 lastCursorPosition = editor.cursorCharPosition;
+ 		if (event.type == SCI_EVENT_KEYBOARD) {
+			switch (event.character) {
+			case SCI_KEY_LEFT:
+				clearTextOnInput = false;
+				if (editor.cursorCharPosition > 0) {
+					--editor.cursorCharPosition;
+				}
+				break;
+
+			case SCI_KEY_RIGHT:
+				clearTextOnInput = false;
+				if (editor.cursorCharPosition < editor.text.size()) {
+					++editor.cursorCharPosition;
+				}
+				break;
+
+			case SCI_KEY_HOME:
+				clearTextOnInput = false;
+				editor.cursorCharPosition = 0;
+				break;
+
+			case SCI_KEY_END:
+				clearTextOnInput = false;
+				editor.cursorCharPosition = editor.text.size();
+				break;
+
+			case SCI_KEY_INSERT:
+				clearTextOnInput = false;
+				// Redrawing also changes the cursor rect to
+				// reflect the new insertion mode
+				shouldRedrawText = true;
+				_overwriteMode = !_overwriteMode;
+				break;
+
+			case SCI_KEY_DELETE:
+				clearTextOnInput = false;
+				if (editor.cursorCharPosition < editor.text.size()) {
+					shouldDeleteChar = true;
+				}
+				break;
+
+			case SCI_KEY_BACKSPACE:
+				clearTextOnInput = false;
+				shouldDeleteChar = true;
+				if (editor.cursorCharPosition > 0) {
+					--editor.cursorCharPosition;
+				}
+				break;
+
+			case SCI_KEY_ETX:
+				editor.text.clear();
+				editor.cursorCharPosition = 0;
+				shouldRedrawText = true;
+				break;
+
+			default: {
+				if (event.character >= 20 && event.character < 257) {
+					if (clearTextOnInput) {
+						clearTextOnInput = false;
+						editor.text.clear();
 					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
-				case SCI_KEY_UP:
-				case SCI_KEY_DOWN:
-				case SCI_KEY_ENTER:
-				case SCI_KEY_ESC:
-				case SCI_KEY_TAB:
-				case SCI_KEY_SHIFT_TAB:
-					captureEvents = false;
-					break;
-				default:
-					if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') {
-						// Control-C in earlier SCI games (SCI0 - SCI1 middle)
-						// Control-C erases the whole line
-						cursorPos = 0; text.clear();
-						textChanged = true;
-					} else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) {
-						// insert pressed character
-						textAddChar = true;
-						textChanged = true;
+
+					if (
+						(_overwriteMode && editor.cursorCharPosition < editor.maxLength) ||
+						(editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width())
+					) {
+						if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) {
+							editor.text.setChar(event.character, editor.cursorCharPosition);
+						} else {
+							editor.text.insertChar(event.character, editor.cursorCharPosition);
+						}
+
+						++editor.cursorCharPosition;
+						shouldRedrawText = true;
 					}
-					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
-					break;
 				}
-				break;
+			}
 			}
 		}
 
-		if (textChanged) {
-			rect = g_sci->_gfxCompare->getNSRect(controlObject);
+		if (shouldDeleteChar) {
+			shouldRedrawText = true;
+			if (editor.cursorCharPosition < editor.text.size()) {
+				editor.text.deleteChar(editor.cursorCharPosition);
+			}
+		}
 
-			if (textAddChar) {
-				const char *textPtr = text.c_str();
+		if (shouldRedrawText) {
+			eraseCursor(editor);
+			_gfxText32->erase(editor.textRect, true);
+			_gfxText32->drawTextBox(editor.text);
+			drawCursor(editor);
+			textChanged = true;
+			screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+		} else if (editor.cursorCharPosition != lastCursorPosition) {
+			eraseCursor(editor);
+			drawCursor(editor);
+			screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+		} else {
+			flashCursor(editor);
+			screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+		}
 
-				// We check if we are really able to add the new char
-				uint16 textWidth = 0;
-				while (*textPtr)
-					textWidth += font->getCharWidth((byte)*textPtr++);
-				textWidth += font->getCharWidth(curEvent.character);
+		g_sci->_gfxFrameout->frameOut(true);
+		g_sci->getSciDebugger()->onFrame();
+		g_sci->getEngineState()->speedThrottler(16);
+		g_sci->getEngineState()->_throttleTrigger = true;
+	}
 
-				// Does it fit?
-				if (textWidth >= rect.width()) {
-					return;
-				}
+	g_sci->_gfxFrameout->deletePlane(*plane);
+	if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) {
+		g_sci->_gfxFrameout->frameOut(true);
+	}
 
-				text.insertChar(curEvent.character, cursorPos++);
+	_segMan->freeHunkEntry(editor.bitmap);
 
-				// Note: the following checkAltInput call might make the text
-				// too wide to fit, but SSCI fails to check that too.
-			}
+	if (textChanged) {
+		editor.text.trim();
+		SciString *string = _segMan->lookupString(textObject);
+		string->fromString(editor.text);
+	}
 
-			reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
-			Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
-			//texteditCursorErase();	// TODO: Cursor
-
-			// Write back string
-			_segMan->strcpy(textReference, text.c_str());
-			// Modify the buffer and show it
-			warning("kernelTexteditChange");
-#if 0
-			_text->createTextBitmap(controlObject, 0, 0, hunkId);
-
-			_text->drawTextBitmap(0, 0, nsRect, controlObject);
-			//texteditCursorDraw(rect, text.c_str(), cursorPos);	// TODO: Cursor
-			g_system->updateScreen();
-#endif
+	return make_reg(0, textChanged);
+}
+
+void GfxControls32::drawCursor(TextEditor &editor) {
+	if (!editor.cursorIsDrawn) {
+		editor.cursorRect.left = editor.textRect.left + _gfxText32->getTextWidth(editor.text, 0, editor.cursorCharPosition);
+
+		const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight());
+
+		// NOTE: The original code branched on borderColor here but
+		// the two branches appeared to be identical, differing only
+		// because the compiler decided to be differently clever
+		// when optimising multiplication in each branch
+		if (_overwriteMode) {
+			editor.cursorRect.top = editor.textRect.top;
+			editor.cursorRect.setHeight(scaledFontHeight);
 		} else {
-			// TODO: Cursor
-			/*
-			if (g_system->getMillis() >= _texteditBlinkTime) {
-				_paint16->invertRect(_texteditCursorRect);
-				_paint16->bitsShow(_texteditCursorRect);
-				_texteditCursorVisible = !_texteditCursorVisible;
-				texteditSetBlinkTime();
-			}
-			*/
+			editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1;
+			editor.cursorRect.setHeight(1);
 		}
 
-		textAddChar = false;
-		textChanged = false;
-		g_sci->sleep(10);
-	}	// while
+		const char currentChar = editor.cursorCharPosition < editor.text.size() ? editor.text[editor.cursorCharPosition] : ' ';
+		editor.cursorRect.setWidth(_gfxText32->getCharWidth(currentChar, true));
+
+		_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+
+		editor.cursorIsDrawn = true;
+	}
+
+	_nextCursorFlashTick = g_sci->getTickCount() + 30;
+}
+
+void GfxControls32::eraseCursor(TextEditor &editor) {
+	if (editor.cursorIsDrawn) {
+		_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+		editor.cursorIsDrawn = false;
+	}
+
+	_nextCursorFlashTick = g_sci->getTickCount() + 30;
 }
 
+void GfxControls32::flashCursor(TextEditor &editor) {
+	if (g_sci->getTickCount() > _nextCursorFlashTick) {
+		_gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+
+		editor.cursorIsDrawn = !editor.cursorIsDrawn;
+		_nextCursorFlashTick = g_sci->getTickCount() + 30;
+	}
+}
 } // End of namespace Sci
diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h
index 5af7c20..0dc3b9c 100644
--- a/engines/sci/graphics/controls32.h
+++ b/engines/sci/graphics/controls32.h
@@ -29,6 +29,76 @@ class GfxCache;
 class GfxScreen;
 class GfxText32;
 
+struct TextEditor {
+	/**
+	 * The bitmap where the editor is rendered.
+	 */
+	reg_t bitmap;
+
+	/**
+	 * The width of the editor, in bitmap pixels.
+	 */
+	int16 width;
+
+	/**
+	 * The text in the editor.
+	 */
+	Common::String text;
+
+	/**
+	 * The rect where text should be drawn into the editor,
+	 * in bitmap pixels.
+	 */
+	Common::Rect textRect;
+
+	/**
+	 * The color of the border. -1 indicates no border.
+	 */
+	int16 borderColor;
+
+	/**
+	 * The text color.
+	 */
+	uint8 foreColor;
+
+	/**
+	 * The background color.
+	 */
+	uint8 backColor;
+
+	/**
+	 * The transparent color.
+	 */
+	uint8 skipColor;
+
+	/**
+	 * The font used to render the text in the editor.
+	 */
+	GuiResourceId fontId;
+
+	/**
+	 * The current position of the cursor within the editor.
+	 */
+	uint16 cursorCharPosition;
+
+	/**
+	 * Whether or not the cursor is currently drawn to the
+	 * screen.
+	 */
+	bool cursorIsDrawn;
+
+	/**
+	 * The rectangle for drawing the input cursor, in bitmap
+	 * pixels.
+	 */
+	Common::Rect cursorRect;
+
+	/**
+	 * The maximum allowed text length, in characters.
+	 */
+	uint16 maxLength;
+};
+
 /**
  * Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games
  */
@@ -37,12 +107,18 @@ public:
 	GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text);
 	~GfxControls32();
 
-	void kernelTexteditChange(reg_t controlObject);
+	reg_t kernelEditText(const reg_t controlObject);
 
 private:
 	SegManager *_segMan;
-	GfxCache *_cache;
-	GfxText32 *_text;
+	GfxCache *_gfxCache;
+	GfxText32 *_gfxText32;
+
+	bool _overwriteMode;
+	uint32 _nextCursorFlashTick;
+	void drawCursor(TextEditor &editor);
+	void eraseCursor(TextEditor &editor);
+	void flashCursor(TextEditor &editor);
 };
 
 } // End of namespace Sci
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index ab2ed7a..55f5b36 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -365,6 +365,21 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) {
 	}
 }
 
+void GfxFrameout::deletePlane(Plane &planeToFind) {
+	Plane *plane = _planes.findByObject(planeToFind._object);
+	if (plane == nullptr) {
+		error("Invalid plane passed to deletePlane");
+	}
+
+	if (plane->_created) {
+		_planes.erase(plane);
+	} else {
+		plane->_created = 0;
+		plane->_moved = 0;
+		plane->_deleted = getScreenCount();
+	}
+}
+
 int16 GfxFrameout::kernelGetHighPlanePri() {
 	return _planes.getTopSciPlanePriority();
 }
@@ -765,7 +780,7 @@ void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) {
 		mergeToShowList(drawItem.rect, _showList, _overdrawThreshold);
 		ScreenItem &screenItem = *drawItem.screenItem;
 		// TODO: Remove
-//		debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), drawItem.rect.left, drawItem.rect.top, drawItem.rect.right, drawItem.rect.bottom);
+//		debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect));
 		CelObj &celObj = *screenItem._celObj;
 		celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX);
 	}
@@ -1010,8 +1025,6 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co
 	// NOTE: This is currBuffer->ptr in SCI engine
 	byte *pixels = (byte *)_currentBuffer.getPixels();
 
-	// TODO: Guessing that display width/height is the correct
-	// equivalent to screen width/height in SCI engine
 	for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) {
 		byte currentValue = pixels[pixelIndex];
 		int8 styleRangeValue = styleRanges[currentValue];
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index f172997..738011a 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -259,6 +259,13 @@ private:
 	PlaneList _planes;
 
 	/**
+	 * Updates an existing plane with properties from the
+	 * given VM object.
+	 */
+	void updatePlane(Plane &plane);
+
+public:
+	/**
 	 * Creates and adds a new plane to the plane list, or
 	 * cancels deletion and updates an already-existing
 	 * plane if a plane matching the given plane VM object
@@ -270,15 +277,19 @@ private:
 	void addPlane(Plane &plane);
 
 	/**
-	 * Updates an existing plane with properties from the
-	 * given VM object.
+	 * Deletes a plane within the current plane list.
+	 *
+	 * @note This method is on Screen in SCI engine, but it
+	 * is only ever called on `GraphicsMgr.screen`.
 	 */
-	void updatePlane(Plane &plane);
+	void deletePlane(Plane &plane);
 
-public:
 	const PlaneList &getPlanes() const {
 		return _planes;
 	}
+	const PlaneList &getVisiblePlanes() const {
+		return _visiblePlanes;
+	}
 	void kernelAddPlane(const reg_t object);
 	void kernelUpdatePlane(const reg_t object);
 	void kernelDeletePlane(const reg_t object);
@@ -423,13 +434,6 @@ private:
 	void drawScreenItemList(const DrawList &screenItemList);
 
 	/**
-	 * Updates the internal screen buffer for the next
-	 * frame. If `shouldShowBits` is true, also sends the
-	 * buffer to hardware.
-	 */
-	void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect());
-
-	/**
 	 * Adds a new rectangle to the list of regions to write
 	 * out to the hardware. The provided rect may be merged
 	 * into an existing rectangle to reduce the number of
@@ -469,6 +473,13 @@ public:
 	void kernelFrameOut(const bool showBits);
 
 	/**
+	 * Updates the internal screen buffer for the next
+	 * frame. If `shouldShowBits` is true, also sends the
+	 * buffer to hardware.
+	 */
+	void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect());
+
+	/**
 	 * Modifies the raw pixel data for the next frame with
 	 * new palette indexes based on matched style ranges.
 	 */
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index e23017f..7d1487b 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -43,10 +43,10 @@ void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) {
 #pragma mark Plane
 uint16 Plane::_nextObjectId = 20000;
 
-Plane::Plane(const Common::Rect &gameRect) :
+Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) :
 _width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
 _height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
-_pictureId(kPlanePicColored),
+_pictureId(pictureId),
 _mirrored(false),
 _back(0),
 _priorityChanged(0),
diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h
index be6f714..65df19d 100644
--- a/engines/sci/graphics/plane32.h
+++ b/engines/sci/graphics/plane32.h
@@ -133,7 +133,7 @@ private:
 	 * synchronised to another plane (which calls
 	 * changePic).
 	 */
-	bool _pictureChanged; // ?
+	bool _pictureChanged;
 
 	// TODO: Are these ever actually used?
 	int _field_34, _field_38; // probably a point or ratio
@@ -241,10 +241,18 @@ public:
 	 */
 	static void init();
 
-	Plane(const Common::Rect &gameRect);
+	// NOTE: This constructor signature originally did not accept a
+	// picture ID, but some calls to construct planes with this signature
+	// immediately set the picture ID and then called setType again, so
+	// it made more sense to just make the picture ID a parameter instead.
+	Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored);
+
 	Plane(const reg_t object);
+
 	Plane(const Plane &other);
+
 	void operator=(const Plane &other);
+
 	inline bool operator<(const Plane &other) const {
 		// TODO: In SCI engine, _object is actually a uint16 and can either
 		// contain a MemID (a handle to MemoryMgr, similar to reg_t) or
@@ -318,12 +326,6 @@ private:
 	inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX);
 
 	/**
-	 * If the plane is a picture plane, re-adds all cels
-	 * from its picture resource to the plane.
-	 */
-	void changePic();
-
-	/**
 	 * Marks all screen items to be deleted that are within
 	 * this plane and match the given picture ID.
 	 */
@@ -352,6 +354,13 @@ public:
 	 */
 	void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX);
 
+	/**
+	 * If the plane is a picture plane, re-adds all cels
+	 * from its picture resource to the plane. Otherwise,
+	 * just clears the _pictureChanged flag.
+	 */
+	void changePic();
+
 #pragma mark -
 #pragma mark Plane - Rendering
 private:
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index a07dace..c8c9cbe 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -83,7 +83,7 @@ _mirrorX(false) {
 	}
 }
 
-ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo) :
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo) :
 _plane(plane),
 _scale(scaleInfo),
 _useInsetRect(false),
@@ -91,7 +91,7 @@ _z(0),
 _celInfo(celInfo),
 _celObj(nullptr),
 _fixPriority(false),
-_position(rect.left, rect.top),
+_position(position),
 _object(make_reg(0, _nextObjectId++)),
 _pictureId(-1),
 _created(g_sci->_gfxFrameout->getScreenCount()),
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index 0ca840a..e054cf3 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -213,7 +213,7 @@ public:
 	ScreenItem(const reg_t screenItem);
 	ScreenItem(const reg_t plane, const CelInfo32 &celInfo);
 	ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect);
-	ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo);
+	ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo);
 	ScreenItem(const ScreenItem &other);
 	void operator=(const ScreenItem &);
 
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index 8e3222f..c1335b9 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -40,10 +40,9 @@ namespace Sci {
 
 int16 GfxText32::_defaultFontId = 0;
 
-GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) :
+GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) :
 	_segMan(segMan),
 	_cache(fonts),
-	_screen(screen),
 	_scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
 	_scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
 	// Not a typo, the original engine did not initialise height, only width
@@ -179,6 +178,11 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &
 	return _bitmap;
 }
 
+reg_t GfxText32::createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling, reg_t *outBitmapObject) {
+	warning("TODO: createTitledBitmap incomplete !");
+	return createFontBitmap(width, height, textRect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, false, doScaling, outBitmapObject);
+}
+
 void GfxText32::setFont(const GuiResourceId fontId) {
 	// NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font
 	// table is built on the FontMgr directly; instead, because we already have
@@ -202,7 +206,7 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint
 	buffer.frameRect(targetRect, color);
 }
 
-void GfxText32::drawChar(const uint8 charIndex) {
+void GfxText32::drawChar(const char charIndex) {
 	byte *bitmap = _segMan->getHunkPointer(_bitmap);
 	byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
 
@@ -210,7 +214,7 @@ void GfxText32::drawChar(const uint8 charIndex) {
 	_drawPosition.x += _font->getCharWidth(charIndex);
 }
 
-uint16 GfxText32::getCharWidth(const uint8 charIndex, const bool doScaling) const {
+uint16 GfxText32::getCharWidth(const char charIndex, const bool doScaling) const {
 	uint16 width = _font->getCharWidth(charIndex);
 	if (doScaling) {
 		width = scaleUpWidth(width);
@@ -253,6 +257,11 @@ void GfxText32::drawTextBox() {
 	}
 }
 
+void GfxText32::drawTextBox(const Common::String &text) {
+	_text = text;
+	drawTextBox();
+}
+
 void GfxText32::drawText(const uint index, uint length) {
 	assert(index + length <= _text.size());
 
@@ -309,6 +318,51 @@ void GfxText32::drawText(const uint index, uint length) {
 	}
 }
 
+void GfxText32::invertRect(const reg_t bitmap, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) {
+	Common::Rect targetRect = rect;
+	if (doScaling) {
+		bitmapStride = bitmapStride * _scaledWidth / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+		targetRect = scaleRect(rect);
+	}
+
+	byte *bitmapData = _segMan->getHunkPointer(bitmap);
+
+	// NOTE: SCI code is super weird here; it seems to be trying to look at the
+	// entire size of the bitmap including the header, instead of just the pixel
+	// data size. We just look at the pixel size. This function generally is an
+	// odd duck since the stride dimension for a bitmap is built in to the bitmap
+	// header, so perhaps it was once an unheadered bitmap format and this
+	// function was never updated to match? Or maybe they exploit the
+	// configurable stride length somewhere else to do stair stepping inverts...
+	uint32 invertSize = targetRect.height() * bitmapStride + targetRect.width();
+	uint32 bitmapSize = READ_SCI11ENDIAN_UINT32(bitmapData + 12);
+
+	if (invertSize >= bitmapSize) {
+		error("InvertRect too big: %u >= %u", invertSize, bitmapSize);
+	}
+
+	// NOTE: Actual engine just added the bitmap header size hardcoded here
+	byte *pixel = bitmapData + READ_SCI11ENDIAN_UINT32(bitmapData + 28) + bitmapStride * targetRect.top + targetRect.left;
+
+	int16 stride = bitmapStride - targetRect.width();
+	int16 targetHeight = targetRect.height();
+	int16 targetWidth = targetRect.width();
+
+	for (int16 y = 0; y < targetHeight; ++y) {
+		for (int16 x = 0; x < targetWidth; ++x) {
+			if (*pixel == foreColor) {
+				*pixel = backColor;
+			} else if (*pixel == backColor) {
+				*pixel = foreColor;
+			}
+
+			++pixel;
+		}
+
+		pixel += stride;
+	}
+}
+
 uint GfxText32::getLongest(uint *charIndex, const int16 width) {
 	assert(width > 0);
 
@@ -543,7 +597,7 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth,
 }
 
 void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
-	Common::Rect targetRect = doScaling ? rect : scaleRect(rect);
+	Common::Rect targetRect = doScaling ? scaleRect(rect) : rect;
 
 	byte *bitmap = _segMan->getHunkPointer(_bitmap);
 	byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
@@ -556,10 +610,7 @@ void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
 }
 
 int16 GfxText32::getStringWidth(const Common::String &text) {
-	// TODO: The fact that this double-scales the text makes it
-	// seem pretty unlikely that this is ever called in real life
-	error("Called weirdo getStringWidth (FontMgr::StringWidth)");
-	return scaleUpWidth(getTextWidth(text, 0, 10000));
+	return getTextWidth(text, 0, 10000);
 }
 
 } // End of namespace Sci
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 5de54d3..9e268e3 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -45,7 +45,6 @@ class GfxText32 {
 private:
 	SegManager *_segMan;
 	GfxCache *_cache;
-	GfxScreen *_screen;
 
 	/**
 	 * The resource ID of the default font used by the game.
@@ -111,11 +110,6 @@ private:
 	 */
 	TextAlign _alignment;
 
-	/**
-	 * The memory handle of the currently active bitmap.
-	 */
-	reg_t _bitmap;
-
 	int16 _field_20;
 
 	/**
@@ -133,18 +127,10 @@ private:
 	Common::Point _drawPosition;
 
 	void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling);
-	void drawTextBox();
-	void erase(const Common::Rect &rect, const bool doScaling);
 
-	void drawChar(const uint8 charIndex);
-	uint16 getCharWidth(const uint8 charIndex, const bool doScaling) const;
+	void drawChar(const char charIndex);
 	void drawText(const uint index, uint length);
 
-	inline int scaleUpWidth(int value) const {
-		const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
-		return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth;
-	}
-
 	/**
 	 * Gets the length of the longest run of text available
 	 * within the currently loaded text, starting from the
@@ -162,24 +148,23 @@ private:
 	 */
 	int16 getTextWidth(const uint index, uint length) const;
 
-	/**
-	 * Gets the pixel width of a substring of the currently
-	 * loaded text, with scaling.
-	 */
-	int16 getTextWidth(const Common::String &text, const uint index, const uint length);
-
 	inline Common::Rect scaleRect(const Common::Rect &rect) {
 		Common::Rect scaledRect(rect);
 		int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
 		int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
 		Ratio scaleX(_scaledWidth, scriptWidth);
 		Ratio scaleY(_scaledHeight, scriptHeight);
-		mul(scaledRect, scaleX, scaleY);
+		mulinc(scaledRect, scaleX, scaleY);
 		return scaledRect;
 	}
 
 public:
-	GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
+	GfxText32(SegManager *segMan, GfxCache *fonts);
+
+	/**
+	 * The memory handle of the currently active bitmap.
+	 */
+	reg_t _bitmap;
 
 	/**
 	 * The size of the x-dimension of the coordinate system
@@ -214,17 +199,68 @@ public:
 	reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, reg_t *outBitmapObject);
 
 	/**
+	 * Creates a font bitmap with a title.
+	 */
+	reg_t createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling, reg_t *outBitmapObject);
+
+	inline int scaleUpWidth(int value) const {
+		const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+		return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth;
+	}
+
+	inline int scaleUpHeight(int value) const {
+		const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+		return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight;
+	}
+
+	/**
+	 * Draws the text to the bitmap.
+	 */
+	void drawTextBox();
+
+	/**
+	 * Draws the given text to the bitmap.
+	 *
+	 * @note The original engine holds a reference to a
+	 * shared string which lets the text be updated from
+	 * outside of the font manager. Instead, we give this
+	 * extra signature to send the text to draw.
+	 *
+	 * TODO: Use shared string instead?
+	 */
+	void drawTextBox(const Common::String &text);
+
+	/**
+	 * Erases the given rect by filling with the background
+	 * color.
+	 */
+	void erase(const Common::Rect &rect, const bool doScaling);
+
+	void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling);
+
+	/**
 	 * Sets the font to be used for rendering and
 	 * calculation of text dimensions.
 	 */
 	void setFont(const GuiResourceId fontId);
 
 	/**
+	 * Gets the width of a character.
+	 */
+	uint16 getCharWidth(const char charIndex, const bool doScaling) const;
+
+	/**
 	 * Retrieves the width and height of a block of text.
 	 */
 	Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling);
 
 	/**
+	 * Gets the pixel width of a substring of the currently
+	 * loaded text, with scaling.
+	 */
+	int16 getTextWidth(const Common::String &text, const uint index, const uint length);
+
+	/**
 	 * Retrieves the width of a line of text.
 	 */
 	int16 getStringWidth(const Common::String &text);
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 52188db..7c985df 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -705,7 +705,7 @@ void SciEngine::initGraphics() {
 		_gfxPaint = _gfxPaint32;
 		_robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
 		_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32);
-		_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen);
+		_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache);
 		_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
 		_gfxFrameout->run();
 	} else {


Commit: e8d6ad1d0bcfef12bdf468c9a6a80cd14ae2d8f7
    https://github.com/scummvm/scummvm/commit/e8d6ad1d0bcfef12bdf468c9a6a80cd14ae2d8f7
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:43-06:00

Commit Message:
SCI32: Fix missing digits in plane item list debug output

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



diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index c8c9cbe..ae0b83b 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -417,7 +417,7 @@ CelObj &ScreenItem::getCelObj() {
 }
 
 void ScreenItem::printDebugInfo(Console *con) const {
-	con->debugPrintf("%x:%x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n",
+	con->debugPrintf("%04x:%04x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n",
 		_object.getSegment(), _object.getOffset(),
 		g_sci->getEngineState()->_segMan->getObjectName(_object),
 		_priority,


Commit: 3e631ca5e3a3e19cb08be83c039ae9681a2e6cbc
    https://github.com/scummvm/scummvm/commit/3e631ca5e3a3e19cb08be83c039ae9681a2e6cbc
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:43-06:00

Commit Message:
SCI32: "Improve" comparison algorithm for planes and screen items

This adds a slightly more accurate comparison algorithm that will
at least ensure that all the engine-generated planes and screen
items with matching priorities will be sorted above
script-generated planes and screen items, like in the original
engine. It still does not sort script-generated items by memory
handle order, so if that is ever a thing that actually happens,
those may still be in the wrong order.

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



diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 7d1487b..0a0b0ad 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -447,7 +447,20 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList
 					if (j < _screenItemList.size() && sli) {
 						if (!sli->_updated && !sli->_deleted && !sli->_created) {
 							ScreenItem *item = dli->screenItem;
-							if (sli->_priority > item->_priority /* TODO: || (sli->_priority == item->_priority && sli->_object > item->_object)*/) {
+
+							bool isAbove = false;
+							if (sli->_priority > item->_priority) {
+								isAbove = true;
+							}
+							else if (sli->_priority == item->_priority) {
+								if (sli->_object.isNumber() && item->_object.isNumber()) {
+									isAbove = sli->_object > item->_object;
+								} else if (sli->_object.isNumber()) {
+									isAbove = true;
+								}
+							}
+
+							if (isAbove) {
 								if (dli->rect.intersects(sli->_screenRect)) {
 									drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect));
 								}
diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h
index 65df19d..c9b9d6a 100644
--- a/engines/sci/graphics/plane32.h
+++ b/engines/sci/graphics/plane32.h
@@ -272,7 +272,19 @@ public:
 		// However, this whole comparison is quite ugly, and if it still
 		// fails, we should try to change it to something equivalent, to avoid
 		// adding loads of workarounds just for this
-		return _priority < other._priority || (_priority == other._priority && _priority > -1 && _object.getOffset() < other._object.getOffset());
+		if (_priority < other._priority) {
+			return true;
+		}
+
+		if (_priority == other._priority) {
+			if (_object.isNumber() && other._object.isNumber()) {
+				return _object < other._object;
+			} else if (other._object.isNumber()) {
+				return true;
+			}
+		}
+
+		return false;
 	}
 
 	/**
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index e054cf3..1c4d87d 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -228,9 +228,14 @@ public:
 			}
 
 			if (_position.y + _z == other._position.y + other._z) {
-				return false;
-				// TODO: Failure in SQ6 room 220
-//				return _object < other._object;
+				// TODO: Failure in SQ6 room 220 when using SCI logic
+				// to compare pointer and numeric memory handles
+
+				if (_object.isNumber() && other._object.isNumber()) {
+					return _object < other._object;
+				} else if (other._object.isNumber()) {
+					return true;
+				}
 			}
 		}
 


Commit: 36800b701764222ec0c527f68504d1d3dc2e1fcc
    https://github.com/scummvm/scummvm/commit/36800b701764222ec0c527f68504d1d3dc2e1fcc
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:43-06:00

Commit Message:
SCI32: Fix memory leaks

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



diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 787d295..b839b44 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -84,10 +84,9 @@ const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio
 #pragma mark CelObj
 
 void CelObj::init() {
+	CelObj::deinit();
 	_nextCacheId = 1;
-	delete _scaler;
 	_scaler = new CelScaler();
-	delete _cache;
 	_cache = new CelCache;
 	_cache->resize(100);
 }
@@ -95,6 +94,11 @@ void CelObj::init() {
 void CelObj::deinit() {
 	delete _scaler;
 	_scaler = nullptr;
+	if (_cache != nullptr) {
+		for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) {
+			delete it->celObj;
+		}
+	}
 	delete _cache;
 	_cache = nullptr;
 }
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index fc028f7..8d9f8a7 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -43,8 +43,6 @@ GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *tex
 	_overwriteMode(false),
 	_nextCursorFlashTick(0) {}
 
-GfxControls32::~GfxControls32() {}
-
 reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
 	SegManager *segMan = _segMan;
 
@@ -149,7 +147,8 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
 		bool focused = true;
 		// Original engine did not have a QUIT event but we have to handle it
 		if (event.type == SCI_EVENT_QUIT) {
-			return NULL_REG;
+			focused = false;
+			break;
 		} else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) {
 			focused = false;
 		} else if (event.type == SCI_EVENT_KEYBOARD) {
diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h
index 0dc3b9c..1bb7679 100644
--- a/engines/sci/graphics/controls32.h
+++ b/engines/sci/graphics/controls32.h
@@ -105,7 +105,6 @@ struct TextEditor {
 class GfxControls32 {
 public:
 	GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text);
-	~GfxControls32();
 
 	reg_t kernelEditText(const reg_t controlObject);
 
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 55f5b36..19eaeef 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -133,8 +133,9 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
 }
 
 GfxFrameout::~GfxFrameout() {
-	CelObj::deinit();
 	clear();
+	CelObj::deinit();
+	free(_currentBuffer.getPixels());
 }
 
 void GfxFrameout::run() {
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 0a0b0ad..de82a40 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -55,6 +55,7 @@ _redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
 _created(g_sci->_gfxFrameout->getScreenCount()),
 _updated(0),
 _deleted(0),
+_moved(0),
 _gameRect(gameRect) {
 	convertGameRectToPlaneRect();
 	_priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1);
@@ -808,6 +809,11 @@ void PlaneList::erase(Plane *plane) {
 	}
 }
 
+PlaneList::iterator PlaneList::erase(iterator it) {
+	delete *it;
+	return PlaneListBase::erase(it);
+}
+
 int PlaneList::findIndexByObject(const reg_t object) const {
 	for (size_type i = 0; i < size(); ++i) {
 		if ((*this)[i] != nullptr && (*this)[i]->_object == object) {
@@ -850,6 +856,10 @@ int16 PlaneList::getTopSciPlanePriority() const {
 	return priority;
 }
 
+void PlaneList::remove_at(size_type index) {
+	delete PlaneListBase::remove_at(index);
+}
+
 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 c9b9d6a..f60f0cc 100644
--- a/engines/sci/graphics/plane32.h
+++ b/engines/sci/graphics/plane32.h
@@ -480,11 +480,12 @@ public:
 
 	void add(Plane *plane);
 	void clear();
-	using PlaneListBase::erase;
+	iterator erase(iterator it);
 	void erase(Plane *plane);
 	inline void sort() {
 		Common::sort(begin(), end(), sortHelper);
 	}
+	void remove_at(size_type index);
 };
 
 }
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index ae0b83b..f3f397d 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -126,6 +126,10 @@ void ScreenItem::operator=(const ScreenItem &other) {
 	_scaledPosition = other._scaledPosition;
 }
 
+ScreenItem::~ScreenItem() {
+	delete _celObj;
+}
+
 void ScreenItem::init() {
 	_nextObjectId = 20000;
 }
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index 1c4d87d..e86efda 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -215,6 +215,7 @@ public:
 	ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect);
 	ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo);
 	ScreenItem(const ScreenItem &other);
+	~ScreenItem();
 	void operator=(const ScreenItem &);
 
 	inline bool operator<(const ScreenItem &other) const {


Commit: 56806161a747b1e806fa75acb56bafe7897dbea0
    https://github.com/scummvm/scummvm/commit/56806161a747b1e806fa75acb56bafe7897dbea0
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:44-06:00

Commit Message:
SCI32: Make PlaneList definition order match declaration order

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



diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index de82a40..6961a9a 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -792,6 +792,17 @@ void Plane::update(const reg_t object) {
 
 #pragma mark -
 #pragma mark PlaneList
+void PlaneList::add(Plane *plane) {
+	for (iterator it = begin(); it != end(); ++it) {
+		if ((*it)->_priority > plane->_priority) {
+			insert(it, plane);
+			return;
+		}
+	}
+
+	push_back(plane);
+}
+
 void PlaneList::clear() {
 	for (iterator it = begin(); it != end(); ++it) {
 		delete *it;
@@ -860,15 +871,4 @@ void PlaneList::remove_at(size_type index) {
 	delete PlaneListBase::remove_at(index);
 }
 
-void PlaneList::add(Plane *plane) {
-	for (iterator it = begin(); it != end(); ++it) {
-		if ((*it)->_priority > plane->_priority) {
-			insert(it, plane);
-			return;
-		}
-	}
-
-	push_back(plane);
-}
-
 }


Commit: 266a9fdd25c2320684124f6b9f37e698960a6176
    https://github.com/scummvm/scummvm/commit/266a9fdd25c2320684124f6b9f37e698960a6176
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-03-06T21:34:44-06:00

Commit Message:
SCI32: Implement variable size frame drawing

Changed paths:
    engines/sci/graphics/text32.cpp



diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index c1335b9..d980f52 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -197,13 +197,32 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint
 	Common::Rect targetRect = doScaling ? scaleRect(rect) : rect;
 
 	byte *bitmap = _segMan->getHunkPointer(_bitmap);
-	byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
+	byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28) + rect.top * _width + rect.left;
 
 	// NOTE: Not fully disassembled, but this should be right
-	// TODO: Implement variable frame size
-	assert(size == 1);
-	Buffer buffer(_width, _height, pixels);
-	buffer.frameRect(targetRect, color);
+	int16 rectWidth = targetRect.width();
+	int16 sidesHeight = targetRect.height() - size * 2;
+	int16 centerWidth = rectWidth - size * 2;
+	int16 stride = _width - rectWidth;
+
+	for (int16 y = 0; y < size; ++y) {
+		memset(pixels, color, rectWidth);
+		pixels += _width;
+	}
+	for (int16 y = 0; y < sidesHeight; ++y) {
+		for (int16 x = 0; x < size; ++x) {
+			*pixels++ = color;
+		}
+		pixels += centerWidth;
+		for (int16 x = 0; x < size; ++x) {
+			*pixels++ = color;
+		}
+		pixels += stride;
+	}
+	for (int16 y = 0; y < size; ++y) {
+		memset(pixels, color, rectWidth);
+		pixels += _width;
+	}
 }
 
 void GfxText32::drawChar(const char charIndex) {






More information about the Scummvm-git-logs mailing list