[Scummvm-git-logs] scummvm master -> bdb555d9a0d72db246084af119f88412f7563ca5

sev- noreply at scummvm.org
Thu Mar 16 13:46:24 UTC 2023


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

Summary:
04d26f4b01 DIRECTOR: LINGO: Start UnitTest Xlib
81cc30f8e4 DIRECTOR: Fix kInkTypeTransparent, kInkTypeNotTrans, kInkTypeGhost and kInkTypeNotGhost
3cb61c54d7 DIRECTOR: Fix kInkTypeBlend, remove redundant _blend field on Sprite
bdb555d9a0 DIRECTOR: Fix kInkTypeAdd, kInkTypeAddPin, kInkTypeSub and kInkTypeSubPin


Commit: 04d26f4b01bc9174bc318772039ba69e983f9511
    https://github.com/scummvm/scummvm/commit/04d26f4b01bc9174bc318772039ba69e983f9511
Author: Scott Percival (code at moral.net.au)
Date: 2023-03-16T14:46:18+01:00

Commit Message:
DIRECTOR: LINGO: Start UnitTest Xlib

This is the start of a cross-platform testing framework for ScummVM and
Macromedia Director. The intention is to create a Xlib DLL for Director 4
with the same API, making it easier to prototype unit tests in the
Director editor.

This Xlib adds the UTScreenshot Lingo builtin, which dumps the current
screen surface to a subfolder in the game directory.

Changed paths:
  A engines/director/lingo/xlibs/unittest.cpp
  A engines/director/lingo/xlibs/unittest.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 2158e0d5cbb..80e99b56f95 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -73,6 +73,7 @@
 #include "director/lingo/xlibs/serialportxobj.h"
 #include "director/lingo/xlibs/soundjam.h"
 #include "director/lingo/xlibs/spacemgr.h"
+#include "director/lingo/xlibs/unittest.h"
 #include "director/lingo/xlibs/videodiscxobj.h"
 #include "director/lingo/xlibs/volumelist.h"
 #include "director/lingo/xlibs/widgetxobj.h"
