50static void convertFromGLImage(QImage &img,
int w,
int h,
const OutputTransform &renderTargetTransformation)
55 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
57 uint *p =
reinterpret_cast<uint *
>(img.bits());
58 uint *end = p + w * h;
66 for (
int y = 0; y < h; y++) {
67 uint *q =
reinterpret_cast<uint *
>(img.scanLine(y));
68 for (
int x = 0; x < w; ++x) {
69 const uint pixel = *q;
70 *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff)
71 | (pixel & 0xff00ff00);
83 img = img.transformed(matrix.toTransform());
101 cancelWindowScreenShots();
102 cancelAreaScreenShots();
103 cancelScreenScreenShots();
109 if (data.screen == screen && data.flags == flags) {
110 return data.promise.future();
119 QFuture<QImage> future = data.
promise.future();
121 m_screenScreenShots.push_back(std::move(data));
130 if (data.area == area && data.flags == flags) {
131 return data.promise.future();
140 for (
Output *screen : screens) {
141 if (screen->geometry().intersects(area)) {
146 qreal devicePixelRatio = 1.0;
149 if (screen->scale() > devicePixelRatio) {
150 devicePixelRatio = screen->scale();
155 data.
result = QImage(area.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
156 data.
result.fill(Qt::transparent);
157 data.
result.setDevicePixelRatio(devicePixelRatio);
160 QFuture<QImage> future = data.
promise.future();
162 m_areaScreenShots.push_back(std::move(data));
171 if (data.window == window && data.flags == flags) {
172 return data.promise.future();
181 QFuture<QImage> future = data.
promise.future();
183 m_windowScreenShots.push_back(std::move(data));
189void ScreenShotEffect::cancelWindowScreenShots()
191 m_windowScreenShots.clear();
194void ScreenShotEffect::cancelAreaScreenShots()
196 m_areaScreenShots.clear();
199void ScreenShotEffect::cancelScreenScreenShots()
201 m_screenScreenShots.clear();
206 m_paintedScreen = screen;
210 takeScreenShot(&data);
212 m_windowScreenShots.clear();
214 for (
int i = m_areaScreenShots.size() - 1; i >= 0; --i) {
215 if (takeScreenShot(renderTarget, viewport, &m_areaScreenShots[i])) {
216 m_areaScreenShots.erase(m_areaScreenShots.begin() + i);
220 for (
int i = m_screenScreenShots.size() - 1; i >= 0; --i) {
221 if (takeScreenShot(renderTarget, viewport, &m_screenScreenShots[i])) {
222 m_screenScreenShots.erase(m_screenScreenShots.begin() + i);
233 qreal devicePixelRatio = 1;
242 if (
const Output *screen = window->
screen()) {
243 devicePixelRatio = screen->scale();
247 bool validTarget =
true;
248 std::unique_ptr<GLTexture> offscreenTexture;
249 std::unique_ptr<GLFramebuffer> target;
251 offscreenTexture =
GLTexture::allocate(GL_RGBA8, QSizeF(geometry.size() * devicePixelRatio).toSize());
252 if (!offscreenTexture) {
255 offscreenTexture->setFilter(GL_LINEAR);
256 offscreenTexture->setWrapMode(GL_CLAMP_TO_EDGE);
257 target = std::make_unique<GLFramebuffer>(offscreenTexture.get());
258 validTarget = target->valid();
268 RenderTarget renderTarget(target.get());
269 RenderViewport viewport(geometry, devicePixelRatio, renderTarget);
271 glClearColor(0.0, 0.0, 0.0, 0.0);
272 glClear(GL_COLOR_BUFFER_BIT);
273 glClearColor(0.0, 0.0, 0.0, 1.0);
275 QMatrix4x4 projection;
276 projection.ortho(QRect(0, 0, geometry.width() * devicePixelRatio, geometry.height() * devicePixelRatio));
282 img = QImage(offscreenTexture->size(), QImage::Format_ARGB32);
283 img.setDevicePixelRatio(devicePixelRatio);
284 glReadnPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, img.sizeInBytes(),
285 static_cast<GLvoid *
>(img.bits()));
287 convertFromGLImage(img, img.width(), img.height(), renderTarget.transform());
291 grabPointerImage(img, geometry.x(), geometry.y());
294 screenshot->
promise.addResult(img);
299bool ScreenShotEffect::takeScreenShot(
const RenderTarget &renderTarget,
const RenderViewport &viewport, ScreenShotAreaData *screenshot)
303 QImage snapshot = blitScreenshot(renderTarget, viewport, screenshot->area);
305 grabPointerImage(snapshot, screenshot->area.x(), screenshot->area.y());
307 screenshot->promise.addResult(snapshot);
308 screenshot->promise.finish();
311 if (!screenshot->screens.contains(m_paintedScreen)) {
314 screenshot->screens.removeOne(m_paintedScreen);
316 const QRect sourceRect = screenshot->area & m_paintedScreen->
geometry();
317 qreal sourceDevicePixelRatio = 1.0;
319 sourceDevicePixelRatio = m_paintedScreen->
scale();
322 const QImage snapshot = blitScreenshot(renderTarget, viewport, sourceRect, sourceDevicePixelRatio);
323 const QRect nativeArea(screenshot->area.topLeft(),
324 screenshot->area.size() * screenshot->result.devicePixelRatio());
326 QPainter painter(&screenshot->result);
327 painter.setRenderHint(QPainter::SmoothPixmapTransform);
328 painter.setWindow(nativeArea);
329 painter.drawImage(sourceRect, snapshot);
332 if (screenshot->screens.isEmpty()) {
334 grabPointerImage(screenshot->result, screenshot->area.x(), screenshot->area.y());
336 screenshot->promise.addResult(screenshot->result);
337 screenshot->promise.finish();
345bool ScreenShotEffect::takeScreenShot(
const RenderTarget &renderTarget,
const RenderViewport &viewport, ScreenShotScreenData *screenshot)
347 if (m_paintedScreen && screenshot->screen != m_paintedScreen) {
351 qreal devicePixelRatio = 1.0;
353 devicePixelRatio = screenshot->screen->scale();
356 QImage snapshot = blitScreenshot(renderTarget, viewport, screenshot->screen->geometry(), devicePixelRatio);
358 const int xOffset = screenshot->screen->geometry().x();
359 const int yOffset = screenshot->screen->geometry().y();
360 grabPointerImage(snapshot, xOffset, yOffset);
363 screenshot->promise.addResult(snapshot);
364 screenshot->promise.finish();
369QImage ScreenShotEffect::blitScreenshot(
const RenderTarget &renderTarget,
const RenderViewport &viewport,
const QRect &geometry, qreal devicePixelRatio)
const
375 const QSize nativeSize = renderTarget.transform().map(
379 image = QImage(nativeSize, QImage::Format_ARGB32);
385 GLFramebuffer target(texture.get());
386 if (renderTarget.texture()) {
389 binder.shader()->setColorspaceUniformsToSRGB(renderTarget.colorDescription());
390 QMatrix4x4 projectionMatrix;
391 projectionMatrix.scale(1, -1);
392 projectionMatrix *= renderTarget.transform().toMatrix();
393 projectionMatrix.scale(1, -1);
394 projectionMatrix.ortho(QRect(QPoint(), nativeSize));
396 renderTarget.texture()->render(viewport.mapToRenderTargetTexture(geometry),
infiniteRegion(), nativeSize);
398 target.blitFromFramebuffer(viewport.mapToRenderTarget(geometry));
401 glReadPixels(0, 0, nativeSize.width(), nativeSize.height(), GL_RGBA,
402 GL_UNSIGNED_BYTE,
static_cast<GLvoid *
>(image.bits()));
404 convertFromGLImage(image, nativeSize.width(), nativeSize.height(), renderTarget.transform());
407 image.setDevicePixelRatio(devicePixelRatio);
411void ScreenShotEffect::grabPointerImage(QImage &snapshot,
int xOffset,
int yOffset)
const
418 if (cursor.image().isNull()) {
422 QPainter painter(&snapshot);
423 painter.setRenderHint(QPainter::SmoothPixmapTransform);
424 painter.drawImage(
effects->
cursorPos() - cursor.hotSpot() - QPoint(xOffset, yOffset), cursor.image());
429 return (!m_windowScreenShots.empty() || !m_areaScreenShots.empty() || !m_screenScreenShots.empty())
438void ScreenShotEffect::handleScreenAdded()
440 cancelAreaScreenShots();
443void ScreenShotEffect::handleScreenRemoved(Output *screen)
445 cancelAreaScreenShots();
447 std::erase_if(m_screenScreenShots, [screen](
const auto &screenshot) {
448 return screenshot.screen == screen;
452void ScreenShotEffect::handleWindowClosed(EffectWindow *window)
454 std::erase_if(m_windowScreenShots, [window](
const auto &screenshot) {
455 return screenshot.window == window;
461#include "moc_screenshot.cpp"
Representation of a window used by/for Effect classes.
QRectF clientGeometry() const
Q_SCRIPTABLE void addRepaintFull()
QRectF frameGeometry() const
void screenAdded(KWin::Output *screen)
Display * waylandDisplay() const
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data)
void windowClosed(KWin::EffectWindow *w)
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen)
void screenRemoved(KWin::Output *screen)
Q_SCRIPTABLE void addRepaint(const QRectF &r)
bool isCursorHidden() const
bool isScreenLocked() const
QRect virtualScreenGeometry
QList< Output * > screens() const
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
PlatformCursorImage cursorImage() const
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
@ ModelViewProjectionMatrix
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
int requestedEffectChainPosition() const override
~ScreenShotEffect() override
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) override
bool isActive() const override
QFuture< QImage > scheduleScreenShot(Output *screen, ScreenShotFlags flags={})
void setYTranslation(qreal translate)
void setProjectionMatrix(const QMatrix4x4 &matrix)
void setXTranslation(qreal translate)
@ PAINT_WINDOW_TRANSFORMED
@ PAINT_WINDOW_TRANSLUCENT
KWIN_EXPORT QRect infiniteRegion()
glReadnPixels_func glReadnPixels
KWIN_EXPORT QPoint snapToPixelGrid(const QPointF &point)
@ ScreenShotIncludeShadow
Include the window shadow.
@ ScreenShotNativeResolution
Take the screenshot at the native resolution.
@ ScreenShotIncludeCursor
Include the cursor.
@ ScreenShotIncludeDecoration
Include window titlebar and borders.
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
QPromise< QImage > promise
QList< Output * > screens
QPromise< QImage > promise
QPromise< QImage > promise