Commit 66898e7f authored by Méven Car's avatar Méven Car

Wayland: Allow to take single screen screenshots using scale factor without loss

Summary:
The screenshot made on screens with scale factor were downscaled by their scale factor making them blurry.
It prevents taking screenshots of missing Hidpi related bugs showing the issues under Wayland.

This fix the case of a single screenshot, but not the rest:
Multiscreen screenshot downscales the screen using scale factor.
Spectacle rectangular selection screenshot is broken as soon as some scale factor different than 1 is used on any screen.

Test Plan:
Under Wayland with a scale factor on a screen, take a screenshot using spectacle.
The output image is not downscaled and has the same size as the screen resolution.

No other change to any other screenshot mode, or under X.

Reviewers: davidedmundson, #kwin

Reviewed By: davidedmundson, #kwin

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D29010
parent e95d1dc9
......@@ -159,6 +159,7 @@ static xcb_pixmap_t xpixmapFromImage(const QImage &image)
void ScreenShotEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
m_cachedOutputGeometry = data.outputGeometry();
m_cachedScale = data.screenScale();
effects->paintScreen(mask, region, data);
}
......@@ -287,12 +288,13 @@ void ScreenShotEffect::postPaintScreen()
// doesn't intersect, not going onto this screenshot
return;
}
const QImage img = blitScreenshot(intersection);
if (img.size() == m_scheduledGeometry.size()) {
QImage img = blitScreenshot(intersection, m_cachedScale);
if (img.size() == (m_scheduledGeometry.size() * m_cachedScale)) {
// we are done
sendReplyImage(img);
return;
}
img.setDevicePixelRatio(m_cachedScale);
if (m_multipleOutputsImage.isNull()) {
m_multipleOutputsImage = QImage(m_scheduledGeometry.size(), QImage::Format_ARGB32);
m_multipleOutputsImage.fill(Qt::transparent);
......@@ -604,24 +606,31 @@ QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bo
return QString();
}
QImage ScreenShotEffect::blitScreenshot(const QRect &geometry)
QImage ScreenShotEffect::blitScreenshot(const QRect &geometry, const 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<int>(width * scale);
height = static_cast<int>(height * scale);
img = QImage(width, height, QImage::Format_ARGB32);
GLTexture tex(GL_RGBA8, width, height);
GLRenderTarget target(tex);
target.blitFromFramebuffer(geometry);
// copy content from framebuffer into image
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<GLvoid*>(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, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)img.bits());
}
ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height());
ScreenShotEffect::convertFromGLImage(img, width, height);
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
......@@ -635,7 +644,7 @@ QImage ScreenShotEffect::blitScreenshot(const QRect &geometry)
#endif
if (m_captureCursor) {
grabPointerImage(img, geometry.x(), geometry.y());
grabPointerImage(img, geometry.x() * scale, geometry.y() * scale);
}
return img;
......
......@@ -141,7 +141,7 @@ private Q_SLOTS:
private:
void grabPointerImage(QImage& snapshot, int offsetx, int offsety);
QImage blitScreenshot(const QRect &geometry);
QImage blitScreenshot(const QRect &geometry, const qreal scale = 1.0);
QString saveTempImage(const QImage &img);
void sendReplyImage(const QImage &img);
enum class InfoMessageMode {
......@@ -167,6 +167,7 @@ private:
};
WindowMode m_windowMode = WindowMode::NoCapture;
int m_fd = -1;
qreal m_cachedScale;
};
} // namespace
......
......@@ -423,6 +423,7 @@ class ScreenPaintData::Private
public:
QMatrix4x4 projectionMatrix;
QRect outputGeometry;
qreal screenScale;
};
ScreenPaintData::ScreenPaintData()
......@@ -431,12 +432,13 @@ ScreenPaintData::ScreenPaintData()
{
}
ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry)
ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry, const qreal screenScale)
: PaintData()
, d(new Private())
{
d->projectionMatrix = projectionMatrix;
d->outputGeometry = outputGeometry;
d->screenScale = screenScale;
}
ScreenPaintData::~ScreenPaintData() = default;
......@@ -526,6 +528,11 @@ QRect ScreenPaintData::outputGeometry() const
return d->outputGeometry;
}
qreal ScreenPaintData::screenScale() const
{
return d->screenScale;
}
//****************************************
// Effect
//****************************************
......
......@@ -2991,7 +2991,7 @@ class KWINEFFECTS_EXPORT ScreenPaintData : public PaintData
{
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 @@ public:
* @since 5.9
*/
QRect outputGeometry() const;
/**
* The scale factor for the output
*
* @since 5.19
*/
qreal screenScale() const;
private:
class Private;
QScopedPointer<Private> d;
......
......@@ -649,7 +649,7 @@ qint64 SceneOpenGL::paint(const QRegion &damage, const QList<Toplevel *> &toplev
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();
......
......@@ -105,7 +105,7 @@ Scene::~Scene()
// 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, const qreal screenScale)
{
const QSize &screenSize = screens()->size();
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
......@@ -145,7 +145,7 @@ void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint
paintBackground(region);
}
ScreenPaintData data(projection, outputGeometry);
ScreenPaintData data(projection, outputGeometry, screenScale);
effects->paintScreen(*mask, region, data);
foreach (Window *w, stacking_order) {
......
......@@ -213,7 +213,7 @@ protected:
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(), const qreal screenScale = 1.0);
// Render cursor texture in case hardware cursor is disabled/non-applicable
virtual void paintCursor() = 0;
friend class EffectsHandlerImpl;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment