File indexing completed on 2024-11-10 04:57:14
0001 /* 0002 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org> 0003 SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "scene/itemgeometry.h" 0009 #include "effect/globals.h" 0010 0011 #include <QMatrix4x4> 0012 0013 namespace KWin 0014 { 0015 0016 WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const 0017 { 0018 Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom()); 0019 WindowQuad ret(*this); 0020 // vertices are clockwise starting from topleft 0021 ret.verts[0].px = x1; 0022 ret.verts[3].px = x1; 0023 ret.verts[1].px = x2; 0024 ret.verts[2].px = x2; 0025 ret.verts[0].py = y1; 0026 ret.verts[1].py = y1; 0027 ret.verts[2].py = y2; 0028 ret.verts[3].py = y2; 0029 0030 const double xOrigin = left(); 0031 const double yOrigin = top(); 0032 0033 const double widthReciprocal = 1 / (right() - xOrigin); 0034 const double heightReciprocal = 1 / (bottom() - yOrigin); 0035 0036 for (int i = 0; i < 4; ++i) { 0037 const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal; 0038 const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal; 0039 0040 // Use bilinear interpolation to compute the texture coords. 0041 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; 0042 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; 0043 } 0044 0045 return ret; 0046 } 0047 0048 WindowQuadList WindowQuadList::splitAtX(double x) const 0049 { 0050 WindowQuadList ret; 0051 ret.reserve(count()); 0052 for (const WindowQuad &quad : *this) { 0053 bool wholeleft = true; 0054 bool wholeright = true; 0055 for (int i = 0; i < 4; ++i) { 0056 if (quad[i].x() < x) { 0057 wholeright = false; 0058 } 0059 if (quad[i].x() > x) { 0060 wholeleft = false; 0061 } 0062 } 0063 if (wholeleft || wholeright) { // is whole in one split part 0064 ret.append(quad); 0065 continue; 0066 } 0067 if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size 0068 ret.append(quad); 0069 continue; 0070 } 0071 ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom())); 0072 ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom())); 0073 } 0074 return ret; 0075 } 0076 0077 WindowQuadList WindowQuadList::splitAtY(double y) const 0078 { 0079 WindowQuadList ret; 0080 ret.reserve(count()); 0081 for (const WindowQuad &quad : *this) { 0082 bool wholetop = true; 0083 bool wholebottom = true; 0084 for (int i = 0; i < 4; ++i) { 0085 if (quad[i].y() < y) { 0086 wholebottom = false; 0087 } 0088 if (quad[i].y() > y) { 0089 wholetop = false; 0090 } 0091 } 0092 if (wholetop || wholebottom) { // is whole in one split part 0093 ret.append(quad); 0094 continue; 0095 } 0096 if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size 0097 ret.append(quad); 0098 continue; 0099 } 0100 ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y)); 0101 ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom())); 0102 } 0103 return ret; 0104 } 0105 0106 WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const 0107 { 0108 if (empty()) { 0109 return *this; 0110 } 0111 0112 // Find the bounding rectangle 0113 double left = first().left(); 0114 double right = first().right(); 0115 double top = first().top(); 0116 double bottom = first().bottom(); 0117 0118 for (const WindowQuad &quad : std::as_const(*this)) { 0119 left = std::min(left, quad.left()); 0120 right = std::max(right, quad.right()); 0121 top = std::min(top, quad.top()); 0122 bottom = std::max(bottom, quad.bottom()); 0123 } 0124 0125 WindowQuadList ret; 0126 0127 for (const WindowQuad &quad : std::as_const(*this)) { 0128 const double quadLeft = quad.left(); 0129 const double quadRight = quad.right(); 0130 const double quadTop = quad.top(); 0131 const double quadBottom = quad.bottom(); 0132 0133 // sanity check, see BUG 390953 0134 if (quadLeft == quadRight || quadTop == quadBottom) { 0135 ret.append(quad); 0136 continue; 0137 } 0138 0139 // Compute the top-left corner of the first intersecting grid cell 0140 const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize; 0141 const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize; 0142 0143 // Loop over all intersecting cells and add sub-quads 0144 for (double y = yBegin; y < quadBottom; y += maxQuadSize) { 0145 const double y0 = std::max(y, quadTop); 0146 const double y1 = std::min(quadBottom, y + maxQuadSize); 0147 0148 for (double x = xBegin; x < quadRight; x += maxQuadSize) { 0149 const double x0 = std::max(x, quadLeft); 0150 const double x1 = std::min(quadRight, x + maxQuadSize); 0151 0152 ret.append(quad.makeSubQuad(x0, y0, x1, y1)); 0153 } 0154 } 0155 } 0156 0157 return ret; 0158 } 0159 0160 WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const 0161 { 0162 if (empty()) { 0163 return *this; 0164 } 0165 0166 // Find the bounding rectangle 0167 double left = first().left(); 0168 double right = first().right(); 0169 double top = first().top(); 0170 double bottom = first().bottom(); 0171 0172 for (const WindowQuad &quad : *this) { 0173 left = std::min(left, quad.left()); 0174 right = std::max(right, quad.right()); 0175 top = std::min(top, quad.top()); 0176 bottom = std::max(bottom, quad.bottom()); 0177 } 0178 0179 double xIncrement = (right - left) / xSubdivisions; 0180 double yIncrement = (bottom - top) / ySubdivisions; 0181 0182 WindowQuadList ret; 0183 0184 for (const WindowQuad &quad : *this) { 0185 const double quadLeft = quad.left(); 0186 const double quadRight = quad.right(); 0187 const double quadTop = quad.top(); 0188 const double quadBottom = quad.bottom(); 0189 0190 // sanity check, see BUG 390953 0191 if (quadLeft == quadRight || quadTop == quadBottom) { 0192 ret.append(quad); 0193 continue; 0194 } 0195 0196 // Compute the top-left corner of the first intersecting grid cell 0197 const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement; 0198 const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement; 0199 0200 // Loop over all intersecting cells and add sub-quads 0201 for (double y = yBegin; y < quadBottom; y += yIncrement) { 0202 const double y0 = std::max(y, quadTop); 0203 const double y1 = std::min(quadBottom, y + yIncrement); 0204 0205 for (double x = xBegin; x < quadRight; x += xIncrement) { 0206 const double x0 = std::max(x, quadLeft); 0207 const double x1 = std::min(quadRight, x + xIncrement); 0208 0209 ret.append(quad.makeSubQuad(x0, y0, x1, y1)); 0210 } 0211 } 0212 } 0213 0214 return ret; 0215 } 0216 0217 void RenderGeometry::copy(std::span<GLVertex2D> destination) 0218 { 0219 Q_ASSERT(int(destination.size()) >= size()); 0220 std::copy(cbegin(), cend(), destination.begin()); 0221 } 0222 0223 void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale) 0224 { 0225 GLVertex2D glVertex; 0226 switch (m_vertexSnappingMode) { 0227 case VertexSnappingMode::None: 0228 glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale; 0229 break; 0230 case VertexSnappingMode::Round: 0231 glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale); 0232 break; 0233 } 0234 glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v()); 0235 append(glVertex); 0236 } 0237 0238 void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale) 0239 { 0240 // Geometry assumes we're rendering triangles, so add the quad's 0241 // vertices as two triangles. Vertex order is top-left, bottom-left, 0242 // top-right followed by top-right, bottom-left, bottom-right. 0243 appendWindowVertex(quad[0], deviceScale); 0244 appendWindowVertex(quad[3], deviceScale); 0245 appendWindowVertex(quad[1], deviceScale); 0246 0247 appendWindowVertex(quad[1], deviceScale); 0248 appendWindowVertex(quad[3], deviceScale); 0249 appendWindowVertex(quad[2], deviceScale); 0250 } 0251 0252 void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale) 0253 { 0254 std::array<GLVertex2D, 4> vertices; 0255 vertices[0].position = QVector2D(subquad.topLeft()); 0256 vertices[1].position = QVector2D(subquad.topRight()); 0257 vertices[2].position = QVector2D(subquad.bottomRight()); 0258 vertices[3].position = QVector2D(subquad.bottomLeft()); 0259 0260 const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)), 0261 QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))}; 0262 0263 const QPointF origin = deviceQuad.topLeft(); 0264 const QSizeF size = deviceQuad.size(); 0265 0266 #pragma GCC unroll 4 0267 for (int i = 0; i < 4; ++i) { 0268 const double weight1 = (vertices[i].position.x() - origin.x()) / size.width(); 0269 const double weight2 = (vertices[i].position.y() - origin.y()) / size.height(); 0270 const double oneMinW1 = 1.0 - weight1; 0271 const double oneMinW2 = 1.0 - weight2; 0272 0273 const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u() 0274 + weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u(); 0275 const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v() 0276 + weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v(); 0277 vertices[i].texcoord = QVector2D(u, v); 0278 } 0279 0280 append(vertices[0]); 0281 append(vertices[3]); 0282 append(vertices[1]); 0283 0284 append(vertices[1]); 0285 append(vertices[3]); 0286 append(vertices[2]); 0287 } 0288 0289 void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix) 0290 { 0291 if (!textureMatrix.isIdentity()) { 0292 const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1)); 0293 const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3)); 0294 0295 for (auto &vertex : (*this)) { 0296 vertex.texcoord = vertex.texcoord * coeff + offset; 0297 } 0298 } 0299 } 0300 0301 } // namespace KWin