1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> |
6 | Copyright (C) 2009 Fredrik Höglund <fredrik@kde.org> |
7 | Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org> |
8 | |
9 | This program is free software; you can redistribute it and/or modify |
10 | it under the terms of the GNU General Public License as published by |
11 | the Free Software Foundation; either version 2 of the License, or |
12 | (at your option) any later version. |
13 | |
14 | This program is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along 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 | |
41 | namespace KWin |
42 | { |
43 | |
44 | //**************************************** |
45 | // SceneXrender |
46 | //**************************************** |
47 | |
48 | xcb_render_picture_t SceneXrender::buffer = XCB_RENDER_PICTURE_NONE; |
49 | ScreenPaintData 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 | |
55 | static 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 | |
91 | SceneXrender::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 | |
109 | SceneXrender::~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 | |
127 | void 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 | |
163 | bool 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, |
170 | void 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 |
182 | qint64 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 | |
206 | void 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 | |
226 | void 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 | |
232 | void SceneXrender::paintDesktop(int desktop, int mask, const QRegion ®ion, 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 |
240 | void 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 | |
247 | void 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 | |
255 | void SceneXrender::windowOpacityChanged(KWin::Toplevel* c) |
256 | { |
257 | Q_UNUSED(c) |
258 | } |
259 | |
260 | void 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 | |
277 | void SceneXrender::windowDeleted(Deleted* c) |
278 | { |
279 | assert(windows.contains(c)); |
280 | delete windows.take(c); |
281 | c->effectWindow()->setSceneWindow(NULL); |
282 | } |
283 | |
284 | void 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 | |
299 | XRenderPicture *SceneXrender::Window::s_tempPicture = 0; |
300 | QRect SceneXrender::Window::temp_visibleRect; |
301 | |
302 | SceneXrender::Window::Window(Toplevel* c) |
303 | : Scene::Window(c) |
304 | , format(findFormatForVisual(c->visual()->visualid)) |
305 | , alpha_cached_opacity(0.0) |
306 | { |
307 | } |
308 | |
309 | SceneXrender::Window::~Window() |
310 | { |
311 | discardShape(); |
312 | } |
313 | |
314 | void SceneXrender::Window::cleanup() |
315 | { |
316 | delete s_tempPicture; |
317 | s_tempPicture = NULL; |
318 | } |
319 | |
320 | // Maps window coordinates to screen coordinates |
321 | QRect 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 |
348 | QPoint 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 | |
370 | void 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 |
391 | void 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_) \ |
611 | xcb_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_) \ |
674 | xcb_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 | |
733 | void 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 | |
747 | WindowPixmap* SceneXrender::Window::createWindowPixmap() |
748 | { |
749 | return new XRenderWindowPixmap(this, format); |
750 | } |
751 | |
752 | void SceneXrender::screenGeometryChanged(const QSize &size) |
753 | { |
754 | Scene::screenGeometryChanged(size); |
755 | initXRender(false); |
756 | } |
757 | |
758 | //**************************************** |
759 | // XRenderWindowPixmap |
760 | //**************************************** |
761 | |
762 | XRenderWindowPixmap::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 | |
769 | XRenderWindowPixmap::~XRenderWindowPixmap() |
770 | { |
771 | if (m_picture != XCB_RENDER_PICTURE_NONE) { |
772 | xcb_render_free_picture(connection(), m_picture); |
773 | } |
774 | } |
775 | |
776 | void 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 | |
793 | XRenderPicture *SceneXrender::EffectFrame::s_effectFrameCircle = NULL; |
794 | |
795 | SceneXrender::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 | |
804 | SceneXrender::EffectFrame::~EffectFrame() |
805 | { |
806 | delete m_picture; |
807 | delete m_textPicture; |
808 | delete m_iconPicture; |
809 | delete m_selectionPicture; |
810 | } |
811 | |
812 | void SceneXrender::EffectFrame::cleanup() |
813 | { |
814 | delete s_effectFrameCircle; |
815 | s_effectFrameCircle = NULL; |
816 | } |
817 | |
818 | void 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 | |
830 | void SceneXrender::EffectFrame::freeIconFrame() |
831 | { |
832 | delete m_iconPicture; |
833 | m_iconPicture = NULL; |
834 | } |
835 | |
836 | void SceneXrender::EffectFrame::freeTextFrame() |
837 | { |
838 | delete m_textPicture; |
839 | m_textPicture = NULL; |
840 | } |
841 | |
842 | void SceneXrender::EffectFrame::freeSelection() |
843 | { |
844 | delete m_selectionPicture; |
845 | m_selectionPicture = NULL; |
846 | } |
847 | |
848 | void SceneXrender::EffectFrame::crossFadeIcon() |
849 | { |
850 | // TODO: implement me |
851 | } |
852 | |
853 | void SceneXrender::EffectFrame::crossFadeText() |
854 | { |
855 | // TODO: implement me |
856 | } |
857 | |
858 | void 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 | |
920 | void 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) \ |
997 | xcb_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 | |
1008 | void 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 | |
1019 | void 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 | |
1057 | SceneXRenderShadow::SceneXRenderShadow(Toplevel *toplevel) |
1058 | :Shadow(toplevel) |
1059 | { |
1060 | for (int i=0; i<ShadowElementsCount; ++i) { |
1061 | m_pictures[i] = NULL; |
1062 | } |
1063 | } |
1064 | |
1065 | SceneXRenderShadow::~SceneXRenderShadow() |
1066 | { |
1067 | for (int i=0; i<ShadowElementsCount; ++i) { |
1068 | delete m_pictures[i]; |
1069 | } |
1070 | } |
1071 | |
1072 | void 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 | |
1102 | void 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 | |
1114 | bool 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 | |
1125 | xcb_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 | |