[Scummvm-git-logs] scummvm master -> 8f3834e3a48d5a38a3bbc47fa2b28abf284eaec3

sev- noreply at scummvm.org
Wed Sep 28 20:40:38 UTC 2022


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

Summary:
ff9313056d GRAPHICS: Implement dithering in Surface::convertTo()
8f3834e3a4 TESTBED: Dither videos


Commit: ff9313056d535ff5ad80db4f91d7ca9e2442a503
    https://github.com/scummvm/scummvm/commit/ff9313056d535ff5ad80db4f91d7ca9e2442a503
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2022-09-28T22:38:08+02:00

Commit Message:
GRAPHICS: Implement dithering in Surface::convertTo()

Changed paths:
    graphics/surface.cpp
    graphics/surface.h


diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 6f6772971e6..be72069231b 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -24,6 +24,7 @@
 #include "common/util.h"
 #include "common/rect.h"
 #include "common/textconsole.h"
+#include "graphics/palette.h"
 #include "graphics/primitives.h"
 #include "graphics/surface.h"
 #include "graphics/conversion.h"
@@ -516,28 +517,41 @@ void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette
 	pitch = w * dstFormat.bytesPerPixel;
 }
 
-Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
+Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *srcPalette, int srcPaletteCount, const byte *dstPalette, int dstPaletteCount) const {
 	assert(pixels);
 
 	Graphics::Surface *surface = new Graphics::Surface();
 
 	// If the target format is the same, just copy
 	if (format == dstFormat) {
-		surface->copyFrom(*this);
-		return surface;
+		if (dstFormat.bytesPerPixel == 1) { // Checking if dithering could be skipped
+			if (!srcPalette // No palette is specified
+					|| !dstPalette // No dst palette
+					|| srcPaletteCount != dstPaletteCount // palettes have different size
+					|| !memcmp(srcPalette, dstPalette, srcPaletteCount * 3)) { // palettes are different
+				surface->copyFrom(*this);
+				return surface;
+			}
+		}
 	}
 
 	if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
 		error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp but have %dbpp", format.bytesPerPixel);
 
-	if (dstFormat.bytesPerPixel < 2 || dstFormat.bytesPerPixel > 4)
-		error("Surface::convertTo(): Can only convert to 2Bpp, 3Bpp and 4Bpp but requested %dbpp", dstFormat.bytesPerPixel);
+	if (dstFormat.bytesPerPixel == 0 || dstFormat.bytesPerPixel > 4)
+		error("Surface::convertTo(): Can only convert to 1Bpp, 2Bpp, 3Bpp and 4Bpp but requested %dbpp", dstFormat.bytesPerPixel);
 
 	surface->create(w, h, dstFormat);
 
+	// We are here when we are converting from a higher bpp or palettes are different
+	if (dstFormat.bytesPerPixel == 1) {
+		ditherFloyd(srcPalette, srcPaletteCount, surface, dstPalette, dstPaletteCount);
+		return surface;
+	}
+
 	if (format.bytesPerPixel == 1) {
 		// Converting from paletted to high color
-		assert(palette);
+		assert(srcPalette);
 
 		for (int y = 0; y < h; y++) {
 			const byte *srcRow = (const byte *)getBasePtr(0, y);
@@ -545,9 +559,9 @@ Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *
 
 			for (int x = 0; x < w; x++) {
 				byte index = *srcRow++;
-				byte r = palette[index * 3];
-				byte g = palette[index * 3 + 1];
-				byte b = palette[index * 3 + 2];
+				byte r = srcPalette[index * 3];
+				byte g = srcPalette[index * 3 + 1];
+				byte b = srcPalette[index * 3 + 2];
 
 				uint32 color = dstFormat.RGBToColor(r, g, b);
 
@@ -688,6 +702,228 @@ void Surface::debugPrint(int debuglevel, int width, int height, int x, int y, in
 	debug(debuglevel, "+");
 }
 
+/*******************************************
+ *
+ * Dithering
+ *
+ *******************************************/
+
+static void updatePixel(byte *surf, int x, int y, int w, int h, int qr, int qg, int qb, int qq, int qdiv) {
+	if (x >= w || y >= h)
+		return;
+
+	byte *ptr = &surf[x * 3 + y * w * 3];
+
+	ptr[0] = CLIP(ptr[0] + qr * qq / qdiv, 0, 255);
+	ptr[1] = CLIP(ptr[1] + qg * qq / qdiv, 0, 255);
+	ptr[2] = CLIP(ptr[2] + qb * qq / qdiv, 0, 255);
+}
+
+void Surface::ditherFloyd(const byte *srcPalette, int srcPaletteCount, Surface *dstSurf, const byte *dstPalette, int dstPaletteCount) const {
+	assert(dstPalette);
+
+	PaletteLookup _paletteLookup;
+
+	_paletteLookup.setPalette(dstPalette, dstPaletteCount);
+
+	byte *tmpSurf = (byte *)malloc(w * h * 3);
+
+	int bpp = format.bytesPerPixel;
+
+	for (int y = 0; y < h; y++) {
+		const byte *src = (const byte *)getBasePtr(0, y);
+		byte *dst = &tmpSurf[y * w * 3];
+
+		byte r, g, b;
+
+		for (int x = 0; x < w; x++) {
+			uint32 color;
+
+			switch (bpp) {
+			case 1:
+				color = *src * 3;
+				src += 1;
+				r = srcPalette[color + 0]; g = srcPalette[color + 1]; b = srcPalette[color + 2];
+				break;
+			case 2:
+				color = *((const uint16 *)src);
+				src += 2;
+				format.colorToRGB(color, r, g, b);
+				break;
+			case 4:
+				color = *((const uint32 *)src);
+				src += 4;
+				format.colorToRGB(color, r, g, b);
+				break;
+			default:
+				error("Surface::ditherFloydImage(): Unsupported bit depth: %d", bpp);
+			}
+
+			dst[0] = r; dst[1] = g; dst[2] = b;
+			dst += 3;
+		}
+	}
+
+	struct DitherParams {
+		int dy, dx, qq;
+	};
+
+	const DitherParams paramsNaive[] = {
+		{ 0, 0, 0 }
+	};
+
+	const DitherParams paramsFloyd[] = {
+		{ 0, +1, 7 },
+		{ 1, -1, 3 },
+		{ 1,  0, 5 },
+		{ 1, +1, 1 },
+		{ 0,  0, 0 }
+	};
+
+	const DitherParams paramsAtkinson[] = {
+		{ 0, +1, 1 },
+		{ 0, +2, 1 },
+		{ 1, -1, 1 },
+		{ 1,  0, 1 },
+		{ 1, +1, 1 },
+		{ 2,  0, 1 },
+		{ 0,  0, 0 }
+	};
+
+	const DitherParams paramsBurkes[] = {
+		{ 0, +1, 8 },
+		{ 0, +2, 4 },
+		{ 1, -2, 2 },
+		{ 1, -1, 4 },
+		{ 1,  0, 8 },
+		{ 1, +1, 4 },
+		{ 1, +2, 2 },
+		{ 0,  0, 0 }
+	};
+
+	const DitherParams paramsFalseFloyd[] = {
+		{ 0, +1, 3 },
+		{ 1,  0, 3 },
+		{ 1, +1, 2 },
+		{ 0,  0, 0 }
+	};
+
+    const DitherParams paramsSierra[] = {
+		{ 0,  1, 5 },
+		{ 0,  2, 3 },
+		{ 1, -2, 2 },
+		{ 1, -1, 4 },
+		{ 1,  0, 5 },
+		{ 1,  1, 4 },
+		{ 1,  2, 2 },
+		{ 2, -1, 2 },
+		{ 2,  0, 3 },
+		{ 2,  1, 2 },
+		{ 0,  0, 0 }
+    };
+
+    const DitherParams paramsSierraTwoRow[] = {
+		{ 0,  1, 4 },
+		{ 0,  2, 3 },
+		{ 1, -2, 1 },
+		{ 1, -1, 2 },
+		{ 1,  0, 3 },
+		{ 1,  1, 2 },
+		{ 1,  2, 1 },
+		{ 0,  0, 0 }
+    };
+
+    const DitherParams paramsSierraLite[] = {
+		{ 0,  1, 2 },
+		{ 1, -1, 1 },
+		{ 1,  0, 1 },
+		{ 0,  0, 0 }
+    };
+
+    const DitherParams paramsStucki[] = {
+		{ 0,  1, 8 },
+		{ 0,  2, 4 },
+		{ 1, -2, 2 },
+		{ 1, -1, 4 },
+		{ 1,  0, 8 },
+		{ 1,  1, 4 },
+		{ 1,  2, 2 },
+		{ 2, -2, 1 },
+		{ 2, -1, 2 },
+		{ 2,  0, 4 },
+		{ 2,  1, 2 },
+		{ 2,  2, 1 },
+		{ 0,  0, 0 }
+    };
+
+    const DitherParams paramsJarvis[] = {
+		{ 0,  1, 7 },
+		{ 0,  2, 5 },
+		{ 1, -2, 3 },
+		{ 1, -1, 5 },
+		{ 1,  0, 7 },
+		{ 1,  1, 5 },
+		{ 1,  2, 3 },
+		{ 2, -2, 1 },
+		{ 2, -1, 3 },
+		{ 2,  0, 5 },
+		{ 2,  1, 3 },
+		{ 2,  2, 1 },
+		{ 0,  0, 0 }
+    };
+
+	struct DitherAlgos {
+		const char *name;
+		const DitherParams *params;
+		int qdiv;
+	} const algos[] = {
+		{ "Naive",                paramsNaive,         1 },
+		{ "Floyd-Steinberg",      paramsFloyd,        16 },
+		{ "Atkinson",             paramsAtkinson,      8 },
+		{ "Burkes",               paramsBurkes,       32 },
+		{ "False Floyd-Steinberg",paramsFalseFloyd,    8 },
+		{ "Sierra",               paramsSierra,       32 },
+		{ "Sierra 2",             paramsSierraTwoRow, 16 },
+		{ "Sierra Lite",          paramsSierraLite,    4 },
+		{ "Stucki",               paramsStucki,       42 },
+		{ "Jarvis-Judice-Ninke ", paramsJarvis,       48 },
+		{ nullptr, nullptr, 0 }
+	};
+
+	for (int y = 0; y < h; y++) {
+		const byte *src = &tmpSurf[y * w * 3];
+		byte *dst = (byte *)dstSurf->getBasePtr(0, y);
+
+		for (int x = 0; x < w; x++) {
+			byte r = src[0], g = src[1], b = src[2];
+			byte col = _paletteLookup.findBestColor(r, g, b);
+
+			*dst = col;
+
+			int qr = r - dstPalette[col * 3 + 0];
+			int qg = g - dstPalette[col * 3 + 1];
+			int qb = b - dstPalette[col * 3 + 2];
+
+			int algo = kDitherFloyd;
+			const DitherParams *params = algos[algo].params;
+
+			for (int i = 0; params[i].dx != 0 || params[i].dy != 0; i++)
+				updatePixel(tmpSurf, x + params[i].dx, y + params[i].dy, w, h, qr, qg, qb, params[i].qq, algos[algo].qdiv);
+
+			src += 3;
+			dst++;
+		}
+	}
+
+	::free(tmpSurf);
+}
+
+/*******************************************
+ *
+ * Flood Fill
+ *
+ *******************************************/
+
 FloodFill::FloodFill(Graphics::Surface *surface, uint32 oldColor, uint32 fillColor, bool maskMode) {
 	_surface = surface;
 	_oldColor = oldColor;
diff --git a/graphics/surface.h b/graphics/surface.h
index 5d2e2a9480b..7e25c7f8d41 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -46,6 +46,19 @@ namespace Graphics {
 
 struct TransformStruct;
 
+enum {
+	kDitherNaive,
+	kDitherFloyd,
+	kDitherAtkinson,
+	kDitherBurkes,
+	kDitherFalseFloyd,
+	kDitherSierra,
+	kDitherSierraTwoRow,
+	kDitherSierraLite,
+	kDitherStucki,
+	kDitherJarvis,
+};
+
 /**
  * An arbitrary graphics surface that can be the target (or source) of blit
  * operations, font rendering, etc.
@@ -341,7 +354,12 @@ public:
 	 * @param dstFormat  The desired format.
 	 * @param palette    The palette (in RGB888), if the source format has a bpp of 1.
 	 */
-	Graphics::Surface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
+	Graphics::Surface *convertTo(const PixelFormat &dstFormat, const byte *srcPalette = 0, int srcPaletteCount = 0, const byte *dstPalette = 0, int dstPaletteCount = 0) const;
+
+private:
+	void ditherFloyd(const byte *srcPalette, int srcPaletteCount, Surface *dstSurf, const byte *dstPalette, int dstPaletteCount) const;
+
+public:
 
 	/**
 	 * Draw a line.


Commit: 8f3834e3a48d5a38a3bbc47fa2b28abf284eaec3
    https://github.com/scummvm/scummvm/commit/8f3834e3a48d5a38a3bbc47fa2b28abf284eaec3
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2022-09-28T22:38:08+02:00

Commit Message:
TESTBED: Dither videos

Changed paths:
    engines/testbed/video.cpp


diff --git a/engines/testbed/video.cpp b/engines/testbed/video.cpp
index 250990fca40..d228ce496bb 100644
--- a/engines/testbed/video.cpp
+++ b/engines/testbed/video.cpp
@@ -59,7 +59,7 @@ void TestbedEngine::videoTest() {
 
 			const Graphics::Surface *frame = video->decodeNextFrame();
 			if (frame) {
-				Graphics::Surface *conv = frame->convertTo(pixelformat);
+				Graphics::Surface *conv = frame->convertTo(pixelformat, 0, 0, palette, 256);
 
 				int x = 0, y = 0;
 




More information about the Scummvm-git-logs mailing list