[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