[education/rkward] rkward/rbackend/rkwarddevice: Implement groups (transformation matrix does not work quite correctly, yet)

Thomas Friedrichsmeier null at kde.org
Sun Apr 3 15:26:23 BST 2022


Git commit 1f0df3333361a0e37fef5ef1d799f6ffed469a7f by Thomas Friedrichsmeier.
Committed on 03/04/2022 at 12:00.
Pushed by tfry into branch 'master'.

Implement groups (transformation matrix does not work quite correctly, yet)

M  +37   -0    rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
M  +6    -0    rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
M  +26   -0    rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
M  +6    -2    rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
M  +1    -1    rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
M  +59   -2    rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp

https://invent.kde.org/education/rkward/commit/1f0df3333361a0e37fef5ef1d799f6ffed469a7f

diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
index 468817a9..1b6486d4 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
@@ -355,6 +355,43 @@ bool RKGraphicsDevice::setMask(int index) {
 	return set == index;
 }
 
+void RKGraphicsDevice::startRecordGroup(){
+	RK_TRACE (GRAPHICS_DEVICE);
+
+	pushContext(area.width(), area.height(), 0, 0);
+}
+
+void RKGraphicsDevice::recordGroupStage2(int compositing_op) {
+	RK_TRACE (GRAPHICS_DEVICE);
+	painter.setCompositionMode((QPainter::CompositionMode) compositing_op);
+}
+
+int RKGraphicsDevice::endRecordGroup() {
+	RK_TRACE (GRAPHICS_DEVICE);
+	static int id = 0;
+	auto c = popContext();
+	cached_groups.insert(++id, c.surface);
+	return id;
+}
+
+void RKGraphicsDevice::destroyGroup(int index) {
+	RK_TRACE (GRAPHICS_DEVICE);
+	if (index < 0) cached_groups.clear();
+	else cached_groups.remove(index);
+}
+
+void RKGraphicsDevice::useGroup(int index, const QTransform& matrix) {
+	RK_TRACE (GRAPHICS_DEVICE);
+
+	if (current_mask) initMaskedDraw();
+	painter.save();
+	painter.setTransform(matrix);
+	painter.drawImage(0, 0, cached_groups.value(index));
+	painter.restore();
+	if (current_mask) commitMaskedDraw();
+	triggerUpdate ();
+}
+
 void RKGraphicsDevice::fillStrokePath(const QPainterPath& path, const QBrush& brush, const QPen& pen) {
 	RK_TRACE (GRAPHICS_DEVICE);
 
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
index 4c2553d3..57fcfa61 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
@@ -110,6 +110,11 @@ public:
 	void destroyMask(int index);
 	bool setMask(int index);
 	void fillStrokePath(const QPainterPath &path, const QBrush &brush, const QPen &pen);
+	void startRecordGroup();
+	void recordGroupStage2(int compositing_op);
+	int endRecordGroup();
+	void useGroup(int index, const QTransform &matrix);
+	void destroyGroup(int index);
 public slots:
 	void stopInteraction ();
 signals:
@@ -140,6 +145,7 @@ private:
 	QHash<int, QBrush> patterns;
 	QHash<int, QPainterPath> cached_paths;
 	QHash<int, QImage> cached_masks;
+	QHash<int, QImage> cached_groups;
 	// NOTE on path recording: In principle, we could really do _all_ painting on QPainterPath, but in regular operation stroke and fill right away.
 	// However, that is noticably slower.
 	QPainterPath recorded_path;
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
index c8b2a15b..580353aa 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
@@ -387,6 +387,7 @@ void RKGraphicsDeviceFrontendTransmitter::newData () {
 				if (type == RKDPattern) device->destroyPattern(index);
 				else if (type == RKDClipPath) device->destroyCachedPath(index);
 				else if (type == RKDMask) device->destroyMask(index);
+				else if (type == RKDGroup) device->destroyGroup(index);
 				else RK_ASSERT(false);
 			}
 		} else if (opcode == RKDStartRecordClipPath) {
@@ -434,6 +435,31 @@ void RKGraphicsDeviceFrontendTransmitter::newData () {
 			}
 			QPainterPath p = device->endRecordPath(fillrule);
 			device->fillStrokePath(p, brush, pen);
+		} else if (opcode == RKDDefineGroupBegin) {
+			device->startRecordGroup();
+		} else if (opcode == RKDDefineGroupStep2) {
+			qint8 compositing_operator;
+			streamer.instream >> compositing_operator;
+			device->recordGroupStage2(compositing_operator);
+		} else if (opcode == RKDDefineGroupEnd) {
+			qint32 index = device->endRecordGroup();
+			streamer.outstream << index;
+			streamer.writeOutBuffer();
+		} else if (opcode == RKDUseGroup) {
+			qint32 index;
+			qint8 have_trans;
+			streamer.instream >> index;
+			streamer.instream >> have_trans;
+			QTransform matrix;
+			if (have_trans) {
+				double m[6];
+				for (int i = 0; i < 6; ++i) streamer.instream >> m[i];
+				// order in cairo terms: xx, xy, x0, yx, yy, y0
+				//                       11, 21, 31, 12, 22, 32
+				// TODO: somehow this still differs from the result in the Cairo device
+				matrix = QTransform(m[0], m[3], m[1], m[4], m[2], m[5]);
+			}
+			device->useGroup(index, matrix);
 		} else if (opcode == RKDCapture) {
 			QImage image = device->capture ();
 			quint32 w = image.width ();
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
index 92944ca4..258fd8b3 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
@@ -96,6 +96,9 @@ enum RKDOpcodes {
 	RKDStartRecordMask,    // 20
 	RKDFillStrokePathBegin,
 	RKDFillStrokePathEnd,
+	RKDDefineGroupBegin,
+	RKDDefineGroupStep2,
+	RKDUseGroup,           // 25
 
 	// Synchronous operations
 	RKDFetchNextEvent      = 100,
@@ -112,7 +115,8 @@ enum RKDOpcodes {
 	RKDEndRecordClipPath,
 	RKDSetMask,
 	RKDEndRecordMask,
-	RKDClose,
+	RKDDefineGroupEnd,
+	RKDClose,             // 115
 
 	// Protocol operations
 	RKDCancel              = 200
@@ -181,7 +185,7 @@ static inline quint8 mapLineJoinStyle(quint8 from) {
 }
 
 #if RKD_RGE_VERSION >= 15
-static inline int mapCompostionModeEnum(int from) {
+static inline int mapCompositionModeEnum(int from) {
 	if (RKD_IN_FRONTEND) return from;
 	switch(from) {
 		MapEnum(R_GE_compositeClear, 2, QPainter::CompositionMode_Clear);
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
index 0e547ad6..c6ef49f3 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
@@ -95,7 +95,7 @@ void RKStartGraphicsDevice (double width, double height, double pointsize, const
 			                   // able to see our own devnum and call RKD_Create. Therefore, initialize
 			                   // devnum to 0, so as not to confuse the frontend
 			desc->id = id++;   // extra identifier to make sure, R and the frontend are really talking about the same device
-			                   // in case of potentially out-of-sync operations (notably RKDADjustSize)
+			                   // in case of potentially out-of-sync operations (notably RKDAdjustSize)
 			pGEDevDesc gdd = GEcreateDevDesc(dev);
 			gdd->displayList = R_NilValue;
 			GEaddDevice2(gdd, "RKGraphicsDevice");
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
index 3398ba45..86d638a8 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
@@ -840,7 +840,6 @@ SEXP RKD_SetMask (SEXP mask, SEXP ref, pDevDesc dev) {
 		RKD_IN_STREAM >> index;
 	}
 	return makeInt(index);
-	return R_NilValue;
 }
 
 void RKD_ReleaseMask (SEXP ref, pDevDesc dev) {
@@ -852,13 +851,71 @@ void RKD_ReleaseMask (SEXP ref, pDevDesc dev) {
 
 #if R_VERSION >= R_Version(4,2,0)
 SEXP RKD_DefineGroup(SEXP source, int op, SEXP destination, pDevDesc dev) {
-	return R_NilValue;
+	RK_TRACE(GRAPHICS_DEVICE);
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDDefineGroupBegin, dev);
+	}
+
+	// Play generator function for destination
+	int error;
+	if (destination != R_NilValue) {
+		SEXP dest_func = PROTECT(Rf_lang1(destination));
+		R_tryEval(dest_func, R_GlobalEnv, &error);
+		UNPROTECT(1);
+	}
+
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDDefineGroupStep2, dev);
+		RKD_OUT_STREAM << (qint8) mapCompositionModeEnum(op);
+	}
+
+	// Play generator function for source
+	SEXP src_func = PROTECT(Rf_lang1(source));
+	R_tryEval(src_func, R_GlobalEnv, &error);
+	UNPROTECT(1);
+
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDDefineGroupEnd, dev);
+	}
+	qint32 index = -1;
+	{
+		RKGraphicsDataStreamReadGuard rguard;
+		RKD_IN_STREAM >> index;
+	}
+	return makeInt(index);
 }
 
 void RKD_UseGroup(SEXP ref, SEXP trans, pDevDesc dev) {
+	RK_TRACE(GRAPHICS_DEVICE);
+
+	// NOTE: chaching parameters before starting the write, in case they are ill-formed and produce errors
+	qint32 index = 0;
+	if (!Rf_isNull(ref)) index = INTEGER(ref)[0];
+	bool have_trans = (trans != R_NilValue);
+	double matrix[6];
+	if (have_trans) {
+		for (int i = 0; i < 6; ++i) matrix[i] = REAL(trans)[i];  // order in cairo terms: xx, xy, x0, yx, yy, y0
+	}
+
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDUseGroup, dev);
+		RKD_OUT_STREAM << index;
+		if (have_trans) {
+			RKD_OUT_STREAM << (qint8) 1;
+			for (int i = 0; i < 6; ++i) RKD_OUT_STREAM << matrix[i];
+		} else {
+			RKD_OUT_STREAM << (qint8) 0;
+		}
+	}
 }
 
 void RKD_ReleaseGroup(SEXP ref, pDevDesc dev) {
+	RK_TRACE(GRAPHICS_DEVICE);
+	releaseCachedResource(RKDGroup, ref, dev);
 }
 
 void doFillAndOrStroke(SEXP path, const pGEcontext gc, pDevDesc dev, bool fill, int rule, bool stroke) {



More information about the rkward-tracker mailing list