@@ -188,6 +189,7 @@ static struct XLibProto {
 	{ SerialPortXObj::fileNames,		SerialPortXObj::open,		SerialPortXObj::close,		kXObj,					200 },	// D2
 	{ SoundJam::fileNames,				SoundJam::open,				SoundJam::close,			kXObj,					400 },	// D4
 	{ SpaceMgr::fileNames,				SpaceMgr::open,				SpaceMgr::close,			kXObj,					400 },	// D4
+	{ UnitTest::fileNames,				UnitTest::open,				UnitTest::close,			kXObj,					400 },	// D4
 	{ VolumeList::fileNames,			VolumeList::open,			VolumeList::close,			kXObj,					300 },	// D3
 	{ WidgetXObj::fileNames,			WidgetXObj::open,			WidgetXObj::close, 			kXObj,					400 },  // D4
 	{ VideodiscXObj::fileNames,			VideodiscXObj::open,		VideodiscXObj::close,		kXObj,					200 },	// D2
diff --git a/engines/director/lingo/xlibs/unittest.cpp b/engines/director/lingo/xlibs/unittest.cpp
new file mode 100644
index 00000000000..c27af2cc131
--- /dev/null
+++ b/engines/director/lingo/xlibs/unittest.cpp
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*************************************
+ *
+ * ScummVM Unit Testing framework.
+ * Used in the Director Test suite - https://github.com/scummvm/director-tests
+ *
+ *************************************/
+
+#include "director/director.h"
+#include "director/archive.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/xlibs/unittest.h"
+#include "director/movie.h"
+#include "director/score.h"
+#include "director/window.h"
+#ifdef USE_PNG
+#include "image/png.h"
+#else
+#include "image/bmp.h"
+#endif
+
+namespace Director {
+
+const char *UnitTest::xlibName = "UnitTest";
+const char *UnitTest::fileNames[] = {
+	"UnitTest",
+	0
+};
+
+static BuiltinProto builtins[] = {
+	{ "UTScreenshot", UnitTest::m_UTScreenshot, 0, 1, 400, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+void UnitTest::open(int type) {
+	g_lingo->initBuiltIns(builtins);
+}
+
+void UnitTest::close(int type) {
+	g_lingo->cleanupBuiltIns(builtins);
+}
+
+void UnitTest::m_UTScreenshot(int nargs) {
+	Common::String filenameBase = g_director->getCurrentMovie()->getArchive()->getFileName();
+	if (filenameBase.hasSuffixIgnoreCase(".dir"))
+		filenameBase = filenameBase.substr(0, filenameBase.size() - 4);
+
+	if (nargs > 1) {
+		g_lingo->dropStack(nargs - 1);
+	}
+	if (nargs == 1) {
+		Datum name = g_lingo->pop();
+		if (name.type == STRING) {
+			filenameBase = *name.u.s;
+		} else if (name.type != VOID) {
+			warning("UnitTest::b_UTScreenshot(): expected string for arg 1, ignoring");
+		}
+	}
+
+	Common::FSNode gameDataDir = g_director->_gameDataDir;
+	Common::FSNode screenDir = gameDataDir.getChild("utscreen");
+	if (!screenDir.exists()) {
+		screenDir.createDirectory();
+	}
+
+	// force a full screen redraw before taking the screenshot
+	Score *score = g_director->getCurrentMovie()->getScore();
+	score->renderSprites(score->getCurrentFrame(), kRenderForceUpdate);
+	Window *window = g_director->getCurrentWindow();
+	window->render();
+	Graphics::ManagedSurface *windowSurface = window->getSurface();
+
+#ifdef USE_PNG
+	Common::FSNode file = screenDir.getChild(Common::String::format("%s.png", filenameBase.c_str()));
+#else
+	Common::FSNode file = screenDir.getChild(Common::String::format("%s.bmp", filenameBase.c_str()));
+#endif
+
+	Common::SeekableWriteStream *stream = file.createWriteStream();
+	if (!stream) {
+		warning("UnitTest::b_UTScreenshot(): could not open file %s", file.getPath().c_str());
+		return;
+	}
+
+	bool success = false;
+#ifdef USE_PNG
+	if (windowSurface->format.bytesPerPixel == 1) {
+		success = Image::writePNG(*stream, *windowSurface, g_director->getPalette());
+	} else {
+		success = Image::writePNG(*stream, *windowSurface);
+	}
+#else
+	success = Image::writeBMP(*stream, *windowSurface);
+#endif
+	if (!success) {
+		warning("UnitTest::b_UTScreenshot(): error writing screenshot data to file %s", file.getPath().c_str());
+	}
+	stream->finalize();
+	delete stream;
+}
+
+} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/unittest.h b/engines/director/lingo/xlibs/unittest.h
new file mode 100644
index 00000000000..faa445d5523
--- /dev/null
+++ b/engines/director/lingo/xlibs/unittest.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DIRECTOR_LINGO_XLIBS_UNITTEST_H
+#define DIRECTOR_LINGO_XLIBS_UNITTEST_H
+
+namespace Director {
+
+namespace UnitTest {
+
+extern const char *xlibName;
+extern const char *fileNames[];
+
+void open(int type);
+void close(int type);
+
+void m_UTScreenshot(int nargs);
+
+} // End of namespace UnitTest
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 824d22e7d2b..e7c4d7f67b9 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -77,6 +77,7 @@ MODULE_OBJS = \
 	lingo/xlibs/serialportxobj.o \
 	lingo/xlibs/soundjam.o \
 	lingo/xlibs/spacemgr.o \
+	lingo/xlibs/unittest.o \
 	lingo/xlibs/videodiscxobj.o \
 	lingo/xlibs/volumelist.o \
 	lingo/xlibs/widgetxobj.o \


Commit: 81cc30f8e4d80e2537c0e888f949b4b4395c2d15
    https://github.com/scummvm/scummvm/commit/81cc30f8e4d80e2537c0e888f949b4b4395c2d15
Author: Scott Percival (code at moral.net.au)
Date: 2023-03-16T14:46:18+01:00

Commit Message:
DIRECTOR: Fix kInkTypeTransparent, kInkTypeNotTrans, kInkTypeGhost and kInkTypeNotGhost

Changed paths:
    engines/director/graphics.cpp


diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp
index 2ca324f12f1..9eb693334b2 100644
--- a/engines/director/graphics.cpp
+++ b/engines/director/graphics.cpp
@@ -360,10 +360,18 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		}
 		break;
 	case kInkTypeTransparent:
-		*dst = p->applyColor ? (~src | p->backColor) & (*dst | src) : *dst | src;
+		if (p->oneBitImage || p->applyColor) {
+			*dst = src == (int)p->colorBlack ? p->foreColor : *dst;
+		} else {
+			*dst = *dst | src;
+		}
 		break;
 	case kInkTypeNotTrans:
-		*dst = p->applyColor ? (src | p->backColor) & (*dst | ~src) : (*dst | ~src);
+		if (p->oneBitImage || p->applyColor) {
+			*dst = src == (int)p->colorWhite ? p->foreColor : *dst;
+		} else {
+			*dst = *dst | ~src;
+		}
 		break;
 	case kInkTypeReverse:
 		*dst ^= src;
@@ -372,10 +380,18 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		*dst ^= ~(src);
 		break;
 	case kInkTypeGhost:
-		*dst = p->applyColor ? (src & p->foreColor) | (*dst & ~src) : (*dst & ~src);
+		if (p->oneBitImage || p->applyColor) {
+			*dst = src == (int)p->colorBlack ? p->backColor : *dst;
+		} else {
+			*dst = *dst & ~src;
+		}
 		break;
 	case kInkTypeNotGhost:
-		*dst = p->applyColor ? (~src & p->foreColor) | (*dst & src) : (*dst & src);
+		if (p->oneBitImage || p->applyColor) {
+			*dst = src == (int)p->colorWhite ? p->backColor : *dst;
+		} else {
+			*dst = *dst & src;
+		}
 		break;
 		// Arithmetic ink types
 	default: {
@@ -424,16 +440,28 @@ Graphics::MacDrawPixPtr DirectorEngine::getInkDrawPixel() {
 }
 
 void DirectorPlotData::setApplyColor() {
+	// Director has two ways of rendering an ink setting.
+	// The default is to incorporate the full range of colors in the image.
+	// "applyColor" is used to denote the other option; reduce the image
+	// to some combination of the currently set foreground and background color.
 	applyColor = false;
 
-	if (foreColor != colorBlack) {
-		if (ink != kInkTypeGhost && ink != kInkTypeNotGhost)
-			applyColor = true;
-	}
-
-	if (backColor != colorWhite) {
-		if (ink != kInkTypeTransparent && ink != kInkTypeNotTrans && ink != kInkTypeBackgndTrans)
-			applyColor = true;
+	switch (ink) {
+	case kInkTypeMatte:
+	case kInkTypeMask:
+	case kInkTypeCopy:
+	case kInkTypeNotCopy:
+		applyColor = (foreColor != colorBlack) || (backColor != colorWhite);
+		break;
+	case kInkTypeTransparent:
+	case kInkTypeNotTrans:
+	case kInkTypeBackgndTrans:
+	case kInkTypeGhost:
+	case kInkTypeNotGhost:
+		applyColor = !((foreColor == colorBlack) && (backColor == colorWhite));
+		break;
+	default:
+		break;
 	}
 }
 


Commit: 3cb61c54d749f385817fb8674236d4046af54271
    https://github.com/scummvm/scummvm/commit/3cb61c54d749f385817fb8674236d4046af54271
Author: Scott Percival (code at moral.net.au)
Date: 2023-03-16T14:46:18+01:00

Commit Message:
DIRECTOR: Fix kInkTypeBlend, remove redundant _blend field on Sprite

Changed paths:
    engines/director/channel.cpp
    engines/director/frame.cpp
    engines/director/graphics.cpp
    engines/director/lingo/lingo-the.cpp
    engines/director/score.cpp
    engines/director/sprite.cpp
    engines/director/sprite.h


diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index 3bf07254721..d4a43dc5136 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -106,7 +106,7 @@ Channel::~Channel() {
 }
 
 DirectorPlotData Channel::getPlotData() {
-	DirectorPlotData pd(g_director, _sprite->_spriteType, _sprite->_ink, _sprite->_blend, _sprite->getBackColor(), _sprite->getForeColor());
+	DirectorPlotData pd(g_director, _sprite->_spriteType, _sprite->_ink, _sprite->_blendAmount, _sprite->getBackColor(), _sprite->getForeColor());
 	pd.colorWhite = 0;
 	pd.colorBlack = 255;
 	pd.dst = nullptr;
@@ -153,7 +153,7 @@ const Graphics::Surface *Channel::getMask(bool forceMatte) {
 		_sprite->_ink == kInkTypeLight ||
 		_sprite->_ink == kInkTypeSub ||
 		_sprite->_ink == kInkTypeDark ||
-		_sprite->_blend > 0;
+		_sprite->_blendAmount > 0;
 
 	Common::Rect bbox(getBbox());
 
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 214b5144bbf..f3d58d92c02 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -412,7 +412,7 @@ Common::String Frame::formatChannelInfo() {
 	for (int i = 0; i < _numChannels; i++) {
 		Sprite &sprite = *_sprites[i + 1];
 		if (sprite._castId.member) {
-			result += Common::String::format("CH: %-3d castId: %s, [inkData: 0x%02x [ink: %d, trails: %d, line: %d], %dx%d@%d,%d type: %d (%s) fg: %d bg: %d], script: %s, flags2: 0x%x, unk2: 0x%x, unk3: 0x%x\n",
+			result += Common::String::format("CH: %-3d castId: %s, [inkData: 0x%02x [ink: %d, trails: %d, line: %d], %dx%d@%d,%d type: %d (%s) fg: %d bg: %d], script: %s, colorcode: 0x%x, blendAmount: 0x%x, unk3: 0x%x\n",
 				i + 1, sprite._castId.asString().c_str(), sprite._inkData,
 				sprite._ink, sprite._trails, sprite._thickness, sprite._width, sprite._height,
 				sprite._startPoint.x, sprite._startPoint.y,
diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp
index 9eb693334b2..fe76f7ddaf0 100644
--- a/engines/director/graphics.cpp
+++ b/engines/director/graphics.cpp
@@ -283,11 +283,9 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		wm->decomposeColor<T>(src, rSrc, gSrc, bSrc);
 		wm->decomposeColor<T>(*dst, rDst, gDst, bDst);
 
-		double alpha = (double)p->alpha / 100.0;
-		rDst = static_cast<byte>((rSrc * alpha) + (rDst * (1.0 - alpha)));
-		gDst = static_cast<byte>((gSrc * alpha) + (gDst * (1.0 - alpha)));
-		bDst = static_cast<byte>((bSrc * alpha) + (bDst * (1.0 - alpha)));
-
+		rDst = lerpByte(rSrc, rDst, p->alpha, 255);
+		gDst = lerpByte(gSrc, gDst, p->alpha, 255);
+		bDst = lerpByte(bSrc, bDst, p->alpha, 255);
 		*dst = wm->findBestColor(rDst, gDst, bDst);
 		return;
 	}
@@ -306,6 +304,9 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		// fall through
 	case kInkTypeMask:
 		// Only unmasked pixels make it here, so copy them straight
+	case kInkTypeBlend:
+		// If there's a blend factor set, it's dealt with in the alpha handling block.
+		// Otherwise, treat it like a Matte image.
 	case kInkTypeCopy: {
 		if (p->applyColor) {
 			if (sizeof(T) == 1) {
@@ -402,9 +403,6 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		wm->decomposeColor<T>(*dst, rDst, gDst, bDst);
 
 		switch (p->ink) {
-		case kInkTypeBlend:
-				*dst = wm->findBestColor((rSrc + rDst) / 2, (gSrc + gDst) / 2, (bSrc + bDst) / 2);
-			break;
 		case kInkTypeAddPin:
 				*dst = wm->findBestColor(MIN((rSrc + rDst), 0xff), MIN((gSrc + gDst), 0xff), MIN((bSrc + bDst), 0xff));
 			break;
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 470ea0030f3..3c05ed2a730 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -21,6 +21,7 @@
 
 #include "common/config-manager.h"
 #include "common/fs.h"
+#include "common/util.h"
 #include "graphics/macgui/macbutton.h"
 #include "graphics/macgui/macmenu.h"
 
@@ -1275,7 +1276,7 @@ Datum Lingo::getTheSprite(Datum &id1, int field) {
 		d = (int)g_director->transformColor(sprite->_backColor);
 		break;
 	case kTheBlend:
-		d = sprite->_blend;
+		d = (255 - sprite->_blendAmount) * 255 / 100;
 		break;
 	case kTheBottom:
 		d = channel->getBbox().bottom;
@@ -1428,9 +1429,13 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 		}
 		break;
 	case kTheBlend:
-		if (d.asInt() != sprite->_blend) {
-			sprite->_blend = (d.asInt() == 100 ? 0 : d.asInt());
-			channel->_dirty = true;
+		{
+			// Convert from (0, 100) range to (0xff, 0x00)
+			int blend = (100 - CLIP(d.asInt(), 0, 100)) * 255 / 100;
+			if (blend != sprite->_blendAmount) {
+				sprite->_blendAmount = blend;
+				channel->_dirty = true;
+			}
 		}
 		break;
 	case kTheCastNum:
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index e6dd62a31fc..70e68ae3d7e 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -1519,7 +1519,7 @@ Common::String Score::formatChannelInfo() {
 		Channel &channel = *_channels[i + 1];
 		Sprite &sprite = *channel._sprite;
 		if (sprite._castId.member) {
-			result += Common::String::format("CH: %-3d castId: %s, visible: %d, [inkData: 0x%02x [ink: %d, trails: %d, line: %d], %dx%d@%d,%d type: %d (%s) fg: %d bg: %d], script: %s, flags2: 0x%x, unk2: 0x%x, unk3: 0x%x, constraint: %d, puppet: %d, stretch: %d\n",
+			result += Common::String::format("CH: %-3d castId: %s, visible: %d, [inkData: 0x%02x [ink: %d, trails: %d, line: %d], %dx%d@%d,%d type: %d (%s) fg: %d bg: %d], script: %s, colorcode: 0x%x, blendAmount: 0x%x, unk3: 0x%x, constraint: %d, puppet: %d, stretch: %d\n",
 				i + 1, sprite._castId.asString().c_str(), channel._visible, sprite._inkData,
 				sprite._ink, sprite._trails, sprite._thickness, channel._width, channel._height,
 				channel._currentPoint.x, channel._currentPoint.y,
diff --git a/engines/director/sprite.cpp b/engines/director/sprite.cpp
index b6e0fbb7f76..745172ca217 100644
--- a/engines/director/sprite.cpp
+++ b/engines/director/sprite.cpp
@@ -64,8 +64,6 @@ Sprite::Sprite(Frame *frame) {
 	_backColor = g_director->_wm->_colorWhite;
 	_foreColor = g_director->_wm->_colorBlack;
 
-	_blend = 0;
-
 	_volume = 0;
 	_stretch = 0;
 }
@@ -109,8 +107,6 @@ Sprite& Sprite::operator=(const Sprite &sprite) {
 	_backColor = sprite._backColor;
 	_foreColor = sprite._foreColor;
 
-	_blend = sprite._blend;
-
 	_volume = sprite._volume;
 	_stretch = sprite._stretch;
 
diff --git a/engines/director/sprite.h b/engines/director/sprite.h
index 4e563a7f46d..340cd7f0756 100644
--- a/engines/director/sprite.h
+++ b/engines/director/sprite.h
@@ -117,8 +117,6 @@ public:
 	uint32 _backColor;
 	uint32 _foreColor;
 
-	byte _blend;
-
 	byte _volume;
 	byte _stretch;
 };


Commit: bdb555d9a0d72db246084af119f88412f7563ca5
    https://github.com/scummvm/scummvm/commit/bdb555d9a0d72db246084af119f88412f7563ca5
Author: Scott Percival (code at moral.net.au)
Date: 2023-03-16T14:46:18+01:00

Commit Message:
DIRECTOR: Fix kInkTypeAdd, kInkTypeAddPin, kInkTypeSub and kInkTypeSubPin

Changed paths:
    engines/director/graphics.cpp


diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp
index fe76f7ddaf0..80288032129 100644
--- a/engines/director/graphics.cpp
+++ b/engines/director/graphics.cpp
@@ -364,6 +364,9 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		if (p->oneBitImage || p->applyColor) {
 			*dst = src == (int)p->colorBlack ? p->foreColor : *dst;
 		} else {
+			// OR dst palette index with src.
+			// Originally designed for 1-bit mode to make white pixels
+			// transparent.
 			*dst = *dst | src;
 		}
 		break;
@@ -371,19 +374,29 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		if (p->oneBitImage || p->applyColor) {
 			*dst = src == (int)p->colorWhite ? p->foreColor : *dst;
 		} else {
+			// OR dst palette index with the inverse of src.
 			*dst = *dst | ~src;
 		}
 		break;
 	case kInkTypeReverse:
+		// XOR dst palette index with src.
+		// Originally designed for 1-bit mode so that
+		// black pixels would appear white on a black
+		// background.
 		*dst ^= src;
 		break;
 	case kInkTypeNotReverse:
+		// XOR dst palette index with the inverse of src.
 		*dst ^= ~(src);
 		break;
 	case kInkTypeGhost:
 		if (p->oneBitImage || p->applyColor) {
 			*dst = src == (int)p->colorBlack ? p->backColor : *dst;
 		} else {
+			// AND dst palette index with the inverse of src.
+			// Originally designed for 1-bit mode so that 
+			// black pixels would be invisible until they were
+			// over a black background, showing as white.
 			*dst = *dst & ~src;
 		}
 		break;
@@ -391,11 +404,12 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 		if (p->oneBitImage || p->applyColor) {
 			*dst = src == (int)p->colorWhite ? p->backColor : *dst;
 		} else {
+			// AND dst palette index with src.
 			*dst = *dst & src;
 		}
 		break;
-		// Arithmetic ink types
 	default: {
+		// Arithmetic ink types, based on real color values
 		byte rSrc, gSrc, bSrc;
 		byte rDst, gDst, bDst;
 
@@ -404,24 +418,28 @@ void inkDrawPixel(int x, int y, int src, void *data) {
 
 		switch (p->ink) {
 		case kInkTypeAddPin:
-				*dst = wm->findBestColor(MIN((rSrc + rDst), 0xff), MIN((gSrc + gDst), 0xff), MIN((bSrc + bDst), 0xff));
+			// Add src to dst, but pinning each channel so it can't go above 0xff.
+			*dst = wm->findBestColor(rDst + MIN(0xff - rDst, (int)rSrc), gDst + MIN(0xff - gDst, (int)gSrc), bDst + MIN(0xff - bDst, (int)bSrc));
 			break;
 		case kInkTypeAdd:
-			// in basilisk, D3.1 is exactly using this method, adding color directly without preventing the overflow.
-			// but i think min(src + dst, 255) will give us a better visual effect
-				*dst = wm->findBestColor(rSrc + rDst, gSrc + gDst, bSrc + bDst);
+			// Add src to dst, allowing each channel to overflow and wrap around.
+			*dst = wm->findBestColor(rDst + rSrc, gDst + gSrc, bDst + bSrc);
 			break;
 		case kInkTypeSubPin:
-				*dst = wm->findBestColor(MAX(rSrc - rDst, 0), MAX(gSrc - gDst, 0), MAX(bSrc - bDst, 0));
+			// Subtract src from dst, but pinning each channel so it can't go below 0x00.
+			*dst = wm->findBestColor(MAX(rDst - rSrc, 1) - 1, MAX(gDst - gSrc, 1) - 1, MAX(bDst - bSrc, 1) - 1);
 			break;
 		case kInkTypeLight:
-				*dst = wm->findBestColor(MAX(rSrc, rDst), MAX(gSrc, gDst), MAX(bSrc, bDst));
+			// Pick the higher of src and dst for each channel, lightening the image.
+			*dst = wm->findBestColor(MAX(rSrc, rDst), MAX(gSrc, gDst), MAX(bSrc, bDst));
 			break;
 		case kInkTypeSub:
-				*dst = wm->findBestColor(abs(rSrc - rDst) % 0xff + 1, abs(gSrc - gDst) % 0xff + 1, abs(bSrc - bDst) % 0xff + 1);
+			// Subtract src from dst, allowing each channel to underflow and wrap around.
+			*dst = wm->findBestColor(rDst - rSrc, gDst - gSrc, bDst - bSrc);
 			break;
 		case kInkTypeDark:
-				*dst = wm->findBestColor(MIN(rSrc, rDst), MIN(gSrc, gDst), MIN(bSrc, bDst));
+			// Pick the lower of src and dst for each channel, darkening the image.
+			*dst = wm->findBestColor(MIN(rSrc, rDst), MIN(gSrc, gDst), MIN(bSrc, bDst));
 			break;
 		default:
 			break;




More information about the Scummvm-git-logs mailing list