Commit 2d01ba64 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

[scenes/qpainter] Draw decoration shadows

Summary:
QPainter doesn't render decoration shadows. It renders only
shadows provided through ShadowInterface.

With this change, painting of shadows is done in similar way OpenGL backend is
currently doing.

Before

{F5734867, layout=center, size=full}

After

{F5734870, layout=center, size=full}

Depends on D10811 (dummy decoration with shadows in autotests)

Test Plan:
* start kwin with QPainter backend enabled:

```
KWIN_COMPOSE=Q kwin_wayland --xwayland --windowed
```

* open konsole and kate:

```
DISPLAY=:1 konsole
DISPLAY=:1 kate
```

Reviewers: #kwin, graesslin, davidedmundson

Reviewed By: davidedmundson

Subscribers: abetts, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D10943
parent 7637cfc2
......@@ -72,6 +72,7 @@ if (XCB_ICCCM_FOUND)
integrationTest(NAME testQuickTiling SRCS quick_tiling_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testGlobalShortcuts SRCS globalshortcuts_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testSceneQPainter SRCS scene_qpainter_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testSceneQPainterShadow SRCS scene_qpainter_shadow_test.cpp LIBS XCB::ICCCM)
if (KWIN_BUILD_ACTIVITIES)
integrationTest(NAME testActivities SRCS activities_test.cpp LIBS XCB::ICCCM)
......
......@@ -38,6 +38,7 @@ class PlasmaWindowManagement;
class PointerConstraints;
class Seat;
class ServerSideDecorationManager;
class ShadowManager;
class Shell;
class ShellSurface;
class ShmPool;
......@@ -85,7 +86,8 @@ enum class AdditionalWaylandInterface {
WindowManagement = 1 << 3,
PointerConstraints = 1 << 4,
IdleInhibition = 1 << 5,
AppMenu = 1 << 6
AppMenu = 1 << 6,
ShadowManager = 1 << 7
};
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
/**
......@@ -106,6 +108,7 @@ void destroyWaylandConnection();
KWayland::Client::ConnectionThread *waylandConnection();
KWayland::Client::Compositor *waylandCompositor();
KWayland::Client::ShadowManager *waylandShadowManager();
KWayland::Client::Shell *waylandShell();
KWayland::Client::ShmPool *waylandShmPool();
KWayland::Client::Seat *waylandSeat();
......
This diff is collapsed.
......@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Client/pointerconstraints.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/shadow.h>
#include <KWayland/Client/shell.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/output.h>
......@@ -62,6 +63,7 @@ static struct {
EventQueue *queue = nullptr;
Compositor *compositor = nullptr;
ServerSideDecorationManager *decoration = nullptr;
ShadowManager *shadowManager = nullptr;
Shell *shell = nullptr;
XdgShell *xdgShellV5 = nullptr;
XdgShell *xdgShellV6 = nullptr;
......@@ -163,6 +165,13 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
return false;
}
}
if (flags.testFlag(AdditionalWaylandInterface::ShadowManager)) {
s_waylandConnection.shadowManager = registry->createShadowManager(registry->interface(Registry::Interface::Shadow).name,
registry->interface(Registry::Interface::Shadow).version);
if (!s_waylandConnection.shadowManager->isValid()) {
return false;
}
}
if (flags.testFlag(AdditionalWaylandInterface::Decoration)) {
s_waylandConnection.decoration = registry->createServerSideDecorationManager(registry->interface(Registry::Interface::ServerSideDecorationManager).name,
registry->interface(Registry::Interface::ServerSideDecorationManager).version);
......@@ -230,6 +239,8 @@ void destroyWaylandConnection()
s_waylandConnection.xdgShellV6 = nullptr;
delete s_waylandConnection.shell;
s_waylandConnection.shell = nullptr;
delete s_waylandConnection.shadowManager;
s_waylandConnection.shadowManager = nullptr;
delete s_waylandConnection.idleInhibit;
s_waylandConnection.idleInhibit = nullptr;
delete s_waylandConnection.shm;
......@@ -264,6 +275,11 @@ Compositor *waylandCompositor()
return s_waylandConnection.compositor;
}
ShadowManager *waylandShadowManager()
{
return s_waylandConnection.shadowManager;
}
Shell *waylandShell()
{
return s_waylandConnection.shell;
......
......@@ -38,6 +38,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QPainter>
#include <KDecoration2/Decoration>
#include <cmath>
namespace KWin
{
......@@ -309,51 +311,21 @@ void SceneQPainter::Window::renderShadow(QPainter* painter)
return;
}
SceneQPainterShadow *shadow = static_cast<SceneQPainterShadow *>(toplevel->shadow());
const QPixmap &topLeft = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTopLeft);
const QPixmap &top = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTop);
const QPixmap &topRight = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementTopRight);
const QPixmap &bottomLeft = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottomLeft);
const QPixmap &bottom = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottom);
const QPixmap &bottomRight = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementBottomRight);
const QPixmap &left = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementLeft);
const QPixmap &right = shadow->shadowPixmap(SceneQPainterShadow::ShadowElementRight);
const int leftOffset = shadow->leftOffset();
const int topOffset = shadow->topOffset();
const int rightOffset = shadow->rightOffset();
const int bottomOffset = shadow->bottomOffset();
// top left
painter->drawPixmap(-leftOffset, -topOffset, topLeft);
// top right
painter->drawPixmap(toplevel->width() - topRight.width() + rightOffset, -topOffset, topRight);
// bottom left
painter->drawPixmap(-leftOffset, toplevel->height() - bottomLeft.height() + bottomOffset, bottomLeft);
// bottom right
painter->drawPixmap(toplevel->width() - bottomRight.width() + rightOffset,
toplevel->height() - bottomRight.height() + bottomOffset,
bottomRight);
// top
painter->drawPixmap(topLeft.width() - leftOffset, -topOffset,
toplevel->width() - topLeft.width() - topRight.width() + leftOffset + rightOffset,
top.height(),
top);
// left
painter->drawPixmap(-leftOffset, topLeft.height() - topOffset, left.width(),
toplevel->height() - topLeft.height() - bottomLeft.height() + topOffset + bottomOffset,
left);
// right
painter->drawPixmap(toplevel->width() - right.width() + rightOffset,
topRight.height() - topOffset,
right.width(),
toplevel->height() - topRight.height() - bottomRight.height() + topOffset + bottomOffset,
right);
// bottom
painter->drawPixmap(bottomLeft.width() - leftOffset,
toplevel->height() - bottom.height() + bottomOffset,
toplevel->width() - bottomLeft.width() - bottomRight.width() + leftOffset + rightOffset,
bottom.height(),
bottom);
const QImage &shadowTexture = shadow->shadowTexture();
const WindowQuadList &shadowQuads = shadow->shadowQuads();
for (const auto &q : shadowQuads) {
auto topLeft = q[0];
auto bottomRight = q[2];
QRectF target(topLeft.x(), topLeft.y(),
bottomRight.x() - topLeft.x(),
bottomRight.y() - topLeft.y());
QRectF source(topLeft.textureX(), topLeft.textureY(),
bottomRight.textureX() - topLeft.textureX(),
bottomRight.textureY() - topLeft.textureY());
painter->drawImage(target, shadowTexture, source);
}
}
void SceneQPainter::Window::renderWindowDecorations(QPainter *painter)
......@@ -557,12 +529,242 @@ SceneQPainterShadow::~SceneQPainterShadow()
{
}
void SceneQPainterShadow::buildQuads()
{
// Do not draw shadows if window width or window height is less than
// 5 px. 5 is an arbitrary choice.
if (topLevel()->width() < 5 || topLevel()->height() < 5) {
m_shadowQuads.clear();
setShadowRegion(QRegion());
return;
}
const QSizeF top(elementSize(ShadowElementTop));
const QSizeF topRight(elementSize(ShadowElementTopRight));
const QSizeF right(elementSize(ShadowElementRight));
const QSizeF bottomRight(elementSize(ShadowElementBottomRight));
const QSizeF bottom(elementSize(ShadowElementBottom));
const QSizeF bottomLeft(elementSize(ShadowElementBottomLeft));
const QSizeF left(elementSize(ShadowElementLeft));
const QSizeF topLeft(elementSize(ShadowElementTopLeft));
const QRectF outerRect(QPointF(-leftOffset(), -topOffset()),
QPointF(topLevel()->width() + rightOffset(),
topLevel()->height() + bottomOffset()));
const int width = std::max({topLeft.width(), left.width(), bottomLeft.width()})
+ std::max(top.width(), bottom.width())
+ std::max({topRight.width(), right.width(), bottomRight.width()});
const int height = std::max({topLeft.height(), top.height(), topRight.height()})
+ std::max(left.height(), right.height())
+ std::max({bottomLeft.height(), bottom.height(), bottomRight.height()});
QRectF topLeftRect(outerRect.topLeft(), topLeft);
QRectF topRightRect(outerRect.topRight() - QPointF(topRight.width(), 0), topRight);
QRectF bottomRightRect(
outerRect.bottomRight() - QPointF(bottomRight.width(), bottomRight.height()),
bottomRight);
QRectF bottomLeftRect(outerRect.bottomLeft() - QPointF(0, bottomLeft.height()), bottomLeft);
// Re-distribute the corner tiles so no one of them is overlapping with others.
// By doing this, we assume that shadow's corner tiles are symmetric
// and it is OK to not draw top/right/bottom/left tile between corners.
// For example, let's say top-left and top-right tiles are overlapping.
// In that case, the right side of the top-left tile will be shifted to left,
// the left side of the top-right tile will shifted to right, and the top
// tile won't be rendered.
bool drawTop = true;
if (topLeftRect.right() >= topRightRect.left()) {
const float halfOverlap = qAbs(topLeftRect.right() - topRightRect.left()) / 2;
topLeftRect.setRight(topLeftRect.right() - std::floor(halfOverlap));
topRightRect.setLeft(topRightRect.left() + std::ceil(halfOverlap));
drawTop = false;
}
bool drawRight = true;
if (topRightRect.bottom() >= bottomRightRect.top()) {
const float halfOverlap = qAbs(topRightRect.bottom() - bottomRightRect.top()) / 2;
topRightRect.setBottom(topRightRect.bottom() - std::floor(halfOverlap));
bottomRightRect.setTop(bottomRightRect.top() + std::ceil(halfOverlap));
drawRight = false;
}
bool drawBottom = true;
if (bottomLeftRect.right() >= bottomRightRect.left()) {
const float halfOverlap = qAbs(bottomLeftRect.right() - bottomRightRect.left()) / 2;
bottomLeftRect.setRight(bottomLeftRect.right() - std::floor(halfOverlap));
bottomRightRect.setLeft(bottomRightRect.left() + std::ceil(halfOverlap));
drawBottom = false;
}
bool drawLeft = true;
if (topLeftRect.bottom() >= bottomLeftRect.top()) {
const float halfOverlap = qAbs(topLeftRect.bottom() - bottomLeftRect.top()) / 2;
topLeftRect.setBottom(topLeftRect.bottom() - std::floor(halfOverlap));
bottomLeftRect.setTop(bottomLeftRect.top() + std::ceil(halfOverlap));
drawLeft = false;
}
qreal tx1 = 0.0,
tx2 = 0.0,
ty1 = 0.0,
ty2 = 0.0;
m_shadowQuads.clear();
tx1 = 0.0;
ty1 = 0.0;
tx2 = topLeftRect.width();
ty2 = topLeftRect.height();
WindowQuad topLeftQuad(WindowQuadShadow);
topLeftQuad[0] = WindowVertex(topLeftRect.left(), topLeftRect.top(), tx1, ty1);
topLeftQuad[1] = WindowVertex(topLeftRect.right(), topLeftRect.top(), tx2, ty1);
topLeftQuad[2] = WindowVertex(topLeftRect.right(), topLeftRect.bottom(), tx2, ty2);
topLeftQuad[3] = WindowVertex(topLeftRect.left(), topLeftRect.bottom(), tx1, ty2);
m_shadowQuads.append(topLeftQuad);
tx1 = width - topRightRect.width();
ty1 = 0.0;
tx2 = width;
ty2 = topRightRect.height();
WindowQuad topRightQuad(WindowQuadShadow);
topRightQuad[0] = WindowVertex(topRightRect.left(), topRightRect.top(), tx1, ty1);
topRightQuad[1] = WindowVertex(topRightRect.right(), topRightRect.top(), tx2, ty1);
topRightQuad[2] = WindowVertex(topRightRect.right(), topRightRect.bottom(), tx2, ty2);
topRightQuad[3] = WindowVertex(topRightRect.left(), topRightRect.bottom(), tx1, ty2);
m_shadowQuads.append(topRightQuad);
tx1 = width - bottomRightRect.width();
tx2 = width;
ty1 = height - bottomRightRect.height();
ty2 = height;
WindowQuad bottomRightQuad(WindowQuadShadow);
bottomRightQuad[0] = WindowVertex(bottomRightRect.left(), bottomRightRect.top(), tx1, ty1);
bottomRightQuad[1] = WindowVertex(bottomRightRect.right(), bottomRightRect.top(), tx2, ty1);
bottomRightQuad[2] = WindowVertex(bottomRightRect.right(), bottomRightRect.bottom(), tx2, ty2);
bottomRightQuad[3] = WindowVertex(bottomRightRect.left(), bottomRightRect.bottom(), tx1, ty2);
m_shadowQuads.append(bottomRightQuad);
tx1 = 0.0;
tx2 = bottomLeftRect.width();
ty1 = height - bottomLeftRect.height();
ty2 = height;
WindowQuad bottomLeftQuad(WindowQuadShadow);
bottomLeftQuad[0] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.top(), tx1, ty1);
bottomLeftQuad[1] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.top(), tx2, ty1);
bottomLeftQuad[2] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.bottom(), tx2, ty2);
bottomLeftQuad[3] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.bottom(), tx1, ty2);
m_shadowQuads.append(bottomLeftQuad);
if (drawTop) {
QRectF topRect(
topLeftRect.topRight(),
topRightRect.bottomLeft());
tx1 = topLeft.width();
ty1 = 0.0;
tx2 = width - topRight.width();
ty2 = topRect.height();
WindowQuad topQuad(WindowQuadShadow);
topQuad[0] = WindowVertex(topRect.left(), topRect.top(), tx1, ty1);
topQuad[1] = WindowVertex(topRect.right(), topRect.top(), tx2, ty1);
topQuad[2] = WindowVertex(topRect.right(), topRect.bottom(), tx2, ty2);
topQuad[3] = WindowVertex(topRect.left(), topRect.bottom(), tx1, ty2);
m_shadowQuads.append(topQuad);
}
if (drawRight) {
QRectF rightRect(
topRightRect.bottomLeft(),
bottomRightRect.topRight());
tx1 = width - rightRect.width();
ty1 = topRight.height();
tx2 = width;
ty2 = height - bottomRight.height();
WindowQuad rightQuad(WindowQuadShadow);
rightQuad[0] = WindowVertex(rightRect.left(), rightRect.top(), tx1, ty1);
rightQuad[1] = WindowVertex(rightRect.right(), rightRect.top(), tx2, ty1);
rightQuad[2] = WindowVertex(rightRect.right(), rightRect.bottom(), tx2, ty2);
rightQuad[3] = WindowVertex(rightRect.left(), rightRect.bottom(), tx1, ty2);
m_shadowQuads.append(rightQuad);
}
if (drawBottom) {
QRectF bottomRect(
bottomLeftRect.topRight(),
bottomRightRect.bottomLeft());
tx1 = bottomLeft.width();
ty1 = height - bottomRect.height();
tx2 = width - bottomRight.width();
ty2 = height;
WindowQuad bottomQuad(WindowQuadShadow);
bottomQuad[0] = WindowVertex(bottomRect.left(), bottomRect.top(), tx1, ty1);
bottomQuad[1] = WindowVertex(bottomRect.right(), bottomRect.top(), tx2, ty1);
bottomQuad[2] = WindowVertex(bottomRect.right(), bottomRect.bottom(), tx2, ty2);
bottomQuad[3] = WindowVertex(bottomRect.left(), bottomRect.bottom(), tx1, ty2);
m_shadowQuads.append(bottomQuad);
}
if (drawLeft) {
QRectF leftRect(
topLeftRect.bottomLeft(),
bottomLeftRect.topRight());
tx1 = 0.0;
ty1 = topLeft.height();
tx2 = leftRect.width();
ty2 = height - bottomRight.height();
WindowQuad leftQuad(WindowQuadShadow);
leftQuad[0] = WindowVertex(leftRect.left(), leftRect.top(), tx1, ty1);
leftQuad[1] = WindowVertex(leftRect.right(), leftRect.top(), tx2, ty1);
leftQuad[2] = WindowVertex(leftRect.right(), leftRect.bottom(), tx2, ty2);
leftQuad[3] = WindowVertex(leftRect.left(), leftRect.bottom(), tx1, ty2);
m_shadowQuads.append(leftQuad);
}
}
bool SceneQPainterShadow::prepareBackend()
{
if (hasDecorationShadow()) {
// TODO: implement for QPainter
m_texture = decorationShadowImage();
return true;
}
const QPixmap &topLeft = shadowPixmap(ShadowElementTopLeft);
const QPixmap &top = shadowPixmap(ShadowElementTop);
const QPixmap &topRight = shadowPixmap(ShadowElementTopRight);
const QPixmap &bottomLeft = shadowPixmap(ShadowElementBottomLeft);
const QPixmap &bottom = shadowPixmap(ShadowElementBottom);
const QPixmap &bottomRight = shadowPixmap(ShadowElementBottomRight);
const QPixmap &left = shadowPixmap(ShadowElementLeft);
const QPixmap &right = shadowPixmap(ShadowElementRight);
const int width = std::max({topLeft.width(), left.width(), bottomLeft.width()})
+ std::max(top.width(), bottom.width())
+ std::max({topRight.width(), right.width(), bottomRight.width()});
const int height = std::max({topLeft.height(), top.height(), topRight.height()})
+ std::max(left.height(), right.height())
+ std::max({bottomLeft.height(), bottom.height(), bottomRight.height()});
if (width == 0 || height == 0) {
return false;
}
QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter painter;
painter.begin(&image);
painter.drawPixmap(0, 0, topLeft);
painter.drawPixmap(topLeft.width(), 0, top);
painter.drawPixmap(width - topRight.width(), 0, topRight);
painter.drawPixmap(0, height - bottomLeft.height(), bottomLeft);
painter.drawPixmap(bottomLeft.width(), height - bottom.height(), bottom);
painter.drawPixmap(width - bottomRight.width(), height - bottomRight.height(), bottomRight);
painter.drawPixmap(0, topLeft.height(), left);
painter.drawPixmap(width - right.width(), topRight.height(), right);
painter.end();
m_texture = image;
return true;
}
......
......@@ -123,23 +123,17 @@ class SceneQPainterShadow : public Shadow
public:
SceneQPainterShadow(Toplevel* toplevel);
virtual ~SceneQPainterShadow();
using Shadow::ShadowElements;
using Shadow::ShadowElementTop;
using Shadow::ShadowElementTopRight;
using Shadow::ShadowElementRight;
using Shadow::ShadowElementBottomRight;
using Shadow::ShadowElementBottom;
using Shadow::ShadowElementBottomLeft;
using Shadow::ShadowElementLeft;
using Shadow::ShadowElementTopLeft;
using Shadow::ShadowElementsCount;
using Shadow::shadowPixmap;
using Shadow::topOffset;
using Shadow::leftOffset;
using Shadow::rightOffset;
using Shadow::bottomOffset;
QImage &shadowTexture() {
return m_texture;
}
protected:
virtual void buildQuads() override;
virtual bool prepareBackend() override;
private:
QImage m_texture;
};
class SceneQPainterDecorationRenderer : public Decoration::Renderer
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment