Move WindowVertex,WindowQuad,RenderGeometry out of effects.h

These are core scene abstractions and don't belong in effects.h.
This commit is contained in:
Vlad Zahorodnii 2023-11-20 12:43:05 +02:00
parent 86084d118c
commit 961bd00919
32 changed files with 624 additions and 581 deletions

View file

@ -6,7 +6,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "effect/effects.h"
#include "scene/itemgeometry.h"
#include <QTest>
Q_DECLARE_METATYPE(KWin::WindowQuadList)

View file

@ -160,6 +160,7 @@ target_sources(kwin PRIVATE
scene/dndiconitem.cpp
scene/imageitem.cpp
scene/item.cpp
scene/itemgeometry.cpp
scene/itemrenderer.cpp
scene/itemrenderer_opengl.cpp
scene/itemrenderer_qpainter.cpp
@ -461,6 +462,12 @@ install(FILES
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/opengl COMPONENT Devel
)
install(FILES
scene/item.h
scene/itemgeometry.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/scene COMPONENT Devel
)
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KWin")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KWinConfig.cmake"

View file

@ -12,6 +12,7 @@
#include "core/overlaywindow.h"
#include "core/renderbackend.h"
#include "core/renderlayer.h"
#include "effect/effects.h"
#include "opengl/glplatform.h"
#include "options.h"
#include "platformsupport/scenes/opengl/openglbackend.h"

View file

@ -10,6 +10,7 @@
#include "compositor.h"
#include "core/graphicsbufferview.h"
#include "core/inputdevice.h"
#include "effect/effects.h"
#include "input_event.h"
#include "internalwindow.h"
#include "keyboard_input.h"

View file

@ -11,6 +11,7 @@
#pragma once
#include "effect/animationeffect.h"
#include "effect/effects.h"
#include "effect/timeline.h"
#include <QEasingCurve>

View file

@ -12,6 +12,7 @@
#include <config-kwin.h>
// KWin
#include "effect/effect.h"
#include "effect/effects.h"
#include "plugin.h"
#include "scripting/scriptedeffect.h"
#include "scripting/scriptedquicksceneeffect.h"
@ -350,7 +351,7 @@ bool PluginEffectLoader::loadEffect(const KPluginMetaData &info, LoadEffectFlags
return false;
}
effects->makeOpenGLContextCurrent();
effects->makeOpenGLContextCurrent(); // TODO: remove it
if (!effectFactory->isSupported()) {
qCDebug(KWIN_CORE) << "Effect is not supported: " << name;
return false;

View file

@ -2119,299 +2119,6 @@ EffectWindowList EffectWindowGroup::members() const
return ret;
}
/***************************************************************
WindowQuad
***************************************************************/
WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const
{
Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom());
WindowQuad ret(*this);
// vertices are clockwise starting from topleft
ret.verts[0].px = x1;
ret.verts[3].px = x1;
ret.verts[1].px = x2;
ret.verts[2].px = x2;
ret.verts[0].py = y1;
ret.verts[1].py = y1;
ret.verts[2].py = y2;
ret.verts[3].py = y2;
const double xOrigin = left();
const double yOrigin = top();
const double widthReciprocal = 1 / (right() - xOrigin);
const double heightReciprocal = 1 / (bottom() - yOrigin);
for (int i = 0; i < 4; ++i) {
const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal;
const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal;
// Use bilinear interpolation to compute the texture coords.
ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx + w1 * (1 - w2) * verts[1].tx + w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx;
ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty + w1 * (1 - w2) * verts[1].ty + w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty;
}
return ret;
}
/***************************************************************
WindowQuadList
***************************************************************/
WindowQuadList WindowQuadList::splitAtX(double x) const
{
WindowQuadList ret;
ret.reserve(count());
for (const WindowQuad &quad : *this) {
bool wholeleft = true;
bool wholeright = true;
for (int i = 0; i < 4; ++i) {
if (quad[i].x() < x) {
wholeright = false;
}
if (quad[i].x() > x) {
wholeleft = false;
}
}
if (wholeleft || wholeright) { // is whole in one split part
ret.append(quad);
continue;
}
if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
ret.append(quad);
continue;
}
ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom()));
ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom()));
}
return ret;
}
WindowQuadList WindowQuadList::splitAtY(double y) const
{
WindowQuadList ret;
ret.reserve(count());
for (const WindowQuad &quad : *this) {
bool wholetop = true;
bool wholebottom = true;
for (int i = 0; i < 4; ++i) {
if (quad[i].y() < y) {
wholebottom = false;
}
if (quad[i].y() > y) {
wholetop = false;
}
}
if (wholetop || wholebottom) { // is whole in one split part
ret.append(quad);
continue;
}
if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
ret.append(quad);
continue;
}
ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y));
ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom()));
}
return ret;
}
WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const
{
if (empty()) {
return *this;
}
// Find the bounding rectangle
double left = first().left();
double right = first().right();
double top = first().top();
double bottom = first().bottom();
for (const WindowQuad &quad : std::as_const(*this)) {
left = std::min(left, quad.left());
right = std::max(right, quad.right());
top = std::min(top, quad.top());
bottom = std::max(bottom, quad.bottom());
}
WindowQuadList ret;
for (const WindowQuad &quad : std::as_const(*this)) {
const double quadLeft = quad.left();
const double quadRight = quad.right();
const double quadTop = quad.top();
const double quadBottom = quad.bottom();
// sanity check, see BUG 390953
if (quadLeft == quadRight || quadTop == quadBottom) {
ret.append(quad);
continue;
}
// Compute the top-left corner of the first intersecting grid cell
const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize;
const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize;
// Loop over all intersecting cells and add sub-quads
for (double y = yBegin; y < quadBottom; y += maxQuadSize) {
const double y0 = std::max(y, quadTop);
const double y1 = std::min(quadBottom, y + maxQuadSize);
for (double x = xBegin; x < quadRight; x += maxQuadSize) {
const double x0 = std::max(x, quadLeft);
const double x1 = std::min(quadRight, x + maxQuadSize);
ret.append(quad.makeSubQuad(x0, y0, x1, y1));
}
}
}
return ret;
}
WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const
{
if (empty()) {
return *this;
}
// Find the bounding rectangle
double left = first().left();
double right = first().right();
double top = first().top();
double bottom = first().bottom();
for (const WindowQuad &quad : *this) {
left = std::min(left, quad.left());
right = std::max(right, quad.right());
top = std::min(top, quad.top());
bottom = std::max(bottom, quad.bottom());
}
double xIncrement = (right - left) / xSubdivisions;
double yIncrement = (bottom - top) / ySubdivisions;
WindowQuadList ret;
for (const WindowQuad &quad : *this) {
const double quadLeft = quad.left();
const double quadRight = quad.right();
const double quadTop = quad.top();
const double quadBottom = quad.bottom();
// sanity check, see BUG 390953
if (quadLeft == quadRight || quadTop == quadBottom) {
ret.append(quad);
continue;
}
// Compute the top-left corner of the first intersecting grid cell
const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement;
const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement;
// Loop over all intersecting cells and add sub-quads
for (double y = yBegin; y < quadBottom; y += yIncrement) {
const double y0 = std::max(y, quadTop);
const double y1 = std::min(quadBottom, y + yIncrement);
for (double x = xBegin; x < quadRight; x += xIncrement) {
const double x0 = std::max(x, quadLeft);
const double x1 = std::min(quadRight, x + xIncrement);
ret.append(quad.makeSubQuad(x0, y0, x1, y1));
}
}
}
return ret;
}
void RenderGeometry::copy(std::span<GLVertex2D> destination)
{
Q_ASSERT(int(destination.size()) >= size());
std::copy(cbegin(), cend(), destination.begin());
}
void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale)
{
GLVertex2D glVertex;
switch (m_vertexSnappingMode) {
case VertexSnappingMode::None:
glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale;
break;
case VertexSnappingMode::Round:
glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale);
break;
}
glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v());
append(glVertex);
}
void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale)
{
// Geometry assumes we're rendering triangles, so add the quad's
// vertices as two triangles. Vertex order is top-left, bottom-left,
// top-right followed by top-right, bottom-left, bottom-right.
appendWindowVertex(quad[0], deviceScale);
appendWindowVertex(quad[3], deviceScale);
appendWindowVertex(quad[1], deviceScale);
appendWindowVertex(quad[1], deviceScale);
appendWindowVertex(quad[3], deviceScale);
appendWindowVertex(quad[2], deviceScale);
}
void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale)
{
std::array<GLVertex2D, 4> vertices;
vertices[0].position = QVector2D(subquad.topLeft());
vertices[1].position = QVector2D(subquad.topRight());
vertices[2].position = QVector2D(subquad.bottomRight());
vertices[3].position = QVector2D(subquad.bottomLeft());
const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)),
QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))};
const QPointF origin = deviceQuad.topLeft();
const QSizeF size = deviceQuad.size();
#pragma GCC unroll 4
for (int i = 0; i < 4; ++i) {
const double weight1 = (vertices[i].position.x() - origin.x()) / size.width();
const double weight2 = (vertices[i].position.y() - origin.y()) / size.height();
const double oneMinW1 = 1.0 - weight1;
const double oneMinW2 = 1.0 - weight2;
const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u()
+ weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u();
const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v()
+ weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v();
vertices[i].texcoord = QVector2D(u, v);
}
append(vertices[0]);
append(vertices[3]);
append(vertices[1]);
append(vertices[1]);
append(vertices[3]);
append(vertices[2]);
}
void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix)
{
if (!textureMatrix.isIdentity()) {
const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1));
const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3));
for (auto &vertex : (*this)) {
vertex.texcoord = vertex.texcoord * coeff + offset;
}
}
}
} // namespace
#include "moc_effects.cpp"

