1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
6Copyright (C) 2009 Fredrik Höglund <fredrik@kde.org>
7Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2 of the License, or
12(at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>.
21*********************************************************************/
22#include "scene_xrender.h"
23
24#ifdef KWIN_HAVE_XRENDER_COMPOSITING
25
26#include "toplevel.h"
27#include "client.h"
28#include "decorations.h"
29#include "deleted.h"
30#include "effects.h"
31#include "overlaywindow.h"
32#include "paintredirector.h"
33#include "xcbutils.h"
34#include "kwinxrenderutils.h"
35
36#include <xcb/xfixes.h>
37
38#include <QtGui/QPainter>
39#include <qmath.h>
40
41namespace KWin
42{
43
44//****************************************
45// SceneXrender
46//****************************************
47
48xcb_render_picture_t SceneXrender::buffer = XCB_RENDER_PICTURE_NONE;
49ScreenPaintData SceneXrender::screen_paint;
50
51#define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536))
52#define FIXED_TO_DOUBLE(f) ((double) ((f) / 65536.0))
53
54
55static xcb_render_pictformat_t findFormatForVisual(xcb_visualid_t visual)
56{
57 static QHash<xcb_visualid_t, xcb_render_pictformat_t> s_cache;
58
59 if (xcb_render_pictformat_t format = s_cache.value(visual, 0)) {
60 return format;
61 }
62 if (!s_cache.isEmpty()) {
63 return 0;
64 }
65
66 ScopedCPointer<xcb_render_query_pict_formats_reply_t> formats(xcb_render_query_pict_formats_reply(
67 connection(), xcb_render_query_pict_formats_unchecked(connection()), NULL));
68 if (!formats) {
69 return 0;
70 }
71 int screen = QX11Info::appScreen();
72 for (xcb_render_pictscreen_iterator_t sit = xcb_render_query_pict_formats_screens_iterator(formats.data());
73 sit.rem;
74 --screen, xcb_render_pictscreen_next(&sit)) {
75 if (screen != 0) {
76 continue;
77 }
78 for (xcb_render_pictdepth_iterator_t dit = xcb_render_pictscreen_depths_iterator(sit.data);
79 dit.rem;
80 xcb_render_pictdepth_next(&dit)) {
81 for (xcb_render_pictvisual_iterator_t vit = xcb_render_pictdepth_visuals_iterator(dit.data);
82 vit.rem;
83 xcb_render_pictvisual_next(&vit)) {
84 s_cache.insert(vit.data->visual, vit.data->format);
85 }
86 }
87 }
88 return s_cache.value(visual, 0);
89}
90
91SceneXrender::SceneXrender(Workspace* ws)
92 : Scene(ws)
93 , format(0)
94 , front(XCB_RENDER_PICTURE_NONE)
95 , m_overlayWindow(new OverlayWindow())
96 , init_ok(false)
97{
98 if (!Xcb::Extensions::self()->isRenderAvailable()) {
99 kError(1212) << "No XRender extension available";
100 return;
101 }
102 if (!Xcb::Extensions::self()->isFixesRegionAvailable()) {
103 kError(1212) << "No XFixes v3+ extension available";
104 return;
105 }
106 initXRender(true);
107}
108
109SceneXrender::~SceneXrender()
110{
111 if (!init_ok) {
112 // TODO this probably needs to clean up whatever has been created until the failure
113 m_overlayWindow->destroy();
114 return;
115 }
116 SceneXrender::Window::cleanup();
117 SceneXrender::EffectFrame::cleanup();
118 xcb_render_free_picture(connection(), front);
119 xcb_render_free_picture(connection(), buffer);
120 buffer = XCB_RENDER_PICTURE_NONE;
121 m_overlayWindow->destroy();
122 foreach (Window * w, windows)
123 delete w;
124 delete m_overlayWindow;
125}
126
127void SceneXrender::initXRender(bool createOverlay)
128{
129 init_ok = false;
130 if (front != XCB_RENDER_PICTURE_NONE)
131 xcb_render_free_picture(connection(), front);
132 bool haveOverlay = createOverlay ? m_overlayWindow->create() : (m_overlayWindow->window() != XCB_WINDOW_NONE);
133 if (haveOverlay) {
134 m_overlayWindow->setup(XCB_WINDOW_NONE);
135 ScopedCPointer<xcb_get_window_attributes_reply_t> attribs(xcb_get_window_attributes_reply(connection(),
136 xcb_get_window_attributes_unchecked(connection(), m_overlayWindow->window()), NULL));
137 if (!attribs) {
138 kError(1212) << "Failed getting window attributes for overlay window";
139 return;
140 }
141 format = findFormatForVisual(attribs->visual);
142 if (format == 0) {
143 kError(1212) << "Failed to find XRender format for overlay window";
144 return;
145 }
146 front = xcb_generate_id(connection());
147 xcb_render_create_picture(connection(), front, m_overlayWindow->window(), format, 0, NULL);
148 } else {
149 // create XRender picture for the root window
150 format = findFormatForVisual(defaultScreen()->root_visual);
151 if (format == 0) {
152 kError(1212) << "Failed to find XRender format for root window";
153 return; // error
154 }
155 front = xcb_generate_id(connection());
156 const uint32_t values[] = {XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS};
157 xcb_render_create_picture(connection(), front, rootWindow(), format, XCB_RENDER_CP_SUBWINDOW_MODE, values);
158 }
159 createBuffer();
160 init_ok = true;
161}
162
163bool SceneXrender::initFailed() const
164{
165 return !init_ok;
166}
167
168// Create the compositing buffer. The root window is not double-buffered,
169// so it is done manually using this buffer,
170void SceneXrender::createBuffer()
171{
172 if (buffer != XCB_RENDER_PICTURE_NONE)
173 xcb_render_free_picture(connection(), buffer);
174 xcb_pixmap_t pixmap = xcb_generate_id(connection());
175 xcb_create_pixmap(connection(), Xcb::defaultDepth(), pixmap, rootWindow(), displayWidth(), displayHeight());
176 buffer = xcb_generate_id(connection());
177 xcb_render_create_picture(connection(), buffer, pixmap, format, 0, NULL);
178 xcb_free_pixmap(connection(), pixmap); // The picture owns the pixmap now
179}
180
181// the entry point for painting
182qint64 SceneXrender::paint(QRegion damage, ToplevelList toplevels)
183{
184 QElapsedTimer renderTimer;
185 renderTimer.start();
186
187 foreach (Toplevel * c, toplevels) {
188 assert(windows.contains(c));
189 stacking_order.append(windows[ c ]);
190 }
191
192 int mask = 0;
193 QRegion updateRegion, validRegion;
194 paintScreen(&mask, damage, QRegion(), &updateRegion, &validRegion);
195
196 if (m_overlayWindow->window()) // show the window only after the first pass, since
197 m_overlayWindow->show(); // that pass may take long
198
199 present(mask, updateRegion);
200 // do cleanup
201 stacking_order.clear();
202
203 return renderTimer.nsecsElapsed();
204}
205
206void SceneXrender::present(int mask, QRegion damage)
207{
208 if (mask & PAINT_SCREEN_REGION) {
209 // Use the damage region as the clip region for the root window
210 XFixesRegion frontRegion(damage);
211 xcb_xfixes_set_picture_clip_region(connection(), front, frontRegion, 0, 0);
212 // copy composed buffer to the root window
213 xcb_xfixes_set_picture_clip_region(connection(), buffer, XCB_XFIXES_REGION_NONE, 0, 0);
214 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_SRC, buffer, XCB_RENDER_PICTURE_NONE,
215 front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
216 xcb_xfixes_set_picture_clip_region(connection(), front, XCB_XFIXES_REGION_NONE, 0, 0);
217 xcb_flush(connection());
218 } else {
219 // copy composed buffer to the root window
220 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_SRC, buffer, XCB_RENDER_PICTURE_NONE,
221 front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
222 xcb_flush(connection());
223 }
224}
225
226void SceneXrender::paintGenericScreen(int mask, ScreenPaintData data)
227{
228 screen_paint = data; // save, transformations will be done when painting windows
229 Scene::paintGenericScreen(mask, data);
230}
231
232void SceneXrender::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
233{
234 PaintClipper::push(region);
235 KWin::Scene::paintDesktop(desktop, mask, region, data);
236 PaintClipper::pop(region);
237}
238
239// fill the screen background
240void SceneXrender::paintBackground(QRegion region)
241{
242 xcb_render_color_t col = { 0, 0, 0, 0xffff }; // black
243 const QVector<xcb_rectangle_t> &rects = Xcb::regionToRects(region);
244 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, buffer, col, rects.count(), rects.data());
245}
246
247void SceneXrender::windowGeometryShapeChanged(KWin::Toplevel* c)
248{
249 if (!windows.contains(c)) // this is ok, shape is not valid by default
250 return;
251 Window* w = windows[ c ];
252 w->discardShape();
253}
254
255void SceneXrender::windowOpacityChanged(KWin::Toplevel* c)
256{
257 Q_UNUSED(c)
258}
259
260void SceneXrender::windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted)
261{
262 assert(windows.contains(c));
263 if (deleted != NULL) {
264 // replace c with deleted
265 Window* w = windows.take(c);
266 w->updateToplevel(deleted);
267 if (w->shadow()) {
268 w->shadow()->setToplevel(deleted);
269 }
270 windows[ deleted ] = w;
271 } else {
272 delete windows.take(c);
273 c->effectWindow()->setSceneWindow(NULL);
274 }
275}
276
277void SceneXrender::windowDeleted(Deleted* c)
278{
279 assert(windows.contains(c));
280 delete windows.take(c);
281 c->effectWindow()->setSceneWindow(NULL);
282}
283
284void SceneXrender::windowAdded(Toplevel* c)
285{
286 assert(!windows.contains(c));
287 windows[ c ] = new Window(c);
288 connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(windowGeometryShapeChanged(KWin::Toplevel*)));
289 connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), SLOT(windowClosed(KWin::Toplevel*,KWin::Deleted*)));
290 c->effectWindow()->setSceneWindow(windows[ c ]);
291 c->getShadow();
292 windows[ c ]->updateShadow(c->shadow());
293}
294
295//****************************************
296// SceneXrender::Window
297//****************************************
298
299XRenderPicture *SceneXrender::Window::s_tempPicture = 0;
300QRect SceneXrender::Window::temp_visibleRect;
301
302SceneXrender::Window::Window(Toplevel* c)
303 : Scene::Window(c)
304 , format(findFormatForVisual(c->visual()->visualid))
305 , alpha_cached_opacity(0.0)
306{
307}
308
309SceneXrender::Window::~Window()
310{
311 discardShape();
312}
313
314void SceneXrender::Window::cleanup()
315{
316 delete s_tempPicture;
317 s_tempPicture = NULL;
318}
319
320// Maps window coordinates to screen coordinates
321QRect SceneXrender::Window::mapToScreen(int mask, const WindowPaintData &data, const QRect &rect) const
322{
323 QRect r = rect;
324
325 if (mask & PAINT_WINDOW_TRANSFORMED) {
326 // Apply the window transformation
327 r.moveTo(r.x() * data.xScale() + data.xTranslation(),
328 r.y() * data.yScale() + data.yTranslation());
329 r.setWidth(r.width() * data.xScale());
330 r.setHeight(r.height() * data.yScale());
331 }
332
333 // Move the rectangle to the screen position
334 r.translate(x(), y());
335
336 if (mask & PAINT_SCREEN_TRANSFORMED) {
337 // Apply the screen transformation
338 r.moveTo(r.x() * screen_paint.xScale() + screen_paint.xTranslation(),
339 r.y() * screen_paint.yScale() + screen_paint.yTranslation());
340 r.setWidth(r.width() * screen_paint.xScale());
341 r.setHeight(r.height() * screen_paint.yScale());
342 }
343
344 return r;
345}
346
347// Maps window coordinates to screen coordinates
348QPoint SceneXrender::Window::mapToScreen(int mask, const WindowPaintData &data, const QPoint &point) const
349{
350 QPoint pt = point;
351
352 if (mask & PAINT_WINDOW_TRANSFORMED) {
353 // Apply the window transformation
354 pt.rx() = pt.x() * data.xScale() + data.xTranslation();
355 pt.ry() = pt.y() * data.yScale() + data.yTranslation();
356 }
357
358 // Move the point to the screen position
359 pt += QPoint(x(), y());
360
361 if (mask & PAINT_SCREEN_TRANSFORMED) {
362 // Apply the screen transformation
363 pt.rx() = pt.x() * screen_paint.xScale() + screen_paint.xTranslation();
364 pt.ry() = pt.y() * screen_paint.yScale() + screen_paint.yTranslation();
365 }
366
367 return pt;
368}
369
370void SceneXrender::Window::prepareTempPixmap()
371{
372 const QSize oldSize = temp_visibleRect.size();
373 temp_visibleRect = toplevel->visibleRect().translated(-toplevel->pos());
374 if (s_tempPicture && (oldSize.width() < temp_visibleRect.width() || oldSize.height() < temp_visibleRect.height())) {
375 delete s_tempPicture;
376 s_tempPicture = NULL;
377 scene_setXRenderOffscreenTarget(0); // invalidate, better crash than cause weird results for developers
378 }
379 if (!s_tempPicture) {
380 xcb_pixmap_t pix = xcb_generate_id(connection());
381 xcb_create_pixmap(connection(), 32, pix, rootWindow(), temp_visibleRect.width(), temp_visibleRect.height());
382 s_tempPicture = new XRenderPicture(pix, 32);
383 xcb_free_pixmap(connection(), pix);
384 }
385 const xcb_render_color_t transparent = {0, 0, 0, 0};
386 const xcb_rectangle_t rect = {0, 0, uint16_t(temp_visibleRect.width()), uint16_t(temp_visibleRect.height())};
387 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, *s_tempPicture, transparent, 1, &rect);
388}
389
390// paint the window
391void SceneXrender::Window::performPaint(int mask, QRegion region, WindowPaintData data)
392{
393 setTransformedShape(QRegion()); // maybe nothing will be painted
394 // check if there is something to paint
395 bool opaque = isOpaque() && qFuzzyCompare(data.opacity(), 1.0);
396 /* HACK: It seems this causes painting glitches, disable temporarily
397 if (( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT ))
398 { // We are only painting either opaque OR translucent windows, not both
399 if ( mask & PAINT_WINDOW_OPAQUE && !opaque )
400 return; // Only painting opaque and window is translucent
401 if ( mask & PAINT_WINDOW_TRANSLUCENT && opaque )
402 return; // Only painting translucent and window is opaque
403 }*/
404 // Intersect the clip region with the rectangle the window occupies on the screen
405 if (!(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)))
406 region &= toplevel->visibleRect();
407
408 if (region.isEmpty())
409 return;
410 XRenderWindowPixmap *pixmap = windowPixmap<XRenderWindowPixmap>();
411 if (!pixmap || !pixmap->isValid()) {
412 return;
413 }
414 xcb_render_picture_t pic = pixmap->picture();
415 if (pic == XCB_RENDER_PICTURE_NONE) // The render format can be null for GL and/or Xv visuals
416 return;
417 toplevel->resetDamage();
418 // set picture filter
419 if (options->isXrenderSmoothScale()) { // only when forced, it's slow
420 if (mask & PAINT_WINDOW_TRANSFORMED)
421 filter = ImageFilterGood;
422 else if (mask & PAINT_SCREEN_TRANSFORMED)
423 filter = ImageFilterGood;
424 else
425 filter = ImageFilterFast;
426 } else
427 filter = ImageFilterFast;
428 // do required transformations
429 const QRect wr = mapToScreen(mask, data, QRect(0, 0, width(), height()));
430 QRect cr = QRect(toplevel->clientPos(), toplevel->clientSize()); // Client rect (in the window)
431 qreal xscale = 1;
432 qreal yscale = 1;
433 bool scaled = false;
434
435 Client *client = dynamic_cast<Client*>(toplevel);
436 Deleted *deleted = dynamic_cast<Deleted*>(toplevel);
437 const QRect decorationRect = toplevel->decorationRect();
438 if (((client && !client->noBorder()) || (deleted && !deleted->noBorder())) &&
439 decorationPlugin()->hasAlpha()) {
440 // decorated client
441 transformed_shape = decorationRect;
442 if (toplevel->shape()) {
443 // "xeyes" + decoration
444 transformed_shape -= cr;
445 transformed_shape += shape();
446 }
447 } else {
448 transformed_shape = shape();
449 }
450 if (toplevel->hasShadow())
451 transformed_shape |= toplevel->shadow()->shadowRegion();
452
453 xcb_render_transform_t xform = {
454 DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0),
455 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0),
456 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1)
457 };
458 static xcb_render_transform_t identity = {
459 DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0),
460 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0),
461 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1)
462 };
463
464 if (mask & PAINT_WINDOW_TRANSFORMED) {
465 xscale = data.xScale();
466 yscale = data.yScale();
467 }
468 if (mask & PAINT_SCREEN_TRANSFORMED) {
469 xscale *= screen_paint.xScale();
470 yscale *= screen_paint.yScale();
471 }
472 if (!qFuzzyCompare(xscale, 1.0) || !qFuzzyCompare(yscale, 1.0)) {
473 scaled = true;
474 xform.matrix11 = DOUBLE_TO_FIXED(1.0 / xscale);
475 xform.matrix22 = DOUBLE_TO_FIXED(1.0 / yscale);
476
477 // transform the shape for clipping in paintTransformedScreen()
478 QVector<QRect> rects = transformed_shape.rects();
479 for (int i = 0; i < rects.count(); ++i) {
480 QRect& r = rects[ i ];
481 r.setRect(qRound(r.x() * xscale), qRound(r.y() * yscale),
482 qRound(r.width() * xscale), qRound(r.height() * yscale));
483 }
484 transformed_shape.setRects(rects.constData(), rects.count());
485 }
486
487 transformed_shape.translate(mapToScreen(mask, data, QPoint(0, 0)));
488 PaintClipper pcreg(region); // clip by the region to paint
489 PaintClipper pc(transformed_shape); // clip by window's shape
490
491 const bool wantShadow = m_shadow && !m_shadow->shadowRegion().isEmpty();
492
493 // In order to obtain a pixel perfect rescaling
494 // we need to blit the window content togheter with
495 // decorations in a temporary pixmap and scale
496 // the temporary pixmap at the end.
497 // We should do this only if there is scaling and
498 // the window has border
499 // This solves a number of glitches and on top of this
500 // it optimizes painting quite a bit
501 const bool blitInTempPixmap = xRenderOffscreen() || (data.crossFadeProgress() < 1.0 && !opaque) ||
502 (scaled && (wantShadow || (client && !client->noBorder()) || (deleted && !deleted->noBorder())));
503
504 xcb_render_picture_t renderTarget = buffer;
505 if (blitInTempPixmap) {
506 if (scene_xRenderOffscreenTarget()) {
507 temp_visibleRect = toplevel->visibleRect().translated(-toplevel->pos());
508 renderTarget = *scene_xRenderOffscreenTarget();
509 } else {
510 prepareTempPixmap();
511 renderTarget = *s_tempPicture;
512 }
513 } else {
514 xcb_render_set_picture_transform(connection(), pic, xform);
515 if (filter == ImageFilterGood) {
516 setPictureFilter(pic, KWin::Scene::ImageFilterGood);
517 }
518
519 //BEGIN OF STUPID RADEON HACK
520 // This is needed to avoid hitting a fallback in the radeon driver.
521 // The Render specification states that sampling pixels outside the
522 // source picture results in alpha=0 pixels. This can be achieved by
523 // setting the border color to transparent black, but since the border
524 // color has the same format as the texture, it only works when the
525 // texture has an alpha channel. So the driver falls back to software
526 // when the repeat mode is RepeatNone, the picture has a non-identity
527 // transformation matrix, and doesn't have an alpha channel.
528 // Since we only scale the picture, we can work around this by setting
529 // the repeat mode to RepeatPad.
530 if (!window()->hasAlpha()) {
531 const uint32_t values[] = {XCB_RENDER_REPEAT_PAD};
532 xcb_render_change_picture(connection(), pic, XCB_RENDER_CP_REPEAT, values);
533 }
534 //END OF STUPID RADEON HACK
535 }
536#define MAP_RECT_TO_TARGET(_RECT_) \
537 if (blitInTempPixmap) _RECT_.translate(-temp_visibleRect.topLeft()); else _RECT_ = mapToScreen(mask, data, _RECT_)
538
539 //BEGIN deco preparations
540 bool noBorder = true;
541 xcb_render_picture_t left = XCB_RENDER_PICTURE_NONE;
542 xcb_render_picture_t top = XCB_RENDER_PICTURE_NONE;
543 xcb_render_picture_t right = XCB_RENDER_PICTURE_NONE;
544 xcb_render_picture_t bottom = XCB_RENDER_PICTURE_NONE;
545 PaintRedirector *redirector = NULL;
546 QRect dtr, dlr, drr, dbr;
547 if (client || deleted) {
548 if (client && !client->noBorder()) {
549 redirector = client->decorationPaintRedirector();
550 noBorder = client->noBorder();
551 client->layoutDecorationRects(dlr, dtr, drr, dbr, Client::WindowRelative);
552 }
553 if (deleted && !deleted->noBorder()) {
554 noBorder = deleted->noBorder();
555 redirector = deleted->decorationPaintRedirector();
556 deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
557 }
558 if (redirector) {
559 redirector->ensurePixmapsPainted();
560 left = redirector->leftDecoPixmap<xcb_render_picture_t>();
561 top = redirector->topDecoPixmap<xcb_render_picture_t>();
562 right = redirector->rightDecoPixmap<xcb_render_picture_t>();
563 bottom = redirector->bottomDecoPixmap<xcb_render_picture_t>();
564 }
565 if (!noBorder) {
566 MAP_RECT_TO_TARGET(dtr);
567 MAP_RECT_TO_TARGET(dlr);
568 MAP_RECT_TO_TARGET(drr);
569 MAP_RECT_TO_TARGET(dbr);
570 }
571 }
572 //END deco preparations
573
574 //BEGIN shadow preparations
575 QRect stlr, str, strr, srr, sbrr, sbr, sblr, slr;
576 SceneXRenderShadow* m_xrenderShadow = static_cast<SceneXRenderShadow*>(m_shadow);
577
578 if (wantShadow) {
579 m_xrenderShadow->layoutShadowRects(str, strr, srr, sbrr, sbr, sblr, slr, stlr);
580 MAP_RECT_TO_TARGET(stlr);
581 MAP_RECT_TO_TARGET(str);
582 MAP_RECT_TO_TARGET(strr);
583 MAP_RECT_TO_TARGET(srr);
584 MAP_RECT_TO_TARGET(sbrr);
585 MAP_RECT_TO_TARGET(sbr);
586 MAP_RECT_TO_TARGET(sblr);
587 MAP_RECT_TO_TARGET(slr);
588 }
589 //BEGIN end preparations
590
591 //BEGIN client preparations
592 QRect dr = cr;
593 if (blitInTempPixmap) {
594 dr.translate(-temp_visibleRect.topLeft());
595 } else {
596 dr = mapToScreen(mask, data, dr); // Destination rect
597 if (scaled) {
598 cr.moveLeft(cr.x() * xscale);
599 cr.moveTop(cr.y() * yscale);
600 }
601 }
602
603 const int clientRenderOp = (opaque || blitInTempPixmap) ? XCB_RENDER_PICT_OP_SRC : XCB_RENDER_PICT_OP_OVER;
604 //END client preparations
605
606#undef MAP_RECT_TO_TARGET
607
608 for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) {
609
610#define RENDER_SHADOW_TILE(_TILE_, _RECT_) \
611xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, m_xrenderShadow->picture(SceneXRenderShadow::ShadowElement##_TILE_), \
612 shadowAlpha, renderTarget, 0, 0, 0, 0, _RECT_.x(), _RECT_.y(), _RECT_.width(), _RECT_.height())
613
614 //shadow
615 if (wantShadow) {
616 xcb_render_picture_t shadowAlpha = XCB_RENDER_PICTURE_NONE;
617 if (!opaque) {
618 shadowAlpha = xRenderBlendPicture(data.opacity());
619 }
620 RENDER_SHADOW_TILE(TopLeft, stlr);
621 RENDER_SHADOW_TILE(Top, str);
622 RENDER_SHADOW_TILE(TopRight, strr);
623 RENDER_SHADOW_TILE(Left, slr);
624 RENDER_SHADOW_TILE(Right, srr);
625 RENDER_SHADOW_TILE(BottomLeft, sblr);
626 RENDER_SHADOW_TILE(Bottom, sbr);
627 RENDER_SHADOW_TILE(BottomRight, sbrr);
628 }
629#undef RENDER_SHADOW_TILE
630
631 // Paint the window contents
632 if (!(client && client->isShade())) {
633 xcb_render_picture_t clientAlpha = XCB_RENDER_PICTURE_NONE;
634 if (!opaque) {
635 clientAlpha = xRenderBlendPicture(data.opacity());
636 }
637 xcb_render_composite(connection(), clientRenderOp, pic, clientAlpha, renderTarget,
638 cr.x(), cr.y(), 0, 0, dr.x(), dr.y(), dr.width(), dr.height());
639 if (data.crossFadeProgress() < 1.0 && data.crossFadeProgress() > 0.0) {
640 XRenderWindowPixmap *previous = previousWindowPixmap<XRenderWindowPixmap>();
641 if (previous && previous != pixmap) {
642 static XRenderPicture cFadeAlpha(XCB_RENDER_PICTURE_NONE);
643 static xcb_render_color_t cFadeColor = {0, 0, 0, 0};
644 cFadeColor.alpha = uint16_t((1.0 - data.crossFadeProgress()) * 0xffff);
645 if (cFadeAlpha == XCB_RENDER_PICTURE_NONE) {
646 cFadeAlpha = xRenderFill(cFadeColor);
647 } else {
648 xcb_rectangle_t rect = {0, 0, 1, 1};
649 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, cFadeAlpha, cFadeColor , 1, &rect);
650 }
651 if (previous->size() != pixmap->size()) {
652 xcb_render_transform_t xform2 = {
653 DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(xform.matrix11) * previous->size().width() / pixmap->size().width()), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0),
654 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(xform.matrix22) * previous->size().height() / pixmap->size().height()), DOUBLE_TO_FIXED(0),
655 DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1)
656 };
657 xcb_render_set_picture_transform(connection(), previous->picture(), xform2);
658 }
659
660 xcb_render_composite(connection(), opaque ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_ATOP,
661 previous->picture(), cFadeAlpha, renderTarget,
662 cr.x(), cr.y(), 0, 0, dr.x(), dr.y(), dr.width(), dr.height());
663
664 if (previous->size() != pixmap->size()) {
665 xcb_render_set_picture_transform(connection(), previous->picture(), identity);
666 }
667 }
668 }
669 if (!opaque)
670 transformed_shape = QRegion();
671 }
672
673#define RENDER_DECO_PART(_PART_, _RECT_) \
674xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, _PART_, decorationAlpha, renderTarget,\
675 0, 0, 0, 0, _RECT_.x(), _RECT_.y(), _RECT_.width(), _RECT_.height())
676
677 if (client || deleted) {
678 if (!noBorder) {
679 xcb_render_picture_t decorationAlpha = xRenderBlendPicture(data.opacity() * data.decorationOpacity());
680 RENDER_DECO_PART(top, dtr);
681 RENDER_DECO_PART(left, dlr);
682 RENDER_DECO_PART(right, drr);
683 RENDER_DECO_PART(bottom, dbr);
684 }
685 if (redirector) {
686 redirector->markAsRepainted();
687 }
688 }
689#undef RENDER_DECO_PART
690
691 if (data.brightness() != 1.0) {
692 // fake brightness change by overlaying black
693 const float alpha = (1 - data.brightness()) * data.opacity();
694 xcb_rectangle_t rect;
695 if (blitInTempPixmap) {
696 rect.x = -temp_visibleRect.left();
697 rect.y = -temp_visibleRect.top();
698 rect.width = width();
699 rect.height = height();
700 } else {
701 rect.x = wr.x();
702 rect.y = wr.y();
703 rect.width = wr.width();
704 rect.height = wr.height();
705 }
706 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_OVER, renderTarget,
707 preMultiply(data.brightness() < 1.0 ? QColor(0,0,0,255*alpha) : QColor(255,255,255,-alpha*255)),
708 1, &rect);
709 }
710 if (blitInTempPixmap) {
711 const QRect r = mapToScreen(mask, data, temp_visibleRect);
712 xcb_render_set_picture_transform(connection(), *s_tempPicture, xform);
713 setPictureFilter(*s_tempPicture, filter);
714 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *s_tempPicture,
715 XCB_RENDER_PICTURE_NONE, buffer,
716 0, 0, 0, 0, r.x(), r.y(), r.width(), r.height());
717 xcb_render_set_picture_transform(connection(), *s_tempPicture, identity);
718 }
719 }
720 if (scaled && !blitInTempPixmap) {
721 xcb_render_set_picture_transform(connection(), pic, identity);
722 if (filter == ImageFilterGood)
723 setPictureFilter(pic, KWin::Scene::ImageFilterFast);
724 if (!window()->hasAlpha()) {
725 const uint32_t values[] = {XCB_RENDER_REPEAT_NONE};
726 xcb_render_change_picture(connection(), pic, XCB_RENDER_CP_REPEAT, values);
727 }
728 }
729 if (xRenderOffscreen())
730 scene_setXRenderOffscreenTarget(*s_tempPicture);
731}
732
733void SceneXrender::Window::setPictureFilter(xcb_render_picture_t pic, Scene::ImageFilterType filter)
734{
735 QByteArray filterName;
736 switch (filter) {
737 case KWin::Scene::ImageFilterFast:
738 filterName = QByteArray("fast");
739 break;
740 case KWin::Scene::ImageFilterGood:
741 filterName = QByteArray("good");
742 break;
743 }
744 xcb_render_set_picture_filter(connection(), pic, filterName.length(), filterName.constData(), 0, NULL);
745}
746
747WindowPixmap* SceneXrender::Window::createWindowPixmap()
748{
749 return new XRenderWindowPixmap(this, format);
750}
751
752void SceneXrender::screenGeometryChanged(const QSize &size)
753{
754 Scene::screenGeometryChanged(size);
755 initXRender(false);
756}
757
758//****************************************
759// XRenderWindowPixmap
760//****************************************
761
762XRenderWindowPixmap::XRenderWindowPixmap(Scene::Window *window, xcb_render_pictformat_t format)
763 : WindowPixmap(window)
764 , m_picture(XCB_RENDER_PICTURE_NONE)
765 , m_format(format)
766{
767}
768
769XRenderWindowPixmap::~XRenderWindowPixmap()
770{
771 if (m_picture != XCB_RENDER_PICTURE_NONE) {
772 xcb_render_free_picture(connection(), m_picture);
773 }
774}
775
776void XRenderWindowPixmap::create()
777{
778 if (isValid()) {
779 return;
780 }
781 KWin::WindowPixmap::create();
782 if (!isValid()) {
783 return;
784 }
785 m_picture = xcb_generate_id(connection());
786 xcb_render_create_picture(connection(), m_picture, pixmap(), m_format, 0, NULL);
787}
788
789//****************************************
790// SceneXrender::EffectFrame
791//****************************************
792
793XRenderPicture *SceneXrender::EffectFrame::s_effectFrameCircle = NULL;
794
795SceneXrender::EffectFrame::EffectFrame(EffectFrameImpl* frame)
796 : Scene::EffectFrame(frame)
797{
798 m_picture = NULL;
799 m_textPicture = NULL;
800 m_iconPicture = NULL;
801 m_selectionPicture = NULL;
802}
803
804SceneXrender::EffectFrame::~EffectFrame()
805{
806 delete m_picture;
807 delete m_textPicture;
808 delete m_iconPicture;
809 delete m_selectionPicture;
810}
811
812void SceneXrender::EffectFrame::cleanup()
813{
814 delete s_effectFrameCircle;
815 s_effectFrameCircle = NULL;
816}
817
818void SceneXrender::EffectFrame::free()
819{
820 delete m_picture;
821 m_picture = NULL;
822 delete m_textPicture;
823 m_textPicture = NULL;
824 delete m_iconPicture;
825 m_iconPicture = NULL;
826 delete m_selectionPicture;
827 m_selectionPicture = NULL;
828}
829
830void SceneXrender::EffectFrame::freeIconFrame()
831{
832 delete m_iconPicture;
833 m_iconPicture = NULL;
834}
835
836void SceneXrender::EffectFrame::freeTextFrame()
837{
838 delete m_textPicture;
839 m_textPicture = NULL;
840}
841
842void SceneXrender::EffectFrame::freeSelection()
843{
844 delete m_selectionPicture;
845 m_selectionPicture = NULL;
846}
847
848void SceneXrender::EffectFrame::crossFadeIcon()
849{
850 // TODO: implement me
851}
852
853void SceneXrender::EffectFrame::crossFadeText()
854{
855 // TODO: implement me
856}
857
858void SceneXrender::EffectFrame::render(QRegion region, double opacity, double frameOpacity)
859{
860 Q_UNUSED(region)
861 if (m_effectFrame->geometry().isEmpty()) {
862 return; // Nothing to display
863 }
864
865 // Render the actual frame
866 if (m_effectFrame->style() == EffectFrameUnstyled) {
867 renderUnstyled(effects->xrenderBufferPicture(), m_effectFrame->geometry(), opacity * frameOpacity);
868 } else if (m_effectFrame->style() == EffectFrameStyled) {
869 if (!m_picture) { // Lazy creation
870 updatePicture();
871 }
872 if (m_picture) {
873 qreal left, top, right, bottom;
874 m_effectFrame->frame().getMargins(left, top, right, bottom); // m_geometry is the inner geometry
875 QRect geom = m_effectFrame->geometry().adjusted(-left, -top, right, bottom);
876 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *m_picture,
877 XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(),
878 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height());
879 }
880 }
881 if (!m_effectFrame->selection().isNull()) {
882 if (!m_selectionPicture) { // Lazy creation
883 const QPixmap pix = m_effectFrame->selectionFrame().framePixmap();
884 if (!pix.isNull()) // don't try if there's no content
885 m_selectionPicture = new XRenderPicture(m_effectFrame->selectionFrame().framePixmap());
886 }
887 if (m_selectionPicture) {
888 const QRect geom = m_effectFrame->selection();
889 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *m_selectionPicture,
890 XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(),
891 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height());
892 }
893 }
894
895 XRenderPicture fill = xRenderBlendPicture(opacity);
896
897 // Render icon
898 if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
899 QPoint topLeft(m_effectFrame->geometry().x(), m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2);
900
901 if (!m_iconPicture) // lazy creation
902 m_iconPicture = new XRenderPicture(m_effectFrame->icon());
903 QRect geom = QRect(topLeft, m_effectFrame->iconSize());
904 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *m_iconPicture, fill,
905 effects->xrenderBufferPicture(),
906 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height());
907 }
908
909 // Render text
910 if (!m_effectFrame->text().isEmpty()) {
911 if (!m_textPicture) { // Lazy creation
912 updateTextPicture();
913 }
914 xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *m_textPicture, fill, effects->xrenderBufferPicture(),
915 0, 0, 0, 0, m_effectFrame->geometry().x(), m_effectFrame->geometry().y(),
916 m_effectFrame->geometry().width(), m_effectFrame->geometry().height());
917 }
918}
919
920void SceneXrender::EffectFrame::renderUnstyled(xcb_render_picture_t pict, const QRect &rect, qreal opacity)
921{
922 const int roundness = 5;
923 const QRect area = rect.adjusted(-roundness, -roundness, roundness, roundness);
924 xcb_rectangle_t rects[3];
925 // center
926 rects[0].x = area.left();
927 rects[0].y = area.top() + roundness;
928 rects[0].width = area.width();
929 rects[0].height = area.height() - roundness * 2;
930 // top
931 rects[1].x = area.left() + roundness;
932 rects[1].y = area.top();
933 rects[1].width = area.width() - roundness * 2;
934 rects[1].height = roundness;
935 // bottom
936 rects[2].x = area.left() + roundness;
937 rects[2].y = area.top() + area.height() - roundness;
938 rects[2].width = area.width() - roundness * 2;
939 rects[2].height = roundness;
940 xcb_render_color_t color = {0, 0, 0, uint16_t(opacity * 0xffff)};
941 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_OVER, pict, color, 3, rects);
942
943 if (!s_effectFrameCircle) {
944 // create the circle
945 const int diameter = roundness * 2;
946 xcb_pixmap_t pix = xcb_generate_id(connection());
947 xcb_create_pixmap(connection(), 32, pix, rootWindow(), diameter, diameter);
948 s_effectFrameCircle = new XRenderPicture(pix, 32);
949 xcb_free_pixmap(connection(), pix);
950
951 // clear it with transparent
952 xcb_rectangle_t xrect = {0, 0, diameter, diameter};
953 xcb_render_color_t tranparent = {0, 0, 0, 0};
954 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, *s_effectFrameCircle, tranparent, 1, &xrect);
955
956 static int num_segments = 80;
957 static qreal theta = 2 * M_PI / qreal(num_segments);
958 static qreal c = qCos(theta); //precalculate the sine and cosine
959 static qreal s = qSin(theta);
960 qreal t;
961
962 qreal x = roundness;//we start at angle = 0
963 qreal y = 0;
964
965 QVector<xcb_render_pointfix_t> points;
966 xcb_render_pointfix_t point;
967 point.x = DOUBLE_TO_FIXED(roundness);
968 point.y = DOUBLE_TO_FIXED(roundness);
969 points << point;
970 for (int ii = 0; ii <= num_segments; ++ii) {
971 point.x = DOUBLE_TO_FIXED(x + roundness);
972 point.y = DOUBLE_TO_FIXED(y + roundness);
973 points << point;
974 //apply the rotation matrix
975 t = x;
976 x = c * x - s * y;
977 y = s * t + c * y;
978 }
979 XRenderPicture fill = xRenderFill(Qt::black);
980 xcb_render_tri_fan(connection(), XCB_RENDER_PICT_OP_OVER, fill, *s_effectFrameCircle,
981 0, 0, 0, points.count(), points.constData());
982 }
983 // TODO: merge alpha mask with SceneXrender::Window::alphaMask
984 // alpha mask
985 xcb_pixmap_t pix = xcb_generate_id(connection());
986 xcb_create_pixmap(connection(), 8, pix, rootWindow(), 1, 1);
987 XRenderPicture alphaMask(pix, 8);
988 xcb_free_pixmap(connection(), pix);
989 const uint32_t values[] = {true};
990 xcb_render_change_picture(connection(), alphaMask, XCB_RENDER_CP_REPEAT, values);
991 color.alpha = int(opacity * 0xffff);
992 xcb_rectangle_t xrect = {0, 0, 1, 1};
993 xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, alphaMask, color, 1, &xrect);
994
995 // TODO: replace by lambda
996#define RENDER_CIRCLE(srcX, srcY, destX, destY) \
997xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *s_effectFrameCircle, alphaMask, \
998 pict, srcX, srcY, 0, 0, destX, destY, roundness, roundness)
999
1000 RENDER_CIRCLE(0, 0, area.left(), area.top());
1001 RENDER_CIRCLE(0, roundness, area.left(), area.top() + area.height() - roundness);
1002 RENDER_CIRCLE(roundness, 0, area.left() + area.width() - roundness, area.top());
1003 RENDER_CIRCLE(roundness, roundness,
1004 area.left() + area.width() - roundness, area.top() + area.height() - roundness);
1005#undef RENDER_CIRCLE
1006}
1007
1008void SceneXrender::EffectFrame::updatePicture()
1009{
1010 delete m_picture;
1011 m_picture = 0L;
1012 if (m_effectFrame->style() == EffectFrameStyled) {
1013 const QPixmap pix = m_effectFrame->frame().framePixmap();
1014 if (!pix.isNull())
1015 m_picture = new XRenderPicture(pix);
1016 }
1017}
1018
1019void SceneXrender::EffectFrame::updateTextPicture()
1020{
1021 // Mostly copied from SceneOpenGL::EffectFrame::updateTextTexture() above
1022 delete m_textPicture;
1023 m_textPicture = 0L;
1024
1025 if (m_effectFrame->text().isEmpty()) {
1026 return;
1027 }
1028
1029 // Determine position on texture to paint text
1030 QRect rect(QPoint(0, 0), m_effectFrame->geometry().size());
1031 if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
1032 rect.setLeft(m_effectFrame->iconSize().width());
1033 }
1034
1035 // If static size elide text as required
1036 QString text = m_effectFrame->text();
1037 if (m_effectFrame->isStatic()) {
1038 QFontMetrics metrics(m_effectFrame->text());
1039 text = metrics.elidedText(text, Qt::ElideRight, rect.width());
1040 }
1041
1042 QPixmap pixmap(m_effectFrame->geometry().size());
1043 pixmap.fill(Qt::transparent);
1044 QPainter p(&pixmap);
1045 p.setFont(m_effectFrame->font());
1046 if (m_effectFrame->style() == EffectFrameStyled) {
1047 p.setPen(m_effectFrame->styledTextColor());
1048 } else {
1049 // TODO: What about no frame? Custom color setting required
1050 p.setPen(Qt::white);
1051 }
1052 p.drawText(rect, m_effectFrame->alignment(), text);
1053 p.end();
1054 m_textPicture = new XRenderPicture(pixmap);
1055}
1056
1057SceneXRenderShadow::SceneXRenderShadow(Toplevel *toplevel)
1058 :Shadow(toplevel)
1059{
1060 for (int i=0; i<ShadowElementsCount; ++i) {
1061 m_pictures[i] = NULL;
1062 }
1063}
1064
1065SceneXRenderShadow::~SceneXRenderShadow()
1066{
1067 for (int i=0; i<ShadowElementsCount; ++i) {
1068 delete m_pictures[i];
1069 }
1070}
1071
1072void SceneXRenderShadow::layoutShadowRects(QRect& top, QRect& topRight,
1073 QRect& right, QRect& bottomRight,
1074 QRect& bottom, QRect& bottomLeft,
1075 QRect& left, QRect& topLeft)
1076{
1077 WindowQuadList quads = shadowQuads();
1078
1079 if (quads.count() == 0) {
1080 return;
1081 }
1082
1083 WindowQuad topQuad = quads.select(WindowQuadShadowTop)[0];
1084 WindowQuad topRightQuad = quads.select(WindowQuadShadowTopRight)[0];
1085 WindowQuad topLeftQuad = quads.select(WindowQuadShadowTopLeft)[0];
1086 WindowQuad leftQuad = quads.select(WindowQuadShadowLeft)[0];
1087 WindowQuad rightQuad = quads.select(WindowQuadShadowRight)[0];
1088 WindowQuad bottomQuad = quads.select(WindowQuadShadowBottom)[0];
1089 WindowQuad bottomRightQuad = quads.select(WindowQuadShadowBottomRight)[0];
1090 WindowQuad bottomLeftQuad = quads.select(WindowQuadShadowBottomLeft)[0];
1091
1092 top = QRect(topQuad.left(), topQuad.top(), (topQuad.right()-topQuad.left()), (topQuad.bottom()-topQuad.top()));
1093 topLeft = QRect(topLeftQuad.left(), topLeftQuad.top(), (topLeftQuad.right()-topLeftQuad.left()), (topLeftQuad.bottom()-topLeftQuad.top()));
1094 topRight = QRect(topRightQuad.left(), topRightQuad.top(), (topRightQuad.right()-topRightQuad.left()), (topRightQuad.bottom()-topRightQuad.top()));
1095 left = QRect(leftQuad.left(), leftQuad.top(), (leftQuad.right()-leftQuad.left()), (leftQuad.bottom()-leftQuad.top()));
1096 right = QRect(rightQuad.left(), rightQuad.top(), (rightQuad.right()-rightQuad.left()), (rightQuad.bottom()-rightQuad.top()));
1097 bottom = QRect(bottomQuad.left(), bottomQuad.top(), (bottomQuad.right()-bottomQuad.left()), (bottomQuad.bottom()-bottomQuad.top()));
1098 bottomLeft = QRect(bottomLeftQuad.left(), bottomLeftQuad.top(), (bottomLeftQuad.right()-bottomLeftQuad.left()), (bottomLeftQuad.bottom()-bottomLeftQuad.top()));
1099 bottomRight = QRect(bottomRightQuad.left(), bottomRightQuad.top(), (bottomRightQuad.right()-bottomRightQuad.left()), (bottomRightQuad.bottom()-bottomRightQuad.top()));
1100}
1101
1102void SceneXRenderShadow::buildQuads()
1103{
1104 Shadow::buildQuads();
1105
1106 if (shadowQuads().count() == 0) {
1107 return;
1108 }
1109
1110 QRect stlr, str, strr, srr, sbrr, sbr, sblr, slr;
1111 layoutShadowRects(str, strr, srr, sbrr, sbr, sblr, slr, stlr);
1112}
1113
1114bool SceneXRenderShadow::prepareBackend()
1115{
1116 const uint32_t values[] = {XCB_RENDER_REPEAT_NORMAL};
1117 for (int i=0; i<ShadowElementsCount; ++i) {
1118 delete m_pictures[i];
1119 m_pictures[i] = new XRenderPicture(shadowPixmap(ShadowElements(i)));
1120 xcb_render_change_picture(connection(), *m_pictures[i], XCB_RENDER_CP_REPEAT, values);
1121 }
1122 return true;
1123}
1124
1125xcb_render_picture_t SceneXRenderShadow::picture(Shadow::ShadowElements element) const
1126{
1127 if (!m_pictures[element]) {
1128 return XCB_RENDER_PICTURE_NONE;
1129 }
1130 return *m_pictures[element];
1131}
1132
1133#undef DOUBLE_TO_FIXED
1134#undef FIXED_TO_DOUBLE
1135
1136} // namespace
1137#endif
1138