/* Differences between OpenGL and XRender: - differently specified rectangles (X: width/height, O: x2,y2) - XRender uses pre-multiplied alpha */ void ShowFpsEffect::paintXrender(int fps) { xcb_pixmap_t pixmap = xcb_generate_id(xcbConnection()); xcb_create_pixmap(xcbConnection(), 32, pixmap, x11RootWindow(), FPS_WIDTH, MAX_TIME); XRenderPicture p(pixmap, 32); xcb_free_pixmap(xcbConnection(), pixmap); xcb_render_color_t col; col.alpha = int(alpha * 0xffff); col.red = int(alpha * 0xffff); // white col.green = int(alpha * 0xffff); col.blue = int(alpha * 0xffff); xcb_rectangle_t rect = {0, 0, FPS_WIDTH, MAX_TIME}; xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, p, col, 1, &rect); col.red = 0; // blue col.green = 0; col.blue = int(alpha * 0xffff); rect.y = MAX_TIME - fps; rect.width = FPS_WIDTH; rect.height = fps; xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, p, col, 1, &rect); col.red = 0; // black col.green = 0; col.blue = 0; QVector<xcb_rectangle_t> rects; for (int i = 10; i < MAX_TIME; i += 10) { xcb_rectangle_t rect = {0, int16_t(MAX_TIME - i), uint16_t(FPS_WIDTH), 1}; rects << rect; } xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, p, col, rects.count(), rects.constData()); xcb_render_composite(xcbConnection(), alpha != 1.0 ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC, p, XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, FPS_WIDTH, MAX_TIME); // Paint FPS graph paintFPSGraph(x + FPS_WIDTH, y); // Paint amount of rendered pixels graph paintDrawSizeGraph(x + FPS_WIDTH + MAX_TIME, y); // Paint FPS numerical value if (fpsTextRect.isValid()) { QImage textImg(fpsTextImage(fps)); XRenderPicture textPic(textImg); xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_OVER, textPic, XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(), 0, 0, 0, 0, fpsTextRect.x(), fpsTextRect.y(), textImg.width(), textImg.height()); effects->addRepaint(fpsTextRect); } }
static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic, vlc_tick_t date) { const video_format_t *fmt = &vd->source; vout_display_sys_t *sys = vd->sys; xcb_connection_t *conn = sys->conn; size_t offset = PictureAttach(vd, pic); if (offset != (size_t)-1) { xcb_shm_put_image(conn, sys->drawable.source, sys->gc, pic->p->i_pitch / pic->p->i_pixel_pitch, pic->p->i_lines, 0, 0, pic->p->i_pitch / pic->p->i_pixel_pitch, pic->p->i_lines, 0, 0, 32, XCB_IMAGE_FORMAT_Z_PIXMAP, 0, sys->segment, offset); } else { xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, sys->drawable.source, sys->gc, pic->p->i_pitch / pic->p->i_pixel_pitch, pic->p->i_lines, 0, 0, 0, 32, pic->p->i_pitch * pic->p->i_lines, pic->p->p_pixels); } /* Crop the picture with pixel accuracy */ xcb_render_composite(conn, XCB_RENDER_PICT_OP_SRC, sys->picture.source, XCB_RENDER_PICTURE_NONE, sys->picture.crop, fmt->i_x_offset, fmt->i_y_offset, 0, 0, 0, 0, fmt->i_visible_width, fmt->i_visible_height); /* Blank background */ static const xcb_render_color_t black_color = { 0, 0, 0, 0xffff }; xcb_rectangle_t rects[] = { { 0, 0, vd->cfg->display.width, vd->cfg->display.height }, }; xcb_render_fill_rectangles(conn, XCB_RENDER_PICT_OP_SRC, sys->picture.scale, black_color, ARRAY_SIZE(rects), rects); /* Scale and orient the picture */ xcb_render_composite(conn, XCB_RENDER_PICT_OP_SRC, sys->picture.crop, XCB_RENDER_PICTURE_NONE, sys->picture.scale, sys->src_x, sys->src_y, 0, 0, sys->place.x, sys->place.y, sys->place.width, sys->place.height); if (offset != (size_t)-1) PictureDetach(vd); /* Blend subpictures */ if (subpic != NULL) for (subpicture_region_t *r = subpic->p_region; r != NULL; r = r->p_next) RenderRegion(vd, subpic, r); xcb_flush(conn); (void) date; }
void SceneXrender::Window::prepareTempPixmap() { const QSize oldSize = temp_visibleRect.size(); temp_visibleRect = toplevel->visibleRect().translated(-toplevel->pos()); if (s_tempPicture && (oldSize.width() < temp_visibleRect.width() || oldSize.height() < temp_visibleRect.height())) { delete s_tempPicture; s_tempPicture = NULL; scene_setXRenderOffscreenTarget(0); // invalidate, better crash than cause weird results for developers } if (!s_tempPicture) { xcb_pixmap_t pix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), 32, pix, rootWindow(), temp_visibleRect.width(), temp_visibleRect.height()); s_tempPicture = new XRenderPicture(pix, 32); xcb_free_pixmap(connection(), pix); } const xcb_render_color_t transparent = {0, 0, 0, 0}; const xcb_rectangle_t rect = {0, 0, uint16_t(temp_visibleRect.width()), uint16_t(temp_visibleRect.height())}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, *s_tempPicture, transparent, 1, &rect); }
void MouseMarkEffect::addRect(const QPoint &p1, const QPoint &p2, xcb_rectangle_t *r, xcb_render_color_t *c) { r->x = qMin(p1.x(), p2.x()) - width_2; r->y = qMin(p1.y(), p2.y()) - width_2; r->width = qAbs(p1.x()-p2.x()) + 1 + width_2; r->height = qAbs(p1.y()-p2.y()) + 1 + width_2; // fast move -> large rect, <strike>tess...</strike> interpolate a line if (r->width > 3*width/2 && r->height > 3*width/2) { const int n = sqrt(r->width*r->width + r->height*r->height) / width; xcb_rectangle_t *rects = new xcb_rectangle_t[n-1]; const int w = p1.x() < p2.x() ? r->width : -r->width; const int h = p1.y() < p2.y() ? r->height : -r->height; for (int i = 1; i < n; ++i) { rects[i-1].x = p1.x() + i*w/n; rects[i-1].y = p1.y() + i*h/n; rects[i-1].width = rects[i-1].height = width; } xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), *c, n - 1, rects); delete [] rects; r->x = p1.x(); r->y = p1.y(); r->width = r->height = width; } }
void NonCompositedOutlineVisual::show() { if (!m_initialized) { const QRect geo(0, 0, 1, 1); const uint32_t values[] = {true}; // TODO: use template variant m_leftOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_rightOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_topOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_bottomOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_initialized = true; } const int defaultDepth = Xcb::defaultDepth(); const QRect &outlineGeometry = outline()->geometry(); // left/right parts are between top/bottom, they don't reach as far as the corners const uint16_t verticalWidth = 5; const uint16_t verticalHeight = outlineGeometry.height() - 10; const uint16_t horizontalWidth = outlineGeometry.width(); const uint horizontalHeight = 5; m_leftOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + 5, verticalWidth, verticalHeight); m_rightOutline.setGeometry(outlineGeometry.x() + outlineGeometry.width() - 5, outlineGeometry.y() + 5, verticalWidth, verticalHeight); m_topOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y(), horizontalWidth, horizontalHeight); m_bottomOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + outlineGeometry.height() - 5, horizontalWidth, horizontalHeight); const xcb_render_color_t white = {0xffff, 0xffff, 0xffff, 0xffff}; QColor qGray(Qt::gray); const xcb_render_color_t gray = { uint16_t(0xffff * qGray.redF()), uint16_t(0xffff * qGray.greenF()), uint16_t(0xffff * qGray.blueF()), 0xffff }; const xcb_render_color_t black = {0, 0, 0, 0xffff}; { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), verticalWidth, verticalHeight); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, 5, verticalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); rect.x = 1; rect.width = 3; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 1, &rect); rect.x = 2; rect.width = 1; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 1, &rect); m_leftOutline.setBackgroundPixmap(xpix); m_rightOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), horizontalWidth, horizontalHeight); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); xcb_rectangle_t grayRects[] = { {1, 1, uint16_t(horizontalWidth -2), 3}, {1, 4, 3, 1}, {int16_t(horizontalWidth - 4), 4, 3, 1} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); xcb_rectangle_t blackRects[] = { {2, 2, uint16_t(horizontalWidth -4), 1}, {2, 3, 1, 2}, {int16_t(horizontalWidth - 3), 3, 1, 2} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); m_topOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), outlineGeometry.width(), 5); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); xcb_rectangle_t grayRects[] = { {1, 1, uint16_t(horizontalWidth -2), 3}, {1, 0, 3, 1}, {int16_t(horizontalWidth - 4), 0, 3, 1} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); xcb_rectangle_t blackRects[] = { {2, 2, uint16_t(horizontalWidth -4), 1}, {2, 0, 1, 2}, {int16_t(horizontalWidth - 3), 0, 1, 2} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); m_bottomOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } forEachWindow(&Xcb::Window::clear); forEachWindow(&Xcb::Window::map); }
void SnapHelperEffect::postPaintScreen() { effects->postPaintScreen(); if (m_timeline.currentValue() != 0.0) { // Display the guide if (effects->isOpenGLCompositing()) { GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); ShaderBinder binder(ShaderManager::ColorShader); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QColor color; color.setRedF(0.5); color.setGreenF(0.5); color.setBlueF(0.5); color.setAlphaF(m_timeline.currentValue() * 0.5); vbo->setColor(color); glLineWidth(4.0); QVector<float> verts; verts.reserve(effects->numScreens() * 24); for (int i = 0; i < effects->numScreens(); ++i) { const QRect& rect = effects->clientArea(ScreenArea, i, 0); int midX = rect.x() + rect.width() / 2; int midY = rect.y() + rect.height() / 2 ; int halfWidth = m_window->width() / 2; int halfHeight = m_window->height() / 2; // Center lines verts << rect.x() + rect.width() / 2 << rect.y(); verts << rect.x() + rect.width() / 2 << rect.y() + rect.height(); verts << rect.x() << rect.y() + rect.height() / 2; verts << rect.x() + rect.width() << rect.y() + rect.height() / 2; // Window outline // The +/- 2 is to prevent line overlap verts << midX - halfWidth + 2 << midY - halfHeight; verts << midX + halfWidth + 2 << midY - halfHeight; verts << midX + halfWidth << midY - halfHeight + 2; verts << midX + halfWidth << midY + halfHeight + 2; verts << midX + halfWidth - 2 << midY + halfHeight; verts << midX - halfWidth - 2 << midY + halfHeight; verts << midX - halfWidth << midY + halfHeight - 2; verts << midX - halfWidth << midY - halfHeight - 2; } vbo->setData(verts.count() / 2, 2, verts.data(), NULL); vbo->render(GL_LINES); glDisable(GL_BLEND); glLineWidth(1.0); } if ( effects->compositingType() == XRenderCompositing ) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING for (int i = 0; i < effects->numScreens(); ++i) { const QRect& rect = effects->clientArea( ScreenArea, i, 0 ); int midX = rect.x() + rect.width() / 2; int midY = rect.y() + rect.height() / 2 ; int halfWidth = m_window->width() / 2; int halfHeight = m_window->height() / 2; xcb_rectangle_t rects[6]; // Center lines rects[0].x = rect.x() + rect.width() / 2 - 2; rects[0].y = rect.y(); rects[0].width = 4; rects[0].height = rect.height(); rects[1].x = rect.x(); rects[1].y = rect.y() + rect.height() / 2 - 2; rects[1].width = rect.width(); rects[1].height = 4; // Window outline // The +/- 4 is to prevent line overlap rects[2].x = midX - halfWidth + 4; rects[2].y = midY - halfHeight; rects[2].width = 2*halfWidth - 4; rects[2].height = 4; rects[3].x = midX + halfWidth - 4; rects[3].y = midY - halfHeight + 4; rects[3].width = 4; rects[3].height = 2*halfHeight - 4; rects[4].x = midX - halfWidth; rects[4].y = midY + halfHeight - 4; rects[4].width = 2*halfWidth - 4; rects[4].height = 4; rects[5].x = midX - halfWidth; rects[5].y = midY - halfHeight; rects[5].width = 4; rects[5].height = 2*halfHeight - 4; xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_OVER, effects->xrenderBufferPicture(), preMultiply(QColor(128, 128, 128, m_timeline.currentValue()*128)), 6, rects); } #endif } if (effects->compositingType() == QPainterCompositing) { QPainter *painter = effects->scenePainter(); painter->save(); QColor color; color.setRedF(0.5); color.setGreenF(0.5); color.setBlueF(0.5); color.setAlphaF(m_timeline.currentValue() * 0.5); QPen pen(color); pen.setWidth(4); painter->setPen(pen); painter->setBrush(Qt::NoBrush); for (int i = 0; i < effects->numScreens(); ++i) { const QRect &rect = effects->clientArea(ScreenArea, i, 0); // Center lines painter->drawLine(rect.center().x(), rect.y(), rect.center().x(), rect.y() + rect.height()); painter->drawLine(rect.x(), rect.center().y(), rect.x() + rect.width(), rect.center().y()); // window outline QRect windowRect(rect.center(), m_window->geometry().size()); painter->drawRect(windowRect.translated(-windowRect.width()/2, -windowRect.height()/2)); } painter->restore(); } } else if (m_window && !m_active) { if (m_window->isDeleted()) m_window->unrefWindow(); m_window = NULL; } }
static void RenderRegion(vout_display_t *vd, const subpicture_t *subpic, const subpicture_region_t *reg) { vout_display_sys_t *sys = vd->sys; xcb_connection_t *conn = sys->conn; const vout_display_place_t *place = &sys->place; picture_t *pic = reg->p_picture; unsigned sw = reg->fmt.i_width; unsigned sh = reg->fmt.i_height; xcb_rectangle_t rects[] = { { 0, 0, sw, sh }, }; xcb_create_pixmap(conn, 32, sys->drawable.subpic, sys->root, sw, sh); xcb_create_pixmap(conn, 8, sys->drawable.alpha, sys->root, sw, sh); xcb_render_create_picture(conn, sys->picture.subpic, sys->drawable.subpic, sys->format.argb, 0, NULL); xcb_render_create_picture(conn, sys->picture.alpha, sys->drawable.alpha, sys->format.alpha, 0, NULL); /* Upload region (TODO: use FD passing for SPU?) */ xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, sys->drawable.subpic, sys->gc, pic->p->i_pitch / pic->p->i_pixel_pitch, pic->p->i_lines, 0, 0, 0, 32, pic->p->i_pitch * pic->p->i_lines, pic->p->p_pixels); /* Copy alpha channel */ xcb_render_composite(conn, XCB_RENDER_PICT_OP_SRC, sys->picture.subpic, XCB_RENDER_PICTURE_NONE, sys->picture.alpha, 0, 0, 0, 0, 0, 0, sw, sh); /* Force alpha channel to maximum (add 100% and clip). * This is to compensate RENDER expecting pre-multiplied RGB * while VLC uses straight RGB. */ static const xcb_render_color_t alpha_one_color = { 0, 0, 0, 0xffff }; xcb_render_fill_rectangles(conn, XCB_RENDER_PICT_OP_ADD, sys->picture.subpic, alpha_one_color, ARRAY_SIZE(rects), rects); /* Multiply by region and subpicture alpha factors */ static const float alpha_fixed = 0xffffp0f / (0xffp0f * 0xffp0f); xcb_render_color_t alpha_color = { 0, 0, 0, lroundf(reg->i_alpha * subpic->i_alpha * alpha_fixed) }; xcb_render_fill_rectangles(conn, XCB_RENDER_PICT_OP_IN_REVERSE, sys->picture.subpic, alpha_color, ARRAY_SIZE(rects), rects); /* Mask in the original alpha channel then renver over the scaled pixmap. * Mask (pre)multiplies RGB channels and restores the alpha channel. */ int_fast16_t dx = place->x + reg->i_x * place->width / subpic->i_original_picture_width; int_fast16_t dy = place->y + reg->i_y * place->height / subpic->i_original_picture_height; uint_fast16_t dw = (reg->i_x + reg->fmt.i_visible_width) * place->width / subpic->i_original_picture_width; uint_fast16_t dh = (reg->i_y + reg->fmt.i_visible_height) * place->height / subpic->i_original_picture_height; xcb_render_composite(conn, XCB_RENDER_PICT_OP_OVER, sys->picture.subpic, sys->picture.alpha, sys->picture.scale, 0, 0, 0, 0, dx, dy, dw, dh); xcb_render_free_picture(conn, sys->picture.alpha); xcb_render_free_picture(conn, sys->picture.subpic); xcb_free_pixmap(conn, sys->drawable.alpha); xcb_free_pixmap(conn, sys->drawable.subpic); }
// paint the window void SceneXrender::Window::performPaint(int mask, QRegion region, WindowPaintData data) { setTransformedShape(QRegion()); // maybe nothing will be painted // check if there is something to paint bool opaque = isOpaque() && qFuzzyCompare(data.opacity(), 1.0); /* HACK: It seems this causes painting glitches, disable temporarily if (( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) { // We are only painting either opaque OR translucent windows, not both if ( mask & PAINT_WINDOW_OPAQUE && !opaque ) return; // Only painting opaque and window is translucent if ( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) return; // Only painting translucent and window is opaque }*/ // Intersect the clip region with the rectangle the window occupies on the screen if (!(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED))) region &= toplevel->visibleRect(); if (region.isEmpty()) return; XRenderWindowPixmap *pixmap = windowPixmap<XRenderWindowPixmap>(); if (!pixmap || !pixmap->isValid()) { return; } xcb_render_picture_t pic = pixmap->picture(); if (pic == XCB_RENDER_PICTURE_NONE) // The render format can be null for GL and/or Xv visuals return; toplevel->resetDamage(); // set picture filter if (options->isXrenderSmoothScale()) { // only when forced, it's slow if (mask & PAINT_WINDOW_TRANSFORMED) filter = ImageFilterGood; else if (mask & PAINT_SCREEN_TRANSFORMED) filter = ImageFilterGood; else filter = ImageFilterFast; } else filter = ImageFilterFast; // do required transformations const QRect wr = mapToScreen(mask, data, QRect(0, 0, width(), height())); QRect cr = QRect(toplevel->clientPos(), toplevel->clientSize()); // Client rect (in the window) qreal xscale = 1; qreal yscale = 1; bool scaled = false; Client *client = dynamic_cast<Client*>(toplevel); Deleted *deleted = dynamic_cast<Deleted*>(toplevel); const QRect decorationRect = toplevel->decorationRect(); if (((client && !client->noBorder()) || (deleted && !deleted->noBorder())) && true) { // decorated client transformed_shape = decorationRect; if (toplevel->shape()) { // "xeyes" + decoration transformed_shape -= cr; transformed_shape += shape(); } } else { transformed_shape = shape(); } if (toplevel->hasShadow()) transformed_shape |= toplevel->shadow()->shadowRegion(); xcb_render_transform_t xform = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; static const xcb_render_transform_t identity = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; if (mask & PAINT_WINDOW_TRANSFORMED) { xscale = data.xScale(); yscale = data.yScale(); } if (mask & PAINT_SCREEN_TRANSFORMED) { xscale *= screen_paint.xScale(); yscale *= screen_paint.yScale(); } if (!qFuzzyCompare(xscale, 1.0) || !qFuzzyCompare(yscale, 1.0)) { scaled = true; xform.matrix11 = DOUBLE_TO_FIXED(1.0 / xscale); xform.matrix22 = DOUBLE_TO_FIXED(1.0 / yscale); // transform the shape for clipping in paintTransformedScreen() QVector<QRect> rects = transformed_shape.rects(); for (int i = 0; i < rects.count(); ++i) { QRect& r = rects[ i ]; r.setRect(qRound(r.x() * xscale), qRound(r.y() * yscale), qRound(r.width() * xscale), qRound(r.height() * yscale)); } transformed_shape.setRects(rects.constData(), rects.count()); } transformed_shape.translate(mapToScreen(mask, data, QPoint(0, 0))); PaintClipper pcreg(region); // clip by the region to paint PaintClipper pc(transformed_shape); // clip by window's shape const bool wantShadow = m_shadow && !m_shadow->shadowRegion().isEmpty(); // In order to obtain a pixel perfect rescaling // we need to blit the window content togheter with // decorations in a temporary pixmap and scale // the temporary pixmap at the end. // We should do this only if there is scaling and // the window has border // This solves a number of glitches and on top of this // it optimizes painting quite a bit const bool blitInTempPixmap = xRenderOffscreen() || (data.crossFadeProgress() < 1.0 && !opaque) || (scaled && (wantShadow || (client && !client->noBorder()) || (deleted && !deleted->noBorder()))); xcb_render_picture_t renderTarget = m_scene->bufferPicture(); if (blitInTempPixmap) { if (scene_xRenderOffscreenTarget()) { temp_visibleRect = toplevel->visibleRect().translated(-toplevel->pos()); renderTarget = *scene_xRenderOffscreenTarget(); } else { prepareTempPixmap(); renderTarget = *s_tempPicture; } } else { xcb_render_set_picture_transform(connection(), pic, xform); if (filter == ImageFilterGood) { setPictureFilter(pic, KWin::Scene::ImageFilterGood); } //BEGIN OF STUPID RADEON HACK // This is needed to avoid hitting a fallback in the radeon driver. // The Render specification states that sampling pixels outside the // source picture results in alpha=0 pixels. This can be achieved by // setting the border color to transparent black, but since the border // color has the same format as the texture, it only works when the // texture has an alpha channel. So the driver falls back to software // when the repeat mode is RepeatNone, the picture has a non-identity // transformation matrix, and doesn't have an alpha channel. // Since we only scale the picture, we can work around this by setting // the repeat mode to RepeatPad. if (!window()->hasAlpha()) { const uint32_t values[] = {XCB_RENDER_REPEAT_PAD}; xcb_render_change_picture(connection(), pic, XCB_RENDER_CP_REPEAT, values); } //END OF STUPID RADEON HACK } #define MAP_RECT_TO_TARGET(_RECT_) \ if (blitInTempPixmap) _RECT_.translate(-temp_visibleRect.topLeft()); else _RECT_ = mapToScreen(mask, data, _RECT_) //BEGIN deco preparations bool noBorder = true; xcb_render_picture_t left = XCB_RENDER_PICTURE_NONE; xcb_render_picture_t top = XCB_RENDER_PICTURE_NONE; xcb_render_picture_t right = XCB_RENDER_PICTURE_NONE; xcb_render_picture_t bottom = XCB_RENDER_PICTURE_NONE; QRect dtr, dlr, drr, dbr; const SceneXRenderDecorationRenderer *renderer = nullptr; if (client) { if (client && !client->noBorder()) { if (client->isDecorated()) { SceneXRenderDecorationRenderer *r = static_cast<SceneXRenderDecorationRenderer*>(client->decoratedClient()->renderer()); if (r) { r->render(); renderer = r; } } noBorder = client->noBorder(); client->layoutDecorationRects(dlr, dtr, drr, dbr); } } if (deleted && !deleted->noBorder()) { renderer = static_cast<const SceneXRenderDecorationRenderer*>(deleted->decorationRenderer()); noBorder = deleted->noBorder(); deleted->layoutDecorationRects(dlr, dtr, drr, dbr); } if (renderer) { left = renderer->picture(SceneXRenderDecorationRenderer::DecorationPart::Left); top = renderer->picture(SceneXRenderDecorationRenderer::DecorationPart::Top); right = renderer->picture(SceneXRenderDecorationRenderer::DecorationPart::Right); bottom = renderer->picture(SceneXRenderDecorationRenderer::DecorationPart::Bottom); } if (!noBorder) { MAP_RECT_TO_TARGET(dtr); MAP_RECT_TO_TARGET(dlr); MAP_RECT_TO_TARGET(drr); MAP_RECT_TO_TARGET(dbr); } //END deco preparations //BEGIN shadow preparations QRect stlr, str, strr, srr, sbrr, sbr, sblr, slr; SceneXRenderShadow* m_xrenderShadow = static_cast<SceneXRenderShadow*>(m_shadow); if (wantShadow) { m_xrenderShadow->layoutShadowRects(str, strr, srr, sbrr, sbr, sblr, slr, stlr); MAP_RECT_TO_TARGET(stlr); MAP_RECT_TO_TARGET(str); MAP_RECT_TO_TARGET(strr); MAP_RECT_TO_TARGET(srr); MAP_RECT_TO_TARGET(sbrr); MAP_RECT_TO_TARGET(sbr); MAP_RECT_TO_TARGET(sblr); MAP_RECT_TO_TARGET(slr); } //BEGIN end preparations //BEGIN client preparations QRect dr = cr; if (blitInTempPixmap) { dr.translate(-temp_visibleRect.topLeft()); } else { dr = mapToScreen(mask, data, dr); // Destination rect if (scaled) { cr.moveLeft(cr.x() * xscale); cr.moveTop(cr.y() * yscale); } } const int clientRenderOp = (opaque || blitInTempPixmap) ? XCB_RENDER_PICT_OP_SRC : XCB_RENDER_PICT_OP_OVER; //END client preparations #undef MAP_RECT_TO_TARGET for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { #define RENDER_SHADOW_TILE(_TILE_, _RECT_) \ xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, m_xrenderShadow->picture(SceneXRenderShadow::ShadowElement##_TILE_), \ shadowAlpha, renderTarget, 0, 0, 0, 0, _RECT_.x(), _RECT_.y(), _RECT_.width(), _RECT_.height()) //shadow if (wantShadow) { xcb_render_picture_t shadowAlpha = XCB_RENDER_PICTURE_NONE; if (!opaque) { shadowAlpha = xRenderBlendPicture(data.opacity()); } RENDER_SHADOW_TILE(TopLeft, stlr); RENDER_SHADOW_TILE(Top, str); RENDER_SHADOW_TILE(TopRight, strr); RENDER_SHADOW_TILE(Left, slr); RENDER_SHADOW_TILE(Right, srr); RENDER_SHADOW_TILE(BottomLeft, sblr); RENDER_SHADOW_TILE(Bottom, sbr); RENDER_SHADOW_TILE(BottomRight, sbrr); } #undef RENDER_SHADOW_TILE // Paint the window contents if (!(client && client->isShade())) { xcb_render_picture_t clientAlpha = XCB_RENDER_PICTURE_NONE; if (!opaque) { clientAlpha = xRenderBlendPicture(data.opacity()); } xcb_render_composite(connection(), clientRenderOp, pic, clientAlpha, renderTarget, cr.x(), cr.y(), 0, 0, dr.x(), dr.y(), dr.width(), dr.height()); if (data.crossFadeProgress() < 1.0 && data.crossFadeProgress() > 0.0) { XRenderWindowPixmap *previous = previousWindowPixmap<XRenderWindowPixmap>(); if (previous && previous != pixmap) { static XRenderPicture cFadeAlpha(XCB_RENDER_PICTURE_NONE); static xcb_render_color_t cFadeColor = {0, 0, 0, 0}; cFadeColor.alpha = uint16_t((1.0 - data.crossFadeProgress()) * 0xffff); if (cFadeAlpha == XCB_RENDER_PICTURE_NONE) { cFadeAlpha = xRenderFill(cFadeColor); } else { xcb_rectangle_t rect = {0, 0, 1, 1}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, cFadeAlpha, cFadeColor , 1, &rect); } if (previous->size() != pixmap->size()) { xcb_render_transform_t xform2 = { DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(xform.matrix11) * previous->size().width() / pixmap->size().width()), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(xform.matrix22) * previous->size().height() / pixmap->size().height()), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; xcb_render_set_picture_transform(connection(), previous->picture(), xform2); } xcb_render_composite(connection(), opaque ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_ATOP, previous->picture(), cFadeAlpha, renderTarget, cr.x(), cr.y(), 0, 0, dr.x(), dr.y(), dr.width(), dr.height()); if (previous->size() != pixmap->size()) { xcb_render_set_picture_transform(connection(), previous->picture(), identity); } } } if (!opaque) transformed_shape = QRegion(); } if (client || deleted) { if (!noBorder) { xcb_render_picture_t decorationAlpha = xRenderBlendPicture(data.opacity()); auto renderDeco = [decorationAlpha, renderTarget](xcb_render_picture_t deco, const QRect &rect) { if (deco == XCB_RENDER_PICTURE_NONE) { return; } xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, deco, decorationAlpha, renderTarget, 0, 0, 0, 0, rect.x(), rect.y(), rect.width(), rect.height()); }; renderDeco(top, dtr); renderDeco(left, dlr); renderDeco(right, drr); renderDeco(bottom, dbr); } } if (data.brightness() != 1.0) { // fake brightness change by overlaying black const float alpha = (1 - data.brightness()) * data.opacity(); xcb_rectangle_t rect; if (blitInTempPixmap) { rect.x = -temp_visibleRect.left(); rect.y = -temp_visibleRect.top(); rect.width = width(); rect.height = height(); } else { rect.x = wr.x(); rect.y = wr.y(); rect.width = wr.width(); rect.height = wr.height(); } xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_OVER, renderTarget, preMultiply(data.brightness() < 1.0 ? QColor(0,0,0,255*alpha) : QColor(255,255,255,-alpha*255)), 1, &rect); } if (blitInTempPixmap) { const QRect r = mapToScreen(mask, data, temp_visibleRect); xcb_render_set_picture_transform(connection(), *s_tempPicture, xform); setPictureFilter(*s_tempPicture, filter); xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *s_tempPicture, XCB_RENDER_PICTURE_NONE, m_scene->bufferPicture(), 0, 0, 0, 0, r.x(), r.y(), r.width(), r.height()); xcb_render_set_picture_transform(connection(), *s_tempPicture, identity); } } if (scaled && !blitInTempPixmap) { xcb_render_set_picture_transform(connection(), pic, identity); if (filter == ImageFilterGood) setPictureFilter(pic, KWin::Scene::ImageFilterFast); if (!window()->hasAlpha()) { const uint32_t values[] = {XCB_RENDER_REPEAT_NONE}; xcb_render_change_picture(connection(), pic, XCB_RENDER_CP_REPEAT, values); } } if (xRenderOffscreen()) scene_setXRenderOffscreenTarget(*s_tempPicture); }
// fill the screen background void SceneXrender::paintBackground(QRegion region) { xcb_render_color_t col = { 0, 0, 0, 0xffff }; // black const QVector<xcb_rectangle_t> &rects = Xcb::regionToRects(region); xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, bufferPicture(), col, rects.count(), rects.data()); }