void GlideEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { InfoHash::const_iterator info = windows.constFind(w); if (info != windows.constEnd()) { const double progress = info->timeLine->currentValue(); data.setRotationAxis(Qt::XAxis); data.setRotationAngle(angle * (1 - progress)); data.multiplyOpacity(progress); switch(effect) { default: case GlideInOut: if (info->added) glideIn(w, data, info); else if (info->closed) glideOut(w, data, info); break; case GlideOutIn: if (info->added) glideOut(w, data, info); if (info->closed) glideIn(w, data, info); break; case GlideIn: glideIn(w, data, info); break; case GlideOut: glideOut(w, data, info); break; } } effects->paintWindow(w, mask, region, data); }
void TaskbarThumbnailEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { effects->paintWindow(w, mask, region, data); // paint window first if (thumbnails.contains(w)) { // paint thumbnails on it int mask = PAINT_WINDOW_TRANSFORMED; if (data.opacity() == 1.0) mask |= PAINT_WINDOW_OPAQUE; else mask |= PAINT_WINDOW_TRANSLUCENT; mask |= PAINT_WINDOW_LANCZOS; foreach (const Data & thumb, thumbnails.values(w)) { EffectWindow* thumbw = effects->findWindow(thumb.window); if (thumbw == NULL) continue; WindowPaintData thumbData(thumbw); thumbData.multiplyOpacity(data.opacity()); QRect r, thumbRect(thumb.rect); thumbRect.translate(w->pos() + QPoint(data.xTranslation(), data.yTranslation())); thumbRect.setSize(QSize(thumbRect.width() * data.xScale(), thumbRect.height() * data.yScale())); // QSize has no vector multiplicator... :-( if (effects->isOpenGLCompositing()) { if (data.shader) { thumbData.shader = data.shader; } } // if ( effects->compositingType() == KWin::OpenGLCompositing ) setPositionTransformations(thumbData, r, thumbw, thumbRect, Qt::KeepAspectRatio); effects->drawWindow(thumbw, mask, r, thumbData); } }
bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const { if (!target->valid() || !shader || !shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) return false; if (w->isDesktop()) return false; bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); bool translated = data.xTranslation() || data.yTranslation(); if (scaled || ((translated || (mask & PAINT_WINDOW_TRANSFORMED)) && !w->data(WindowForceBlurRole).toBool())) return false; bool blurBehindDecos = effects->decorationsHaveAlpha() && effects->decorationSupportsBlurBehind(); if (!w->hasAlpha() && !(blurBehindDecos && w->hasDecoration())) return false; return true; }
void DimScreenEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { if (mActivated && (w != window) && w->isManaged()) { data.multiplyBrightness((1.0 - 0.33 * timeline.currentValue())); data.multiplySaturation((1.0 - 0.33 * timeline.currentValue())); } effects->paintWindow(w, mask, region, data); }
void SlidingPopupsEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { bool animating = false; bool appearing = false; if (mAppearingWindows.contains(w)) { appearing = true; animating = true; } else if (mDisappearingWindows.contains(w) && w->isDeleted()) { appearing = false; animating = true; } if (animating) { qreal progress; if (appearing) progress = 1.0 - mAppearingWindows[ w ]->currentValue(); else { if (mDisappearingWindows.contains(w)) progress = mDisappearingWindows[ w ]->currentValue(); else progress = 1.0; } const int start = mWindowsData[ w ].start; const QRect screenRect = effects->clientArea(FullScreenArea, w->screen(), w->desktop()); int splitPoint = 0; const QRect geo = w->expandedGeometry(); switch(mWindowsData[ w ].from) { case West: data.translate(- geo.width() * progress); splitPoint = geo.width() - (geo.x() + geo.width() - screenRect.x() - start); region = QRegion(geo.x() + splitPoint, geo.y(), geo.width() - splitPoint, geo.height()); break; case North: data.translate(0.0, - geo.height() * progress); splitPoint = geo.height() - (geo.y() + geo.height() - screenRect.y() - start); region = QRegion(geo.x(), geo.y() + splitPoint, geo.width(), geo.height() - splitPoint); break; case East: data.translate(geo.width() * progress); splitPoint = screenRect.x() + screenRect.width() - geo.x() - start; region = QRegion(geo.x(), geo.y(), splitPoint, geo.height()); break; case South: default: data.translate(0.0, geo.height() * progress); splitPoint = screenRect.y() + screenRect.height() - geo.y() - start; region = QRegion(geo.x(), geo.y(), geo.width(), splitPoint); } } effects->paintWindow(w, mask, region, data); }
void DimInactiveEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (dimWindow(w) || w == previousActive) { double previous = 1.0; if (w == previousActive) previous = previousActiveTimeline.currentValue(); if (previousActiveTimeline.currentValue() == 1.0) previousActive = NULL; data.multiplyBrightness((1.0 - (dim_strength / 100.0) * timeline.currentValue() * previous)); data.multiplySaturation((1.0 - (dim_strength / 100.0) * timeline.currentValue() * previous)); } effects->paintWindow(w, mask, region, data); }
void GlideEffect::glideOut(EffectWindow* w, WindowPaintData& data) { InfoHash::const_iterator info = windows.constFind(w); if (info == windows.constEnd()) return; const double progress = info->timeLine->currentValue(); data *= (2 - progress); data.translate(- int(w->width() / 2 * (1 - progress)), - int(w->height() / 2 * (1 - progress))); }
void KscreenEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { switch (m_state) { case StateFadingOut: data.multiplyOpacity(1.0 - m_timeLine.currentValue()); break; case StateFadedOut: data.multiplyOpacity(0.0); break; case StateFadingIn: data.multiplyOpacity(m_timeLine.currentValue()); break; default: // no adjustment break; } effects->paintWindow(w, mask, region, data); }
void ScaleInEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (mTimeLineWindows.contains(w) && isScaleWindow(w)) { const qreal value = mTimeLineWindows[ w ]->currentValue(); data.multiplyOpacity(value); data *= QVector2D(value, value); data += QPoint(int(w->width() / 2 * (1 - value)), int(w->height() / 2 * (1 - value))); } effects->paintWindow(w, mask, region, data); }
void ExplosionEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { // Make sure we have OpenGL compositing and the window is vidible and not a // special window bool useshader = (mValid && mWindows.contains(w)); if (useshader) { double maxscaleadd = 1.5f; double scale = 1 + maxscaleadd * mWindows[w]; data.setXScale(scale); data.setYScale(scale); data.translate(int(w->width() / 2 * (1 - scale)), int(w->height() / 2 * (1 - scale))); data.multiplyOpacity(0.99); // Force blending ShaderManager *manager = ShaderManager::instance(); GLShader *shader = manager->pushShader(ShaderManager::GenericShader); QMatrix4x4 screenTransformation = shader->getUniformMatrix4x4("screenTransformation"); manager->popShader(); ShaderManager::instance()->pushShader(mShader); mShader->setUniform("screenTransformation", screenTransformation); mShader->setUniform("factor", (float)mWindows[w]); mShader->setUniform("scale", (float)scale); mShader->setUniform("windowSize", QVector2D(w->width(), w->height())); glActiveTexture(GL_TEXTURE4); mStartOffsetTex->bind(); glActiveTexture(GL_TEXTURE5); mEndOffsetTex->bind(); glActiveTexture(GL_TEXTURE0); data.shader = mShader; } // Call the next effect. effects->paintWindow(w, mask, region, data); if (useshader) { ShaderManager::instance()->popShader(); glActiveTexture(GL_TEXTURE4); mStartOffsetTex->unbind(); glActiveTexture(GL_TEXTURE5); mEndOffsetTex->unbind(); glActiveTexture(GL_TEXTURE0); } }
// Maps window coordinates to screen coordinates QPoint SceneXrender::Window::mapToScreen(int mask, const WindowPaintData &data, const QPoint &point) const { QPoint pt = point; if (mask & PAINT_WINDOW_TRANSFORMED) { // Apply the window transformation pt.rx() = pt.x() * data.xScale() + data.xTranslation(); pt.ry() = pt.y() * data.yScale() + data.yTranslation(); } // Move the point to the screen position pt += QPoint(x(), y()); if (mask & PAINT_SCREEN_TRANSFORMED) { // Apply the screen transformation pt.rx() = pt.x() * screen_paint.xScale() + screen_paint.xTranslation(); pt.ry() = pt.y() * screen_paint.yScale() + screen_paint.yTranslation(); } return pt; }
// Maps window coordinates to screen coordinates QRect SceneXrender::Window::mapToScreen(int mask, const WindowPaintData &data, const QRect &rect) const { QRect r = rect; if (mask & PAINT_WINDOW_TRANSFORMED) { // Apply the window transformation r.moveTo(r.x() * data.xScale() + data.xTranslation(), r.y() * data.yScale() + data.yTranslation()); r.setWidth(r.width() * data.xScale()); r.setHeight(r.height() * data.yScale()); } // Move the rectangle to the screen position r.translate(x(), y()); if (mask & PAINT_SCREEN_TRANSFORMED) { // Apply the screen transformation r.moveTo(r.x() * screen_paint.xScale() + screen_paint.xTranslation(), r.y() * screen_paint.yScale() + screen_paint.yTranslation()); r.setWidth(r.width() * screen_paint.xScale()); r.setHeight(r.height() * screen_paint.yScale()); } return r; }
void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { const QRect screen = effects->virtualScreenGeometry(); if (shouldBlur(w, mask, data)) { QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; const bool translated = data.xTranslation() || data.yTranslation(); // let's do the evil parts - someone wants to blur behind a transformed window if (translated) { shape = shape.translated(data.xTranslation(), data.yTranslation()); shape = shape & region; } if (!shape.isEmpty()) { if (m_shouldCache && !translated) { doCachedBlur(w, region, data.opacity()); } else { doBlur(shape, screen, data.opacity()); } } } // Draw the window over the blurred area effects->drawWindow(w, mask, region, data); }
void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (progress > 0.0) { if (effects->compositingType() == KWin::OpenGLCompositing) { // In OpenGL mode we add vignetting and, if supported, a slight blur if (blurSupported) { // When using blur we render everything to an FBO and as such don't do the vignetting // until after we render the FBO to the screen. if (w == logoutWindow) { // Window is rendered after the FBO windowOpacity = data.opacity(); data.setOpacity(0.0); // Cheat, we need the opacity for later but don't want to blur it } else { if (logoutWindowPassed || ignoredWindows.contains(w)) { // Window is rendered after the FBO windows.append(w); windowsOpacities[ w ] = data.opacity(); data.setOpacity(0.0); } else // Window is added to the FBO data.multiplySaturation((1.0 - progress * 0.2)); } } else { // If we are not blurring then we are not rendering to an FBO if (w == logoutWindow) // This is the logout window don't alter it but render our vignetting now renderVignetting(); else if (!logoutWindowPassed && !ignoredWindows.contains(w)) // Window is in the background, desaturate data.multiplySaturation((1.0 - progress * 0.2)); // All other windows are unaltered } } if (effects->compositingType() == KWin::XRenderCompositing) { // Since we can't do vignetting in XRender just do a stronger desaturation and darken if (w != logoutWindow && !logoutWindowPassed && !ignoredWindows.contains(w)) { data.multiplySaturation((1.0 - progress * 0.8)); data.multiplyBrightness((1.0 - progress * 0.3)); } } if (w == logoutWindow || ignoredWindows.contains(w)) // HACK: All windows past the first ignored one should not be // blurred as it affects the stacking order. // All following windows are on top of the logout window and should not be altered either logoutWindowPassed = true; } effects->paintWindow(w, mask, region, data); }
void Effect::setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w, const QRect& r, Qt::AspectRatioMode aspect) { QSize size = w->size(); size.scale(r.size(), aspect); data.setXScale(size.width() / double(w->width())); data.setYScale(size.height() / double(w->height())); int width = int(w->width() * data.xScale()); int height = int(w->height() * data.yScale()); int x = r.x() + (r.width() - width) / 2; int y = r.y() + (r.height() - height) / 2; region = QRect(x, y, width, height); data.setXTranslation(x - w->x()); data.setYTranslation(y - w->y()); }
void TranslucencyEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (w->isDesktop() || w->isDock()) { effects->paintWindow(w, mask, region, data); return; } // Handling active and inactive windows if (inactive != 1.0 && isInactive(w)) { data.multiplyOpacity(inactive); if (w == previous) { data.multiplyOpacity((inactive + ((1.0 - inactive) * (1.0 - activeinactive_timeline.currentValue())))); if (activeinactive_timeline.currentValue() < 1.0) w->addRepaintFull(); else previous = NULL; } } else { // Fading in if (!isInactive(w) && !w->isDesktop()) { data.multiplyOpacity((inactive + ((1.0 - inactive) * activeinactive_timeline.currentValue()))); if (activeinactive_timeline.currentValue() < 1.0) w->addRepaintFull(); } // decoration and dialogs if (decoration != 1.0 && w->hasDecoration()) data.multiplyDecorationOpacity(decoration); if (dialogs != 1.0 && w->isDialog()) data.multiplyOpacity(dialogs); // Handling moving and resizing if (moveresize != 1.0 && !w->isDesktop() && !w->isDock()) { double progress = moveresize_timeline.currentValue(); if (w->isUserMove() || w->isUserResize()) { // Fading to translucent data.multiplyOpacity((moveresize + ((1.0 - moveresize) * (1.0 - progress)))); if (progress < 1.0 && progress > 0.0) { w->addRepaintFull(); fadeout = w; } } else { // Fading back to more opaque if (w == fadeout && !w->isUserMove() && !w->isUserResize()) { data.multiplyOpacity((moveresize + ((1.0 - moveresize) * (progress)))); if (progress == 1.0 || progress == 0.0) fadeout = NULL; else w->addRepaintFull(); } } } // Menus and combos if (dropdownmenus != 1.0 && w->isDropdownMenu()) data.multiplyOpacity(dropdownmenus); if (popupmenus != 1.0 && w->isPopupMenu()) data.multiplyOpacity(popupmenus); if (tornoffmenus != 1.0 && w->isMenu()) data.multiplyOpacity(tornoffmenus); if (comboboxpopups != 1.0 && w->isComboBox()) data.multiplyOpacity(comboboxpopups); } effects->paintWindow(w, mask, region, data); }
void HighlightWindowEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { data.multiplyOpacity(m_windowOpacity.value(w, 1.0f)); effects->paintWindow(w, mask, region, data); }
void GlideEffect::glideOut(EffectWindow* w, WindowPaintData& data, const InfoHash::const_iterator &info) { const double progress = info->timeLine->currentValue(); data *= (2 - progress); data.translate(- int(w->width() / 2 * (1 - progress)), - int(w->height() / 2 * (1 - progress))); }
WindowPaintData::WindowPaintData(const WindowPaintData &other) : PaintData() , quads(other.quads) , shader(other.shader) , d(new WindowPaintDataPrivate()) { setXScale(other.xScale()); setYScale(other.yScale()); setZScale(other.zScale()); translate(other.translation()); setRotationOrigin(other.rotationOrigin()); setRotationAxis(other.rotationAxis()); setRotationAngle(other.rotationAngle()); setOpacity(other.opacity()); setSaturation(other.saturation()); setBrightness(other.brightness()); setScreen(other.screen()); setCrossFadeProgress(other.crossFadeProgress()); setProjectionMatrix(other.projectionMatrix()); setModelViewMatrix(other.modelViewMatrix()); d->screenProjectionMatrix = other.d->screenProjectionMatrix; }
WindowPaintData::WindowPaintData(const WindowPaintData &other) : PaintData() , quads(other.quads) , shader(other.shader) , d(new WindowPaintDataPrivate()) { setXScale(other.xScale()); setYScale(other.yScale()); setZScale(other.zScale()); translate(other.translation()); setRotationOrigin(other.rotationOrigin()); setRotationAxis(other.rotationAxis()); setRotationAngle(other.rotationAngle()); setOpacity(other.opacity()); setDecorationOpacity(other.decorationOpacity()); setSaturation(other.saturation()); setBrightness(other.brightness()); setScreen(other.screen()); setCrossFadeProgress(other.crossFadeProgress()); }
void LanczosFilter::performPaint(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data) { if (effects->compositingType() == KWin::OpenGLCompositing && (data.xScale() < 0.9 || data.yScale() < 0.9) && KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) { if (!m_inited) init(); const QRect screenRect = Workspace::self()->clientArea(ScreenArea, w->screen(), w->desktop()); // window geometry may not be bigger than screen geometry to fit into the FBO if (m_shader && w->width() <= screenRect.width() && w->height() <= screenRect.height()) { double left = 0; double top = 0; double right = w->width(); double bottom = w->height(); foreach (const WindowQuad & quad, data.quads) { // we need this loop to include the decoration padding left = qMin(left, quad.left()); top = qMin(top, quad.top()); right = qMax(right, quad.right()); bottom = qMax(bottom, quad.bottom()); } double width = right - left; double height = bottom - top; if (width > screenRect.width() || height > screenRect.height()) { // window with padding does not fit into the framebuffer // so cut of the shadow left = 0; top = 0; width = w->width(); height = w->height(); } int tx = data.xTranslation() + w->x() + left * data.xScale(); int ty = data.yTranslation() + w->y() + top * data.yScale(); int tw = width * data.xScale(); int th = height * data.yScale(); const QRect textureRect(tx, ty, tw, th); const bool hardwareClipping = !(QRegion(textureRect)-region).isEmpty(); int sw = width; int sh = height; GLTexture *cachedTexture = static_cast< GLTexture*>(w->data(LanczosCacheRole).value<void*>()); if (cachedTexture) { if (cachedTexture->width() == tw && cachedTexture->height() == th) { cachedTexture->bind(); if (hardwareClipping) { glEnable(GL_SCISSOR_TEST); } if (ShaderManager::instance()->isValid()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); const qreal rgb = data.brightness() * data.opacity(); const qreal a = data.opacity(); GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); shader->setUniform(GLShader::Offset, QVector2D(0, 0)); shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a)); shader->setUniform(GLShader::Saturation, data.saturation()); shader->setUniform(GLShader::AlphaToOne, 0); cachedTexture->render(region, textureRect, hardwareClipping); ShaderManager::instance()->popShader(); glDisable(GL_BLEND); } else { prepareRenderStates(cachedTexture, data.opacity(), data.brightness(), data.saturation()); cachedTexture->render(region, textureRect, hardwareClipping); restoreRenderStates(cachedTexture, data.opacity(), data.brightness(), data.saturation()); } if (hardwareClipping) { glDisable(GL_SCISSOR_TEST); } cachedTexture->unbind(); m_timer.start(5000, this); return; } else { // offscreen texture not matching - delete delete cachedTexture; cachedTexture = 0; w->setData(LanczosCacheRole, QVariant()); } } WindowPaintData thumbData = data; thumbData.setXScale(1.0); thumbData.setYScale(1.0); thumbData.setXTranslation(-w->x() - left); thumbData.setYTranslation(-w->y() - top); thumbData.setBrightness(1.0); thumbData.setOpacity(1.0); thumbData.setSaturation(1.0); // Bind the offscreen FBO and draw the window on it unscaled updateOffscreenSurfaces(); GLRenderTarget::pushRenderTarget(m_offscreenTarget); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); w->sceneWindow()->performPaint(mask, infiniteRegion(), thumbData); // Create a scratch texture and copy the rendered window into it GLTexture tex(sw, sh); tex.setFilter(GL_LINEAR); tex.setWrapMode(GL_CLAMP_TO_EDGE); tex.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, sw, sh); // Set up the shader for horizontal scaling float dx = sw / float(tw); int kernelSize; m_shader->createKernel(dx, &kernelSize); m_shader->createOffsets(kernelSize, sw, Qt::Horizontal); m_shader->bind(); m_shader->setUniforms(); // Draw the window back into the FBO, this time scaled horizontally glClear(GL_COLOR_BUFFER_BIT); QVector<float> verts; QVector<float> texCoords; verts.reserve(12); texCoords.reserve(12); texCoords << 1.0 << 0.0; verts << tw << 0.0; // Top right texCoords << 0.0 << 0.0; verts << 0.0 << 0.0; // Top left texCoords << 0.0 << 1.0; verts << 0.0 << sh; // Bottom left texCoords << 0.0 << 1.0; verts << 0.0 << sh; // Bottom left texCoords << 1.0 << 1.0; verts << tw << sh; // Bottom right texCoords << 1.0 << 0.0; verts << tw << 0.0; // Top right GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setData(6, 2, verts.constData(), texCoords.constData()); vbo->render(GL_TRIANGLES); // At this point we don't need the scratch texture anymore tex.unbind(); tex.discard(); // create scratch texture for second rendering pass GLTexture tex2(tw, sh); tex2.setFilter(GL_LINEAR); tex2.setWrapMode(GL_CLAMP_TO_EDGE); tex2.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, tw, sh); // Set up the shader for vertical scaling float dy = sh / float(th); m_shader->createKernel(dy, &kernelSize); m_shader->createOffsets(kernelSize, m_offscreenTex->height(), Qt::Vertical); m_shader->setUniforms(); // Now draw the horizontally scaled window in the FBO at the right // coordinates on the screen, while scaling it vertically and blending it. glClear(GL_COLOR_BUFFER_BIT); verts.clear(); verts << tw << 0.0; // Top right verts << 0.0 << 0.0; // Top left verts << 0.0 << th; // Bottom left verts << 0.0 << th; // Bottom left verts << tw << th; // Bottom right verts << tw << 0.0; // Top right vbo->setData(6, 2, verts.constData(), texCoords.constData()); vbo->render(GL_TRIANGLES); tex2.unbind(); tex2.discard(); m_shader->unbind(); // create cache texture GLTexture *cache = new GLTexture(tw, th); cache->setFilter(GL_LINEAR); cache->setWrapMode(GL_CLAMP_TO_EDGE); cache->bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - th, tw, th); GLRenderTarget::popRenderTarget(); if (hardwareClipping) { glEnable(GL_SCISSOR_TEST); } if (ShaderManager::instance()->isValid()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); const qreal rgb = data.brightness() * data.opacity(); const qreal a = data.opacity(); GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); shader->setUniform(GLShader::Offset, QVector2D(0, 0)); shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a)); shader->setUniform(GLShader::Saturation, data.saturation()); shader->setUniform(GLShader::AlphaToOne, 0); cache->render(region, textureRect, hardwareClipping); ShaderManager::instance()->popShader(); glDisable(GL_BLEND); } else { prepareRenderStates(cache, data.opacity(), data.brightness(), data.saturation()); cache->render(region, textureRect, hardwareClipping); restoreRenderStates(cache, data.opacity(), data.brightness(), data.saturation()); } if (hardwareClipping) { glDisable(GL_SCISSOR_TEST); } cache->unbind(); w->setData(LanczosCacheRole, QVariant::fromValue(static_cast<void*>(cache))); // Delete the offscreen surface after 5 seconds m_timer.start(5000, this); return; } } // if ( effects->compositingType() == KWin::OpenGLCompositing )
// 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); }