[education/rkward] /: Implement fill/stroke/Path

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


Git commit 5d4156ae248b086c08b92c530b3f10c1482c389d by Thomas Friedrichsmeier.
Committed on 03/04/2022 at 07:41.
Pushed by tfry into branch 'master'.

Implement fill/stroke/Path

M  +2    -1    ChangeLog
M  +13   -3    rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
M  +1    -1    rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
M  +20   -1    rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
M  +11   -5    rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
M  +31   -10   rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp

https://invent.kde.org/education/rkward/commit/5d4156ae248b086c08b92c530b3f10c1482c389d

diff --git a/ChangeLog b/ChangeLog
index bace3374..b83ca7ce 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,7 +9,8 @@ TODOS for autotests:
 - Space-saving placement of main window status bar, and display more tips in it
 - Fixed: Intial graphics window size would be (mostly) ignored
 - Fix some buglets around closing on-screen devices
-- Implement new R graphics functions: gradients, patterns, clip paths, masks
+- Impelment R 4.2 graphics functions: stroked and filled paths
+- Implement R 4.1 graphics functions: gradients, patterns, clip paths, masks
 - Add icons to settings dialog for quick visual orientation
 - Merge ktexteditor (script) settings into the main settings dialog
 - Internal: Code cleanup around settings
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
index f3c2b2b4..468817a9 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
@@ -285,8 +285,7 @@ QPainterPath RKGraphicsDevice::endRecordPath(int fillrule) {
 	RK_TRACE(GRAPHICS_DEVICE);
 
 	QPainterPath ret = recorded_path;
-	if (fillrule == NonZeroWindingRule) ret.setFillRule(Qt::WindingFill);
-	else ret.setFillRule(Qt::OddEvenFill);
+	ret.setFillRule((Qt::FillRule) fillrule);
 
 	RK_ASSERT(!stashed_paths.isEmpty());
 	recorded_path = stashed_paths.takeLast();
@@ -356,6 +355,17 @@ bool RKGraphicsDevice::setMask(int index) {
 	return set == index;
 }
 
+void RKGraphicsDevice::fillStrokePath(const QPainterPath& path, const QBrush& brush, const QPen& pen) {
+	RK_TRACE (GRAPHICS_DEVICE);
+
+	if (current_mask) initMaskedDraw();
+	painter.setBrush(brush);
+	painter.setPen(pen);
+	painter.drawPath(path);
+	if (current_mask) commitMaskedDraw();
+	triggerUpdate ();
+}
+
 void RKGraphicsDevice::setClip (const QRectF& new_clip) {
 	RK_TRACE (GRAPHICS_DEVICE);
 
@@ -434,7 +444,7 @@ void RKGraphicsDevice::text(double x, double y, const QString& text, double rot,
 	if (recording_path) {
 		QPainterPath sub;
 		QSizeF size = strSize(text, font);
-		sub.addText(-(hadj * size.width()), y, font, text);
+		sub.addText(-(hadj * size.width()), 0, font, text);
 		QMatrix trans;
 		trans.translate(x, y);
 		trans.rotate(-rot);
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
index 9a69437c..4c2553d3 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
@@ -104,12 +104,12 @@ public:
 	int cachePath(QPainterPath &path);
 	void destroyCachedPath(int index);
 	bool setClipToCachedPath(int index);
-	void forceSync();
 	void startRecordMask();
 	QImage endRecordMask(bool luminance);
 	int registerMask(const QImage &mask);
 	void destroyMask(int index);
 	bool setMask(int index);
+	void fillStrokePath(const QPainterPath &path, const QBrush &brush, const QPen &pen);
 public slots:
 	void stopInteraction ();
 signals:
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
index 65e20212..c8b2a15b 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
@@ -394,7 +394,7 @@ void RKGraphicsDeviceFrontendTransmitter::newData () {
 		} else if (opcode == RKDEndRecordClipPath) {
 			qint8 fillrule;
 			streamer.instream >> fillrule;
-			QPainterPath p = device->endRecordPath(fillrule);
+			QPainterPath p = device->endRecordPath(mapFillRule(fillrule));
 			qint32 index = device->cachePath(p);
 			device->setClipToCachedPath(index);
 			streamer.outstream << (qint32) index;
@@ -415,6 +415,25 @@ void RKGraphicsDeviceFrontendTransmitter::newData () {
 			device->setMask(index);
 			streamer.outstream << (qint32) index;
 			streamer.writeOutBuffer();
+		} else if (opcode == RKDFillStrokePathBegin) {
+			device->startRecordPath();
+		} else if (opcode == RKDFillStrokePathEnd) {
+			quint8 fill, stroke;
+			qint8 fillrule = Qt::OddEvenFill;
+			QBrush brush;
+			QPen pen(Qt::NoPen);
+			streamer.instream >> fill;
+			if (fill) {
+				streamer.instream >> fillrule;
+				fillrule = mapFillRule(fillrule);
+				brush = readBrush(streamer.instream, device);
+			}
+			streamer.instream >> stroke;
+			if (stroke) {
+				pen = readPen(streamer.instream);
+			}
+			QPainterPath p = device->endRecordPath(fillrule);
+			device->fillStrokePath(p, brush, pen);
 		} 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 75574020..92944ca4 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
@@ -70,11 +70,6 @@ enum RKDGradientExtend {
 	GradientExtendRepeat
 };
 
-enum RKDFillRule {
-	NonZeroWindingRule,
-	EvenOddRule
-};
-
 enum RKDOpcodes {
 	// NOTE: the only point of the assigned int values is to ease debugging in case of trouble
 	// Asynchronous operations
@@ -99,6 +94,8 @@ enum RKDOpcodes {
 	RKDStartRecordTilingPattern,      // part of setPattern in R
 	RKDStartRecordClipPath,
 	RKDStartRecordMask,    // 20
+	RKDFillStrokePathBegin,
+	RKDFillStrokePathEnd,
 
 	// Synchronous operations
 	RKDFetchNextEvent      = 100,
@@ -216,6 +213,15 @@ static inline int mapCompostionModeEnum(int from) {
 	}
 	MapDefault(Rf_warning("Unsupported enumeration value %d", from), 0, QPainter::CompositionMode_SourceOver);
 }
+
+static inline quint8 mapFillRule(quint8 from) {
+	if (RKD_IN_FRONTEND) return from;
+	switch(from) {
+		MapEnum(R_GE_evenOddRule, 0, Qt::OddEvenFill);
+		MapEnum(R_GE_nonZeroWindingRule, 1, Qt::WindingFill);
+	}
+	MapDefault({}, 0x00, Qt::OddEvenFill);
+}
 #endif
 
 #endif
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
index 56e0d343..3398ba45 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
@@ -459,7 +459,7 @@ static void RKD_Mode (int mode, pDevDesc dev) {
 
 static void RKD_Raster (unsigned int *raster, int w, int h, double x, double y, double width, double height, double rot, Rboolean interpolate, const pGEcontext gc, pDevDesc dev) {
 	RK_TRACE(GRAPHICS_DEVICE);
-	Q_UNUSED (gc);
+	Q_UNUSED(gc); // No idea what this is supposed to be good for. R's own cairo device ignores it.
 
 	RKGraphicsDataStreamWriteGuard wguard;
 	WRITE_HEADER (RKDRaster, dev);
@@ -778,11 +778,7 @@ SEXP RKD_SetClipPath (SEXP path, SEXP ref, pDevDesc dev) {
 		RKGraphicsDataStreamWriteGuard wguard;
 		WRITE_HEADER(RKDEndRecordClipPath, dev);
 #if R_VERSION >= R_Version(4, 2, 0)
-		if (R_GE_clipPathFillRule(path) == R_GE_nonZeroWindingRule) {
-			RKD_OUT_STREAM << (qint8) NonZeroWindingRule;
-		} else {
-			RKD_OUT_STREAM << (qint8) EvenOddRule;
-		}
+		RKD_OUT_STREAM << (qint8) mapFillRule(R_GE_clipPathFillRule(path));
 #else
 		RKD_OUT_STREAM << (qint8) EvenOddRule;
 #endif
@@ -865,18 +861,43 @@ void RKD_UseGroup(SEXP ref, SEXP trans, pDevDesc dev) {
 void RKD_ReleaseGroup(SEXP ref, pDevDesc dev) {
 }
 
-void doFillAndOrStroke(SEXP path, const pGEcontext g, pDevDesc dev, bool fill, bool stroke) {
+void doFillAndOrStroke(SEXP path, const pGEcontext gc, pDevDesc dev, bool fill, int rule, bool stroke) {
+	RK_TRACE(GRAPHICS_DEVICE);
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDFillStrokePathBegin, dev);
+	}
+
+	// record the actual path
+	int error;
+	SEXP path_func = PROTECT(Rf_lang1(path));
+	R_tryEval(path_func, R_GlobalEnv, &error);
+	UNPROTECT(1);
+
+	{
+		RKGraphicsDataStreamWriteGuard wguard;
+		WRITE_HEADER(RKDFillStrokePathEnd, dev);
+		RKD_OUT_STREAM << (quint8) fill;
+		if (fill) {
+			RKD_OUT_STREAM << (qint8) mapFillRule(rule);
+			WRITE_FILL();
+		}
+		RKD_OUT_STREAM << (quint8) stroke;
+		if (stroke) {
+			WRITE_PEN();
+		}
+	}
 }
 
 void RKD_Stroke(SEXP path, const pGEcontext gc, pDevDesc dev) {
-	doFillAndOrStroke(path, gc, dev, false, true);
+	doFillAndOrStroke(path, gc, dev, false, 0, true);
 }
 
 void RKD_Fill(SEXP path, int rule, const pGEcontext gc, pDevDesc dev) {
-	doFillAndOrStroke(path, gc, dev, true, false);
+	doFillAndOrStroke(path, gc, dev, true, rule, false);
 }
 
 void RKD_FillStroke(SEXP path, int rule, const pGEcontext gc, pDevDesc dev) {
-	doFillAndOrStroke(path, gc, dev, true, true);
+	doFillAndOrStroke(path, gc, dev, true, rule, true);
 }
 #endif



More information about the rkward-tracker mailing list