File indexing completed on 2024-05-19 09:43:13
0001 /***************************************************************************** 0002 * Copyright 2007 - 2010 Craig Drummond <craig.p.drummond@gmail.com> * 0003 * Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU Lesser General Public License as * 0007 * published by the Free Software Foundation; either version 2.1 of the * 0008 * License, or (at your option) version 3, or any later version accepted * 0009 * by the membership of KDE e.V. (or its successor approved by the * 0010 * membership of KDE e.V.), which shall act as a proxy defined in * 0011 * Section 6 of version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, * 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 0016 * Lesser General Public License for more details. * 0017 * * 0018 * You should have received a copy of the GNU Lesser General Public * 0019 * License along with this library. If not, * 0020 * see <http://www.gnu.org/licenses/>. * 0021 *****************************************************************************/ 0022 0023 ////////////////////////////////////////////////////////////////////////////// 0024 // Taken from: oxygenshadowcache.cpp 0025 // handles caching of TileSet objects to draw shadows 0026 // ------------------- 0027 // 0028 // Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0029 // 0030 // Permission is hereby granted, free of charge, to any person obtaining a copy 0031 // of this software and associated documentation files (the "Software"), to 0032 // deal in the Software without restriction, including without limitation the 0033 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 0034 // sell copies of the Software, and to permit persons to whom the Software is 0035 // furnished to do so, subject to the following conditions: 0036 // 0037 // The above copyright notice and this permission notice shall be included in 0038 // all copies or substantial portions of the Software. 0039 // 0040 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0041 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0042 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0043 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0044 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 0045 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 0046 // IN THE SOFTWARE. 0047 ////////////////////////////////////////////////////////////////////////////// 0048 0049 #include <kdeversion.h> 0050 0051 #include <KColorUtils> 0052 #include <KColorScheme> 0053 #include <QPainter> 0054 0055 #include <style/qtcurve.h> 0056 0057 #include "qtcurveshadowcache.h" 0058 #include "qtcurveclient.h" 0059 #include "qtcurvehandler.h" 0060 0061 namespace QtCurve { 0062 namespace KWin { 0063 0064 static bool lowThreshold(const QColor &color) 0065 { 0066 QColor darker = KColorScheme::shade(color, KColorScheme::MidShade, 0.5); 0067 return KColorUtils::luma(darker) > KColorUtils::luma(color); 0068 } 0069 0070 static QColor backgroundTopColor(const QColor &color) 0071 { 0072 0073 if(lowThreshold(color)) return KColorScheme::shade(color, KColorScheme::MidlightShade, 0.0); 0074 qreal my = KColorUtils::luma(KColorScheme::shade(color, KColorScheme::LightShade, 0.0)); 0075 qreal by = KColorUtils::luma(color); 0076 return KColorUtils::shade(color, (my - by) * 0.9/*_bgcontrast*/); 0077 0078 } 0079 0080 static QColor backgroundBottomColor(const QColor &color) 0081 { 0082 QColor midColor = KColorScheme::shade(color, KColorScheme::MidShade, 0.0); 0083 if(lowThreshold(color)) return midColor; 0084 0085 qreal by = KColorUtils::luma(color); 0086 qreal my = KColorUtils::luma(midColor); 0087 return KColorUtils::shade(color, (my - by) * 0.9/*_bgcontrast*/ * 0.85); 0088 } 0089 0090 static QColor calcLightColor(const QColor &color) 0091 { 0092 return KColorScheme::shade(color, KColorScheme::LightShade, 0.7/*_contrast*/); 0093 } 0094 0095 QtCurveShadowCache::QtCurveShadowCache() 0096 : m_activeShadowConfig(ShadowConfig(QPalette::Active)) 0097 , m_inactiveShadowConfig(ShadowConfig(QPalette::Inactive)) 0098 { 0099 m_shadowCache.setMaxCost(1<<6); 0100 } 0101 0102 bool QtCurveShadowCache::shadowConfigChanged(const ShadowConfig &other) const 0103 { 0104 const auto &local = (other.colorGroup() == QPalette::Active ? 0105 m_activeShadowConfig : m_inactiveShadowConfig); 0106 return !(local == other); 0107 } 0108 0109 void QtCurveShadowCache::setShadowConfig(const ShadowConfig &other) 0110 { 0111 auto &local = (other.colorGroup() == QPalette::Active ? 0112 m_activeShadowConfig : m_inactiveShadowConfig); 0113 local = other; 0114 reset(); 0115 } 0116 0117 TileSet* 0118 QtCurveShadowCache::tileSet(const QtCurveClient *client, bool roundAllCorners) 0119 { 0120 Key key(client); 0121 int hash(key.hash()); 0122 0123 if (m_shadowCache.contains(hash)) 0124 return m_shadowCache.object(hash); 0125 0126 qreal size(shadowSize()); 0127 TileSet *tileSet = new TileSet(shadowPixmap(client, key.active, roundAllCorners), size, size, 1, 1); 0128 0129 m_shadowCache.insert(hash, tileSet); 0130 return tileSet; 0131 } 0132 0133 QPixmap QtCurveShadowCache::shadowPixmap(const QtCurveClient *client, bool active, bool roundAllCorners) const 0134 { 0135 Key key(client); 0136 QPalette palette(client->widget()->palette()); 0137 QColor color(palette.color(client->widget()->backgroundRole())); 0138 0139 return simpleShadowPixmap(color, active, roundAllCorners); 0140 } 0141 0142 QPixmap QtCurveShadowCache::simpleShadowPixmap(const QColor &color, bool active, bool roundAllCorners) const 0143 { 0144 static const qreal fixedSize = 25.5; 0145 0146 const ShadowConfig &shadowConfig(active ? m_activeShadowConfig : m_inactiveShadowConfig); 0147 0148 // offsets are scaled with the shadow size 0149 // so that the ratio Top-shadow/Bottom-shadow is kept constant when shadow size is changed 0150 qreal size(shadowSize()), 0151 shadowSize(shadowConfig.shadowSize()); 0152 QPixmap shadow(size*2, size*2); 0153 0154 shadow.fill(Qt::transparent); 0155 0156 QPainter p(&shadow); 0157 0158 p.setRenderHint(QPainter::Antialiasing); 0159 p.setPen(Qt::NoPen); 0160 0161 if (shadowSize) { 0162 if (ShadowConfig::SH_ACTIVE == shadowConfig.shadowType()) { 0163 { 0164 // inner (shark) gradient 0165 const qreal gradientSize = qMin(shadowSize, 0166 (shadowSize + fixedSize) / 2); 0167 const qreal hoffset = (shadowConfig.horizontalOffset() / 100. * 0168 gradientSize / fixedSize); 0169 const qreal voffset = (shadowConfig.verticalOffset() / 100. * 0170 gradientSize / fixedSize); 0171 0172 QRadialGradient rg = QRadialGradient(size + 12. * hoffset, 0173 size + 12. * voffset, 0174 gradientSize); 0175 rg.setColorAt(1, Qt::transparent); 0176 0177 // gaussian shadow is used 0178 int nPoints(10 * gradientSize / fixedSize); 0179 Gaussian f(0.85, 0.25); 0180 QColor c = shadowConfig.innerColor(); 0181 for (int i = 0;i < nPoints;i++) { 0182 qreal x = qreal(i) / nPoints; 0183 c.setAlphaF(f(x)); 0184 rg.setColorAt(x, c); 0185 } 0186 0187 p.setBrush(rg); 0188 renderGradient(p, shadow.rect(), rg, roundAllCorners); 0189 } 0190 0191 { 0192 // outer (spread) gradient 0193 const qreal gradientSize = shadowSize; 0194 const qreal hoffset = (shadowConfig.horizontalOffset() / 100. * 0195 gradientSize / fixedSize); 0196 const qreal voffset = (shadowConfig.verticalOffset() / 100. * 0197 gradientSize / fixedSize); 0198 0199 QRadialGradient rg = QRadialGradient(size + 12. * hoffset, 0200 size + 12. * voffset, 0201 gradientSize); 0202 rg.setColorAt(1, Qt::transparent); 0203 0204 // gaussian shadow is used 0205 int nPoints(10 * gradientSize / fixedSize); 0206 Gaussian f(0.46, 0.42); 0207 QColor c = shadowConfig.outerColor(); 0208 for (int i = 0;i < nPoints;i++) { 0209 qreal x = qreal(i) / nPoints; 0210 c.setAlphaF(f(x)); 0211 rg.setColorAt(x, c); 0212 } 0213 p.setBrush(rg); 0214 p.drawRect(shadow.rect()); 0215 } 0216 } else { 0217 { 0218 // inner (sharp gradient) 0219 const qreal gradientSize = qMin(shadowSize, fixedSize); 0220 const qreal hoffset = (shadowConfig.horizontalOffset() / 100. * 0221 gradientSize / fixedSize); 0222 const qreal voffset = (shadowConfig.verticalOffset() / 100. * 0223 gradientSize / fixedSize); 0224 0225 QRadialGradient rg = QRadialGradient(size + hoffset, 0226 size + voffset, 0227 gradientSize); 0228 rg.setColorAt(1, Qt::transparent); 0229 0230 // parabolic shadow is used 0231 int nPoints(10 * gradientSize / fixedSize); 0232 Parabolic f(0.85, 0.22); 0233 QColor c = shadowConfig.outerColor(); 0234 for (int i = 0;i < nPoints;i++) { 0235 qreal x = qreal(i) / nPoints; 0236 c.setAlphaF(f(x)); 0237 rg.setColorAt(x, c); 0238 } 0239 0240 p.setBrush(rg); 0241 renderGradient(p, shadow.rect(), rg, roundAllCorners); 0242 } 0243 0244 { 0245 // mid gradient 0246 const qreal gradientSize = qMin(shadowSize, 0247 (shadowSize + 0248 2 * fixedSize) / 3); 0249 const qreal hoffset = (shadowConfig.horizontalOffset() / 100. * 0250 gradientSize / fixedSize); 0251 const qreal voffset = (shadowConfig.verticalOffset() / 100. * 0252 gradientSize / fixedSize); 0253 0254 // gaussian shadow is used 0255 QRadialGradient rg = QRadialGradient(size + 8. * hoffset, 0256 size + 8. * voffset, 0257 gradientSize); 0258 rg.setColorAt(1, Qt::transparent); 0259 0260 int nPoints(10 * gradientSize / fixedSize); 0261 Gaussian f(0.54, 0.21); 0262 QColor c = shadowConfig.outerColor(); 0263 for (int i = 0;i < nPoints;i++) { 0264 qreal x = qreal(i) / nPoints; 0265 c.setAlphaF(f(x)); 0266 rg.setColorAt(x, c); 0267 } 0268 0269 p.setBrush(rg); 0270 p.drawRect(shadow.rect()); 0271 } 0272 { 0273 // outer (spread) gradient 0274 const qreal gradientSize = shadowSize; 0275 const qreal hoffset = (shadowConfig.horizontalOffset() / 100. * 0276 gradientSize / fixedSize); 0277 const qreal voffset = (shadowConfig.verticalOffset() / 100. * 0278 gradientSize / fixedSize); 0279 0280 // gaussian shadow is used 0281 QRadialGradient rg = QRadialGradient(size + 20. * hoffset, 0282 size + 20. * voffset, 0283 gradientSize); 0284 rg.setColorAt(1, Qt::transparent); 0285 0286 int nPoints(20 * gradientSize / fixedSize); 0287 Gaussian f(0.155, 0.445); 0288 QColor c = shadowConfig.outerColor(); 0289 for (int i = 0;i < nPoints;i++) { 0290 qreal x = qreal(i) / nPoints; 0291 c.setAlphaF(f(x)); 0292 rg.setColorAt(x, c); 0293 } 0294 0295 p.setBrush(rg); 0296 p.drawRect(shadow.rect()); 0297 } 0298 } 0299 } 0300 0301 // draw the corner of the window - actually all 4 corners as one circle 0302 // this is all fixedSize. Does not scale with shadow size 0303 QLinearGradient lg = QLinearGradient(0.0, size-4.5, 0.0, size+4.5); 0304 lg.setColorAt(0.0, calcLightColor(backgroundTopColor(color))); 0305 lg.setColorAt(0.51, backgroundBottomColor(color)); 0306 lg.setColorAt(1.0, backgroundBottomColor(color)); 0307 0308 p.setBrush(lg); 0309 p.drawEllipse(QRectF(size-4, size-4, 8, 8)); 0310 p.end(); 0311 return shadow; 0312 } 0313 0314 void QtCurveShadowCache::renderGradient(QPainter &p, const QRectF &rect, const QRadialGradient &rg, bool hasBorder) const 0315 { 0316 if( hasBorder ) 0317 { 0318 p.setBrush( rg ); 0319 p.drawRect( rect ); 0320 return; 0321 } 0322 0323 0324 qreal size(rect.width() / 2.0), 0325 hoffset(rg.center().x() - size), 0326 voffset(rg.center().y() - size), 0327 radius(rg.radius()); 0328 QGradientStops stops(rg.stops()); 0329 0330 // draw ellipse for the upper rect 0331 { 0332 QRectF rect(hoffset, voffset, 2*size-hoffset, size); 0333 p.setBrush(rg); 0334 p.drawRect(rect); 0335 } 0336 0337 // draw square gradients for the lower rect 0338 { 0339 // vertical lines 0340 QRectF rect(hoffset, size+voffset, 2*size-hoffset, 4); 0341 QLinearGradient lg(hoffset, 0.0, 2*size+hoffset, 0.0); 0342 0343 for(int i = 0; i<stops.size(); i++) 0344 { 0345 QColor c(stops[i].second); 0346 qreal xx(stops[i].first*radius); 0347 0348 lg.setColorAt((size - xx) / (2. * size), c); 0349 lg.setColorAt((size + xx) / (2. * size), c); 0350 } 0351 0352 p.setBrush(lg); 0353 p.drawRect(rect); 0354 } 0355 0356 { 0357 // horizontal line 0358 QRectF rect(size-4+hoffset, size+voffset, 8, size); 0359 QLinearGradient lg = QLinearGradient(0, voffset, 0, 2*size+voffset); 0360 0361 for(int i = 0; i<stops.size(); i++) 0362 { 0363 QColor c(stops[i].second); 0364 qreal xx(stops[i].first*radius); 0365 0366 lg.setColorAt((size + xx) / (2. * size), c); 0367 } 0368 0369 p.setBrush(lg); 0370 p.drawRect(rect); 0371 } 0372 0373 { 0374 // bottom-left corner 0375 QRectF rect(hoffset, size+4+voffset, size-4, size); 0376 QRadialGradient rg = QRadialGradient(size+hoffset-4, size+4+voffset, radius); 0377 0378 for(int i = 0; i<stops.size(); i++) 0379 { 0380 QColor c(stops[i].second); 0381 qreal xx(stops[i].first -4.0/rg.radius()); 0382 0383 if(xx<0 && i < stops.size()-1) 0384 { 0385 qreal x1(stops[i+1].first -4.0/rg.radius()); 0386 c = KColorUtils::mix(c, stops[i+1].second, -xx/(x1-xx)); 0387 xx = 0; 0388 } 0389 0390 rg.setColorAt(xx, c); 0391 } 0392 0393 p.setBrush(rg); 0394 p.drawRect(rect); 0395 } 0396 0397 { 0398 // bottom-right corner 0399 QRectF rect(size+4+hoffset, size+4+voffset, size-4, size); 0400 QRadialGradient rg = QRadialGradient(size+hoffset+4, size+4+voffset, radius); 0401 0402 for(int i = 0; i<stops.size(); i++) 0403 { 0404 QColor c(stops[i].second); 0405 qreal xx(stops[i].first -4.0/rg.radius()); 0406 0407 if(xx<0 && i < stops.size()-1) 0408 { 0409 qreal x1(stops[i+1].first -4.0/rg.radius()); 0410 c = KColorUtils::mix(c, stops[i+1].second, -xx/(x1-xx)); 0411 xx = 0; 0412 } 0413 0414 rg.setColorAt(xx, c); 0415 } 0416 0417 p.setBrush(rg); 0418 p.drawRect(rect); 0419 } 0420 } 0421 0422 QtCurveShadowCache::Key::Key(const QtCurveClient *client) 0423 : active(client->isActive()) 0424 , isShade(client->isShade()) 0425 { 0426 } 0427 0428 } 0429 }