View file

@ -13,7 +13,6 @@
#pragma once
#include "effect/effect.h"
#include "opengl/glvertexbuffer.h"
#include <QEasingCurve>
#include <QIcon>
@ -21,8 +20,6 @@
#include <QRect>
#include <QRegion>
#include <QSet>
#include <QVector2D>
#include <QVector3D>
#include <QHash>
#include <QList>
@ -80,8 +77,6 @@ class TabletToolId;
class Window;
class WindowItem;
class WindowPropertyNotifyX11Filter;
class WindowQuad;
class WindowQuadList;
class WorkspaceScene;
class VirtualDesktop;
@ -2067,289 +2062,11 @@ private:
Group *m_group;
};
/**
* @short Vertex class
*
* A vertex is one position in a window. WindowQuad consists of four WindowVertex objects
* and represents one part of a window.
*/
class KWIN_EXPORT WindowVertex
{
public:
WindowVertex();
WindowVertex(const QPointF &position, const QPointF &textureCoordinate);
WindowVertex(double x, double y, double tx, double ty);
double x() const
{
return px;
}
double y() const
{
return py;
}
double u() const
{
return tx;
}
double v() const
{
return ty;
}
void move(double x, double y);
void setX(double x);
void setY(double y);
private:
friend class WindowQuad;
friend class WindowQuadList;
double px, py; // position
double tx, ty; // texture coords
};
/**
* @short Class representing one area of a window.
*
* WindowQuads consists of four WindowVertex objects and represents one part of a window.
*/
// NOTE: This class expects the (original) vertices to be in the clockwise order starting from topleft.
class KWIN_EXPORT WindowQuad
{
public:
WindowQuad();
WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const;
WindowVertex &operator[](int index);
const WindowVertex &operator[](int index) const;
double left() const;
double right() const;
double top() const;
double bottom() const;
QRectF bounds() const;
private:
friend class WindowQuadList;
WindowVertex verts[4];
};
class KWIN_EXPORT WindowQuadList
: public QList<WindowQuad>
{
public:
WindowQuadList splitAtX(double x) const;
WindowQuadList splitAtY(double y) const;
WindowQuadList makeGrid(int maxquadsize) const;
WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const;
};
/**
* A helper class for render geometry in device coordinates.
*
* This mostly represents a vector of vertices, with some convenience methods
* for easily converting from WindowQuad and related classes to lists of
* GLVertex2D. This class assumes rendering happens as unindexed triangles.
*/
class KWIN_EXPORT RenderGeometry : public QList<GLVertex2D>
{
public:
/**
* In what way should vertices snap to integer device coordinates?
*
* Vertices are converted to device coordinates before being sent to the
* rendering system. Depending on scaling factors, this may lead to device
* coordinates with fractional parts. For some cases, this may not be ideal
* as fractional coordinates need to be interpolated and can lead to
* "blurry" rendering. To avoid that, we can snap the vertices to integer
* device coordinates when they are added.
*/
enum class VertexSnappingMode {
None, //< No rounding, device coordinates containing fractional parts
// are passed directly to the rendering system.
Round, //< Perform a simple rounding, device coordinates will not have
// any fractional parts.
};
/**
* The vertex snapping mode to use for this geometry.
*
* By default, this is VertexSnappingMode::Round.
*/
inline VertexSnappingMode vertexSnappingMode() const
{
return m_vertexSnappingMode;
}
/**
* Set the vertex snapping mode to use for this geometry.
*
* Note that this doesn't change vertices retroactively, so you should set
* this before adding any vertices, or clear and rebuild the geometry after
* setting it.
*
* @param mode The new rounding mode.
*/
void setVertexSnappingMode(VertexSnappingMode mode)
{
m_vertexSnappingMode = mode;
}
/**
* Copy geometry data into another buffer.
*
* This is primarily intended for copying into a vertex buffer for rendering.
*
* @param destination The destination buffer. This needs to be at least large
* enough to contain all elements.
*/
void copy(std::span<GLVertex2D> destination);
/**
* Append a WindowVertex as a geometry vertex.
*
* WindowVertex is assumed to be in logical coordinates. It will be converted
* to device coordinates using the specified device scale and then rounded
* so it fits correctly on the device pixel grid.
*
* @param windowVertex The WindowVertex instance to append.
* @param deviceScale The scaling factor to use to go from logical to device
* coordinates.
*/
void appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale);
/**
* Append a WindowQuad as two triangles.
*
* This will append the corners of the specified WindowQuad in the right
* order so they make two triangles that can be rendered by OpenGL. The
* corners are converted to device coordinates and rounded, just like
* `appendWindowVertex()` does.
*
* @param quad The WindowQuad instance to append.
* @param deviceScale The scaling factor to use to go from logical to device
* coordinates.
*/
void appendWindowQuad(const WindowQuad &quad, qreal deviceScale);
/**
* Append a sub-quad of a WindowQuad as two triangles.
*
* This will append the sub-quad specified by `intersection` as two
* triangles. The quad is expected to be in logical coordinates, while the
* intersection is expected to be in device coordinates. The texture
* coordinates of the resulting vertices are based upon those of the quad,
* using bilinear interpolation for interpolating how much of the original
* texture coordinates to use.
*
* @param quad The WindowQuad instance to use a sub-quad of.
* @param subquad The sub-quad to append.
* @param deviceScale The scaling factor used to convert from logical to
* device coordinates.
*/
void appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale);
/**
* Modify this geometry's texture coordinates based on a matrix.
*
* This is primarily intended to convert from non-normalised to normalised
* texture coordinates.
*
* @param textureMatrix The texture matrix to use for modifying the
* texture coordinates. Note that only the 2D scale and
* translation are used.
*/
void postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix);
private:
VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round;
};
/**
* Pointer to the global EffectsHandler object.
*/
extern KWIN_EXPORT EffectsHandler *effects;
/***************************************************************
WindowVertex
***************************************************************/
inline WindowVertex::WindowVertex()
: px(0)
, py(0)
, tx(0)
, ty(0)
{
}
inline WindowVertex::WindowVertex(double _x, double _y, double _tx, double _ty)
: px(_x)
, py(_y)
, tx(_tx)
, ty(_ty)
{
}
inline WindowVertex::WindowVertex(const QPointF &position, const QPointF &texturePosition)
: px(position.x())
, py(position.y())
, tx(texturePosition.x())
, ty(texturePosition.y())
{
}
inline void WindowVertex::move(double x, double y)
{
px = x;
py = y;
}
inline void WindowVertex::setX(double x)
{
px = x;
}
inline void WindowVertex::setY(double y)
{
py = y;
}
/***************************************************************
WindowQuad
***************************************************************/
inline WindowQuad::WindowQuad()
{
}
inline WindowVertex &WindowQuad::operator[](int index)
{
Q_ASSERT(index >= 0 && index < 4);
return verts[index];
}
inline const WindowVertex &WindowQuad::operator[](int index) const
{
Q_ASSERT(index >= 0 && index < 4);
return verts[index];
}
inline double WindowQuad::left() const
{
return std::min(verts[0].px, std::min(verts[1].px, std::min(verts[2].px, verts[3].px)));
}
inline double WindowQuad::right() const
{
return std::max(verts[0].px, std::max(verts[1].px, std::max(verts[2].px, verts[3].px)));
}
inline double WindowQuad::top() const
{
return std::min(verts[0].py, std::min(verts[1].py, std::min(verts[2].py, verts[3].py)));
}
inline double WindowQuad::bottom() const
{
return std::max(verts[0].py, std::max(verts[1].py, std::max(verts[2].py, verts[3].py)));
}
inline QRectF WindowQuad::bounds() const
{
return QRectF(QPointF(left(), top()), QPointF(right(), bottom()));
}
/***************************************************************
EffectWindow
***************************************************************/

