plugins/glide: Port to OffscreenEffect

The main motivation behind this change is to make the glide effect stop
setting a custom projection matrix in order to assist with making the scene
2d only.
This commit is contained in:
Vlad Zahorodnii 2024-02-23 13:40:49 +02:00
parent 36f6f2fb2d
commit 5f043f2d61
2 changed files with 61 additions and 100 deletions

View file

@ -39,38 +39,6 @@ static const QSet<QString> s_blacklist{
QStringLiteral("spectacle org.kde.spectacle"), // wayland
};
static QMatrix4x4 createPerspectiveMatrix(const QRectF &rect, const qreal scale, const QMatrix4x4 &renderTargetTransformation)
{
QMatrix4x4 ret;
ret.scale(1, -1);
ret *= renderTargetTransformation;
ret.scale(1, -1);
const float fovY = std::tan(qDegreesToRadians(60.0f) / 2);
const float aspect = 1.0f;
const float zNear = 0.1f;
const float zFar = 100.0f;
const float yMax = zNear * fovY;
const float yMin = -yMax;
const float xMin = yMin * aspect;
const float xMax = yMax * aspect;
ret.frustum(xMin, xMax, yMin, yMax, zNear, zFar);
const auto deviceRect = scaledRect(rect, scale);
const float scaleFactor = 1.1 * fovY / yMax;
ret.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
ret.scale((xMax - xMin) * scaleFactor / deviceRect.width(),
-(yMax - yMin) * scaleFactor / deviceRect.height(),
0.001);
ret.translate(-deviceRect.x(), -deviceRect.y());
return ret;
}
GlideEffect::GlideEffect()
{
GlideConfig::instance(effects->config());
@ -105,13 +73,6 @@ void GlideEffect::reconfigure(ReconfigureFlags flags)
m_outParams.opacity.to = GlideConfig::outOpacity();
}
void GlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
{
data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
effects->prePaintScreen(data, presentTime);
}
void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
{
auto animationIt = m_animations.find(w);
@ -123,92 +84,90 @@ void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:
effects->prePaintWindow(w, data, presentTime);
}
void GlideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
void GlideEffect::apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads)
{
auto animationIt = m_animations.constFind(w);
auto animationIt = m_animations.constFind(window);
if (animationIt == m_animations.constEnd()) {
effects->paintWindow(renderTarget, viewport, w, mask, region, data);
return;
}
// Perspective projection distorts objects near edges
// of the viewport. This is critical because distortions
// near edges of the viewport are not desired with this effect.
// To fix this, the center of the window will be moved to the origin,
// after applying perspective projection, the center is moved back
// to its "original" projected position. Overall, this is how the window
// will be transformed:
// [move to the origin] -> [rotate] -> [translate] ->
// -> [perspective projection] -> [reverse "move to the origin"]
const QMatrix4x4 oldProjMatrix = createPerspectiveMatrix(viewport.renderRect(), viewport.scale(), renderTarget.transform().toMatrix());
const auto frame = w->frameGeometry();
const QRectF windowGeo = scaledRect(frame, viewport.scale());
const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center()));
QMatrix4x4 invOffsetMatrix;
invOffsetMatrix.translate(invOffset.x(), invOffset.y());
data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix);
// Move the center of the window to the origin.
const QPointF offset = viewport.renderRect().center() - w->frameGeometry().center();
data.translate(offset.x(), offset.y());
const GlideParams params = w->isDeleted() ? m_outParams : m_inParams;
const GlideParams params = window->isDeleted() ? m_outParams : m_inParams;
const qreal t = (*animationIt).timeLine.value();
switch (params.edge) {
case RotationEdge::Top:
data.setRotationAxis(Qt::XAxis);
data.setRotationOrigin(QVector3D(0, 0, 0));
data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
break;
const QRectF rect = window->expandedGeometry().translated(-window->pos());
const float fovY = std::tan(qDegreesToRadians(60.0f) / 2);
const float aspect = rect.width() / rect.height();
const float zNear = 0.1f;
const float zFar = 100.0f;
const float yMax = zNear * fovY;
const float yMin = -yMax;
const float xMin = yMin * aspect;
const float xMax = yMax * aspect;
const float scaleFactor = 1.1 * fovY / yMax;
QMatrix4x4 matrix;
matrix.viewport(rect);
matrix.frustum(xMin, xMax, yMax, yMin, zNear, zFar);
matrix.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
matrix.scale((xMax - xMin) * scaleFactor / rect.width(), -(yMax - yMin) * scaleFactor / rect.height(), 0.001);
matrix.translate(-rect.x(), -rect.y());
const qreal angle = interpolate(params.angle.from, params.angle.to, t);
const qreal distance = interpolate(params.distance.from, params.distance.to, t);
switch (params.edge) {
case RotationEdge::Right:
data.setRotationAxis(Qt::YAxis);
data.setRotationOrigin(QVector3D(w->width(), 0, 0));
data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
matrix.translate(window->width(), window->height() / 2, -distance);
matrix.rotate(-angle, 0, 1, 0);
matrix.translate(-window->width(), -window->height() / 2);
break;
case RotationEdge::Bottom:
data.setRotationAxis(Qt::XAxis);
data.setRotationOrigin(QVector3D(0, w->height(), 0));
data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t));
matrix.translate(window->width() / 2, window->height(), -distance);
matrix.rotate(angle, 1, 0, 0);
matrix.translate(-window->width() / 2, -window->height());
break;
case RotationEdge::Left:
data.setRotationAxis(Qt::YAxis);
data.setRotationOrigin(QVector3D(0, 0, 0));
data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t));
matrix.translate(0, window->height() / 2, -distance);
matrix.rotate(angle, 0, 1, 0);
matrix.translate(0, -window->height() / 2);
break;
case RotationEdge::Top:
default:
// Fallback to Top.
data.setRotationAxis(Qt::XAxis);
data.setRotationOrigin(QVector3D(0, 0, 0));
data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
matrix.translate(window->width() / 2, 0, -distance);
matrix.rotate(-angle, 1, 0, 0);
matrix.translate(-window->width() / 2, 0);
break;
}
data.setZTranslation(-interpolate(params.distance.from, params.distance.to, t));
data.multiplyOpacity(interpolate(params.opacity.from, params.opacity.to, t));
for (WindowQuad &quad : quads) {
for (int i = 0; i < 4; ++i) {
const QPointF transformed = matrix.map(QPointF(quad[i].x(), quad[i].y()));
quad[i].setX(transformed.x());
quad[i].setY(transformed.y());
}
}
effects->paintWindow(renderTarget, viewport, w, mask, region, data);
data.multiplyOpacity(interpolate(params.opacity.from, params.opacity.to, t));
}
void GlideEffect::postPaintScreen()
void GlideEffect::postPaintWindow(EffectWindow *w)
{
auto animationIt = m_animations.begin();
while (animationIt != m_animations.end()) {
if (auto animationIt = m_animations.find(w); animationIt != m_animations.end()) {
w->addRepaintFull();
if ((*animationIt).timeLine.done()) {
unredirect(animationIt.key());
animationIt = m_animations.erase(animationIt);
} else {
++animationIt;
}
}
effects->addRepaintFull();
effects->postPaintScreen();
effects->postPaintWindow(w);
}
bool GlideEffect::isActive() const
@ -249,6 +208,7 @@ void GlideEffect::windowAdded(EffectWindow *w)
animation.timeLine.setDuration(m_duration);
animation.timeLine.setEasingCurve(QEasingCurve::InCurve);
redirect(w);
effects->addRepaintFull();
}
@ -280,6 +240,7 @@ void GlideEffect::windowClosed(EffectWindow *w)
animation.timeLine.setDuration(m_duration);
animation.timeLine.setEasingCurve(QEasingCurve::OutCurve);
redirect(w);
effects->addRepaintFull();
}
@ -295,6 +256,7 @@ void GlideEffect::windowDataChanged(EffectWindow *w, int role)
auto animationIt = m_animations.find(w);
if (animationIt != m_animations.end()) {
unredirect(animationIt.key());
m_animations.erase(animationIt);
}
}

View file

@ -13,8 +13,8 @@
#pragma once
// kwineffects
#include "effect/effect.h"
#include "effect/effectwindow.h"
#include "effect/offscreeneffect.h"
#include "effect/timeline.h"
namespace KWin
@ -26,7 +26,7 @@ struct GlideAnimation
TimeLine timeLine;
};
class GlideEffect : public Effect
class GlideEffect : public OffscreenEffect
{
Q_OBJECT
Q_PROPERTY(int duration READ duration)
@ -44,12 +44,8 @@ public:
~GlideEffect() override;
void reconfigure(ReconfigureFlags flags) override;
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override;
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override;
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override;
void postPaintScreen() override;
void postPaintWindow(EffectWindow *w) override;
bool isActive() const override;
int requestedEffectChainPosition() const override;
@ -73,6 +69,9 @@ public:
qreal outDistance() const;
qreal outOpacity() const;
protected:
void apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) override;
private Q_SLOTS:
void windowAdded(EffectWindow *w);
void windowClosed(EffectWindow *w);