2010-03-05 23:42:10 +03:00
|
|
|
/*
|
|
|
|
* Copyright © 2010 Fredrik Höglund <fredrik@kde.org>
|
2011-12-10 14:28:38 +04:00
|
|
|
* Copyright © 2011 Philipp Knechtges <philipp-dev@knechtges.com>
|
2010-03-05 23:42:10 +03:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. if not, write to
|
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "blur.h"
|
|
|
|
#include "blurshader.h"
|
2012-09-14 16:28:30 +04:00
|
|
|
// KConfigSkeleton
|
|
|
|
#include "blurconfig.h"
|
2010-03-05 23:42:10 +03:00
|
|
|
|
2010-03-06 19:14:25 +03:00
|
|
|
#include <X11/Xatom.h>
|
|
|
|
|
2011-01-08 21:56:22 +03:00
|
|
|
#include <QMatrix4x4>
|
2011-05-02 01:17:28 +04:00
|
|
|
#include <QLinkedList>
|
2010-11-10 19:42:33 +03:00
|
|
|
#include <KDebug>
|
2010-03-05 23:42:10 +03:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
2011-05-02 00:52:43 +04:00
|
|
|
|
2010-03-05 23:42:10 +03:00
|
|
|
KWIN_EFFECT(blur, BlurEffect)
|
|
|
|
KWIN_EFFECT_SUPPORTED(blur, BlurEffect::supported())
|
2011-05-02 00:52:43 +04:00
|
|
|
KWIN_EFFECT_ENABLEDBYDEFAULT(blur, BlurEffect::enabledByDefault())
|
2010-03-05 23:42:10 +03:00
|
|
|
|
|
|
|
BlurEffect::BlurEffect()
|
|
|
|
{
|
2010-03-08 23:45:58 +03:00
|
|
|
shader = BlurShader::create();
|
2010-03-05 23:42:10 +03:00
|
|
|
|
|
|
|
// Offscreen texture that's used as the target for the horizontal blur pass
|
|
|
|
// and the source for the vertical pass.
|
2011-07-17 19:57:30 +04:00
|
|
|
tex = GLTexture(displayWidth(), displayHeight());
|
|
|
|
tex.setFilter(GL_LINEAR);
|
|
|
|
tex.setWrapMode(GL_CLAMP_TO_EDGE);
|
2010-03-05 23:42:10 +03:00
|
|
|
|
|
|
|
target = new GLRenderTarget(tex);
|
2010-03-06 19:14:25 +03:00
|
|
|
|
2010-03-23 01:03:32 +03:00
|
|
|
net_wm_blur_region = XInternAtom(display(), "_KDE_NET_WM_BLUR_BEHIND_REGION", False);
|
2010-03-06 19:14:25 +03:00
|
|
|
effects->registerPropertyType(net_wm_blur_region, true);
|
|
|
|
|
2010-09-25 04:11:06 +04:00
|
|
|
reconfigure(ReconfigureAll);
|
|
|
|
|
2010-03-06 19:14:25 +03:00
|
|
|
// ### Hackish way to announce support.
|
|
|
|
// Should be included in _NET_SUPPORTED instead.
|
2010-09-25 04:11:06 +04:00
|
|
|
if (shader->isValid() && target->valid()) {
|
|
|
|
XChangeProperty(display(), rootWindow(), net_wm_blur_region, net_wm_blur_region,
|
|
|
|
32, PropModeReplace, 0, 0);
|
|
|
|
} else {
|
|
|
|
XDeleteProperty(display(), rootWindow(), net_wm_blur_region);
|
|
|
|
}
|
2012-01-29 15:29:24 +04:00
|
|
|
connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*)));
|
|
|
|
connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*)));
|
|
|
|
connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long)));
|
2011-11-26 19:15:46 +04:00
|
|
|
connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged()));
|
2010-03-05 23:42:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BlurEffect::~BlurEffect()
|
|
|
|
{
|
2010-03-06 19:14:25 +03:00
|
|
|
effects->registerPropertyType(net_wm_blur_region, false);
|
|
|
|
XDeleteProperty(display(), rootWindow(), net_wm_blur_region);
|
|
|
|
|
2011-07-17 19:57:30 +04:00
|
|
|
windows.clear();
|
|
|
|
|
2010-03-05 23:42:10 +03:00
|
|
|
delete shader;
|
|
|
|
delete target;
|
|
|
|
}
|
|
|
|
|
2011-11-26 19:15:46 +04:00
|
|
|
void BlurEffect::slotScreenGeometryChanged()
|
|
|
|
{
|
|
|
|
effects->reloadEffect(this);
|
|
|
|
}
|
|
|
|
|
2010-03-13 18:03:29 +03:00
|
|
|
void BlurEffect::reconfigure(ReconfigureFlags flags)
|
|
|
|
{
|
|
|
|
Q_UNUSED(flags)
|
|
|
|
|
2012-09-14 16:28:30 +04:00
|
|
|
BlurConfig::self()->readConfig();
|
|
|
|
int radius = qBound(2, BlurConfig::blurRadius(), 14);
|
2010-03-13 18:03:29 +03:00
|
|
|
shader->setRadius(radius);
|
2010-09-25 04:11:06 +04:00
|
|
|
|
2012-09-14 16:28:30 +04:00
|
|
|
m_shouldCache = BlurConfig::cacheTexture();
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
windows.clear();
|
|
|
|
|
2010-09-25 04:11:06 +04:00
|
|
|
if (!shader->isValid())
|
|
|
|
XDeleteProperty(display(), rootWindow(), net_wm_blur_region);
|
2010-03-13 18:03:29 +03:00
|
|
|
}
|
|
|
|
|
2010-03-06 19:14:25 +03:00
|
|
|
void BlurEffect::updateBlurRegion(EffectWindow *w) const
|
|
|
|
{
|
|
|
|
QRegion region;
|
|
|
|
|
|
|
|
const QByteArray value = w->readProperty(net_wm_blur_region, XA_CARDINAL, 32);
|
2010-05-25 21:34:17 +04:00
|
|
|
if (value.size() > 0 && !(value.size() % (4 * sizeof(unsigned long)))) {
|
|
|
|
const unsigned long *cardinals = reinterpret_cast<const unsigned long*>(value.constData());
|
|
|
|
for (unsigned int i = 0; i < value.size() / sizeof(unsigned long);) {
|
2010-03-06 19:14:25 +03:00
|
|
|
int x = cardinals[i++];
|
|
|
|
int y = cardinals[i++];
|
|
|
|
int w = cardinals[i++];
|
|
|
|
int h = cardinals[i++];
|
|
|
|
region += QRect(x, y, w, h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-12 19:47:45 +03:00
|
|
|
if (region.isEmpty() && !value.isNull()) {
|
|
|
|
// Set the data to a dummy value.
|
|
|
|
// This is needed to be able to distinguish between the value not
|
|
|
|
// being set, and being set to an empty region.
|
2010-07-23 23:52:20 +04:00
|
|
|
w->setData(WindowBlurBehindRole, 1);
|
2010-03-12 19:47:45 +03:00
|
|
|
} else
|
2010-07-23 23:52:20 +04:00
|
|
|
w->setData(WindowBlurBehindRole, region);
|
2010-03-06 19:14:25 +03:00
|
|
|
}
|
|
|
|
|
2011-02-26 00:06:02 +03:00
|
|
|
void BlurEffect::slotWindowAdded(EffectWindow *w)
|
2010-03-06 19:14:25 +03:00
|
|
|
{
|
|
|
|
updateBlurRegion(w);
|
|
|
|
}
|
|
|
|
|
2011-07-17 19:57:30 +04:00
|
|
|
void BlurEffect::slotWindowDeleted(EffectWindow *w)
|
|
|
|
{
|
|
|
|
if (windows.contains(w)) {
|
|
|
|
windows.remove(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-12 21:18:19 +03:00
|
|
|
void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom)
|
2010-03-06 19:14:25 +03:00
|
|
|
{
|
2011-10-12 17:16:46 +04:00
|
|
|
if (w && atom == net_wm_blur_region) {
|
2010-03-06 19:14:25 +03:00
|
|
|
updateBlurRegion(w);
|
2012-02-19 01:07:39 +04:00
|
|
|
CacheEntry it = windows.find(w);
|
|
|
|
if (it != windows.end()) {
|
2011-10-12 17:16:46 +04:00
|
|
|
const QRect screen(0, 0, displayWidth(), displayHeight());
|
2012-02-19 01:07:39 +04:00
|
|
|
it->damagedRegion = expand(blurRegion(w).translated(w->pos())) & screen;
|
2011-10-12 17:16:46 +04:00
|
|
|
}
|
|
|
|
}
|
2010-03-06 19:14:25 +03:00
|
|
|
}
|
|
|
|
|
2011-05-02 00:52:43 +04:00
|
|
|
bool BlurEffect::enabledByDefault()
|
|
|
|
{
|
|
|
|
GLPlatform *gl = GLPlatform::instance();
|
|
|
|
|
2012-02-04 14:23:04 +04:00
|
|
|
if (gl->isIntel() && gl->chipClass() < SandyBridge)
|
2011-05-02 00:52:43 +04:00
|
|
|
return false;
|
2011-12-31 11:54:15 +04:00
|
|
|
if (gl->driver() == Driver_Catalyst) {
|
|
|
|
// fglrx supports only ARB shaders and those tend to crash KWin (see Bug #270818 and #286795)
|
|
|
|
return false;
|
|
|
|
}
|
2011-05-02 00:52:43 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-03-05 23:42:10 +03:00
|
|
|
bool BlurEffect::supported()
|
|
|
|
{
|
2010-07-05 17:42:34 +04:00
|
|
|
bool supported = GLRenderTarget::supported() && GLTexture::NPOTTextureSupported() &&
|
2011-01-30 17:34:42 +03:00
|
|
|
(GLSLBlurShader::supported() || ARBBlurShader::supported());
|
2010-09-25 04:11:06 +04:00
|
|
|
|
|
|
|
if (supported) {
|
|
|
|
int maxTexSize;
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
|
|
|
|
|
|
|
|
if (displayWidth() > maxTexSize || displayHeight() > maxTexSize)
|
|
|
|
supported = false;
|
|
|
|
}
|
2010-07-05 17:42:34 +04:00
|
|
|
return supported;
|
2010-03-05 23:42:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QRect BlurEffect::expand(const QRect &rect) const
|
|
|
|
{
|
2010-03-13 18:03:29 +03:00
|
|
|
const int radius = shader->radius();
|
2010-03-05 23:42:10 +03:00
|
|
|
return rect.adjusted(-radius, -radius, radius, radius);
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegion BlurEffect::expand(const QRegion ®ion) const
|
|
|
|
{
|
|
|
|
QRegion expanded;
|
|
|
|
|
2011-07-09 14:43:32 +04:00
|
|
|
foreach (const QRect & rect, region.rects()) {
|
2011-01-30 17:34:42 +03:00
|
|
|
expanded += expand(rect);
|
2011-07-09 14:43:32 +04:00
|
|
|
}
|
2010-03-05 23:42:10 +03:00
|
|
|
|
|
|
|
return expanded;
|
|
|
|
}
|
|
|
|
|
2010-03-06 19:14:25 +03:00
|
|
|
QRegion BlurEffect::blurRegion(const EffectWindow *w) const
|
|
|
|
{
|
|
|
|
QRegion region;
|
|
|
|
|
2010-07-23 23:52:20 +04:00
|
|
|
const QVariant value = w->data(WindowBlurBehindRole);
|
2010-03-06 19:14:25 +03:00
|
|
|
if (value.isValid()) {
|
|
|
|
const QRegion appRegion = qvariant_cast<QRegion>(value);
|
|
|
|
if (!appRegion.isEmpty()) {
|
2010-11-10 21:33:07 +03:00
|
|
|
if (w->hasDecoration() && effects->decorationSupportsBlurBehind()) {
|
2010-03-06 19:14:25 +03:00
|
|
|
region = w->shape();
|
2010-03-12 19:47:45 +03:00
|
|
|
region -= w->decorationInnerRect();
|
2011-10-15 18:19:19 +04:00
|
|
|
}
|
|
|
|
region |= appRegion.translated(w->contentsRect().topLeft()) &
|
|
|
|
w->decorationInnerRect();
|
2010-03-12 19:47:45 +03:00
|
|
|
} else {
|
|
|
|
// An empty region means that the blur effect should be enabled
|
|
|
|
// for the whole window.
|
2010-03-06 19:14:25 +03:00
|
|
|
region = w->shape();
|
2010-03-12 19:47:45 +03:00
|
|
|
}
|
2010-11-10 21:33:07 +03:00
|
|
|
} else if (w->hasDecoration() && effects->decorationSupportsBlurBehind()) {
|
2010-03-12 19:47:45 +03:00
|
|
|
// If the client hasn't specified a blur region, we'll only enable
|
|
|
|
// the effect behind the decoration.
|
2010-03-06 19:14:25 +03:00
|
|
|
region = w->shape();
|
2010-03-12 19:47:45 +03:00
|
|
|
region -= w->decorationInnerRect();
|
|
|
|
}
|
2010-03-06 19:14:25 +03:00
|
|
|
|
|
|
|
return region;
|
|
|
|
}
|
|
|
|
|
2010-03-12 21:29:16 +03:00
|
|
|
void BlurEffect::drawRegion(const QRegion ®ion)
|
|
|
|
{
|
2011-01-08 21:56:22 +03:00
|
|
|
const int vertexCount = region.rectCount() * 6;
|
2010-03-12 21:29:16 +03:00
|
|
|
if (vertices.size() < vertexCount)
|
|
|
|
vertices.resize(vertexCount);
|
|
|
|
|
|
|
|
int i = 0;
|
2011-01-30 17:34:42 +03:00
|
|
|
foreach (const QRect & r, region.rects()) {
|
2011-01-08 21:56:22 +03:00
|
|
|
vertices[i++] = QVector2D(r.x() + r.width(), r.y());
|
|
|
|
vertices[i++] = QVector2D(r.x(), r.y());
|
|
|
|
vertices[i++] = QVector2D(r.x(), r.y() + r.height());
|
|
|
|
vertices[i++] = QVector2D(r.x(), r.y() + r.height());
|
|
|
|
vertices[i++] = QVector2D(r.x() + r.width(), r.y() + r.height());
|
|
|
|
vertices[i++] = QVector2D(r.x() + r.width(), r.y());
|
2010-03-12 21:29:16 +03:00
|
|
|
}
|
2011-01-08 21:56:22 +03:00
|
|
|
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
|
|
|
vbo->reset();
|
|
|
|
vbo->setData(vertexCount, 2, (float*)vertices.constData(), (float*)vertices.constData());
|
|
|
|
vbo->render(GL_TRIANGLES);
|
2010-03-12 21:29:16 +03:00
|
|
|
}
|
|
|
|
|
2011-05-02 01:17:28 +04:00
|
|
|
void BlurEffect::prePaintScreen(ScreenPrePaintData &data, int time)
|
2010-03-05 23:42:10 +03:00
|
|
|
{
|
2011-07-09 14:43:32 +04:00
|
|
|
m_damagedArea = QRegion();
|
2011-07-17 19:57:30 +04:00
|
|
|
m_paintedArea = QRegion();
|
2011-07-09 14:43:32 +04:00
|
|
|
m_currentBlur = QRegion();
|
2011-05-02 01:17:28 +04:00
|
|
|
|
|
|
|
effects->prePaintScreen(data, time);
|
2011-07-09 14:43:32 +04:00
|
|
|
}
|
2011-05-02 01:17:28 +04:00
|
|
|
|
2011-07-09 14:43:32 +04:00
|
|
|
void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
|
|
|
|
{
|
|
|
|
// this effect relies on prePaintWindow being called in the bottom to top order
|
2011-05-02 01:17:28 +04:00
|
|
|
|
2011-07-09 14:43:32 +04:00
|
|
|
effects->prePaintWindow(w, data, time);
|
2011-05-02 01:17:28 +04:00
|
|
|
|
2011-07-09 14:43:32 +04:00
|
|
|
if (!w->isPaintingEnabled()) {
|
2011-05-02 01:17:28 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-07-09 14:43:32 +04:00
|
|
|
// to blur an area partially we have to shrink the opaque area of a window
|
|
|
|
QRegion newClip;
|
|
|
|
const QRegion oldClip = data.clip;
|
|
|
|
const int radius = shader->radius();
|
|
|
|
foreach (const QRect& rect, data.clip.rects()) {
|
|
|
|
newClip |= rect.adjusted(radius,radius,-radius,-radius);
|
|
|
|
}
|
|
|
|
data.clip = newClip;
|
|
|
|
|
2011-07-17 19:57:30 +04:00
|
|
|
const QRegion oldPaint = data.paint;
|
|
|
|
|
2011-08-03 10:16:12 +04:00
|
|
|
// we don't have to blur a region we don't see
|
2011-07-09 14:43:32 +04:00
|
|
|
m_currentBlur -= newClip;
|
|
|
|
// if we have to paint a non-opaque part of this window that intersects with the
|
2011-10-12 18:02:19 +04:00
|
|
|
// currently blurred region (which is not cached) we have to redraw the whole region
|
2011-07-09 14:43:32 +04:00
|
|
|
if ((data.paint-oldClip).intersects(m_currentBlur)) {
|
|
|
|
data.paint |= m_currentBlur;
|
|
|
|
}
|
|
|
|
|
|
|
|
// in case this window has regions to be blurred
|
2012-02-17 00:11:51 +04:00
|
|
|
const QRect screen(0, 0, displayWidth(), displayHeight());
|
|
|
|
const QRegion blurArea = blurRegion(w).translated(w->pos()) & screen;
|
|
|
|
const QRegion expandedBlur = expand(blurArea) & screen;
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
if (m_shouldCache) {
|
|
|
|
// we are caching the horizontally blurred background texture
|
|
|
|
|
|
|
|
// if a window underneath the blurred area is damaged we have to
|
2011-10-12 18:02:19 +04:00
|
|
|
// update the cached texture
|
2011-12-10 20:31:18 +04:00
|
|
|
QRegion damagedCache;
|
2012-02-19 01:07:39 +04:00
|
|
|
CacheEntry it = windows.find(w);
|
|
|
|
if (it != windows.end() && !it->dropCache &&
|
|
|
|
it->windowPos == w->pos() &&
|
|
|
|
it->blurredBackground.size() == expandedBlur.boundingRect().size()) {
|
2012-02-17 00:11:51 +04:00
|
|
|
damagedCache = (expand(expandedBlur & m_damagedArea) |
|
2012-02-19 01:07:39 +04:00
|
|
|
(it->damagedRegion & data.paint)) & expandedBlur;
|
2011-12-10 20:31:18 +04:00
|
|
|
} else {
|
|
|
|
damagedCache = expandedBlur;
|
|
|
|
}
|
2011-12-10 14:28:38 +04:00
|
|
|
if (!damagedCache.isEmpty()) {
|
2011-10-12 18:02:19 +04:00
|
|
|
// This is the area of the blurry window which really can change.
|
2011-12-10 14:28:38 +04:00
|
|
|
const QRegion damagedArea = damagedCache & blurArea;
|
2011-10-12 18:02:19 +04:00
|
|
|
// In order to be able to recalculate this area we have to make sure the
|
|
|
|
// background area is painted before.
|
2011-09-18 19:53:14 +04:00
|
|
|
data.paint |= expand(damagedArea);
|
2012-02-19 01:07:39 +04:00
|
|
|
if (it != windows.end()) {
|
2011-10-12 18:02:19 +04:00
|
|
|
// In case we already have a texture cache mark the dirty regions invalid.
|
2012-02-19 01:07:39 +04:00
|
|
|
it->damagedRegion &= expandedBlur;
|
|
|
|
it->damagedRegion |= damagedCache;
|
2012-02-19 04:19:32 +04:00
|
|
|
// The valid part of the cache can be considered as being opaque
|
|
|
|
// as long as we don't need to update a bordering part
|
|
|
|
data.clip |= blurArea - expand(it->damagedRegion);
|
2012-02-19 01:07:39 +04:00
|
|
|
it->dropCache = false;
|
2011-09-18 19:53:14 +04:00
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
// we keep track of the "damage propagation"
|
2011-09-18 19:53:14 +04:00
|
|
|
m_damagedArea |= damagedArea;
|
2011-07-17 19:57:30 +04:00
|
|
|
// we have to check again whether we do not damage a blurred area
|
|
|
|
// of a window we do not cache
|
|
|
|
if (expandedBlur.intersects(m_currentBlur)) {
|
|
|
|
data.paint |= m_currentBlur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// we are not caching the window
|
|
|
|
|
|
|
|
// if this window or an window underneath the blurred area is painted again we have to
|
|
|
|
// blur everything
|
2011-12-10 14:28:38 +04:00
|
|
|
if (m_paintedArea.intersects(expandedBlur) || data.paint.intersects(blurArea)) {
|
2011-07-17 19:57:30 +04:00
|
|
|
data.paint |= expandedBlur;
|
|
|
|
// we keep track of the "damage propagation"
|
2011-12-10 14:28:38 +04:00
|
|
|
m_damagedArea |= expand(expandedBlur & m_damagedArea) & blurArea;
|
2011-07-17 19:57:30 +04:00
|
|
|
// we have to check again whether we do not damage a blurred area
|
|
|
|
// of a window we do not cache
|
|
|
|
if (expandedBlur.intersects(m_currentBlur)) {
|
|
|
|
data.paint |= m_currentBlur;
|
|
|
|
}
|
2011-05-02 01:17:28 +04:00
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
m_currentBlur |= expandedBlur;
|
2011-05-02 01:17:28 +04:00
|
|
|
}
|
|
|
|
|
2011-08-03 10:16:12 +04:00
|
|
|
// we don't consider damaged areas which are occluded and are not
|
2011-07-09 14:43:32 +04:00
|
|
|
// explicitly damaged by this window
|
|
|
|
m_damagedArea -= data.clip;
|
2011-07-17 19:57:30 +04:00
|
|
|
m_damagedArea |= oldPaint;
|
|
|
|
|
|
|
|
// in contrast to m_damagedArea does m_paintedArea keep track of all repainted areas
|
|
|
|
m_paintedArea -= data.clip;
|
|
|
|
m_paintedArea |= data.paint;
|
2010-03-05 23:42:10 +03:00
|
|
|
}
|
|
|
|
|
2011-05-14 20:22:38 +04:00
|
|
|
bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const
|
2010-03-05 23:42:10 +03:00
|
|
|
{
|
2011-05-14 20:22:38 +04:00
|
|
|
if (!target->valid() || !shader->isValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (w->isDesktop())
|
|
|
|
return false;
|
|
|
|
|
2012-05-28 14:00:31 +04:00
|
|
|
bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0);
|
2012-05-28 16:45:46 +04:00
|
|
|
bool translated = data.xTranslation() || data.yTranslation();
|
2011-05-14 20:22:38 +04:00
|
|
|
|
2011-12-11 01:32:47 +04:00
|
|
|
if (scaled || ((translated || (mask & PAINT_WINDOW_TRANSFORMED)) && !w->data(WindowForceBlurRole).toBool()))
|
2011-05-14 20:22:38 +04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
bool blurBehindDecos = effects->decorationsHaveAlpha() &&
|
|
|
|
effects->decorationSupportsBlurBehind();
|
|
|
|
|
|
|
|
if (!w->hasAlpha() && !(blurBehindDecos && w->hasDecoration()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
|
|
|
|
{
|
2011-09-18 19:53:14 +04:00
|
|
|
const QRect screen(0, 0, displayWidth(), displayHeight());
|
2011-05-14 20:22:38 +04:00
|
|
|
if (shouldBlur(w, mask, data)) {
|
2011-12-11 01:32:47 +04:00
|
|
|
QRegion shape = region & blurRegion(w).translated(w->pos()) & screen;
|
|
|
|
|
2012-05-28 16:45:46 +04:00
|
|
|
const bool translated = data.xTranslation() || data.yTranslation();
|
2011-12-11 01:32:47 +04:00
|
|
|
// let's do the evil parts - someone wants to blur behind a transformed window
|
|
|
|
if (translated) {
|
2012-05-28 16:45:46 +04:00
|
|
|
shape = shape.translated(data.xTranslation(), data.yTranslation());
|
2011-12-11 01:32:47 +04:00
|
|
|
shape = shape & region;
|
|
|
|
}
|
2011-05-14 20:22:38 +04:00
|
|
|
|
2011-07-17 19:57:30 +04:00
|
|
|
if (!shape.isEmpty()) {
|
2011-12-11 01:32:47 +04:00
|
|
|
if (m_shouldCache && !translated) {
|
2012-07-12 19:20:17 +04:00
|
|
|
doCachedBlur(w, region, data.opacity());
|
2011-07-17 19:57:30 +04:00
|
|
|
} else {
|
2012-07-12 19:20:17 +04:00
|
|
|
doBlur(shape, screen, data.opacity());
|
2011-07-17 19:57:30 +04:00
|
|
|
}
|
|
|
|
}
|
2010-03-05 23:42:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the window over the blurred area
|
|
|
|
effects->drawWindow(w, mask, region, data);
|
|
|
|
}
|
|
|
|
|
2010-07-25 00:48:19 +04:00
|
|
|
void BlurEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity)
|
|
|
|
{
|
|
|
|
const QRect screen(0, 0, displayWidth(), displayHeight());
|
|
|
|
bool valid = target->valid() && shader->isValid();
|
2011-01-30 17:34:42 +03:00
|
|
|
QRegion shape = frame->geometry().adjusted(-5, -5, 5, 5) & screen;
|
2011-04-27 16:48:34 +04:00
|
|
|
if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) {
|
2010-07-25 00:48:19 +04:00
|
|
|
doBlur(shape, screen, opacity * frameOpacity);
|
|
|
|
}
|
|
|
|
effects->paintEffectFrame(frame, region, opacity, frameOpacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity)
|
|
|
|
{
|
|
|
|
const QRegion expanded = expand(shape) & screen;
|
|
|
|
const QRect r = expanded.boundingRect();
|
|
|
|
|
|
|
|
// Create a scratch texture and copy the area in the back buffer that we're
|
|
|
|
// going to blur into it
|
|
|
|
GLTexture scratch(r.width(), r.height());
|
|
|
|
scratch.setFilter(GL_LINEAR);
|
|
|
|
scratch.setWrapMode(GL_CLAMP_TO_EDGE);
|
|
|
|
scratch.bind();
|
|
|
|
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r.x(), displayHeight() - r.y() - r.height(),
|
|
|
|
r.width(), r.height());
|
|
|
|
|
|
|
|
// Draw the texture on the offscreen framebuffer object, while blurring it horizontally
|
2011-07-17 19:57:30 +04:00
|
|
|
target->attachTexture(tex);
|
2011-03-13 16:34:30 +03:00
|
|
|
GLRenderTarget::pushRenderTarget(target);
|
2010-07-25 00:48:19 +04:00
|
|
|
|
|
|
|
shader->bind();
|
|
|
|
shader->setDirection(Qt::Horizontal);
|
|
|
|
shader->setPixelDistance(1.0 / r.width());
|
|
|
|
|
|
|
|
// Set up the texture matrix to transform from screen coordinates
|
|
|
|
// to texture coordinates.
|
2011-01-08 21:56:22 +03:00
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
pushMatrix();
|
|
|
|
}
|
2011-01-08 21:56:22 +03:00
|
|
|
#endif
|
|
|
|
QMatrix4x4 textureMatrix;
|
|
|
|
textureMatrix.scale(1.0 / scratch.width(), -1.0 / scratch.height(), 1);
|
|
|
|
textureMatrix.translate(-r.x(), -scratch.height() - r.y(), 0);
|
|
|
|
loadMatrix(textureMatrix);
|
|
|
|
shader->setTextureMatrix(textureMatrix);
|
2010-07-25 00:48:19 +04:00
|
|
|
|
|
|
|
drawRegion(expanded);
|
|
|
|
|
2011-03-13 16:34:30 +03:00
|
|
|
GLRenderTarget::popRenderTarget();
|
2010-07-25 00:48:19 +04:00
|
|
|
scratch.unbind();
|
|
|
|
scratch.discard();
|
|
|
|
|
|
|
|
// Now draw the horizontally blurred area back to the backbuffer, while
|
|
|
|
// blurring it vertically and clipping it to the window shape.
|
2011-07-17 19:57:30 +04:00
|
|
|
tex.bind();
|
2010-07-25 00:48:19 +04:00
|
|
|
|
|
|
|
shader->setDirection(Qt::Vertical);
|
2011-07-17 19:57:30 +04:00
|
|
|
shader->setPixelDistance(1.0 / tex.height());
|
2010-07-25 00:48:19 +04:00
|
|
|
|
|
|
|
// Modulate the blurred texture with the window opacity if the window isn't opaque
|
|
|
|
if (opacity < 1.0) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendColor(0, 0, 0, opacity);
|
|
|
|
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the up the texture matrix to transform from screen coordinates
|
|
|
|
// to texture coordinates.
|
2011-01-08 21:56:22 +03:00
|
|
|
textureMatrix.setToIdentity();
|
2011-07-17 19:57:30 +04:00
|
|
|
textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1);
|
|
|
|
textureMatrix.translate(0, -tex.height(), 0);
|
2011-01-08 21:56:22 +03:00
|
|
|
loadMatrix(textureMatrix);
|
|
|
|
shader->setTextureMatrix(textureMatrix);
|
2010-07-25 00:48:19 +04:00
|
|
|
|
|
|
|
drawRegion(shape);
|
|
|
|
|
2011-01-08 21:56:22 +03:00
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
popMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
2011-01-08 21:56:22 +03:00
|
|
|
#endif
|
2010-07-25 00:48:19 +04:00
|
|
|
|
2011-01-08 21:56:22 +03:00
|
|
|
if (opacity < 1.0) {
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
2010-07-25 00:48:19 +04:00
|
|
|
|
2011-07-17 19:57:30 +04:00
|
|
|
tex.unbind();
|
|
|
|
shader->unbind();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlurEffect::doCachedBlur(EffectWindow *w, const QRegion& region, const float opacity)
|
|
|
|
{
|
|
|
|
const QRect screen(0, 0, displayWidth(), displayHeight());
|
|
|
|
const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screen;
|
|
|
|
const QRegion expanded = expand(blurredRegion) & screen;
|
|
|
|
const QRect r = expanded.boundingRect();
|
|
|
|
|
2011-10-12 18:02:19 +04:00
|
|
|
// The background texture we get is only partially valid.
|
2011-07-17 19:57:30 +04:00
|
|
|
|
2012-02-19 01:07:39 +04:00
|
|
|
CacheEntry it = windows.find(w);
|
|
|
|
if (it == windows.end()) {
|
2011-07-17 19:57:30 +04:00
|
|
|
BlurWindowInfo bwi;
|
|
|
|
bwi.blurredBackground = GLTexture(r.width(),r.height());
|
2011-09-18 19:53:14 +04:00
|
|
|
bwi.damagedRegion = expanded;
|
2011-12-10 20:31:18 +04:00
|
|
|
bwi.dropCache = false;
|
2012-02-07 20:01:41 +04:00
|
|
|
bwi.windowPos = w->pos();
|
2012-02-19 01:07:39 +04:00
|
|
|
it = windows.insert(w, bwi);
|
|
|
|
} else if (it->blurredBackground.size() != r.size()) {
|
|
|
|
it->blurredBackground = GLTexture(r.width(),r.height());
|
|
|
|
it->dropCache = false;
|
|
|
|
it->windowPos = w->pos();
|
|
|
|
} else if (it->windowPos != w->pos()) {
|
|
|
|
it->dropCache = false;
|
|
|
|
it->windowPos = w->pos();
|
2011-07-17 19:57:30 +04:00
|
|
|
}
|
|
|
|
|
2012-02-19 01:07:39 +04:00
|
|
|
GLTexture targetTexture = it->blurredBackground;
|
2011-07-17 19:57:30 +04:00
|
|
|
targetTexture.setFilter(GL_LINEAR);
|
|
|
|
targetTexture.setWrapMode(GL_CLAMP_TO_EDGE);
|
|
|
|
shader->bind();
|
|
|
|
QMatrix4x4 textureMatrix;
|
|
|
|
QMatrix4x4 modelViewProjectionMatrix;
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
pushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
pushMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
pushMatrix();
|
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
#endif
|
|
|
|
|
2012-02-17 00:11:51 +04:00
|
|
|
/**
|
|
|
|
* Which part of the background texture can be updated ?
|
|
|
|
*
|
|
|
|
* Well this is a rather difficult question. We kind of rely on the fact, that
|
|
|
|
* we need a bigger background region being painted before, more precisely if we want to
|
|
|
|
* blur region A we need the background region expand(A). This business logic is basically
|
|
|
|
* done in prePaintWindow:
|
|
|
|
* data.paint |= expand(damagedArea);
|
|
|
|
*
|
|
|
|
* Now "data.paint" gets clipped and becomes what we receive as the "region" variable
|
|
|
|
* in this function. In theory there is now only one function that does this clipping
|
|
|
|
* and this is paintSimpleScreen. The clipping has the effect that "damagedRegion"
|
|
|
|
* is no longer a subset of "region" and we cannot fully validate the cache within one
|
|
|
|
* rendering pass. If we would now update the "damageRegion & region" part of the cache
|
|
|
|
* we would wrongly update the part of the cache that is next to the "region" border and
|
|
|
|
* which lies within "damagedRegion", just because we cannot assume that the framebuffer
|
|
|
|
* outside of "region" is valid. Therefore the maximal damaged region of the cache that can
|
|
|
|
* be repainted is given by:
|
|
|
|
* validUpdate = damagedRegion - expand(damagedRegion - region);
|
|
|
|
*
|
|
|
|
* Now you may ask what is with the rest of "damagedRegion & region" that is not part
|
|
|
|
* of "validUpdate" but also might end up on the screen. Well under the assumption
|
|
|
|
* that only the occlusion culling can shrink "data.paint", we can control this by reducing
|
|
|
|
* the opaque area of every window by a margin of the blurring radius (c.f. prePaintWindow).
|
|
|
|
* This way we are sure that this area is overpainted by a higher opaque window.
|
|
|
|
*
|
|
|
|
* Apparently paintSimpleScreen is not the only function that can influence "region".
|
|
|
|
* In fact every effect's paintWindow that is called before Blur::paintWindow
|
|
|
|
* can do so (e.g. SlidingPopups). Hence we have to make the compromise that we update
|
|
|
|
* "damagedRegion & region" of the cache but only mark "validUpdate" as valid.
|
|
|
|
**/
|
2012-02-19 01:07:39 +04:00
|
|
|
const QRegion damagedRegion = it->damagedRegion;
|
2012-02-17 00:11:51 +04:00
|
|
|
const QRegion updateBackground = damagedRegion & region;
|
|
|
|
const QRegion validUpdate = damagedRegion - expand(damagedRegion - region);
|
|
|
|
|
|
|
|
if (!validUpdate.isEmpty()) {
|
2011-09-18 19:53:14 +04:00
|
|
|
const QRect updateRect = (expand(updateBackground) & expanded).boundingRect();
|
2012-01-08 20:23:36 +04:00
|
|
|
// First we have to copy the background from the frontbuffer
|
|
|
|
// into a scratch texture (in this case "tex").
|
|
|
|
tex.bind();
|
2011-07-17 19:57:30 +04:00
|
|
|
|
2011-09-18 19:53:14 +04:00
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, updateRect.x(), displayHeight() - updateRect.y() - updateRect.height(),
|
|
|
|
updateRect.width(), updateRect.height());
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
// Draw the texture on the offscreen framebuffer object, while blurring it horizontally
|
|
|
|
target->attachTexture(targetTexture);
|
|
|
|
GLRenderTarget::pushRenderTarget(target);
|
|
|
|
|
|
|
|
shader->setDirection(Qt::Horizontal);
|
2012-01-08 20:23:36 +04:00
|
|
|
shader->setPixelDistance(1.0 / tex.width());
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
modelViewProjectionMatrix.ortho(0, r.width(), r.height(), 0 , 0, 65535);
|
|
|
|
modelViewProjectionMatrix.translate(-r.x(), -r.y(), 0);
|
|
|
|
loadMatrix(modelViewProjectionMatrix);
|
|
|
|
shader->setModelViewProjectionMatrix(modelViewProjectionMatrix);
|
|
|
|
|
|
|
|
// Set up the texture matrix to transform from screen coordinates
|
|
|
|
// to texture coordinates.
|
2012-01-08 20:23:36 +04:00
|
|
|
textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1);
|
|
|
|
textureMatrix.translate(-updateRect.x(), -updateRect.height() - updateRect.y(), 0);
|
2011-07-17 19:57:30 +04:00
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
loadMatrix(textureMatrix);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
#endif
|
|
|
|
shader->setTextureMatrix(textureMatrix);
|
|
|
|
|
2011-09-18 19:53:14 +04:00
|
|
|
drawRegion(updateBackground & screen);
|
2011-07-17 19:57:30 +04:00
|
|
|
|
|
|
|
GLRenderTarget::popRenderTarget();
|
2012-01-08 20:23:36 +04:00
|
|
|
tex.unbind();
|
2011-10-12 18:02:19 +04:00
|
|
|
// mark the updated region as valid
|
2012-02-19 01:07:39 +04:00
|
|
|
it->damagedRegion -= validUpdate;
|
2011-07-17 19:57:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now draw the horizontally blurred area back to the backbuffer, while
|
|
|
|
// blurring it vertically and clipping it to the window shape.
|
|
|
|
targetTexture.bind();
|
|
|
|
|
|
|
|
shader->setDirection(Qt::Vertical);
|
|
|
|
shader->setPixelDistance(1.0 / targetTexture.height());
|
|
|
|
|
|
|
|
// Modulate the blurred texture with the window opacity if the window isn't opaque
|
|
|
|
if (opacity < 1.0) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendColor(0, 0, 0, opacity);
|
|
|
|
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
|
|
|
}
|
|
|
|
|
|
|
|
modelViewProjectionMatrix.setToIdentity();
|
|
|
|
modelViewProjectionMatrix.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535);
|
|
|
|
loadMatrix(modelViewProjectionMatrix);
|
|
|
|
shader->setModelViewProjectionMatrix(modelViewProjectionMatrix);
|
|
|
|
|
|
|
|
// Set the up the texture matrix to transform from screen coordinates
|
|
|
|
// to texture coordinates.
|
|
|
|
textureMatrix.setToIdentity();
|
|
|
|
textureMatrix.scale(1.0 / targetTexture.width(), -1.0 / targetTexture.height(), 1);
|
|
|
|
textureMatrix.translate(-r.x(), -targetTexture.height() - r.y(), 0);
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
loadMatrix(textureMatrix);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
#endif
|
|
|
|
shader->setTextureMatrix(textureMatrix);
|
|
|
|
|
|
|
|
drawRegion(blurredRegion & region);
|
|
|
|
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
2012-01-27 19:50:27 +04:00
|
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
|
|
popMatrix();
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
popMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
popMatrix();
|
|
|
|
}
|
2011-07-17 19:57:30 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (opacity < 1.0) {
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
targetTexture.unbind();
|
2010-07-25 00:48:19 +04:00
|
|
|
shader->unbind();
|
|
|
|
}
|
|
|
|
|
2012-08-11 13:24:37 +04:00
|
|
|
int BlurEffect::blurRadius() const
|
|
|
|
{
|
|
|
|
return shader->radius();
|
|
|
|
}
|
|
|
|
|
2010-03-05 23:42:10 +03:00
|
|
|
} // namespace KWin
|
|
|
|
|