Refactor window quad handling
The scene items depend on the scene windows for caching window quads. The goal of this change is to move window quads management to item. Merging window quads in one list and then splitting them is inefficient, it will be highly desirable if window quads are removed from the public api so we can optimize window quad management. With this change, the window quad type becomes irrelevant to render backends for the most part. Note that the Xrender backend is a bit nitpicky about window quads, so the shadow item doesn't create generic "WindowQuadShadow" quads anymore.
This commit is contained in:
parent
d109e7d69b
commit
86bb4e68ef
17 changed files with 506 additions and 755 deletions
|
@ -9,6 +9,7 @@
|
|||
#include "abstract_client.h"
|
||||
#include "composite.h"
|
||||
#include "decorations/decoratedclient.h"
|
||||
#include "deleted.h"
|
||||
#include "scene.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -144,4 +145,87 @@ DecorationRenderer *DecorationItem::renderer() const
|
|||
return m_renderer.data();
|
||||
}
|
||||
|
||||
WindowQuadList DecorationItem::buildQuads() const
|
||||
{
|
||||
const Toplevel *toplevel = window()->window();
|
||||
if (toplevel->frameMargins().isNull()) {
|
||||
return WindowQuadList();
|
||||
}
|
||||
|
||||
QRect rects[4];
|
||||
|
||||
if (const AbstractClient *client = qobject_cast<const AbstractClient *>(toplevel)) {
|
||||
client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
|
||||
} else if (const Deleted *deleted = qobject_cast<const Deleted *>(toplevel)) {
|
||||
deleted->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
|
||||
}
|
||||
|
||||
const qreal textureScale = toplevel->screenScale();
|
||||
const int padding = 1;
|
||||
|
||||
const QPoint topSpritePosition(padding, padding);
|
||||
const QPoint bottomSpritePosition(padding, topSpritePosition.y() + rects[1].height() + 2 * padding);
|
||||
const QPoint leftSpritePosition(bottomSpritePosition.y() + rects[3].height() + 2 * padding, padding);
|
||||
const QPoint rightSpritePosition(leftSpritePosition.x() + rects[0].width() + 2 * padding, padding);
|
||||
|
||||
const QPoint offsets[4] = {
|
||||
QPoint(-rects[0].x(), -rects[0].y()) + leftSpritePosition,
|
||||
QPoint(-rects[1].x(), -rects[1].y()) + topSpritePosition,
|
||||
QPoint(-rects[2].x(), -rects[2].y()) + rightSpritePosition,
|
||||
QPoint(-rects[3].x(), -rects[3].y()) + bottomSpritePosition,
|
||||
};
|
||||
|
||||
const Qt::Orientation orientations[4] = {
|
||||
Qt::Vertical, // Left
|
||||
Qt::Horizontal, // Top
|
||||
Qt::Vertical, // Right
|
||||
Qt::Horizontal, // Bottom
|
||||
};
|
||||
|
||||
const QRegion shape = QRegion(toplevel->rect()) - toplevel->transparentRect();
|
||||
|
||||
WindowQuadList list;
|
||||
list.reserve(shape.rectCount());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const QRegion intersectedRegion = (shape & rects[i]);
|
||||
for (const QRect &r : intersectedRegion) {
|
||||
if (!r.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool swap = orientations[i] == Qt::Vertical;
|
||||
|
||||
const int x0 = r.x();
|
||||
const int y0 = r.y();
|
||||
const int x1 = r.x() + r.width();
|
||||
const int y1 = r.y() + r.height();
|
||||
|
||||
const int u0 = (x0 + offsets[i].x()) * textureScale;
|
||||
const int v0 = (y0 + offsets[i].y()) * textureScale;
|
||||
const int u1 = (x1 + offsets[i].x()) * textureScale;
|
||||
const int v1 = (y1 + offsets[i].y()) * textureScale;
|
||||
|
||||
WindowQuad quad(WindowQuadDecoration, const_cast<DecorationItem *>(this));
|
||||
quad.setUVAxisSwapped(swap);
|
||||
|
||||
if (swap) {
|
||||
quad[0] = WindowVertex(x0, y0, v0, u0); // Top-left
|
||||
quad[1] = WindowVertex(x1, y0, v0, u1); // Top-right
|
||||
quad[2] = WindowVertex(x1, y1, v1, u1); // Bottom-right
|
||||
quad[3] = WindowVertex(x0, y1, v1, u0); // Bottom-left
|
||||
} else {
|
||||
quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left
|
||||
quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right
|
||||
quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right
|
||||
quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left
|
||||
}
|
||||
|
||||
list.append(quad);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -74,6 +74,7 @@ private Q_SLOTS:
|
|||
|
||||
protected:
|
||||
void preprocess() override;
|
||||
WindowQuadList buildQuads() const override;
|
||||
|
||||
private:
|
||||
QPointer<KDecoration2::Decoration> m_decoration;
|
||||
|
|
17
src/item.cpp
17
src/item.cpp
|
@ -394,9 +394,24 @@ void Item::preprocess()
|
|||
{
|
||||
}
|
||||
|
||||
WindowQuadList Item::buildQuads() const
|
||||
{
|
||||
return WindowQuadList();
|
||||
}
|
||||
|
||||
void Item::discardQuads()
|
||||
{
|
||||
window()->discardQuads();
|
||||
m_quads = WindowQuadList();
|
||||
m_quadsValid = false;
|
||||
}
|
||||
|
||||
WindowQuadList Item::quads() const
|
||||
{
|
||||
if (!m_quadsValid) {
|
||||
m_quads = buildQuads();
|
||||
m_quadsValid = true;
|
||||
}
|
||||
return m_quads;
|
||||
}
|
||||
|
||||
QRegion Item::repaints(int screen) const
|
||||
|
|
|
@ -99,6 +99,8 @@ public:
|
|||
QRegion repaints(int screen) const;
|
||||
void resetRepaints(int screen);
|
||||
|
||||
WindowQuadList quads() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the x coordinate of this item has changed.
|
||||
|
@ -125,6 +127,7 @@ Q_SIGNALS:
|
|||
|
||||
protected:
|
||||
virtual void preprocess();
|
||||
virtual WindowQuadList buildQuads() const;
|
||||
void discardQuads();
|
||||
|
||||
private:
|
||||
|
@ -148,6 +151,8 @@ private:
|
|||
bool m_visible = true;
|
||||
bool m_effectiveVisible = true;
|
||||
QVector<QRegion> m_repaints;
|
||||
mutable WindowQuadList m_quads;
|
||||
mutable bool m_quadsValid = false;
|
||||
|
||||
friend class Scene::Window;
|
||||
};
|
||||
|
|
|
@ -2614,14 +2614,14 @@ private:
|
|||
class KWINEFFECTS_EXPORT WindowQuad
|
||||
{
|
||||
public:
|
||||
explicit WindowQuad(WindowQuadType type, int id = -1);
|
||||
explicit WindowQuad(WindowQuadType type, void *userData = nullptr);
|
||||
WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const;
|
||||
WindowVertex& operator[](int index);
|
||||
const WindowVertex& operator[](int index) const;
|
||||
WindowQuadType type() const;
|
||||
void setUVAxisSwapped(bool value) { uvSwapped = value; }
|
||||
bool uvAxisSwapped() const { return uvSwapped; }
|
||||
int id() const;
|
||||
void *userData() const;
|
||||
bool decoration() const;
|
||||
bool effect() const;
|
||||
double left() const;
|
||||
|
@ -2637,9 +2637,9 @@ public:
|
|||
private:
|
||||
friend class WindowQuadList;
|
||||
WindowVertex verts[ 4 ];
|
||||
void *m_userData;
|
||||
WindowQuadType quadType; // 0 - contents, 1 - decoration
|
||||
bool uvSwapped;
|
||||
int quadID;
|
||||
};
|
||||
|
||||
class KWINEFFECTS_EXPORT WindowQuadList
|
||||
|
@ -3822,10 +3822,10 @@ void WindowVertex::setY(double y)
|
|||
***************************************************************/
|
||||
|
||||
inline
|
||||
WindowQuad::WindowQuad(WindowQuadType t, int id)
|
||||
: quadType(t)
|
||||
WindowQuad::WindowQuad(WindowQuadType t, void *userData)
|
||||
: m_userData(userData)
|
||||
, quadType(t)
|
||||
, uvSwapped(false)
|
||||
, quadID(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -3851,9 +3851,9 @@ WindowQuadType WindowQuad::type() const
|
|||
}
|
||||
|
||||
inline
|
||||
int WindowQuad::id() const
|
||||
void *WindowQuad::userData() const
|
||||
{
|
||||
return quadID;
|
||||
return m_userData;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
@ -1196,16 +1196,6 @@ void OpenGLWindow::endRenderWindow()
|
|||
}
|
||||
}
|
||||
|
||||
GLTexture *OpenGLWindow::getDecorationTexture() const
|
||||
{
|
||||
const DecorationItem *decorationItem = windowItem()->decorationItem();
|
||||
if (decorationItem) {
|
||||
auto renderer = static_cast<const SceneOpenGLDecorationRenderer *>(decorationItem->renderer());
|
||||
return renderer->texture();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QVector4D OpenGLWindow::modulate(float opacity, float brightness) const
|
||||
{
|
||||
const float a = opacity;
|
||||
|
@ -1224,30 +1214,9 @@ void OpenGLWindow::setBlendEnabled(bool enabled)
|
|||
m_blendingEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* \internal
|
||||
*
|
||||
* Counts the total number of items in the tree with the given \a root.
|
||||
*/
|
||||
static int countChildren(Item *root)
|
||||
{
|
||||
int count = 1; // 1 for the root itself.
|
||||
|
||||
const QList<Item *> children = root->childItems();
|
||||
for (Item *child : children) {
|
||||
count += countChildren(child);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool bindSurfaceTexture(SurfaceItem *surfaceItem)
|
||||
static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem)
|
||||
{
|
||||
SurfacePixmap *surfacePixmap = surfaceItem->pixmap();
|
||||
if (!surfacePixmap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto platformSurfaceTexture =
|
||||
static_cast<PlatformOpenGLSurfaceTexture *>(surfacePixmap->platformTexture());
|
||||
if (surfacePixmap->isDiscarded()) {
|
||||
|
@ -1260,152 +1229,66 @@ static bool bindSurfaceTexture(SurfaceItem *surfaceItem)
|
|||
platformSurfaceTexture->update(region);
|
||||
surfaceItem->resetDamage();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!surfacePixmap->isValid()) {
|
||||
return false;
|
||||
} else {
|
||||
if (!surfacePixmap->isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!platformSurfaceTexture->create()) {
|
||||
qCDebug(KWIN_OPENGL) << "Failed to bind window";
|
||||
return nullptr;
|
||||
}
|
||||
surfaceItem->resetDamage();
|
||||
}
|
||||
|
||||
if (!platformSurfaceTexture->create()) {
|
||||
qCDebug(KWIN_OPENGL) << "Failed to bind window";
|
||||
return false;
|
||||
}
|
||||
|
||||
surfaceItem->resetDamage();
|
||||
return true;
|
||||
return platformSurfaceTexture->texture();
|
||||
}
|
||||
|
||||
void OpenGLWindow::initializeRenderContext(RenderContext &context, const WindowPaintData &data)
|
||||
void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const WindowPaintData &data)
|
||||
{
|
||||
context.shadowOffset = 0;
|
||||
context.decorationOffset = 1;
|
||||
context.contentOffset = 2;
|
||||
context.previousContentOffset = (surfaceItem() ? countChildren(surfaceItem()) : 0) + 2;
|
||||
context.quadCount = data.quads.count();
|
||||
|
||||
const int nodeCount = context.previousContentOffset + 1;
|
||||
|
||||
QVector<RenderNode> &renderNodes = context.renderNodes;
|
||||
renderNodes.resize(nodeCount);
|
||||
|
||||
for (const WindowQuad &quad : data.quads) {
|
||||
switch (quad.type()) {
|
||||
case WindowQuadShadow:
|
||||
renderNodes[context.shadowOffset].quads << quad;
|
||||
break;
|
||||
|
||||
case WindowQuadDecoration:
|
||||
renderNodes[context.decorationOffset].quads << quad;
|
||||
break;
|
||||
|
||||
case WindowQuadContents:
|
||||
renderNodes[context.contentOffset + quad.id()].quads << quad;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore window quad generated by effects.
|
||||
break;
|
||||
if (auto shadowItem = qobject_cast<ShadowItem *>(item)) {
|
||||
WindowQuadList quads = context->quads.value(item);
|
||||
if (!quads.isEmpty()) {
|
||||
SceneOpenGLShadow *shadow = static_cast<SceneOpenGLShadow *>(shadowItem->shadow());
|
||||
context->renderNodes.append(RenderNode{
|
||||
.texture = shadow->shadowTexture(),
|
||||
.quads = quads,
|
||||
.opacity = data.opacity(),
|
||||
.hasAlpha = true,
|
||||
.coordinateType = NormalizedCoordinates,
|
||||
});
|
||||
}
|
||||
} else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) {
|
||||
WindowQuadList quads = context->quads.value(item);
|
||||
if (!quads.isEmpty()) {
|
||||
auto renderer = static_cast<const SceneOpenGLDecorationRenderer *>(decorationItem->renderer());
|
||||
context->renderNodes.append(RenderNode{
|
||||
.texture = renderer->texture(),
|
||||
.quads = quads,
|
||||
.opacity = data.opacity(),
|
||||
.hasAlpha = true,
|
||||
.coordinateType = UnnormalizedCoordinates,
|
||||
});
|
||||
}
|
||||
} else if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
|
||||
WindowQuadList quads = context->quads.value(item);
|
||||
if (!quads.isEmpty()) {
|
||||
SurfacePixmap *pixmap = surfaceItem->pixmap();
|
||||
if (pixmap) {
|
||||
context->renderNodes.append(RenderNode{
|
||||
.texture = bindSurfaceTexture(surfaceItem),
|
||||
.quads = quads,
|
||||
.opacity = data.opacity(),
|
||||
.hasAlpha = pixmap->hasAlphaChannel(),
|
||||
.coordinateType = UnnormalizedCoordinates,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderNode &shadowRenderNode = renderNodes[context.shadowOffset];
|
||||
if (!shadowRenderNode.quads.isEmpty()) {
|
||||
SceneOpenGLShadow *shadow = static_cast<SceneOpenGLShadow *>(shadowItem()->shadow());
|
||||
shadowRenderNode.texture = shadow->shadowTexture();
|
||||
shadowRenderNode.opacity = data.opacity();
|
||||
shadowRenderNode.hasAlpha = true;
|
||||
shadowRenderNode.coordinateType = NormalizedCoordinates;
|
||||
}
|
||||
|
||||
RenderNode &decorationRenderNode = renderNodes[context.decorationOffset];
|
||||
if (!decorationRenderNode.quads.isEmpty()) {
|
||||
decorationRenderNode.texture = getDecorationTexture();
|
||||
decorationRenderNode.opacity = data.opacity();
|
||||
decorationRenderNode.hasAlpha = true;
|
||||
decorationRenderNode.coordinateType = UnnormalizedCoordinates;
|
||||
}
|
||||
|
||||
if (surfaceItem()) {
|
||||
// FIXME: Cross-fading must be implemented in a shader.
|
||||
float contentOpacity = data.opacity();
|
||||
if (data.crossFadeProgress() != 1.0 && (data.opacity() < 0.95 || toplevel->hasAlpha())) {
|
||||
const float opacity = 1.0 - data.crossFadeProgress();
|
||||
contentOpacity *= 1 - pow(opacity, 1.0f + 2.0f * data.opacity());
|
||||
}
|
||||
|
||||
// The main surface and all of its sub-surfaces form a tree. In order to initialize
|
||||
// the render nodes for the window pixmaps we need to traverse the tree in the
|
||||
// depth-first search manner. The id of content window quads corresponds to the time
|
||||
// when we visited the corresponding window pixmap. The DFS traversal probably doesn't
|
||||
// have a significant impact on performance. However, if that's the case, we could
|
||||
// keep a cache of window pixmaps in the order in which they'll be rendered.
|
||||
QStack<SurfaceItem *> stack;
|
||||
stack.push(surfaceItem());
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
SurfaceItem *item = stack.pop();
|
||||
if (!bindSurfaceTexture(item)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const SurfacePixmap *surfaceTexture = item->pixmap();
|
||||
const PlatformOpenGLSurfaceTexture *platformSurfaceTexture =
|
||||
static_cast<PlatformOpenGLSurfaceTexture *>(surfaceTexture->platformTexture());
|
||||
|
||||
RenderNode &contentRenderNode = renderNodes[context.contentOffset + i++];
|
||||
contentRenderNode.texture = platformSurfaceTexture->texture();
|
||||
contentRenderNode.hasAlpha = surfaceTexture->hasAlphaChannel();
|
||||
contentRenderNode.opacity = contentOpacity;
|
||||
contentRenderNode.coordinateType = UnnormalizedCoordinates;
|
||||
|
||||
const QList<Item *> children = item->childItems();
|
||||
for (auto it = children.rbegin(); it != children.rend(); ++it) {
|
||||
stack.push(static_cast<SurfaceItem *>(*it));
|
||||
}
|
||||
}
|
||||
|
||||
// Note that cross-fading is currently working properly only on X11. In order to make it
|
||||
// work on Wayland, we have to render the current and the previous window pixmap trees in
|
||||
// offscreen render targets, then use a cross-fading shader to blend those two layers.
|
||||
if (data.crossFadeProgress() != 1.0) {
|
||||
const SurfacePixmap *previous = surfaceItem()->previousPixmap();
|
||||
if (previous && previous->isValid()) { // TODO(vlad): Should cross-fading be disabled on Wayland?
|
||||
const QRect &oldGeometry = previous->contentsRect();
|
||||
RenderNode &previousContentRenderNode = renderNodes[context.previousContentOffset];
|
||||
for (const WindowQuad &quad : qAsConst(renderNodes[context.contentOffset].quads)) {
|
||||
// We need to create new window quads with normalized texture coordinates.
|
||||
// Normal quads divide the x/y position by width/height. This would not work
|
||||
// as the texture is larger than the visible content in case of a decorated
|
||||
// Client resulting in garbage being shown. So we calculate the normalized
|
||||
// texture coordinate in the Client's new content space and map it to the
|
||||
// previous Client's content space.
|
||||
WindowQuad newQuad(WindowQuadContents);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const qreal xFactor = (quad[i].textureX() - toplevel->clientPos().x())
|
||||
/ qreal(toplevel->clientSize().width());
|
||||
const qreal yFactor = (quad[i].textureY() - toplevel->clientPos().y())
|
||||
/ qreal(toplevel->clientSize().height());
|
||||
const qreal u = (xFactor * oldGeometry.width() + oldGeometry.x())
|
||||
/ qreal(previous->size().width());
|
||||
const qreal v = (yFactor * oldGeometry.height() + oldGeometry.y())
|
||||
/ qreal(previous->size().height());
|
||||
newQuad[i] = WindowVertex(quad[i].x(), quad[i].y(), u, v);
|
||||
}
|
||||
previousContentRenderNode.quads.append(newQuad);
|
||||
}
|
||||
|
||||
const auto previousPlatformTexture =
|
||||
static_cast<PlatformOpenGLSurfaceTexture *>(previous->platformTexture());
|
||||
|
||||
previousContentRenderNode.texture = previousPlatformTexture->texture();
|
||||
previousContentRenderNode.hasAlpha = previous->hasAlphaChannel();
|
||||
previousContentRenderNode.opacity = data.opacity() * (1.0 - data.crossFadeProgress());
|
||||
previousContentRenderNode.coordinateType = NormalizedCoordinates;
|
||||
|
||||
context.quadCount += previousContentRenderNode.quads.count();
|
||||
}
|
||||
const QList<Item *> childItems = item->childItems();
|
||||
for (Item *childItem : childItems) {
|
||||
if (childItem->isVisible()) {
|
||||
createRenderNode(childItem, context, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1475,13 +1358,19 @@ void OpenGLWindow::performPaint(int mask, const QRegion ®ion, const WindowPai
|
|||
shader->setUniform(GLShader::Saturation, data.saturation());
|
||||
|
||||
RenderContext renderContext;
|
||||
initializeRenderContext(renderContext, data);
|
||||
// TODO: This is somewhat inefficient, remove window quads from the public api.
|
||||
for (const WindowQuad &quad : qAsConst(data.quads)) {
|
||||
Item *item = static_cast<Item *>(quad.userData());
|
||||
Q_ASSERT(item);
|
||||
renderContext.quads[item].append(quad);
|
||||
}
|
||||
createRenderNode(windowItem(), &renderContext, data);
|
||||
|
||||
const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads();
|
||||
const GLenum primitiveType = indexedQuads ? GL_QUADS : GL_TRIANGLES;
|
||||
const int verticesPerQuad = indexedQuads ? 4 : 6;
|
||||
|
||||
const size_t size = verticesPerQuad * renderContext.quadCount * sizeof(GLVertex2D);
|
||||
const size_t size = verticesPerQuad * data.quads.count() * sizeof(GLVertex2D);
|
||||
|
||||
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
||||
GLVertex2D *map = (GLVertex2D *) vbo->map(size);
|
||||
|
@ -2096,255 +1985,6 @@ SceneOpenGLShadow::~SceneOpenGLShadow()
|
|||
}
|
||||
}
|
||||
|
||||
static inline void distributeHorizontally(QRectF &leftRect, QRectF &rightRect)
|
||||
{
|
||||
if (leftRect.right() > rightRect.left()) {
|
||||
const qreal boundedRight = qMin(leftRect.right(), rightRect.right());
|
||||
const qreal boundedLeft = qMax(leftRect.left(), rightRect.left());
|
||||
const qreal halfOverlap = (boundedRight - boundedLeft) / 2.0;
|
||||
leftRect.setRight(boundedRight - halfOverlap);
|
||||
rightRect.setLeft(boundedLeft + halfOverlap);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void distributeVertically(QRectF &topRect, QRectF &bottomRect)
|
||||
{
|
||||
if (topRect.bottom() > bottomRect.top()) {
|
||||
const qreal boundedBottom = qMin(topRect.bottom(), bottomRect.bottom());
|
||||
const qreal boundedTop = qMax(topRect.top(), bottomRect.top());
|
||||
const qreal halfOverlap = (boundedBottom - boundedTop) / 2.0;
|
||||
topRect.setBottom(boundedBottom - halfOverlap);
|
||||
bottomRect.setTop(boundedTop + halfOverlap);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneOpenGLShadow::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 QMarginsF shadowMargins(
|
||||
std::max({topLeft.width(), left.width(), bottomLeft.width()}),
|
||||
std::max({topLeft.height(), top.height(), topRight.height()}),
|
||||
std::max({topRight.width(), right.width(), bottomRight.width()}),
|
||||
std::max({bottomRight.height(), bottom.height(), bottomLeft.height()}));
|
||||
|
||||
const QRectF outerRect(QPointF(-leftOffset(), -topOffset()),
|
||||
QPointF(topLevel()->width() + rightOffset(),
|
||||
topLevel()->height() + bottomOffset()));
|
||||
|
||||
const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right();
|
||||
const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom();
|
||||
|
||||
QRectF topLeftRect;
|
||||
if (!topLeft.isEmpty()) {
|
||||
topLeftRect = QRectF(outerRect.topLeft(), topLeft);
|
||||
} else {
|
||||
topLeftRect = QRectF(
|
||||
outerRect.left() + shadowMargins.left(),
|
||||
outerRect.top() + shadowMargins.top(),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
QRectF topRightRect;
|
||||
if (!topRight.isEmpty()) {
|
||||
topRightRect = QRectF(
|
||||
outerRect.right() - topRight.width(), outerRect.top(),
|
||||
topRight.width(), topRight.height());
|
||||
} else {
|
||||
topRightRect = QRectF(
|
||||
outerRect.right() - shadowMargins.right(),
|
||||
outerRect.top() + shadowMargins.top(),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
QRectF bottomRightRect;
|
||||
if (!bottomRight.isEmpty()) {
|
||||
bottomRightRect = QRectF(
|
||||
outerRect.right() - bottomRight.width(),
|
||||
outerRect.bottom() - bottomRight.height(),
|
||||
bottomRight.width(), bottomRight.height());
|
||||
} else {
|
||||
bottomRightRect = QRectF(
|
||||
outerRect.right() - shadowMargins.right(),
|
||||
outerRect.bottom() - shadowMargins.bottom(),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
QRectF bottomLeftRect;
|
||||
if (!bottomLeft.isEmpty()) {
|
||||
bottomLeftRect = QRectF(
|
||||
outerRect.left(), outerRect.bottom() - bottomLeft.height(),
|
||||
bottomLeft.width(), bottomLeft.height());
|
||||
} else {
|
||||
bottomLeftRect = QRectF(
|
||||
outerRect.left() + shadowMargins.left(),
|
||||
outerRect.bottom() - shadowMargins.bottom(),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
// 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.
|
||||
distributeHorizontally(topLeftRect, topRightRect);
|
||||
distributeHorizontally(bottomLeftRect, bottomRightRect);
|
||||
distributeVertically(topLeftRect, bottomLeftRect);
|
||||
distributeVertically(topRightRect, bottomRightRect);
|
||||
|
||||
qreal tx1 = 0.0,
|
||||
tx2 = 0.0,
|
||||
ty1 = 0.0,
|
||||
ty2 = 0.0;
|
||||
|
||||
m_shadowQuads.clear();
|
||||
|
||||
if (topLeftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
ty1 = 0.0;
|
||||
tx2 = topLeftRect.width() / width;
|
||||
ty2 = topLeftRect.height() / 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);
|
||||
}
|
||||
|
||||
if (topRightRect.isValid()) {
|
||||
tx1 = 1.0 - topRightRect.width() / width;
|
||||
ty1 = 0.0;
|
||||
tx2 = 1.0;
|
||||
ty2 = topRightRect.height() / 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);
|
||||
}
|
||||
|
||||
if (bottomRightRect.isValid()) {
|
||||
tx1 = 1.0 - bottomRightRect.width() / width;
|
||||
tx2 = 1.0;
|
||||
ty1 = 1.0 - bottomRightRect.height() / height;
|
||||
ty2 = 1.0;
|
||||
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);
|
||||
}
|
||||
|
||||
if (bottomLeftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
tx2 = bottomLeftRect.width() / width;
|
||||
ty1 = 1.0 - bottomLeftRect.height() / height;
|
||||
ty2 = 1.0;
|
||||
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);
|
||||
}
|
||||
|
||||
QRectF topRect(
|
||||
QPointF(topLeftRect.right(), outerRect.top()),
|
||||
QPointF(topRightRect.left(), outerRect.top() + top.height()));
|
||||
|
||||
QRectF rightRect(
|
||||
QPointF(outerRect.right() - right.width(), topRightRect.bottom()),
|
||||
QPointF(outerRect.right(), bottomRightRect.top()));
|
||||
|
||||
QRectF bottomRect(
|
||||
QPointF(bottomLeftRect.right(), outerRect.bottom() - bottom.height()),
|
||||
QPointF(bottomRightRect.left(), outerRect.bottom()));
|
||||
|
||||
QRectF leftRect(
|
||||
QPointF(outerRect.left(), topLeftRect.bottom()),
|
||||
QPointF(outerRect.left() + left.width(), bottomLeftRect.top()));
|
||||
|
||||
// Re-distribute left/right and top/bottom shadow tiles so they don't
|
||||
// overlap when the window is too small. Please notice that we don't
|
||||
// fix overlaps between left/top(left/bottom, right/top, and so on)
|
||||
// corner tiles because corresponding counter parts won't be valid when
|
||||
// the window is too small, which means they won't be rendered.
|
||||
distributeHorizontally(leftRect, rightRect);
|
||||
distributeVertically(topRect, bottomRect);
|
||||
|
||||
if (topRect.isValid()) {
|
||||
tx1 = shadowMargins.left() / width;
|
||||
ty1 = 0.0;
|
||||
tx2 = tx1 + top.width() / width;
|
||||
ty2 = topRect.height() / 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 (rightRect.isValid()) {
|
||||
tx1 = 1.0 - rightRect.width() / width;
|
||||
ty1 = shadowMargins.top() / height;
|
||||
tx2 = 1.0;
|
||||
ty2 = ty1 + right.height() / 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 (bottomRect.isValid()) {
|
||||
tx1 = shadowMargins.left() / width;
|
||||
ty1 = 1.0 - bottomRect.height() / height;
|
||||
tx2 = tx1 + bottom.width() / width;
|
||||
ty2 = 1.0;
|
||||
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 (leftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
ty1 = shadowMargins.top() / height;
|
||||
tx2 = leftRect.width() / width;
|
||||
ty2 = ty1 + left.height() / 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 SceneOpenGLShadow::prepareBackend()
|
||||
{
|
||||
if (hasDecorationShadow()) {
|
||||
|
|
|
@ -134,33 +134,19 @@ class OpenGLWindow final : public Scene::Window
|
|||
public:
|
||||
struct RenderNode
|
||||
{
|
||||
RenderNode()
|
||||
: texture(nullptr)
|
||||
, firstVertex(0)
|
||||
, vertexCount(0)
|
||||
, opacity(1.0)
|
||||
, hasAlpha(false)
|
||||
, coordinateType(UnnormalizedCoordinates)
|
||||
{
|
||||
}
|
||||
|
||||
GLTexture *texture;
|
||||
GLTexture *texture = nullptr;
|
||||
WindowQuadList quads;
|
||||
int firstVertex;
|
||||
int vertexCount;
|
||||
float opacity;
|
||||
bool hasAlpha;
|
||||
TextureCoordinateType coordinateType;
|
||||
int firstVertex = 0;
|
||||
int vertexCount = 0;
|
||||
qreal opacity = 1;
|
||||
bool hasAlpha = false;
|
||||
TextureCoordinateType coordinateType = UnnormalizedCoordinates;
|
||||
};
|
||||
|
||||
struct RenderContext
|
||||
{
|
||||
QVector<RenderNode> renderNodes;
|
||||
int shadowOffset = 0;
|
||||
int decorationOffset = 0;
|
||||
int contentOffset = 0;
|
||||
int previousContentOffset = 0;
|
||||
int quadCount = 0;
|
||||
QHash<Item *, WindowQuadList> quads;
|
||||
};
|
||||
|
||||
OpenGLWindow(Toplevel *toplevel, SceneOpenGL *scene);
|
||||
|
@ -171,11 +157,10 @@ public:
|
|||
|
||||
private:
|
||||
QMatrix4x4 transformation(int mask, const WindowPaintData &data) const;
|
||||
GLTexture *getDecorationTexture() const;
|
||||
QMatrix4x4 modelViewProjectionMatrix(int mask, const WindowPaintData &data) const;
|
||||
QVector4D modulate(float opacity, float brightness) const;
|
||||
void setBlendEnabled(bool enabled);
|
||||
void initializeRenderContext(RenderContext &context, const WindowPaintData &data);
|
||||
void createRenderNode(Item *item, RenderContext *context, const WindowPaintData &data);
|
||||
bool beginRenderWindow(int mask, const QRegion ®ion, WindowPaintData &data);
|
||||
void endRenderWindow();
|
||||
|
||||
|
@ -239,7 +224,6 @@ public:
|
|||
return m_texture.data();
|
||||
}
|
||||
protected:
|
||||
void buildQuads() override;
|
||||
bool prepareBackend() override;
|
||||
private:
|
||||
QSharedPointer<GLTexture> m_texture;
|
||||
|
|
|
@ -228,8 +228,8 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
|
|||
tempPainter.translate(toplevel->frameGeometry().topLeft() - toplevel->visibleGeometry().topLeft());
|
||||
painter = &tempPainter;
|
||||
}
|
||||
renderWindowDecorations(painter);
|
||||
renderSurfaceItem(painter, surfaceItem());
|
||||
|
||||
renderItem(painter, windowItem());
|
||||
|
||||
if (!opaque) {
|
||||
tempPainter.restore();
|
||||
|
@ -245,7 +245,23 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem)
|
||||
void SceneQPainter::Window::renderItem(QPainter *painter, Item *item) const
|
||||
{
|
||||
if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
|
||||
renderSurfaceItem(painter, surfaceItem);
|
||||
} else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) {
|
||||
renderDecorationItem(painter, decorationItem);
|
||||
}
|
||||
|
||||
const QList<Item *> childItems = item->childItems();
|
||||
for (Item *childItem : childItems) {
|
||||
if (childItem->isVisible()) {
|
||||
renderItem(painter, childItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem) const
|
||||
{
|
||||
const SurfacePixmap *surfaceTexture = surfaceItem->pixmap();
|
||||
if (!surfaceTexture || !surfaceTexture->isValid()) {
|
||||
|
@ -273,21 +289,10 @@ void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *su
|
|||
platformSurfaceTexture->image(),
|
||||
QRectF(bufferTopLeft, bufferBottomRight));
|
||||
}
|
||||
|
||||
const QList<Item *> children = surfaceItem->childItems();
|
||||
for (Item *child : children) {
|
||||
renderSurfaceItem(painter, static_cast<SurfaceItem *>(child));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneQPainter::Window::renderWindowDecorations(QPainter *painter)
|
||||
void SceneQPainter::Window::renderDecorationItem(QPainter *painter, DecorationItem *decorationItem) const
|
||||
{
|
||||
// TODO: custom decoration opacity
|
||||
const DecorationItem *decorationItem = windowItem()->decorationItem();
|
||||
if (!decorationItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto renderer = static_cast<const SceneQPainterDecorationRenderer *>(decorationItem->renderer());
|
||||
QRect dtr, dlr, drr, dbr;
|
||||
if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
|
||||
|
|
|
@ -71,8 +71,9 @@ public:
|
|||
~Window() override;
|
||||
void performPaint(int mask, const QRegion ®ion, const WindowPaintData &data) override;
|
||||
private:
|
||||
void renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem);
|
||||
void renderWindowDecorations(QPainter *painter);
|
||||
void renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem) const;
|
||||
void renderDecorationItem(QPainter *painter, DecorationItem *decorationItem) const;
|
||||
void renderItem(QPainter *painter, Item *item) const;
|
||||
SceneQPainter *m_scene;
|
||||
};
|
||||
|
||||
|
|
169
src/scene.cpp
169
src/scene.cpp
|
@ -752,7 +752,6 @@ Scene::Window::Window(Toplevel *client, QObject *parent)
|
|||
, toplevel(client)
|
||||
, filter(ImageFilterFast)
|
||||
, disable_painting(0)
|
||||
, cached_quad_list(nullptr)
|
||||
{
|
||||
if (qobject_cast<WaylandClient *>(client)) {
|
||||
m_windowItem.reset(new WindowItemWayland(this));
|
||||
|
@ -834,11 +833,6 @@ bool Scene::Window::isOpaque() const
|
|||
return toplevel->opacity() == 1.0 && !toplevel->hasAlpha();
|
||||
}
|
||||
|
||||
bool Scene::Window::isShaded() const
|
||||
{
|
||||
return toplevel->isShade();
|
||||
}
|
||||
|
||||
bool Scene::Window::isPaintingEnabled() const
|
||||
{
|
||||
return !disable_painting;
|
||||
|
@ -878,166 +872,23 @@ void Scene::Window::disablePainting(int reason)
|
|||
disable_painting |= reason;
|
||||
}
|
||||
|
||||
WindowQuadList Scene::Window::buildQuads(bool force) const
|
||||
static void buildQuadsHelper(const Item *item, WindowQuadList *ret)
|
||||
{
|
||||
if (cached_quad_list != nullptr && !force)
|
||||
return *cached_quad_list;
|
||||
*ret += item->quads();
|
||||
|
||||
WindowQuadList *ret = new WindowQuadList;
|
||||
|
||||
if (!isShaded()) {
|
||||
*ret += makeContentsQuads();
|
||||
}
|
||||
|
||||
if (!toplevel->frameMargins().isNull()) {
|
||||
QRect rects[4];
|
||||
|
||||
if (AbstractClient *client = qobject_cast<AbstractClient *>(toplevel)) {
|
||||
client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
|
||||
} else if (Deleted *deleted = qobject_cast<Deleted *>(toplevel)) {
|
||||
deleted->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
|
||||
const QList<Item *> childItems = item->childItems();
|
||||
for (const Item *childItem : childItems) {
|
||||
if (childItem->isVisible()) {
|
||||
buildQuadsHelper(childItem, ret);
|
||||
}
|
||||
|
||||
*ret += makeDecorationQuads(rects, decorationShape());
|
||||
}
|
||||
if (shadowItem() && toplevel->wantsShadowToBeRendered()) {
|
||||
*ret << shadowItem()->shadow()->shadowQuads();
|
||||
}
|
||||
cached_quad_list.reset(ret);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion) const
|
||||
WindowQuadList Scene::Window::buildQuads() const
|
||||
{
|
||||
WindowQuadList list;
|
||||
|
||||
const qreal textureScale = toplevel->screenScale();
|
||||
const int padding = 1;
|
||||
|
||||
const QPoint topSpritePosition(padding, padding);
|
||||
const QPoint bottomSpritePosition(padding, topSpritePosition.y() + rects[1].height() + 2 * padding);
|
||||
const QPoint leftSpritePosition(bottomSpritePosition.y() + rects[3].height() + 2 * padding, padding);
|
||||
const QPoint rightSpritePosition(leftSpritePosition.x() + rects[0].width() + 2 * padding, padding);
|
||||
|
||||
const QPoint offsets[4] = {
|
||||
QPoint(-rects[0].x(), -rects[0].y()) + leftSpritePosition,
|
||||
QPoint(-rects[1].x(), -rects[1].y()) + topSpritePosition,
|
||||
QPoint(-rects[2].x(), -rects[2].y()) + rightSpritePosition,
|
||||
QPoint(-rects[3].x(), -rects[3].y()) + bottomSpritePosition,
|
||||
};
|
||||
|
||||
const Qt::Orientation orientations[4] = {
|
||||
Qt::Vertical, // Left
|
||||
Qt::Horizontal, // Top
|
||||
Qt::Vertical, // Right
|
||||
Qt::Horizontal, // Bottom
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const QRegion intersectedRegion = (region & rects[i]);
|
||||
for (const QRect &r : intersectedRegion) {
|
||||
if (!r.isValid())
|
||||
continue;
|
||||
|
||||
const bool swap = orientations[i] == Qt::Vertical;
|
||||
|
||||
const int x0 = r.x();
|
||||
const int y0 = r.y();
|
||||
const int x1 = r.x() + r.width();
|
||||
const int y1 = r.y() + r.height();
|
||||
|
||||
const int u0 = (x0 + offsets[i].x()) * textureScale;
|
||||
const int v0 = (y0 + offsets[i].y()) * textureScale;
|
||||
const int u1 = (x1 + offsets[i].x()) * textureScale;
|
||||
const int v1 = (y1 + offsets[i].y()) * textureScale;
|
||||
|
||||
WindowQuad quad(WindowQuadDecoration);
|
||||
quad.setUVAxisSwapped(swap);
|
||||
|
||||
if (swap) {
|
||||
quad[0] = WindowVertex(x0, y0, v0, u0); // Top-left
|
||||
quad[1] = WindowVertex(x1, y0, v0, u1); // Top-right
|
||||
quad[2] = WindowVertex(x1, y1, v1, u1); // Bottom-right
|
||||
quad[3] = WindowVertex(x0, y1, v1, u0); // Bottom-left
|
||||
} else {
|
||||
quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left
|
||||
quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right
|
||||
quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right
|
||||
quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left
|
||||
}
|
||||
|
||||
list.append(quad);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
WindowQuadList Scene::Window::makeContentsQuads() const
|
||||
{
|
||||
// TODO(vlad): What about the case where we need to build window quads for a deleted
|
||||
// window? Presumably, the current window will be invalid so no window quads will be
|
||||
// generated. Is it okay?
|
||||
|
||||
SurfaceItem *currentItem = surfaceItem();
|
||||
if (!currentItem)
|
||||
return WindowQuadList();
|
||||
|
||||
WindowQuadList quads;
|
||||
int id = 0;
|
||||
|
||||
// We need to assign an id to each generated window quad in order to be able to match
|
||||
// a list of window quads against a particular window pixmap. We traverse the window
|
||||
// pixmap tree in the depth-first search manner and assign an id to each window quad.
|
||||
// The id is the time when we visited the window pixmap.
|
||||
|
||||
QStack<SurfaceItem *> stack;
|
||||
stack.push(currentItem);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
SurfaceItem *item = stack.pop();
|
||||
|
||||
const QRegion region = item->shape();
|
||||
const int quadId = id++;
|
||||
|
||||
for (const QRectF rect : region) {
|
||||
// Note that the window quad id is not unique if the window is shaped, i.e. the
|
||||
// region contains more than just one rectangle. We assume that the "source" quad
|
||||
// had been subdivided.
|
||||
WindowQuad quad(WindowQuadContents, quadId);
|
||||
|
||||
const QPointF windowTopLeft = item->mapToWindow(rect.topLeft());
|
||||
const QPointF windowTopRight = item->mapToWindow(rect.topRight());
|
||||
const QPointF windowBottomRight = item->mapToWindow(rect.bottomRight());
|
||||
const QPointF windowBottomLeft = item->mapToWindow(rect.bottomLeft());
|
||||
|
||||
const QPointF bufferTopLeft = item->mapToBuffer(rect.topLeft());
|
||||
const QPointF bufferTopRight = item->mapToBuffer(rect.topRight());
|
||||
const QPointF bufferBottomRight = item->mapToBuffer(rect.bottomRight());
|
||||
const QPointF bufferBottomLeft = item->mapToBuffer(rect.bottomLeft());
|
||||
|
||||
quad[0] = WindowVertex(windowTopLeft, bufferTopLeft);
|
||||
quad[1] = WindowVertex(windowTopRight, bufferTopRight);
|
||||
quad[2] = WindowVertex(windowBottomRight, bufferBottomRight);
|
||||
quad[3] = WindowVertex(windowBottomLeft, bufferBottomLeft);
|
||||
|
||||
quads << quad;
|
||||
}
|
||||
|
||||
// Push the child window pixmaps onto the stack, remember we're visiting the pixmaps
|
||||
// in the depth-first search manner.
|
||||
const QList<Item *> children = item->childItems();
|
||||
for (auto it = children.rbegin(); it != children.rend(); ++it) {
|
||||
stack.push(static_cast<SurfaceItem *>(*it));
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
void Scene::Window::discardQuads()
|
||||
{
|
||||
cached_quad_list.reset();
|
||||
WindowQuadList ret;
|
||||
buildQuadsHelper(windowItem(), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Scene::Window::preprocess(Item *item)
|
||||
|
|
|
@ -337,15 +337,11 @@ public:
|
|||
bool isVisible() const;
|
||||
// is the window fully opaque
|
||||
bool isOpaque() const;
|
||||
// is the window shaded
|
||||
bool isShaded() const;
|
||||
QRegion decorationShape() const;
|
||||
void updateToplevel(Deleted *deleted);
|
||||
// creates initial quad list for the window
|
||||
virtual WindowQuadList buildQuads(bool force = false) const;
|
||||
WindowQuadList buildQuads() const;
|
||||
void referencePreviousPixmap();
|
||||
void unreferencePreviousPixmap();
|
||||
void discardQuads();
|
||||
void preprocess(Item *item);
|
||||
WindowItem *windowItem() const;
|
||||
SurfaceItem *surfaceItem() const;
|
||||
|
@ -356,8 +352,6 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion) const;
|
||||
WindowQuadList makeContentsQuads() const;
|
||||
Toplevel* toplevel;
|
||||
ImageFilterType filter;
|
||||
private:
|
||||
|
@ -367,7 +361,6 @@ private:
|
|||
void updateWindowPosition();
|
||||
|
||||
int disable_painting;
|
||||
mutable QScopedPointer<WindowQuadList> cached_quad_list;
|
||||
QScopedPointer<WindowItem> m_windowItem;
|
||||
Q_DISABLE_COPY(Window)
|
||||
};
|
||||
|
|
|
@ -184,7 +184,6 @@ bool Shadow::init(const QVector< uint32_t > &data)
|
|||
if (!prepareBackend()) {
|
||||
return false;
|
||||
}
|
||||
buildQuads();
|
||||
Q_EMIT textureChanged();
|
||||
return true;
|
||||
}
|
||||
|
@ -215,7 +214,6 @@ bool Shadow::init(KDecoration2::Decoration *decoration)
|
|||
if (!prepareBackend()) {
|
||||
return false;
|
||||
}
|
||||
buildQuads();
|
||||
Q_EMIT textureChanged();
|
||||
return true;
|
||||
}
|
||||
|
@ -244,7 +242,6 @@ bool Shadow::init(const QPointer< KWaylandServer::ShadowInterface > &shadow)
|
|||
if (!prepareBackend()) {
|
||||
return false;
|
||||
}
|
||||
buildQuads();
|
||||
Q_EMIT textureChanged();
|
||||
return true;
|
||||
}
|
||||
|
@ -286,10 +283,7 @@ bool Shadow::init(const QWindow *window)
|
|||
if (!prepareBackend()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buildQuads();
|
||||
Q_EMIT textureChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -303,86 +297,6 @@ void Shadow::updateShadowRegion()
|
|||
Q_EMIT regionChanged();
|
||||
}
|
||||
|
||||
void Shadow::buildQuads()
|
||||
{
|
||||
// prepare window quads
|
||||
m_shadowQuads.clear();
|
||||
const QSize top(m_shadowElements[ShadowElementTop].size());
|
||||
const QSize topRight(m_shadowElements[ShadowElementTopRight].size());
|
||||
const QSize right(m_shadowElements[ShadowElementRight].size());
|
||||
const QSize bottomRight(m_shadowElements[ShadowElementBottomRight].size());
|
||||
const QSize bottom(m_shadowElements[ShadowElementBottom].size());
|
||||
const QSize bottomLeft(m_shadowElements[ShadowElementBottomLeft].size());
|
||||
const QSize left(m_shadowElements[ShadowElementLeft].size());
|
||||
const QSize topLeft(m_shadowElements[ShadowElementTopLeft].size());
|
||||
if ((left.width() - m_leftOffset > m_topLevel->width()) ||
|
||||
(right.width() - m_rightOffset > m_topLevel->width()) ||
|
||||
(top.height() - m_topOffset > m_topLevel->height()) ||
|
||||
(bottom.height() - m_bottomOffset > m_topLevel->height())) {
|
||||
// if our shadow is bigger than the window, we don't render the shadow
|
||||
m_shadowRegion = QRegion();
|
||||
return;
|
||||
}
|
||||
|
||||
const QRect outerRect(QPoint(-m_leftOffset, -m_topOffset), QPoint(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset));
|
||||
|
||||
WindowQuad topLeftQuad(WindowQuadShadowTopLeft);
|
||||
topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), 0.0, 0.0);
|
||||
topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 1.0, 0.0);
|
||||
topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), 1.0, 1.0);
|
||||
topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 1.0);
|
||||
m_shadowQuads.append(topLeftQuad);
|
||||
|
||||
WindowQuad topQuad(WindowQuadShadowTop);
|
||||
topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 0.0, 0.0);
|
||||
topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 1.0, 0.0);
|
||||
topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(), 1.0, 1.0);
|
||||
topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), 0.0, 1.0);
|
||||
m_shadowQuads.append(topQuad);
|
||||
|
||||
WindowQuad topRightQuad(WindowQuadShadowTopRight);
|
||||
topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 0.0, 0.0);
|
||||
topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), 1.0, 0.0);
|
||||
topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 1.0);
|
||||
topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), 0.0, 1.0);
|
||||
m_shadowQuads.append(topRightQuad);
|
||||
|
||||
WindowQuad rightQuad(WindowQuadShadowRight);
|
||||
rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), 0.0, 0.0);
|
||||
rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 0.0);
|
||||
rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 1.0);
|
||||
rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), 0.0, 1.0);
|
||||
m_shadowQuads.append(rightQuad);
|
||||
|
||||
WindowQuad bottomRightQuad(WindowQuadShadowBottomRight);
|
||||
bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), 0.0, 0.0);
|
||||
bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 0.0);
|
||||
bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), 1.0, 1.0);
|
||||
bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 0.0, 1.0);
|
||||
m_shadowQuads.append(bottomRightQuad);
|
||||
|
||||
WindowQuad bottomQuad(WindowQuadShadowBottom);
|
||||
bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), 0.0, 0.0);
|
||||
bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), 1.0, 0.0);
|
||||
bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 1.0, 1.0);
|
||||
bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 0.0, 1.0);
|
||||
m_shadowQuads.append(bottomQuad);
|
||||
|
||||
WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft);
|
||||
bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 0.0);
|
||||
bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 0.0);
|
||||
bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 1.0, 1.0);
|
||||
bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), 0.0, 1.0);
|
||||
m_shadowQuads.append(bottomLeftQuad);
|
||||
|
||||
WindowQuad leftQuad(WindowQuadShadowLeft);
|
||||
leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 0.0);
|
||||
leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), 1.0, 0.0);
|
||||
leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 1.0);
|
||||
leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 1.0);
|
||||
m_shadowQuads.append(leftQuad);
|
||||
}
|
||||
|
||||
bool Shadow::updateShadow()
|
||||
{
|
||||
if (!m_topLevel) {
|
||||
|
@ -438,7 +352,6 @@ void Shadow::geometryChanged()
|
|||
}
|
||||
m_cachedSize = m_topLevel->size();
|
||||
updateShadowRegion();
|
||||
buildQuads();
|
||||
}
|
||||
|
||||
QImage Shadow::decorationShadowImage() const
|
||||
|
|
45
src/shadow.h
45
src/shadow.h
|
@ -56,15 +56,6 @@ public:
|
|||
const QRegion &shadowRegion() const {
|
||||
return m_shadowRegion;
|
||||
};
|
||||
/**
|
||||
* @return Cached Shadow Quads
|
||||
*/
|
||||
const WindowQuadList &shadowQuads() const {
|
||||
return m_shadowQuads;
|
||||
};
|
||||
WindowQuadList &shadowQuads() {
|
||||
return m_shadowQuads;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method updates the Shadow when the property has been changed.
|
||||
|
@ -105,15 +96,6 @@ public:
|
|||
return m_decorationShadow.toWeakRef();
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void regionChanged();
|
||||
void textureChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void geometryChanged();
|
||||
|
||||
protected:
|
||||
Shadow(Toplevel *toplevel);
|
||||
enum ShadowElements {
|
||||
ShadowElementTop,
|
||||
ShadowElementTopRight,
|
||||
|
@ -125,10 +107,6 @@ protected:
|
|||
ShadowElementTopLeft,
|
||||
ShadowElementsCount
|
||||
};
|
||||
|
||||
inline const QPixmap &shadowPixmap(ShadowElements element) const {
|
||||
return m_shadowElements[element];
|
||||
};
|
||||
QSize elementSize(ShadowElements element) const;
|
||||
|
||||
int topOffset() const {
|
||||
|
@ -143,16 +121,23 @@ protected:
|
|||
int leftOffset() const {
|
||||
return m_leftOffset;
|
||||
};
|
||||
virtual void buildQuads();
|
||||
|
||||
Q_SIGNALS:
|
||||
void regionChanged();
|
||||
void textureChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void geometryChanged();
|
||||
|
||||
protected:
|
||||
Shadow(Toplevel *toplevel);
|
||||
|
||||
inline const QPixmap &shadowPixmap(ShadowElements element) const {
|
||||
return m_shadowElements[element];
|
||||
};
|
||||
|
||||
void updateShadowRegion();
|
||||
Toplevel *topLevel() {
|
||||
return m_topLevel;
|
||||
};
|
||||
void setShadowRegion(const QRegion ®ion) {
|
||||
m_shadowRegion = region;
|
||||
};
|
||||
virtual bool prepareBackend() = 0;
|
||||
WindowQuadList m_shadowQuads;
|
||||
void setShadowElement(const QPixmap &shadow, ShadowElements element);
|
||||
|
||||
private:
|
||||
|
|
|
@ -36,11 +36,250 @@ void ShadowItem::handleRegionChanged()
|
|||
|
||||
setPosition(rect.topLeft());
|
||||
setSize(rect.size());
|
||||
discardQuads();
|
||||
}
|
||||
|
||||
void ShadowItem::handleTextureChanged()
|
||||
{
|
||||
scheduleRepaint(rect());
|
||||
discardQuads();
|
||||
}
|
||||
|
||||
static inline void distributeHorizontally(QRectF &leftRect, QRectF &rightRect)
|
||||
{
|
||||
if (leftRect.right() > rightRect.left()) {
|
||||
const qreal boundedRight = std::min(leftRect.right(), rightRect.right());
|
||||
const qreal boundedLeft = std::max(leftRect.left(), rightRect.left());
|
||||
const qreal halfOverlap = (boundedRight - boundedLeft) / 2.0;
|
||||
leftRect.setRight(boundedRight - halfOverlap);
|
||||
rightRect.setLeft(boundedLeft + halfOverlap);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void distributeVertically(QRectF &topRect, QRectF &bottomRect)
|
||||
{
|
||||
if (topRect.bottom() > bottomRect.top()) {
|
||||
const qreal boundedBottom = std::min(topRect.bottom(), bottomRect.bottom());
|
||||
const qreal boundedTop = std::max(topRect.top(), bottomRect.top());
|
||||
const qreal halfOverlap = (boundedBottom - boundedTop) / 2.0;
|
||||
topRect.setBottom(boundedBottom - halfOverlap);
|
||||
bottomRect.setTop(boundedTop + halfOverlap);
|
||||
}
|
||||
}
|
||||
|
||||
WindowQuadList ShadowItem::buildQuads() const
|
||||
{
|
||||
const Toplevel *toplevel = window()->window();
|
||||
|
||||
// Do not draw shadows if window width or window height is less than 5 px. 5 is an arbitrary choice.
|
||||
if (!toplevel->wantsShadowToBeRendered() || toplevel->width() < 5 || toplevel->height() < 5) {
|
||||
return WindowQuadList();
|
||||
}
|
||||
|
||||
const QSizeF top(m_shadow->elementSize(Shadow::ShadowElementTop));
|
||||
const QSizeF topRight(m_shadow->elementSize(Shadow::ShadowElementTopRight));
|
||||
const QSizeF right(m_shadow->elementSize(Shadow::ShadowElementRight));
|
||||
const QSizeF bottomRight(m_shadow->elementSize(Shadow::ShadowElementBottomRight));
|
||||
const QSizeF bottom(m_shadow->elementSize(Shadow::ShadowElementBottom));
|
||||
const QSizeF bottomLeft(m_shadow->elementSize(Shadow::ShadowElementBottomLeft));
|
||||
const QSizeF left(m_shadow->elementSize(Shadow::ShadowElementLeft));
|
||||
const QSizeF topLeft(m_shadow->elementSize(Shadow::ShadowElementTopLeft));
|
||||
|
||||
const QMarginsF shadowMargins(
|
||||
std::max({topLeft.width(), left.width(), bottomLeft.width()}),
|
||||
std::max({topLeft.height(), top.height(), topRight.height()}),
|
||||
std::max({topRight.width(), right.width(), bottomRight.width()}),
|
||||
std::max({bottomRight.height(), bottom.height(), bottomLeft.height()}));
|
||||
|
||||
const QRectF outerRect(QPointF(-m_shadow->leftOffset(), -m_shadow->topOffset()),
|
||||
QPointF(toplevel->width() + m_shadow->rightOffset(),
|
||||
toplevel->height() + m_shadow->bottomOffset()));
|
||||
|
||||
const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right();
|
||||
const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom();
|
||||
|
||||
QRectF topLeftRect;
|
||||
if (!topLeft.isEmpty()) {
|
||||
topLeftRect = QRectF(outerRect.topLeft(), topLeft);
|
||||
} else {
|
||||
topLeftRect = QRectF(outerRect.left() + shadowMargins.left(),
|
||||
outerRect.top() + shadowMargins.top(), 0, 0);
|
||||
}
|
||||
|
||||
QRectF topRightRect;
|
||||
if (!topRight.isEmpty()) {
|
||||
topRightRect = QRectF(outerRect.right() - topRight.width(), outerRect.top(),
|
||||
topRight.width(), topRight.height());
|
||||
} else {
|
||||
topRightRect = QRectF(outerRect.right() - shadowMargins.right(),
|
||||
outerRect.top() + shadowMargins.top(), 0, 0);
|
||||
}
|
||||
|
||||
QRectF bottomRightRect;
|
||||
if (!bottomRight.isEmpty()) {
|
||||
bottomRightRect = QRectF(outerRect.right() - bottomRight.width(),
|
||||
outerRect.bottom() - bottomRight.height(),
|
||||
bottomRight.width(), bottomRight.height());
|
||||
} else {
|
||||
bottomRightRect = QRectF(outerRect.right() - shadowMargins.right(),
|
||||
outerRect.bottom() - shadowMargins.bottom(), 0, 0);
|
||||
}
|
||||
|
||||
QRectF bottomLeftRect;
|
||||
if (!bottomLeft.isEmpty()) {
|
||||
bottomLeftRect = QRectF(outerRect.left(), outerRect.bottom() - bottomLeft.height(),
|
||||
bottomLeft.width(), bottomLeft.height());
|
||||
} else {
|
||||
bottomLeftRect = QRectF(outerRect.left() + shadowMargins.left(),
|
||||
outerRect.bottom() - shadowMargins.bottom(), 0, 0);
|
||||
}
|
||||
|
||||
// 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.
|
||||
distributeHorizontally(topLeftRect, topRightRect);
|
||||
distributeHorizontally(bottomLeftRect, bottomRightRect);
|
||||
distributeVertically(topLeftRect, bottomLeftRect);
|
||||
distributeVertically(topRightRect, bottomRightRect);
|
||||
|
||||
qreal tx1 = 0.0,
|
||||
tx2 = 0.0,
|
||||
ty1 = 0.0,
|
||||
ty2 = 0.0;
|
||||
|
||||
WindowQuadList quads;
|
||||
quads.reserve(8);
|
||||
void *tag = const_cast<ShadowItem *>(this);
|
||||
|
||||
if (topLeftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
ty1 = 0.0;
|
||||
tx2 = topLeftRect.width() / width;
|
||||
ty2 = topLeftRect.height() / height;
|
||||
WindowQuad topLeftQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(topLeftQuad);
|
||||
}
|
||||
|
||||
if (topRightRect.isValid()) {
|
||||
tx1 = 1.0 - topRightRect.width() / width;
|
||||
ty1 = 0.0;
|
||||
tx2 = 1.0;
|
||||
ty2 = topRightRect.height() / height;
|
||||
WindowQuad topRightQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(topRightQuad);
|
||||
}
|
||||
|
||||
if (bottomRightRect.isValid()) {
|
||||
tx1 = 1.0 - bottomRightRect.width() / width;
|
||||
tx2 = 1.0;
|
||||
ty1 = 1.0 - bottomRightRect.height() / height;
|
||||
ty2 = 1.0;
|
||||
WindowQuad bottomRightQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(bottomRightQuad);
|
||||
}
|
||||
|
||||
if (bottomLeftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
tx2 = bottomLeftRect.width() / width;
|
||||
ty1 = 1.0 - bottomLeftRect.height() / height;
|
||||
ty2 = 1.0;
|
||||
WindowQuad bottomLeftQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(bottomLeftQuad);
|
||||
}
|
||||
|
||||
QRectF topRect(QPointF(topLeftRect.right(), outerRect.top()),
|
||||
QPointF(topRightRect.left(), outerRect.top() + top.height()));
|
||||
|
||||
QRectF rightRect(QPointF(outerRect.right() - right.width(), topRightRect.bottom()),
|
||||
QPointF(outerRect.right(), bottomRightRect.top()));
|
||||
|
||||
QRectF bottomRect(QPointF(bottomLeftRect.right(), outerRect.bottom() - bottom.height()),
|
||||
QPointF(bottomRightRect.left(), outerRect.bottom()));
|
||||
|
||||
QRectF leftRect(QPointF(outerRect.left(), topLeftRect.bottom()),
|
||||
QPointF(outerRect.left() + left.width(), bottomLeftRect.top()));
|
||||
|
||||
// Re-distribute left/right and top/bottom shadow tiles so they don't
|
||||
// overlap when the window is too small. Please notice that we don't
|
||||
// fix overlaps between left/top(left/bottom, right/top, and so on)
|
||||
// corner tiles because corresponding counter parts won't be valid when
|
||||
// the window is too small, which means they won't be rendered.
|
||||
distributeHorizontally(leftRect, rightRect);
|
||||
distributeVertically(topRect, bottomRect);
|
||||
|
||||
if (topRect.isValid()) {
|
||||
tx1 = shadowMargins.left() / width;
|
||||
ty1 = 0.0;
|
||||
tx2 = tx1 + top.width() / width;
|
||||
ty2 = topRect.height() / height;
|
||||
WindowQuad topQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(topQuad);
|
||||
}
|
||||
|
||||
if (rightRect.isValid()) {
|
||||
tx1 = 1.0 - rightRect.width() / width;
|
||||
ty1 = shadowMargins.top() / height;
|
||||
tx2 = 1.0;
|
||||
ty2 = ty1 + right.height() / height;
|
||||
WindowQuad rightQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(rightQuad);
|
||||
}
|
||||
|
||||
if (bottomRect.isValid()) {
|
||||
tx1 = shadowMargins.left() / width;
|
||||
ty1 = 1.0 - bottomRect.height() / height;
|
||||
tx2 = tx1 + bottom.width() / width;
|
||||
ty2 = 1.0;
|
||||
WindowQuad bottomQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(bottomQuad);
|
||||
}
|
||||
|
||||
if (leftRect.isValid()) {
|
||||
tx1 = 0.0;
|
||||
ty1 = shadowMargins.top() / height;
|
||||
tx2 = leftRect.width() / width;
|
||||
ty2 = ty1 + left.height() / height;
|
||||
WindowQuad leftQuad(WindowQuadShadow, tag);
|
||||
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);
|
||||
quads.append(leftQuad);
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -24,6 +24,9 @@ public:
|
|||
|
||||
Shadow *shadow() const;
|
||||
|
||||
protected:
|
||||
WindowQuadList buildQuads() const override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleTextureChanged();
|
||||
void handleRegionChanged();
|
||||
|
|
|
@ -117,6 +117,37 @@ void SurfaceItem::preprocess()
|
|||
updatePixmap();
|
||||
}
|
||||
|
||||
WindowQuadList SurfaceItem::buildQuads() const
|
||||
{
|
||||
const QRegion region = shape();
|
||||
|
||||
WindowQuadList quads;
|
||||
quads.reserve(region.rectCount());
|
||||
|
||||
for (const QRectF rect : region) {
|
||||
WindowQuad quad(WindowQuadContents, const_cast<SurfaceItem *>(this));
|
||||
|
||||
const QPointF windowTopLeft = mapToWindow(rect.topLeft());
|
||||
const QPointF windowTopRight = mapToWindow(rect.topRight());
|
||||
const QPointF windowBottomRight = mapToWindow(rect.bottomRight());
|
||||
const QPointF windowBottomLeft = mapToWindow(rect.bottomLeft());
|
||||
|
||||
const QPointF bufferTopLeft = mapToBuffer(rect.topLeft());
|
||||
const QPointF bufferTopRight = mapToBuffer(rect.topRight());
|
||||
const QPointF bufferBottomRight = mapToBuffer(rect.bottomRight());
|
||||
const QPointF bufferBottomLeft = mapToBuffer(rect.bottomLeft());
|
||||
|
||||
quad[0] = WindowVertex(windowTopLeft, bufferTopLeft);
|
||||
quad[1] = WindowVertex(windowTopRight, bufferTopRight);
|
||||
quad[2] = WindowVertex(windowBottomRight, bufferBottomRight);
|
||||
quad[3] = WindowVertex(windowBottomLeft, bufferBottomLeft);
|
||||
|
||||
quads << quad;
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
PlatformSurfaceTexture::~PlatformSurfaceTexture()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ protected:
|
|||
|
||||
virtual SurfacePixmap *createPixmap() = 0;
|
||||
void preprocess() override;
|
||||
WindowQuadList buildQuads() const override;
|
||||
|
||||
QRegion m_damage;
|
||||
QScopedPointer<SurfacePixmap> m_pixmap;
|
||||
|
|
Loading…
Reference in a new issue