1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "scene_qpainter.h"
10 #include "platformqpaintersurfacetexture.h"
11 // KWin
12 #include "abstract_client.h"
13 #include "composite.h"
14 #include "cursor.h"
15 #include "decorations/decoratedclient.h"
16 #include "deleted.h"
17 #include "effects.h"
18 #include "main.h"
19 #include "renderloop.h"
20 #include "screens.h"
21 #include "surfaceitem.h"
22 #include "toplevel.h"
23 #include "platform.h"
24 #include "windowitem.h"
25 #include "abstract_output.h"
26 
27 #include <kwineffectquickview.h>
28 // Qt
29 #include <QDebug>
30 #include <QPainter>
31 #include <KDecoration2/Decoration>
32 
33 #include <cmath>
34 
35 namespace KWin
36 {
37 
38 //****************************************
39 // SceneQPainter
40 //****************************************
createScene(QObject * parent)41 SceneQPainter *SceneQPainter::createScene(QObject *parent)
42 {
43     QScopedPointer<QPainterBackend> backend(kwinApp()->platform()->createQPainterBackend());
44     if (backend.isNull()) {
45         return nullptr;
46     }
47     if (backend->isFailed()) {
48         return nullptr;
49     }
50     return new SceneQPainter(backend.take(), parent);
51 }
52 
SceneQPainter(QPainterBackend * backend,QObject * parent)53 SceneQPainter::SceneQPainter(QPainterBackend *backend, QObject *parent)
54     : Scene(parent)
55     , m_backend(backend)
56     , m_painter(new QPainter())
57 {
58 }
59 
~SceneQPainter()60 SceneQPainter::~SceneQPainter()
61 {
62 }
63 
compositingType() const64 CompositingType SceneQPainter::compositingType() const
65 {
66     return QPainterCompositing;
67 }
68 
initFailed() const69 bool SceneQPainter::initFailed() const
70 {
71     return false;
72 }
73 
paintGenericScreen(int mask,const ScreenPaintData & data)74 void SceneQPainter::paintGenericScreen(int mask, const ScreenPaintData &data)
75 {
76     m_painter->save();
77     m_painter->translate(data.xTranslation(), data.yTranslation());
78     m_painter->scale(data.xScale(), data.yScale());
79     Scene::paintGenericScreen(mask, data);
80     m_painter->restore();
81 }
82 
paint(AbstractOutput * output,const QRegion & damage,const QList<Toplevel * > & toplevels,RenderLoop * renderLoop)83 void SceneQPainter::paint(AbstractOutput *output, const QRegion &damage, const QList<Toplevel *> &toplevels,
84                           RenderLoop *renderLoop)
85 {
86     Q_ASSERT(kwinApp()->platform()->isPerScreenRenderingEnabled());
87     painted_screen = output;
88 
89     createStackingOrder(toplevels);
90 
91     const QRegion repaint = m_backend->beginFrame(output);
92     const QRect geometry = output->geometry();
93 
94     QImage *buffer = m_backend->bufferForScreen(output);
95     if (buffer && !buffer->isNull()) {
96         renderLoop->beginFrame();
97         m_painter->begin(buffer);
98         m_painter->setWindow(geometry);
99 
100         QRegion updateRegion, validRegion;
101         paintScreen(damage.intersected(geometry), repaint, &updateRegion, &validRegion, renderLoop);
102         paintCursor(updateRegion);
103 
104         m_painter->end();
105         renderLoop->endFrame();
106         m_backend->endFrame(output, updateRegion);
107     }
108 
109     // do cleanup
110     clearStackingOrder();
111 }
112 
paintBackground(const QRegion & region)113 void SceneQPainter::paintBackground(const QRegion &region)
114 {
115     for (const QRect &rect : region) {
116         m_painter->fillRect(rect, Qt::black);
117     }
118 }
119 
paintCursor(const QRegion & rendered)120 void SceneQPainter::paintCursor(const QRegion &rendered)
121 {
122     if (!kwinApp()->platform()->usesSoftwareCursor()) {
123         return;
124     }
125 
126     Cursor* cursor = Cursors::self()->currentCursor();
127     const QImage img = cursor->image();
128     if (img.isNull()) {
129         return;
130     }
131 
132     m_painter->save();
133     m_painter->setClipRegion(rendered.intersected(cursor->geometry()));
134     m_painter->drawImage(cursor->geometry(), img);
135     m_painter->restore();
136 }
137 
paintEffectQuickView(EffectQuickView * w)138 void SceneQPainter::paintEffectQuickView(EffectQuickView *w)
139 {
140     QPainter *painter = effects->scenePainter();
141     const QImage buffer = w->bufferAsImage();
142     if (buffer.isNull()) {
143         return;
144     }
145     painter->drawImage(w->geometry(), buffer);
146 }
147 
createWindow(Toplevel * toplevel)148 Scene::Window *SceneQPainter::createWindow(Toplevel *toplevel)
149 {
150     return new SceneQPainter::Window(this, toplevel);
151 }
152 
createEffectFrame(EffectFrameImpl * frame)153 Scene::EffectFrame *SceneQPainter::createEffectFrame(EffectFrameImpl *frame)
154 {
155     return new QPainterEffectFrame(frame, this);
156 }
157 
createShadow(Toplevel * toplevel)158 Shadow *SceneQPainter::createShadow(Toplevel *toplevel)
159 {
160     return new SceneQPainterShadow(toplevel);
161 }
162 
qpainterRenderBuffer(AbstractOutput * output) const163 QImage *SceneQPainter::qpainterRenderBuffer(AbstractOutput *output) const
164 {
165     return m_backend->bufferForScreen(output);
166 }
167 
168 //****************************************
169 // SceneQPainter::Window
170 //****************************************
Window(SceneQPainter * scene,Toplevel * c)171 SceneQPainter::Window::Window(SceneQPainter *scene, Toplevel *c)
172     : Scene::Window(c)
173     , m_scene(scene)
174 {
175 }
176 
~Window()177 SceneQPainter::Window::~Window()
178 {
179 }
180 
performPaint(int mask,const QRegion & _region,const WindowPaintData & data)181 void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const WindowPaintData &data)
182 {
183     QRegion region = _region;
184 
185     const QRect boundingRect = windowItem()->mapToGlobal(windowItem()->boundingRect());
186     if (!(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)))
187         region &= boundingRect;
188 
189     if (region.isEmpty())
190         return;
191 
192     QPainter *scenePainter = m_scene->scenePainter();
193     QPainter *painter = scenePainter;
194     painter->save();
195     painter->setClipRegion(region);
196     painter->setClipping(true);
197 
198     if (mask & PAINT_WINDOW_TRANSFORMED) {
199         painter->translate(data.xTranslation(), data.yTranslation());
200         painter->scale(data.xScale(), data.yScale());
201     }
202 
203     const bool opaque = qFuzzyCompare(1.0, data.opacity());
204     QImage tempImage;
205     QPainter tempPainter;
206     if (!opaque) {
207         // need a temp render target which we later on blit to the screen
208         tempImage = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
209         tempImage.fill(Qt::transparent);
210         tempPainter.begin(&tempImage);
211         tempPainter.save();
212         tempPainter.translate(-boundingRect.topLeft());
213         painter = &tempPainter;
214     }
215 
216     renderItem(painter, windowItem());
217 
218     if (!opaque) {
219         tempPainter.restore();
220         tempPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
221         QColor translucent(Qt::transparent);
222         translucent.setAlphaF(data.opacity());
223         tempPainter.fillRect(QRect(QPoint(0, 0), boundingRect.size()), translucent);
224         tempPainter.end();
225         painter = scenePainter;
226         painter->drawImage(boundingRect.topLeft(), tempImage);
227     }
228 
229     painter->restore();
230 }
231 
renderItem(QPainter * painter,Item * item) const232 void SceneQPainter::Window::renderItem(QPainter *painter, Item *item) const
233 {
234     const QList<Item *> sortedChildItems = item->sortedChildItems();
235 
236     painter->save();
237     painter->translate(item->position());
238 
239     for (Item *childItem : sortedChildItems) {
240         if (childItem->z() >= 0) {
241             break;
242         }
243         if (childItem->isVisible()) {
244             renderItem(painter, childItem);
245         }
246     }
247 
248     item->preprocess();
249     if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
250         renderSurfaceItem(painter, surfaceItem);
251     } else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) {
252         renderDecorationItem(painter, decorationItem);
253     }
254 
255     for (Item *childItem : sortedChildItems) {
256         if (childItem->z() < 0) {
257             continue;
258         }
259         if (childItem->isVisible()) {
260             renderItem(painter, childItem);
261         }
262     }
263 
264     painter->restore();
265 }
266 
renderSurfaceItem(QPainter * painter,SurfaceItem * surfaceItem) const267 void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem) const
268 {
269     const SurfacePixmap *surfaceTexture = surfaceItem->pixmap();
270     if (!surfaceTexture || !surfaceTexture->isValid()) {
271         return;
272     }
273 
274     PlatformQPainterSurfaceTexture *platformSurfaceTexture =
275             static_cast<PlatformQPainterSurfaceTexture *>(surfaceTexture->platformTexture());
276     if (!platformSurfaceTexture->isValid()) {
277         platformSurfaceTexture->create();
278     } else {
279         platformSurfaceTexture->update(surfaceItem->damage());
280     }
281     surfaceItem->resetDamage();
282 
283     const QRegion shape = surfaceItem->shape();
284     for (const QRectF rect : shape) {
285         const QMatrix4x4 matrix = surfaceItem->surfaceToBufferMatrix();
286         const QPointF bufferTopLeft = matrix.map(rect.topLeft());
287         const QPointF bufferBottomRight = matrix.map(rect.bottomRight());
288 
289         painter->drawImage(rect, platformSurfaceTexture->image(),
290                            QRectF(bufferTopLeft, bufferBottomRight));
291     }
292 }
293 
renderDecorationItem(QPainter * painter,DecorationItem * decorationItem) const294 void SceneQPainter::Window::renderDecorationItem(QPainter *painter, DecorationItem *decorationItem) const
295 {
296     const auto renderer = static_cast<const SceneQPainterDecorationRenderer *>(decorationItem->renderer());
297     QRect dtr, dlr, drr, dbr;
298     if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
299         client->layoutDecorationRects(dlr, dtr, drr, dbr);
300     } else if (auto deleted = qobject_cast<Deleted *>(toplevel)) {
301         deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
302     } else {
303         return;
304     }
305 
306     painter->drawImage(dtr, renderer->image(SceneQPainterDecorationRenderer::DecorationPart::Top));
307     painter->drawImage(dlr, renderer->image(SceneQPainterDecorationRenderer::DecorationPart::Left));
308     painter->drawImage(drr, renderer->image(SceneQPainterDecorationRenderer::DecorationPart::Right));
309     painter->drawImage(dbr, renderer->image(SceneQPainterDecorationRenderer::DecorationPart::Bottom));
310 }
311 
createDecorationRenderer(Decoration::DecoratedClientImpl * impl)312 DecorationRenderer *SceneQPainter::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
313 {
314     return new SceneQPainterDecorationRenderer(impl);
315 }
316 
createPlatformSurfaceTextureInternal(SurfacePixmapInternal * pixmap)317 PlatformSurfaceTexture *SceneQPainter::createPlatformSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
318 {
319     return m_backend->createPlatformSurfaceTextureInternal(pixmap);
320 }
321 
createPlatformSurfaceTextureWayland(SurfacePixmapWayland * pixmap)322 PlatformSurfaceTexture *SceneQPainter::createPlatformSurfaceTextureWayland(SurfacePixmapWayland *pixmap)
323 {
324     return m_backend->createPlatformSurfaceTextureWayland(pixmap);
325 }
326 
QPainterEffectFrame(EffectFrameImpl * frame,SceneQPainter * scene)327 QPainterEffectFrame::QPainterEffectFrame(EffectFrameImpl *frame, SceneQPainter *scene)
328     : Scene::EffectFrame(frame)
329     , m_scene(scene)
330 {
331 }
332 
~QPainterEffectFrame()333 QPainterEffectFrame::~QPainterEffectFrame()
334 {
335 }
336 
render(const QRegion & region,double opacity,double frameOpacity)337 void QPainterEffectFrame::render(const QRegion &region, double opacity, double frameOpacity)
338 {
339     Q_UNUSED(region)
340     Q_UNUSED(opacity)
341     // TODO: adjust opacity
342     if (m_effectFrame->geometry().isEmpty()) {
343         return; // Nothing to display
344     }
345     QPainter *painter = m_scene->scenePainter();
346 
347 
348     // Render the actual frame
349     if (m_effectFrame->style() == EffectFrameUnstyled) {
350         painter->save();
351         painter->setPen(Qt::NoPen);
352         QColor color(Qt::black);
353         color.setAlphaF(frameOpacity);
354         painter->setBrush(color);
355         painter->setRenderHint(QPainter::Antialiasing);
356         painter->drawRoundedRect(m_effectFrame->geometry().adjusted(-5, -5, 5, 5), 5.0, 5.0);
357         painter->restore();
358     } else if (m_effectFrame->style() == EffectFrameStyled) {
359         qreal left, top, right, bottom;
360         m_effectFrame->frame().getMargins(left, top, right, bottom);   // m_geometry is the inner geometry
361         QRect geom = m_effectFrame->geometry().adjusted(-left, -top, right, bottom);
362         painter->drawPixmap(geom, m_effectFrame->frame().framePixmap());
363     }
364     if (!m_effectFrame->selection().isNull()) {
365         painter->drawPixmap(m_effectFrame->selection(), m_effectFrame->selectionFrame().framePixmap());
366     }
367 
368     // Render icon
369     if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
370         const QPoint topLeft(m_effectFrame->geometry().x(),
371                              m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2);
372 
373         const QRect geom = QRect(topLeft, m_effectFrame->iconSize());
374         painter->drawPixmap(geom, m_effectFrame->icon().pixmap(m_effectFrame->iconSize()));
375     }
376 
377     // Render text
378     if (!m_effectFrame->text().isEmpty()) {
379         // Determine position on texture to paint text
380         QRect rect(QPoint(0, 0), m_effectFrame->geometry().size());
381         if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
382             rect.setLeft(m_effectFrame->iconSize().width());
383         }
384 
385         // If static size elide text as required
386         QString text = m_effectFrame->text();
387         if (m_effectFrame->isStatic()) {
388             QFontMetrics metrics(m_effectFrame->text());
389             text = metrics.elidedText(text, Qt::ElideRight, rect.width());
390         }
391 
392         painter->save();
393         painter->setFont(m_effectFrame->font());
394         if (m_effectFrame->style() == EffectFrameStyled) {
395             painter->setPen(m_effectFrame->styledTextColor());
396         } else {
397             // TODO: What about no frame? Custom color setting required
398             painter->setPen(Qt::white);
399         }
400         painter->drawText(rect.translated(m_effectFrame->geometry().topLeft()), m_effectFrame->alignment(), text);
401         painter->restore();
402     }
403 }
404 
405 //****************************************
406 // QPainterShadow
407 //****************************************
SceneQPainterShadow(Toplevel * toplevel)408 SceneQPainterShadow::SceneQPainterShadow(Toplevel* toplevel)
409     : Shadow(toplevel)
410 {
411 }
412 
~SceneQPainterShadow()413 SceneQPainterShadow::~SceneQPainterShadow()
414 {
415 }
416 
prepareBackend()417 bool SceneQPainterShadow::prepareBackend()
418 {
419     return true;
420 }
421 
422 //****************************************
423 // QPainterDecorationRenderer
424 //****************************************
SceneQPainterDecorationRenderer(Decoration::DecoratedClientImpl * client)425 SceneQPainterDecorationRenderer::SceneQPainterDecorationRenderer(Decoration::DecoratedClientImpl *client)
426     : DecorationRenderer(client)
427 {
428 }
429 
image(SceneQPainterDecorationRenderer::DecorationPart part) const430 QImage SceneQPainterDecorationRenderer::image(SceneQPainterDecorationRenderer::DecorationPart part) const
431 {
432     Q_ASSERT(part != DecorationPart::Count);
433     return m_images[int(part)];
434 }
435 
render(const QRegion & region)436 void SceneQPainterDecorationRenderer::render(const QRegion &region)
437 {
438     if (areImageSizesDirty()) {
439         resizeImages();
440         resetImageSizesDirty();
441     }
442 
443     auto imageSize = [this](DecorationPart part) {
444         return m_images[int(part)].size() / m_images[int(part)].devicePixelRatio();
445     };
446 
447     const QRect top(QPoint(0, 0), imageSize(DecorationPart::Top));
448     const QRect left(QPoint(0, top.height()), imageSize(DecorationPart::Left));
449     const QRect right(QPoint(top.width() - imageSize(DecorationPart::Right).width(), top.height()), imageSize(DecorationPart::Right));
450     const QRect bottom(QPoint(0, left.y() + left.height()), imageSize(DecorationPart::Bottom));
451 
452     const QRect geometry = region.boundingRect();
453     auto renderPart = [this](const QRect &rect, const QRect &partRect, int index) {
454         if (rect.isEmpty()) {
455             return;
456         }
457         QPainter painter(&m_images[index]);
458         painter.setRenderHint(QPainter::Antialiasing);
459         painter.setWindow(QRect(partRect.topLeft(), partRect.size() * qPainterEffectiveDevicePixelRatio(&painter)));
460         painter.setClipRect(rect);
461         painter.save();
462         // clear existing part
463         painter.setCompositionMode(QPainter::CompositionMode_Source);
464         painter.fillRect(rect, Qt::transparent);
465         painter.restore();
466         client()->decoration()->paint(&painter, rect);
467     };
468 
469     renderPart(left.intersected(geometry), left, int(DecorationPart::Left));
470     renderPart(top.intersected(geometry), top, int(DecorationPart::Top));
471     renderPart(right.intersected(geometry), right, int(DecorationPart::Right));
472     renderPart(bottom.intersected(geometry), bottom, int(DecorationPart::Bottom));
473 }
474 
resizeImages()475 void SceneQPainterDecorationRenderer::resizeImages()
476 {
477     QRect left, top, right, bottom;
478     client()->client()->layoutDecorationRects(left, top, right, bottom);
479 
480     auto checkAndCreate = [this](int index, const QSize &size) {
481         auto dpr = client()->client()->screenScale();
482         if (m_images[index].size() != size * dpr ||
483             m_images[index].devicePixelRatio() != dpr)
484         {
485             m_images[index] = QImage(size * dpr, QImage::Format_ARGB32_Premultiplied);
486             m_images[index].setDevicePixelRatio(dpr);
487             m_images[index].fill(Qt::transparent);
488         }
489     };
490     checkAndCreate(int(DecorationPart::Left), left.size());
491     checkAndCreate(int(DecorationPart::Right), right.size());
492     checkAndCreate(int(DecorationPart::Top), top.size());
493     checkAndCreate(int(DecorationPart::Bottom), bottom.size());
494 }
495 
QPainterFactory(QObject * parent)496 QPainterFactory::QPainterFactory(QObject *parent)
497     : SceneFactory(parent)
498 {
499 }
500 
501 QPainterFactory::~QPainterFactory() = default;
502 
create(QObject * parent) const503 Scene *QPainterFactory::create(QObject *parent) const
504 {
505     auto s = SceneQPainter::createScene(parent);
506     if (s && s->initFailed()) {
507         delete s;
508         s = nullptr;
509     }
510     return s;
511 }
512 
513 } // KWin
514