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
This commit is contained in:
Méven Car 2020-04-20 16:11:21 +02:00
parent e95d1dc950
commit 66898e7f46
7 changed files with 40 additions and 16 deletions

View file

@ -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;

View file

@ -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

View file

@ -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
//****************************************

View file

@ -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;

View file

@ -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();

View file

@ -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) {

View file

@ -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;