[education/rkward] /: Implement masking for all drawing ops
Thomas Friedrichsmeier
null at kde.org
Thu Mar 24 21:02:51 GMT 2022
Git commit fbdc600c14a3871388628e7fc1516fe8005b8873 by Thomas Friedrichsmeier.
Committed on 24/03/2022 at 20:40.
Pushed by tfry into branch 'master'.
Implement masking for all drawing ops
M +1 -1 ChangeLog
M +44 -43 rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
M +2 -0 rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
https://invent.kde.org/education/rkward/commit/fbdc600c14a3871388628e7fc1516fe8005b8873
diff --git a/ChangeLog b/ChangeLog
index e2ebe0cb..92e08b89 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@ 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
+- Implement new R 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 fa72f383..fb269003 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
@@ -49,6 +49,7 @@ RKGraphicsDevice::RKGraphicsDevice (double width, double height, const QString &
dialog = 0;
id = 0;
recording_path = false;
+ current_mask = 0;
view = new QLabel ();
view->installEventFilter (this);
view->setScaledContents (true); // this is just for preview during scaling. The area will be re-sized and re-drawn from R.
@@ -123,6 +124,19 @@ RKGraphicsDevice::PaintContext RKGraphicsDevice::popContext() {
return ret;
}
+void RKGraphicsDevice::initMaskedDraw(){
+ RK_ASSERT(current_mask);
+ pushContext(area.width(), area.height(), 0, 0, false);
+}
+
+void RKGraphicsDevice::commitMaskedDraw() {
+ RK_ASSERT(current_mask);
+ QImage mask = cached_masks.value(current_mask);
+ QImage masked = popContext().surface;
+ masked.setAlphaChannel(mask); // NOTE: Qt docs: "If the image already has an alpha channel, the existing alpha channel is multiplied with the new one."
+ painter.drawImage(0, 0, masked);
+}
+
void RKGraphicsDevice::startRecordTilingPattern(double width, double height, double x, double y) {
RK_TRACE (GRAPHICS_DEVICE);
pushContext(width, height, x, y, false);
@@ -187,22 +201,6 @@ void RKGraphicsDevice::updateNow () {
beginPainter();
}
-/** This is definitely lame, but at least as of Qt 5.12.8, calling QPainter::end() does _not_ mean, changes are synced to the
- * paint device. They will be synced, eventually, but when trying to capture a tiling pattern, we want a clean separation
- * between the paints going to the main canvas and those going to the pattern space, immediately.
- *
- * This function achieves that. Note that it is also implemented as a synchronous operation on the stream, so Qt will definitely
- * get around to process some events after this was called, which is probably also needed.
- *
- * Without this function artifacts will occur inside tiling patterns some, but not all of the time.
- *
- * KF6 TODO: check if still needed with Qt6. */
-void RKGraphicsDevice::forceSync() {
- RK_TRACE (GRAPHICS_DEVICE);
- updateNow();
- if(painter.isActive()) painter.end();
-}
-
void RKGraphicsDevice::checkSize() {
RK_TRACE (GRAPHICS_DEVICE);
if(!view) return;
@@ -240,12 +238,14 @@ void RKGraphicsDevice::clear(const QBrush& brush) {
return;
}
- if (painter.isActive()) painter.end();
- if (brush.style() == Qt::NoBrush) area.fill(QColor(255, 255, 255, 255));
- else {
+ if (current_mask) initMaskedDraw();
+ if (brush.style() == Qt::NoBrush) {
+ painter.setBrush(QColor(255, 255, 255, 255));
+ } else {
painter.setBrush(brush);
- painter.drawRect(0, 0, area.width(), area.height());
}
+ painter.drawRect(0, 0, area.width(), area.height());
+ if (current_mask) commitMaskedDraw();
updateNow ();
setClip (area.rect ()); // R's devX11.c resets clip on clear, so we do this, too.
@@ -365,9 +365,11 @@ void RKGraphicsDevice::circle (double x, double y, double r, const QPen& pen, co
recorded_path.addEllipse(x-r, y-r, r+r, r+r);
return;
}
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
painter.setBrush (brush);
painter.drawEllipse (x - r, y - r, r+r, r+r);
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
@@ -379,6 +381,8 @@ void RKGraphicsDevice::line (double x1, double y1, double x2, double y2, const Q
recorded_path.lineTo(x2, y2);
return;
}
+
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
// HACK: There seems to be a bug in QPainter (Qt 4.8.4), which can shift connected lines (everything but the first polyline)
// towards the direction where the previous line came from. The result is that line drawn via drawLine() and drawPolyline() do
@@ -389,30 +393,24 @@ void RKGraphicsDevice::line (double x1, double y1, double x2, double y2, const Q
points[1] = QPointF (x2, y2);
painter.drawPolyline (points, 2);
// painter.drawLine (x1, y1, x2, y2);
+ if (current_mask) commitMaskedDraw();
+
triggerUpdate ();
}
void RKGraphicsDevice::rect (const QRectF& rec, const QPen& pen, const QBrush& brush) {
RK_TRACE (GRAPHICS_DEVICE);
- if (current_mask) {
- pushContext(area.width(), area.height(), 0, 0, false);
- }
-
if (recording_path) {
recorded_path.addRect(rec);
return;
}
+
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
painter.setBrush (brush);
painter.drawRect (rec);
-
- if (current_mask) {
- QImage mask = cached_masks.value(current_mask);
- QImage masked = popContext().surface;
- masked.setAlphaChannel(mask); // NOTE: QT docs: "If the image already has an alpha channel, the existing alpha channel is multiplied with the new one."
- painter.drawImage(0, 0, masked);
- }
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
@@ -428,14 +426,9 @@ QSizeF RKGraphicsDevice::strSize(const QString& text, const QFont& font) {
void RKGraphicsDevice::text(double x, double y, const QString& text, double rot, double hadj, const QColor& col, const QFont& font) {
RK_TRACE(GRAPHICS_DEVICE);
- if (current_mask) {
- pushContext(area.width(), area.height(), 0, 0, false);
- }
-
- painter.save();
- QSizeF size = strSize(text, font); // NOTE: side-effect of setting font!
if (recording_path) {
QPainterPath sub;
+ QSizeF size = strSize(text, font);
sub.addText(-(hadj * size.width()), y, font, text);
QMatrix trans;
trans.translate(x, y);
@@ -443,6 +436,10 @@ void RKGraphicsDevice::text(double x, double y, const QString& text, double rot,
recorded_path.addPath(trans.map(sub));
return;
}
+
+ if (current_mask) initMaskedDraw();
+ painter.save();
+ QSizeF size = strSize(text, font); // NOTE: side-effect of setting font!
// painter.setFont(font);
painter.setPen(QPen(col));
painter.translate(x, y);
@@ -450,13 +447,8 @@ void RKGraphicsDevice::text(double x, double y, const QString& text, double rot,
painter.drawText(-(hadj * size.width()), 0, text);
// painter.drawRect(painter.fontMetrics().boundingRect(text)); // for debugging
painter.restore(); // undo rotation / translation
+ if (current_mask) commitMaskedDraw();
- if (current_mask) {
- painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
- painter.drawImage(0, 0, cached_masks.value(current_mask));
- QImage masked = popContext().surface;
- painter.drawImage(0, 0, masked);
- }
triggerUpdate();
}
@@ -478,9 +470,11 @@ void RKGraphicsDevice::polygon (const QPolygonF& pol, const QPen& pen, const QBr
recorded_path.addPolygon(pol);
return;
}
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
painter.setBrush (brush);
painter.drawPolygon (pol);
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
@@ -491,8 +485,11 @@ void RKGraphicsDevice::polyline (const QPolygonF& pol, const QPen& pen) {
recorded_path.addPolygon(pol);
return;
}
+
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
painter.drawPolyline (pol);
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
@@ -511,15 +508,18 @@ void RKGraphicsDevice::polypath (const QVector<QPolygonF>& polygons, bool windin
return;
}
+ if (current_mask) initMaskedDraw();
painter.setPen (pen);
painter.setBrush (brush);
painter.drawPath (path);
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
void RKGraphicsDevice::image (const QImage& image, const QRectF& target_rect, double rot, bool interpolate) {
RK_TRACE (GRAPHICS_DEVICE);
+ if (current_mask) initMaskedDraw();
painter.save ();
QRectF tr = target_rect;
painter.translate (tr.x (), tr.y ());
@@ -528,6 +528,7 @@ void RKGraphicsDevice::image (const QImage& image, const QRectF& target_rect, do
painter.setRenderHint (QPainter::SmoothPixmapTransform, interpolate);
painter.drawImage (tr, image, image.rect ());
painter.restore ();
+ if (current_mask) commitMaskedDraw();
triggerUpdate ();
}
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
index b60a88a7..eaa3f52c 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
@@ -164,6 +164,8 @@ private:
void beginPainter();
void pushContext(double width, double height, double x, double y, bool record_path);
PaintContext popContext();
+ void initMaskedDraw();
+ void commitMaskedDraw();
};
#endif
More information about the rkward-tracker
mailing list