View file

@ -8,6 +8,7 @@
#include "core/output.h"
#include "core/rendertarget.h"
#include "core/renderviewport.h"
#include "effect/effects.h"
#include "opengl/gltexture.h"
#include "opengl/glutils.h"

View file

@ -6,7 +6,8 @@
#pragma once
#include "effect/effects.h"
#include "effect/effect.h"
#include "scene/itemgeometry.h"
namespace KWin
{

View file

@ -9,6 +9,7 @@
*/
#include "group.h"
#include "effect/effects.h"
#include "workspace.h"
#include "x11window.h"

View file

@ -15,6 +15,7 @@
#include "backends/libinput/device.h"
#include "core/inputbackend.h"
#include "core/session.h"
#include "effect/effects.h"
#include "gestures.h"
#include "globalshortcuts.h"
#include "hide_cursor_spy.h"

View file

@ -9,6 +9,7 @@
#pragma once
#include "effect/effects.h"
#include "effect/offscreeneffect.h"
namespace KWin

View file

@ -9,6 +9,7 @@
*/
#include "highlightwindow.h"
#include "effect/effects.h"
#include <QDBusConnection>

View file

@ -10,6 +10,7 @@
#include "invert.h"
#include "effect/effects.h"
#include "opengl/glplatform.h"
#include "opengl/glutils.h"
#include <KGlobalAccel>

View file

@ -9,6 +9,7 @@
#pragma once
#include "effect/effects.h"
#include "effect/offscreeneffect.h"
#include "effect/timeline.h"

View file

@ -12,6 +12,7 @@
#include "core/renderloop.h"
#include "core/rendertarget.h"
#include "core/renderviewport.h"
#include "effect/effect.h"
#include "opengl/gltexture.h"
#include "opengl/glutils.h"
#include "scene/itemrenderer.h"

View file

@ -8,6 +8,7 @@
*/
#include "wobblywindows.h"
#include "effect/effects.h"
#include "wobblywindowsconfig.h"
#include <cmath>

View file

@ -16,6 +16,7 @@
#include "core/output.h"
#include "cursorsource.h"
#include "decorations/decoratedclient.h"
#include "effect/effects.h"
#include "input_event.h"
#include "input_event_spy.h"
#include "mousebuttons.h"

View file

@ -7,6 +7,7 @@
#include "scene/cursoritem.h"
#include "cursor.h"
#include "cursorsource.h"
#include "effect/effect.h"
#include "scene/imageitem.h"
#include "scene/itemrenderer.h"
#include "scene/scene.h"

View file

@ -12,6 +12,7 @@ namespace KWin
{
class ImageItem;
class SurfaceInterface;
class SurfaceItemWayland;
class CursorItem : public Item

View file

@ -8,6 +8,7 @@
#include "core/output.h"
#include "core/rendertarget.h"
#include "core/renderviewport.h"
#include "effect/effect.h"
#include "scene/cursoritem.h"
#include "scene/itemrenderer.h"

View file

@ -14,6 +14,7 @@ namespace KWin
{
class DragAndDropIcon;
class SurfaceInterface;
class SurfaceItemWayland;
class PresentationFeedback;

View file

@ -8,6 +8,8 @@
#include "scene/item.h"
#include <QImage>
namespace KWin
{

View file

@ -7,8 +7,7 @@
#pragma once
#include "core/colorspace.h"
#include "effect/effects.h"
#include "effect/globals.h"
#include "scene/itemgeometry.h"
#include <QList>
#include <QMatrix4x4>

301
src/scene/itemgeometry.cpp Normal file
View file

@ -0,0 +1,301 @@
/*
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "scene/itemgeometry.h"
#include "effect/globals.h"
#include <QMatrix4x4>
namespace KWin
{
WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const
{
Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom());
WindowQuad ret(*this);
// vertices are clockwise starting from topleft
ret.verts[0].px = x1;
ret.verts[3].px = x1;
ret.verts[1].px = x2;
ret.verts[2].px = x2;
ret.verts[0].py = y1;
ret.verts[1].py = y1;
ret.verts[2].py = y2;
ret.verts[3].py = y2;
const double xOrigin = left();
const double yOrigin = top();
const double widthReciprocal = 1 / (right() - xOrigin);
const double heightReciprocal = 1 / (bottom() - yOrigin);
for (int i = 0; i < 4; ++i) {
const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal;
const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal;
// Use bilinear interpolation to compute the texture coords.
ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx + w1 * (1 - w2) * verts[1].tx + w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx;
ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty + w1 * (1 - w2) * verts[1].ty + w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty;
}
return ret;
}
WindowQuadList WindowQuadList::splitAtX(double x) const
{
WindowQuadList ret;
ret.reserve(count());
for (const WindowQuad &quad : *this) {
bool wholeleft = true;
bool wholeright = true;
for (int i = 0; i < 4; ++i) {
if (quad[i].x() < x) {
wholeright = false;
}
if (quad[i].x() > x) {
wholeleft = false;
}
}
if (wholeleft || wholeright) { // is whole in one split part
ret.append(quad);
continue;
}
if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
ret.append(quad);
continue;
}
ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom()));
ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom()));
}
return ret;
}
WindowQuadList WindowQuadList::splitAtY(double y) const
{
WindowQuadList ret;
ret.reserve(count());
for (const WindowQuad &quad : *this) {
bool wholetop = true;
bool wholebottom = true;
for (int i = 0; i < 4; ++i) {
if (quad[i].y() < y) {
wholebottom = false;
}
if (quad[i].y() > y) {
wholetop = false;
}
}
if (wholetop || wholebottom) { // is whole in one split part
ret.append(quad);
continue;
}
if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
ret.append(quad);
continue;
}
ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y));
ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom()));
}
return ret;
}
WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const
{
if (empty()) {
return *this;
}
// Find the bounding rectangle
double left = first().left();
double right = first().right();
double top = first().top();
double bottom = first().bottom();
for (const WindowQuad &quad : std::as_const(*this)) {
left = std::min(left, quad.left());
right = std::max(right, quad.right());
top = std::min(top, quad.top());
bottom = std::max(bottom, quad.bottom());
}
WindowQuadList ret;
for (const WindowQuad &quad : std::as_const(*this)) {
const double quadLeft = quad.left();
const double quadRight = quad.right();
const double quadTop = quad.top();
const double quadBottom = quad.bottom();
// sanity check, see BUG 390953
if (quadLeft == quadRight || quadTop == quadBottom) {
ret.append(quad);
continue;
}
// Compute the top-left corner of the first intersecting grid cell
const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize;
const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize;
// Loop over all intersecting cells and add sub-quads
for (double y = yBegin; y < quadBottom; y += maxQuadSize) {
const double y0 = std::max(y, quadTop);
const double y1 = std::min(quadBottom, y + maxQuadSize);
for (double x = xBegin; x < quadRight; x += maxQuadSize) {
const double x0 = std::max(x, quadLeft);
const double x1 = std::min(quadRight, x + maxQuadSize);
ret.append(quad.makeSubQuad(x0, y0, x1, y1));
}
}
}
return ret;
}
WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const
{
if (empty()) {
return *this;
}
// Find the bounding rectangle
double left = first().left();
double right = first().right();
double top = first().top();
double bottom = first().bottom();
for (const WindowQuad &quad : *this) {
left = std::min(left, quad.left());
right = std::max(right, quad.right());
top = std::min(top, quad.top());
bottom = std::max(bottom, quad.bottom());
}
double xIncrement = (right - left) / xSubdivisions;
double yIncrement = (bottom - top) / ySubdivisions;
WindowQuadList ret;
for (const WindowQuad &quad : *this) {
const double quadLeft = quad.left();
const double quadRight = quad.right();
const double quadTop = quad.top();
const double quadBottom = quad.bottom();
// sanity check, see BUG 390953
if (quadLeft == quadRight || quadTop == quadBottom) {
ret.append(quad);
continue;
}
// Compute the top-left corner of the first intersecting grid cell
const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement;
const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement;
// Loop over all intersecting cells and add sub-quads
for (double y = yBegin; y < quadBottom; y += yIncrement) {
const double y0 = std::max(y, quadTop);
const double y1 = std::min(quadBottom, y + yIncrement);
for (double x = xBegin; x < quadRight; x += xIncrement) {
const double x0 = std::max(x, quadLeft);
const double x1 = std::min(quadRight, x + xIncrement);
ret.append(quad.makeSubQuad(x0, y0, x1, y1));
}
}
}
return ret;
}
void RenderGeometry::copy(std::span<GLVertex2D> destination)
{
Q_ASSERT(int(destination.size()) >= size());
std::copy(cbegin(), cend(), destination.begin());
}
void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale)
{
GLVertex2D glVertex;
switch (m_vertexSnappingMode) {
case VertexSnappingMode::None:
glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale;
break;
case VertexSnappingMode::Round:
glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale);
break;
}
glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v());
append(glVertex);
}
void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale)
{
// Geometry assumes we're rendering triangles, so add the quad's
// vertices as two triangles. Vertex order is top-left, bottom-left,
// top-right followed by top-right, bottom-left, bottom-right.
appendWindowVertex(quad[0], deviceScale);
appendWindowVertex(quad[3], deviceScale);
appendWindowVertex(quad[1], deviceScale);
appendWindowVertex(quad[1], deviceScale);
appendWindowVertex(quad[3], deviceScale);
appendWindowVertex(quad[2], deviceScale);
}
void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale)
{
std::array<GLVertex2D, 4> vertices;
vertices[0].position = QVector2D(subquad.topLeft());
vertices[1].position = QVector2D(subquad.topRight());
vertices[2].position = QVector2D(subquad.bottomRight());
vertices[3].position = QVector2D(subquad.bottomLeft());
const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)),
QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))};
const QPointF origin = deviceQuad.topLeft();
const QSizeF size = deviceQuad.size();
#pragma GCC unroll 4
for (int i = 0; i < 4; ++i) {
const double weight1 = (vertices[i].position.x() - origin.x()) / size.width();
const double weight2 = (vertices[i].position.y() - origin.y()) / size.height();
const double oneMinW1 = 1.0 - weight1;
const double oneMinW2 = 1.0 - weight2;
const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u()
+ weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u();
const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v()
+ weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v();
vertices[i].texcoord = QVector2D(u, v);
}
append(vertices[0]);
append(vertices[3]);
append(vertices[1]);
append(vertices[1]);
append(vertices[3]);
append(vertices[2]);
}
void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix)
{
if (!textureMatrix.isIdentity()) {
const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1));
const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3));
for (auto &vertex : (*this)) {
vertex.texcoord = vertex.texcoord * coeff + offset;
}
}
}
} // namespace KWin

285
src/scene/itemgeometry.h Normal file
View file

@ -0,0 +1,285 @@
/*
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "opengl/glvertexbuffer.h"
namespace KWin
{
/**
* @short Vertex class
*
* A vertex is one position in a window. WindowQuad consists of four WindowVertex objects
* and represents one part of a window.
*/
class KWIN_EXPORT WindowVertex
{
public:
WindowVertex();
WindowVertex(const QPointF &position, const QPointF &textureCoordinate);
WindowVertex(double x, double y, double tx, double ty);
double x() const
{
return px;
}
double y() const
{
return py;
}
double u() const
{
return tx;
}
double v() const
{
return ty;
}
void move(double x, double y);
void setX(double x);
void setY(double y);
private:
friend class WindowQuad;
friend class WindowQuadList;
double px, py; // position
double tx, ty; // texture coords
};
/**
* @short Class representing one area of a window.
*
* WindowQuads consists of four WindowVertex objects and represents one part of a window.
*/
// NOTE: This class expects the (original) vertices to be in the clockwise order starting from topleft.
class KWIN_EXPORT WindowQuad
{
public:
WindowQuad();
WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const;
WindowVertex &operator[](int index);
const WindowVertex &operator[](int index) const;
double left() const;
double right() const;
double top() const;
double bottom() const;
QRectF bounds() const;
private:
friend class WindowQuadList;
WindowVertex verts[4];
};
class KWIN_EXPORT WindowQuadList
: public QList<WindowQuad>
{
public:
WindowQuadList splitAtX(double x) const;
WindowQuadList splitAtY(double y) const;
WindowQuadList makeGrid(int maxquadsize) const;
WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const;
};
/**
* A helper class for render geometry in device coordinates.
*
* This mostly represents a vector of vertices, with some convenience methods
* for easily converting from WindowQuad and related classes to lists of
* GLVertex2D. This class assumes rendering happens as unindexed triangles.
*/
class KWIN_EXPORT RenderGeometry : public QList<GLVertex2D>
{
public:
/**
* In what way should vertices snap to integer device coordinates?
*
* Vertices are converted to device coordinates before being sent to the
* rendering system. Depending on scaling factors, this may lead to device
* coordinates with fractional parts. For some cases, this may not be ideal
* as fractional coordinates need to be interpolated and can lead to
* "blurry" rendering. To avoid that, we can snap the vertices to integer
* device coordinates when they are added.
*/
enum class VertexSnappingMode {
None, //< No rounding, device coordinates containing fractional parts
// are passed directly to the rendering system.
Round, //< Perform a simple rounding, device coordinates will not have
// any fractional parts.
};
/**
* The vertex snapping mode to use for this geometry.
*
* By default, this is VertexSnappingMode::Round.
*/
inline VertexSnappingMode vertexSnappingMode() const
{
return m_vertexSnappingMode;
}
/**
* Set the vertex snapping mode to use for this geometry.
*
* Note that this doesn't change vertices retroactively, so you should set
* this before adding any vertices, or clear and rebuild the geometry after
* setting it.
*
* @param mode The new rounding mode.
*/
void setVertexSnappingMode(VertexSnappingMode mode)
{
m_vertexSnappingMode = mode;
}
/**
* Copy geometry data into another buffer.
*
* This is primarily intended for copying into a vertex buffer for rendering.
*
* @param destination The destination buffer. This needs to be at least large
* enough to contain all elements.
*/
void copy(std::span<GLVertex2D> destination);
/**
* Append a WindowVertex as a geometry vertex.
*
* WindowVertex is assumed to be in logical coordinates. It will be converted
* to device coordinates using the specified device scale and then rounded
* so it fits correctly on the device pixel grid.
*
* @param windowVertex The WindowVertex instance to append.
* @param deviceScale The scaling factor to use to go from logical to device
* coordinates.
*/
void appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale);
/**
* Append a WindowQuad as two triangles.
*
* This will append the corners of the specified WindowQuad in the right
* order so they make two triangles that can be rendered by OpenGL. The
* corners are converted to device coordinates and rounded, just like
* `appendWindowVertex()` does.
*
* @param quad The WindowQuad instance to append.
* @param deviceScale The scaling factor to use to go from logical to device
* coordinates.
*/
void appendWindowQuad(const WindowQuad &quad, qreal deviceScale);
/**
* Append a sub-quad of a WindowQuad as two triangles.
*
* This will append the sub-quad specified by `intersection` as two
* triangles. The quad is expected to be in logical coordinates, while the
* intersection is expected to be in device coordinates. The texture
* coordinates of the resulting vertices are based upon those of the quad,
* using bilinear interpolation for interpolating how much of the original
* texture coordinates to use.
*
* @param quad The WindowQuad instance to use a sub-quad of.
* @param subquad The sub-quad to append.
* @param deviceScale The scaling factor used to convert from logical to
* device coordinates.
*/
void appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale);
/**
* Modify this geometry's texture coordinates based on a matrix.
*
* This is primarily intended to convert from non-normalised to normalised
* texture coordinates.
*
* @param textureMatrix The texture matrix to use for modifying the
* texture coordinates. Note that only the 2D scale and
* translation are used.
*/
void postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix);
private:
VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round;
};
inline WindowVertex::WindowVertex()
: px(0)
, py(0)
, tx(0)
, ty(0)
{
}
inline WindowVertex::WindowVertex(double _x, double _y, double _tx, double _ty)
: px(_x)
, py(_y)
, tx(_tx)
, ty(_ty)
{
}
inline WindowVertex::WindowVertex(const QPointF &position, const QPointF &texturePosition)
: px(position.x())
, py(position.y())
, tx(texturePosition.x())
, ty(texturePosition.y())
{
}
inline void WindowVertex::move(double x, double y)
{
px = x;
py = y;
}
inline void WindowVertex::setX(double x)
{
px = x;
}
inline void WindowVertex::setY(double y)
{
py = y;
}
inline WindowQuad::WindowQuad()
{
}
inline WindowVertex &WindowQuad::operator[](int index)
{
Q_ASSERT(index >= 0 && index < 4);
return verts[index];
}
inline const WindowVertex &WindowQuad::operator[](int index) const
{
Q_ASSERT(index >= 0 && index < 4);
return verts[index];
}
inline double WindowQuad::left() const
{
return std::min(verts[0].px, std::min(verts[1].px, std::min(verts[2].px, verts[3].px)));
}
inline double WindowQuad::right() const
{
return std::max(verts[0].px, std::max(verts[1].px, std::max(verts[2].px, verts[3].px)));
}
inline double WindowQuad::top() const
{
return std::min(verts[0].py, std::min(verts[1].py, std::min(verts[2].py, verts[3].py)));
}
inline double WindowQuad::bottom() const
{
return std::max(verts[0].py, std::max(verts[1].py, std::max(verts[2].py, verts[3].py)));
}
inline QRectF WindowQuad::bounds() const
{
return QRectF(QPointF(left(), top()), QPointF(right(), bottom()));
}
} // namespace KWin

View file

@ -6,6 +6,7 @@
#include "scene/itemrenderer_qpainter.h"
#include "core/renderviewport.h"
#include "effect/effect.h"
#include "platformsupport/scenes/qpainter/qpaintersurfacetexture.h"
#include "scene/imageitem.h"
#include "scene/workspacescene_qpainter.h"

View file

@ -5,6 +5,7 @@
*/
#include "scene/windowitem.h"
#include "effect/effects.h"
#include "internalwindow.h"
#include "scene/decorationitem.h"
#include "scene/shadowitem.h"

View file

@ -14,6 +14,7 @@
#include "workspace_wrapper.h"
#include "core/output.h"
#include "effect/effects.h"
#include "input.h"
#include "screenedge.h"
#include "workspace.h"

View file

@ -18,6 +18,8 @@
class KConfigLoader;
class KPluginMetaData;
class QAction;
namespace KWin
{

View file

@ -11,6 +11,7 @@
#include "core/renderbackend.h"
#include "core/rendertarget.h"
#include "core/renderviewport.h"
#include "effect/effect.h"
#include "scene/itemrenderer.h"
#include "scene/windowitem.h"
#include "scene/workspacescene.h"