diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h --- a/effects/screenshot/screenshot.h +++ b/effects/screenshot/screenshot.h @@ -141,7 +141,7 @@ private: void grabPointerImage(QImage& snapshot, int offsetx, int offsety); - QImage blitScreenshot(const QRect &geometry); + QImage blitScreenshot(const QRect &geometry, qreal scale = 1.0); QString saveTempImage(const QImage &img); void sendReplyImage(const QImage &img); enum class InfoMessageMode { @@ -156,7 +156,7 @@ QRect m_scheduledGeometry; QDBusMessage m_replyMessage; QRect m_cachedOutputGeometry; - QImage m_multipleOutputsImage; + QVector m_cacheOutputsImages; QRegion m_multipleOutputsRendered; bool m_captureCursor = false; enum class WindowMode { @@ -167,6 +167,7 @@ }; WindowMode m_windowMode = WindowMode::NoCapture; int m_fd = -1; + qreal m_cachedScale; }; } // namespace diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -159,6 +159,7 @@ void ScreenShotEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data) { m_cachedOutputGeometry = data.outputGeometry(); + m_cachedScale = data.screenScale(); effects->paintScreen(mask, region, data); } @@ -283,31 +284,54 @@ if (!m_cachedOutputGeometry.isNull()) { // special handling for per-output geometry rendering const QRect intersection = m_scheduledGeometry.intersected(m_cachedOutputGeometry); + if (intersection.isEmpty()) { // doesn't intersect, not going onto this screenshot return; } - const QImage img = blitScreenshot(intersection); - if (img.size() == m_scheduledGeometry.size()) { + + const QImage img = blitScreenshot(intersection, m_cachedScale); + + if (img.size() == QSize(m_scheduledGeometry.width() * m_cachedScale, + m_scheduledGeometry.height() * m_cachedScale)) { // we are done sendReplyImage(img); return; } - if (m_multipleOutputsImage.isNull()) { - m_multipleOutputsImage = QImage(m_scheduledGeometry.size(), QImage::Format_ARGB32); - m_multipleOutputsImage.fill(Qt::transparent); - } - QPainter p; - p.begin(&m_multipleOutputsImage); - p.drawImage(intersection.topLeft() - m_scheduledGeometry.topLeft(), img); - p.end(); + + m_cacheOutputsImages.append(img); + m_multipleOutputsRendered = m_multipleOutputsRendered.united(intersection); + if (m_multipleOutputsRendered.boundingRect() == m_scheduledGeometry) { + + int width = 0; + int height = 0; + // find the output image size + for (const QImage& img: m_cacheOutputsImages) { + width = qMax(width, img.width()); + height = height + img.height(); + } + QSize outputSize(width, height); + QImage m_multipleOutputsImage = QImage(outputSize, QImage::Format_ARGB32); + + QPoint pointer(0, 0); + QPainter p; + p.begin(&m_multipleOutputsImage); + + // reassemble images together + // stacking them on the y axis on top of each until better implemented + for (const QImage& img: m_cacheOutputsImages) { + p.drawImage(pointer, img); + pointer.setY(pointer.y() + img.height()); + } + p.end(); + sendReplyImage(m_multipleOutputsImage); } } else { - const QImage img = blitScreenshot(m_scheduledGeometry); + const QImage img = blitScreenshot(m_scheduledGeometry, m_cachedScale); sendReplyImage(img); } } @@ -332,10 +356,10 @@ QDBusConnection::sessionBus().send(m_replyMessage.createReply(saveTempImage(img))); } m_scheduledGeometry = QRect(); - m_multipleOutputsImage = QImage(); m_multipleOutputsRendered = QRegion(); m_captureCursor = false; m_windowMode = WindowMode::NoCapture; + m_cacheOutputsImages = QVector(); } QString ScreenShotEffect::saveTempImage(const QImage &img) @@ -604,38 +628,47 @@ return QString(); } -QImage ScreenShotEffect::blitScreenshot(const QRect &geometry) +QImage ScreenShotEffect::blitScreenshot(const QRect &geometry, qreal scale) { QImage img; if (effects->isOpenGLCompositing()) { - img = QImage(geometry.size(), QImage::Format_ARGB32); + int width = geometry.width(); + int height = geometry.height(); if (GLRenderTarget::blitSupported() && !GLPlatform::instance()->isGLES()) { - GLTexture tex(GL_RGBA8, geometry.width(), geometry.height()); + + width = static_cast(width * scale); + height = static_cast(height * scale); + + img = QImage(width, height, QImage::Format_ARGB32); + img.setDevicePixelRatio(scale); + + GLTexture tex(GL_RGBA8, width, height); GLRenderTarget target(tex); - target.blitFromFramebuffer(geometry); // copy content from framebuffer into image + target.blitFromFramebuffer(geometry); tex.bind(); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)img.bits()); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast(img.bits())); tex.unbind(); } else { - glReadPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)img.bits()); + img = QImage(width, height, QImage::Format_ARGB32); + glReadPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, static_cast(img.bits())); } - ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height()); + ScreenShotEffect::convertFromGLImage(img, width, height); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { - xcb_image_t *xImage = nullptr; + xcb_image_t *xImage = nullptr; img = xPictureToImage(effects->xrenderBufferPicture(), geometry, &xImage); if (xImage) { xcb_image_destroy(xImage); } } #endif if (m_captureCursor) { - grabPointerImage(img, geometry.x(), geometry.y()); + grabPointerImage(img, static_cast(geometry.x() * scale), static_cast(geometry.y() * scale)); } return img; diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -2991,7 +2991,7 @@ { public: ScreenPaintData(); - ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry = QRect()); + ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry = QRect(), const qreal screenScale = 1.0); ScreenPaintData(const ScreenPaintData &other); ~ScreenPaintData() override; /** @@ -3053,6 +3053,13 @@ * @since 5.9 */ QRect outputGeometry() const; + + /** + * @returns the ratio between the virtual geometry space and the rendering for the painted screen + * @since 5.19 + */ + qreal screenScale() const; + private: class Private; QScopedPointer d; diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -423,20 +423,22 @@ public: QMatrix4x4 projectionMatrix; QRect outputGeometry; + qreal screenScale; }; ScreenPaintData::ScreenPaintData() : PaintData() , d(new Private()) { } -ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry) +ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry, qreal screenScale) : PaintData() , d(new Private()) { d->projectionMatrix = projectionMatrix; d->outputGeometry = outputGeometry; + d->screenScale = screenScale; } ScreenPaintData::~ScreenPaintData() = default; @@ -454,6 +456,7 @@ setRotationAngle(other.rotationAngle()); d->projectionMatrix = other.d->projectionMatrix; d->outputGeometry = other.d->outputGeometry; + d->screenScale = other.d->screenScale; } ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs) @@ -469,6 +472,7 @@ setRotationAngle(rhs.rotationAngle()); d->projectionMatrix = rhs.d->projectionMatrix; d->outputGeometry = rhs.d->outputGeometry; + d->screenScale = rhs.d->screenScale; return *this; } @@ -526,6 +530,11 @@ return d->outputGeometry; } +qreal ScreenPaintData::screenScale() const +{ + return d->screenScale; +} + //**************************************** // Effect //**************************************** diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -649,7 +649,7 @@ int mask = 0; updateProjectionMatrix(); - paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo); // call generic implementation + paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo, screens()->scale(i)); // call generic implementation paintCursor(); GLVertexBuffer::streamingBuffer()->endOfFrame(); diff --git a/scene.h b/scene.h --- a/scene.h +++ b/scene.h @@ -213,7 +213,7 @@ void clearStackingOrder(); // shared implementation, starts painting the screen void paintScreen(int *mask, const QRegion &damage, const QRegion &repaint, - QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection = QMatrix4x4(), const QRect &outputGeometry = QRect()); + QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection = QMatrix4x4(), const QRect &outputGeometry = QRect(), qreal scale = 1); // Render cursor texture in case hardware cursor is disabled/non-applicable virtual void paintCursor() = 0; friend class EffectsHandlerImpl; diff --git a/scene.cpp b/scene.cpp --- a/scene.cpp +++ b/scene.cpp @@ -105,7 +105,7 @@ // returns mask and possibly modified region void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint, - QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection, const QRect &outputGeometry) + QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection, const QRect &outputGeometry, qreal scale) { const QSize &screenSize = screens()->size(); const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height()); @@ -145,7 +145,7 @@ paintBackground(region); } - ScreenPaintData data(projection, outputGeometry); + ScreenPaintData data(projection, outputGeometry, scale); effects->paintScreen(*mask, region, data); foreach (Window *w, stacking_order) {