[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