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 ®ion)
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 ®ion, 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 ®ion)
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