File indexing completed on 2024-11-10 04:57:15
0001 /* 0002 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "scene/shadowitem.h" 0008 #include "compositor.h" 0009 #include "scene/workspacescene.h" 0010 #include "shadow.h" 0011 #include "window.h" 0012 0013 namespace KWin 0014 { 0015 0016 ShadowTextureProvider::ShadowTextureProvider(Shadow *shadow) 0017 : m_shadow(shadow) 0018 { 0019 } 0020 0021 ShadowTextureProvider::~ShadowTextureProvider() 0022 { 0023 } 0024 0025 ShadowItem::ShadowItem(Shadow *shadow, Window *window, Scene *scene, Item *parent) 0026 : Item(scene, parent) 0027 , m_window(window) 0028 , m_shadow(shadow) 0029 , m_textureProvider(Compositor::self()->scene()->createShadowTextureProvider(shadow)) 0030 { 0031 connect(shadow, &Shadow::offsetChanged, this, &ShadowItem::updateGeometry); 0032 connect(shadow, &Shadow::rectChanged, this, &ShadowItem::updateGeometry); 0033 connect(shadow, &Shadow::textureChanged, this, &ShadowItem::handleTextureChanged); 0034 0035 updateGeometry(); 0036 handleTextureChanged(); 0037 } 0038 0039 ShadowItem::~ShadowItem() 0040 { 0041 } 0042 0043 Shadow *ShadowItem::shadow() const 0044 { 0045 return m_shadow; 0046 } 0047 0048 ShadowTextureProvider *ShadowItem::textureProvider() const 0049 { 0050 return m_textureProvider.get(); 0051 } 0052 0053 void ShadowItem::updateGeometry() 0054 { 0055 const QRectF rect = m_shadow->rect() + m_shadow->offset(); 0056 0057 setPosition(rect.topLeft()); 0058 setSize(rect.size()); 0059 discardQuads(); 0060 } 0061 0062 void ShadowItem::handleTextureChanged() 0063 { 0064 scheduleRepaint(rect()); 0065 discardQuads(); 0066 m_textureDirty = true; 0067 } 0068 0069 static inline void distributeHorizontally(QRectF &leftRect, QRectF &rightRect) 0070 { 0071 if (leftRect.right() > rightRect.left()) { 0072 const qreal boundedRight = std::min(leftRect.right(), rightRect.right()); 0073 const qreal boundedLeft = std::max(leftRect.left(), rightRect.left()); 0074 const qreal halfOverlap = (boundedRight - boundedLeft) / 2.0; 0075 leftRect.setRight(boundedRight - halfOverlap); 0076 rightRect.setLeft(boundedLeft + halfOverlap); 0077 } 0078 } 0079 0080 static inline void distributeVertically(QRectF &topRect, QRectF &bottomRect) 0081 { 0082 if (topRect.bottom() > bottomRect.top()) { 0083 const qreal boundedBottom = std::min(topRect.bottom(), bottomRect.bottom()); 0084 const qreal boundedTop = std::max(topRect.top(), bottomRect.top()); 0085 const qreal halfOverlap = (boundedBottom - boundedTop) / 2.0; 0086 topRect.setBottom(boundedBottom - halfOverlap); 0087 bottomRect.setTop(boundedTop + halfOverlap); 0088 } 0089 } 0090 0091 WindowQuadList ShadowItem::buildQuads() const 0092 { 0093 // Do not draw shadows if window width or window height is less than 5 px. 5 is an arbitrary choice. 0094 if (!m_window->wantsShadowToBeRendered() || m_window->width() < 5 || m_window->height() < 5) { 0095 return WindowQuadList(); 0096 } 0097 0098 const QSizeF top(m_shadow->elementSize(Shadow::ShadowElementTop)); 0099 const QSizeF topRight(m_shadow->elementSize(Shadow::ShadowElementTopRight)); 0100 const QSizeF right(m_shadow->elementSize(Shadow::ShadowElementRight)); 0101 const QSizeF bottomRight(m_shadow->elementSize(Shadow::ShadowElementBottomRight)); 0102 const QSizeF bottom(m_shadow->elementSize(Shadow::ShadowElementBottom)); 0103 const QSizeF bottomLeft(m_shadow->elementSize(Shadow::ShadowElementBottomLeft)); 0104 const QSizeF left(m_shadow->elementSize(Shadow::ShadowElementLeft)); 0105 const QSizeF topLeft(m_shadow->elementSize(Shadow::ShadowElementTopLeft)); 0106 0107 const QMarginsF shadowMargins( 0108 std::max({topLeft.width(), left.width(), bottomLeft.width()}), 0109 std::max({topLeft.height(), top.height(), topRight.height()}), 0110 std::max({topRight.width(), right.width(), bottomRight.width()}), 0111 std::max({bottomRight.height(), bottom.height(), bottomLeft.height()})); 0112 0113 const QRectF outerRect = rect(); 0114 0115 const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right(); 0116 const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom(); 0117 0118 QRectF topLeftRect; 0119 if (!topLeft.isEmpty()) { 0120 topLeftRect = QRectF(outerRect.topLeft(), topLeft); 0121 } else { 0122 topLeftRect = QRectF(outerRect.left() + shadowMargins.left(), 0123 outerRect.top() + shadowMargins.top(), 0, 0); 0124 } 0125 0126 QRectF topRightRect; 0127 if (!topRight.isEmpty()) { 0128 topRightRect = QRectF(outerRect.right() - topRight.width(), outerRect.top(), 0129 topRight.width(), topRight.height()); 0130 } else { 0131 topRightRect = QRectF(outerRect.right() - shadowMargins.right(), 0132 outerRect.top() + shadowMargins.top(), 0, 0); 0133 } 0134 0135 QRectF bottomRightRect; 0136 if (!bottomRight.isEmpty()) { 0137 bottomRightRect = QRectF(outerRect.right() - bottomRight.width(), 0138 outerRect.bottom() - bottomRight.height(), 0139 bottomRight.width(), bottomRight.height()); 0140 } else { 0141 bottomRightRect = QRectF(outerRect.right() - shadowMargins.right(), 0142 outerRect.bottom() - shadowMargins.bottom(), 0, 0); 0143 } 0144 0145 QRectF bottomLeftRect; 0146 if (!bottomLeft.isEmpty()) { 0147 bottomLeftRect = QRectF(outerRect.left(), outerRect.bottom() - bottomLeft.height(), 0148 bottomLeft.width(), bottomLeft.height()); 0149 } else { 0150 bottomLeftRect = QRectF(outerRect.left() + shadowMargins.left(), 0151 outerRect.bottom() - shadowMargins.bottom(), 0, 0); 0152 } 0153 0154 // Re-distribute the corner tiles so no one of them is overlapping with others. 0155 // By doing this, we assume that shadow's corner tiles are symmetric 0156 // and it is OK to not draw top/right/bottom/left tile between corners. 0157 // For example, let's say top-left and top-right tiles are overlapping. 0158 // In that case, the right side of the top-left tile will be shifted to left, 0159 // the left side of the top-right tile will shifted to right, and the top 0160 // tile won't be rendered. 0161 distributeHorizontally(topLeftRect, topRightRect); 0162 distributeHorizontally(bottomLeftRect, bottomRightRect); 0163 distributeVertically(topLeftRect, bottomLeftRect); 0164 distributeVertically(topRightRect, bottomRightRect); 0165 0166 qreal tx1 = 0.0, 0167 tx2 = 0.0, 0168 ty1 = 0.0, 0169 ty2 = 0.0; 0170 0171 WindowQuadList quads; 0172 quads.reserve(8); 0173 0174 if (topLeftRect.isValid()) { 0175 tx1 = 0.0; 0176 ty1 = 0.0; 0177 tx2 = topLeftRect.width(); 0178 ty2 = topLeftRect.height(); 0179 WindowQuad topLeftQuad; 0180 topLeftQuad[0] = WindowVertex(topLeftRect.left(), topLeftRect.top(), tx1, ty1); 0181 topLeftQuad[1] = WindowVertex(topLeftRect.right(), topLeftRect.top(), tx2, ty1); 0182 topLeftQuad[2] = WindowVertex(topLeftRect.right(), topLeftRect.bottom(), tx2, ty2); 0183 topLeftQuad[3] = WindowVertex(topLeftRect.left(), topLeftRect.bottom(), tx1, ty2); 0184 quads.append(topLeftQuad); 0185 } 0186 0187 if (topRightRect.isValid()) { 0188 tx1 = width - topRightRect.width(); 0189 ty1 = 0.0; 0190 tx2 = width; 0191 ty2 = topRightRect.height(); 0192 WindowQuad topRightQuad; 0193 topRightQuad[0] = WindowVertex(topRightRect.left(), topRightRect.top(), tx1, ty1); 0194 topRightQuad[1] = WindowVertex(topRightRect.right(), topRightRect.top(), tx2, ty1); 0195 topRightQuad[2] = WindowVertex(topRightRect.right(), topRightRect.bottom(), tx2, ty2); 0196 topRightQuad[3] = WindowVertex(topRightRect.left(), topRightRect.bottom(), tx1, ty2); 0197 quads.append(topRightQuad); 0198 } 0199 0200 if (bottomRightRect.isValid()) { 0201 tx1 = width - bottomRightRect.width(); 0202 tx2 = width; 0203 ty1 = height - bottomRightRect.height(); 0204 ty2 = height; 0205 WindowQuad bottomRightQuad; 0206 bottomRightQuad[0] = WindowVertex(bottomRightRect.left(), bottomRightRect.top(), tx1, ty1); 0207 bottomRightQuad[1] = WindowVertex(bottomRightRect.right(), bottomRightRect.top(), tx2, ty1); 0208 bottomRightQuad[2] = WindowVertex(bottomRightRect.right(), bottomRightRect.bottom(), tx2, ty2); 0209 bottomRightQuad[3] = WindowVertex(bottomRightRect.left(), bottomRightRect.bottom(), tx1, ty2); 0210 quads.append(bottomRightQuad); 0211 } 0212 0213 if (bottomLeftRect.isValid()) { 0214 tx1 = 0.0; 0215 tx2 = bottomLeftRect.width(); 0216 ty1 = height - bottomLeftRect.height(); 0217 ty2 = height; 0218 WindowQuad bottomLeftQuad; 0219 bottomLeftQuad[0] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.top(), tx1, ty1); 0220 bottomLeftQuad[1] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.top(), tx2, ty1); 0221 bottomLeftQuad[2] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.bottom(), tx2, ty2); 0222 bottomLeftQuad[3] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.bottom(), tx1, ty2); 0223 quads.append(bottomLeftQuad); 0224 } 0225 0226 QRectF topRect(QPointF(topLeftRect.right(), outerRect.top()), 0227 QPointF(topRightRect.left(), outerRect.top() + top.height())); 0228 0229 QRectF rightRect(QPointF(outerRect.right() - right.width(), topRightRect.bottom()), 0230 QPointF(outerRect.right(), bottomRightRect.top())); 0231 0232 QRectF bottomRect(QPointF(bottomLeftRect.right(), outerRect.bottom() - bottom.height()), 0233 QPointF(bottomRightRect.left(), outerRect.bottom())); 0234 0235 QRectF leftRect(QPointF(outerRect.left(), topLeftRect.bottom()), 0236 QPointF(outerRect.left() + left.width(), bottomLeftRect.top())); 0237 0238 // Re-distribute left/right and top/bottom shadow tiles so they don't 0239 // overlap when the window is too small. Please notice that we don't 0240 // fix overlaps between left/top(left/bottom, right/top, and so on) 0241 // corner tiles because corresponding counter parts won't be valid when 0242 // the window is too small, which means they won't be rendered. 0243 distributeHorizontally(leftRect, rightRect); 0244 distributeVertically(topRect, bottomRect); 0245 0246 if (topRect.isValid()) { 0247 tx1 = shadowMargins.left(); 0248 ty1 = 0.0; 0249 tx2 = tx1 + top.width(); 0250 ty2 = topRect.height(); 0251 WindowQuad topQuad; 0252 topQuad[0] = WindowVertex(topRect.left(), topRect.top(), tx1, ty1); 0253 topQuad[1] = WindowVertex(topRect.right(), topRect.top(), tx2, ty1); 0254 topQuad[2] = WindowVertex(topRect.right(), topRect.bottom(), tx2, ty2); 0255 topQuad[3] = WindowVertex(topRect.left(), topRect.bottom(), tx1, ty2); 0256 quads.append(topQuad); 0257 } 0258 0259 if (rightRect.isValid()) { 0260 tx1 = width - rightRect.width(); 0261 ty1 = shadowMargins.top(); 0262 tx2 = width; 0263 ty2 = ty1 + right.height(); 0264 WindowQuad rightQuad; 0265 rightQuad[0] = WindowVertex(rightRect.left(), rightRect.top(), tx1, ty1); 0266 rightQuad[1] = WindowVertex(rightRect.right(), rightRect.top(), tx2, ty1); 0267 rightQuad[2] = WindowVertex(rightRect.right(), rightRect.bottom(), tx2, ty2); 0268 rightQuad[3] = WindowVertex(rightRect.left(), rightRect.bottom(), tx1, ty2); 0269 quads.append(rightQuad); 0270 } 0271 0272 if (bottomRect.isValid()) { 0273 tx1 = shadowMargins.left(); 0274 ty1 = height - bottomRect.height(); 0275 tx2 = tx1 + bottom.width(); 0276 ty2 = height; 0277 WindowQuad bottomQuad; 0278 bottomQuad[0] = WindowVertex(bottomRect.left(), bottomRect.top(), tx1, ty1); 0279 bottomQuad[1] = WindowVertex(bottomRect.right(), bottomRect.top(), tx2, ty1); 0280 bottomQuad[2] = WindowVertex(bottomRect.right(), bottomRect.bottom(), tx2, ty2); 0281 bottomQuad[3] = WindowVertex(bottomRect.left(), bottomRect.bottom(), tx1, ty2); 0282 quads.append(bottomQuad); 0283 } 0284 0285 if (leftRect.isValid()) { 0286 tx1 = 0.0; 0287 ty1 = shadowMargins.top(); 0288 tx2 = leftRect.width(); 0289 ty2 = ty1 + left.height(); 0290 WindowQuad leftQuad; 0291 leftQuad[0] = WindowVertex(leftRect.left(), leftRect.top(), tx1, ty1); 0292 leftQuad[1] = WindowVertex(leftRect.right(), leftRect.top(), tx2, ty1); 0293 leftQuad[2] = WindowVertex(leftRect.right(), leftRect.bottom(), tx2, ty2); 0294 leftQuad[3] = WindowVertex(leftRect.left(), leftRect.bottom(), tx1, ty2); 0295 quads.append(leftQuad); 0296 } 0297 0298 return quads; 0299 } 0300 0301 void ShadowItem::preprocess() 0302 { 0303 if (m_textureDirty) { 0304 m_textureDirty = false; 0305 m_textureProvider->update(); 0306 } 0307 } 0308 0309 } // namespace KWin 0310 0311 #include "moc_shadowitem.cpp"