Changeset View
Changeset View
Standalone View
Standalone View
kdecoration/breezedecorationshadow.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> | ||||
3 | * Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> | ||||
4 | * Copyright 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org> | ||||
5 | * Copyright 2020 David Redondo <kde@david-redondo.de> | ||||
6 | * | ||||
7 | * This program is free software; you can redistribute it and/or | ||||
8 | * modify it under the terms of the GNU General Public License as | ||||
9 | * published by the Free Software Foundation; either version 2 of | ||||
10 | * the License or (at your option) version 3 or any later version | ||||
11 | * accepted by the membership of KDE e.V. (or its successor approved | ||||
12 | * by the membership of KDE e.V.), which shall act as a proxy | ||||
13 | * defined in Section 14 of version 3 of the license. | ||||
14 | * | ||||
15 | * This program is distributed in the hope that it will be useful, | ||||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
18 | * GNU General Public License for more details. | ||||
19 | * | ||||
20 | * You should have received a copy of the GNU General Public License | ||||
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
22 | */ | ||||
23 | | ||||
24 | #include "breezeboxshadowrenderer.h" | ||||
25 | #include "breezedecorationshadow.h" | ||||
26 | | ||||
27 | #include <QPainter> | ||||
28 | | ||||
29 | namespace { | ||||
30 | struct ShadowParams { | ||||
31 | ShadowParams() | ||||
32 | : offset(QPoint(0, 0)) | ||||
33 | , radius(0) | ||||
34 | , opacity(0) {} | ||||
35 | | ||||
36 | ShadowParams(const QPoint &offset, int radius, qreal opacity) | ||||
37 | : offset(offset) | ||||
38 | , radius(radius) | ||||
39 | , opacity(opacity) {} | ||||
40 | | ||||
41 | QPoint offset; | ||||
42 | int radius; | ||||
43 | qreal opacity; | ||||
44 | }; | ||||
45 | | ||||
46 | struct CompositeShadowParams { | ||||
47 | CompositeShadowParams() = default; | ||||
48 | | ||||
49 | CompositeShadowParams( | ||||
50 | const QPoint &offset, | ||||
51 | const ShadowParams &shadow1, | ||||
52 | const ShadowParams &shadow2) | ||||
53 | : offset(offset) | ||||
54 | , shadow1(shadow1) | ||||
55 | , shadow2(shadow2) {} | ||||
56 | | ||||
57 | bool isNone() const { | ||||
58 | return qMax(shadow1.radius, shadow2.radius) == 0; | ||||
59 | } | ||||
60 | | ||||
61 | QPoint offset; | ||||
62 | ShadowParams shadow1; | ||||
63 | ShadowParams shadow2; | ||||
64 | }; | ||||
65 | | ||||
66 | const CompositeShadowParams s_shadowParams[] = { | ||||
67 | // None | ||||
68 | CompositeShadowParams(), | ||||
69 | // Small | ||||
70 | CompositeShadowParams( | ||||
71 | QPoint(0, 4), | ||||
72 | ShadowParams(QPoint(0, 0), 16, 1), | ||||
73 | ShadowParams(QPoint(0, -2), 8, 0.4)), | ||||
74 | // Medium | ||||
75 | CompositeShadowParams( | ||||
76 | QPoint(0, 8), | ||||
77 | ShadowParams(QPoint(0, 0), 32, 0.9), | ||||
78 | ShadowParams(QPoint(0, -4), 16, 0.3)), | ||||
79 | // Large | ||||
80 | CompositeShadowParams( | ||||
81 | QPoint(0, 12), | ||||
82 | ShadowParams(QPoint(0, 0), 48, 0.8), | ||||
83 | ShadowParams(QPoint(0, -6), 24, 0.2)), | ||||
84 | // Very large | ||||
85 | CompositeShadowParams( | ||||
86 | QPoint(0, 16), | ||||
87 | ShadowParams(QPoint(0, 0), 64, 0.7), | ||||
88 | ShadowParams(QPoint(0, -8), 32, 0.1)), | ||||
89 | }; | ||||
90 | | ||||
91 | inline CompositeShadowParams lookupShadowParams(int size) | ||||
92 | { | ||||
93 | switch (size) { | ||||
94 | case Breeze::InternalSettings::ShadowNone: | ||||
95 | return s_shadowParams[0]; | ||||
96 | case Breeze::InternalSettings::ShadowSmall: | ||||
97 | return s_shadowParams[1]; | ||||
98 | case Breeze::InternalSettings::ShadowMedium: | ||||
99 | return s_shadowParams[2]; | ||||
100 | case Breeze::InternalSettings::ShadowLarge: | ||||
101 | return s_shadowParams[3]; | ||||
102 | case Breeze::InternalSettings::ShadowVeryLarge: | ||||
103 | return s_shadowParams[4]; | ||||
104 | default: | ||||
105 | // Fallback to the Large size. | ||||
106 | return s_shadowParams[3]; | ||||
107 | } | ||||
108 | }; | ||||
109 | } | ||||
110 | | ||||
111 | using namespace Breeze; | ||||
112 | | ||||
113 | void Shadow::createShadowImage(InternalSettings::EnumShadowSize size, int strength, QColor color) | ||||
114 | { | ||||
115 | if (shadowImage.isNull() | ||||
116 | || s_size != size | ||||
117 | || s_strength != strength | ||||
118 | || s_color != color) | ||||
119 | { | ||||
120 | s_size = size; | ||||
121 | s_strength = strength; | ||||
122 | s_color = color; | ||||
123 | | ||||
124 | const CompositeShadowParams params = lookupShadowParams(size); | ||||
125 | if (params.isNone()) { | ||||
126 | shadowImage = QImage(); | ||||
127 | return; | ||||
128 | } | ||||
129 | | ||||
130 | auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { | ||||
131 | QColor c(color); | ||||
132 | c.setAlphaF(opacity); | ||||
133 | return c; | ||||
134 | }; | ||||
135 | | ||||
136 | const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) | ||||
137 | .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); | ||||
138 | | ||||
139 | BoxShadowRenderer shadowRenderer; | ||||
140 | shadowRenderer.setBorderRadius(Metrics::Frame_FrameRadius + 0.5); | ||||
141 | shadowRenderer.setBoxSize(boxSize); | ||||
142 | shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows? | ||||
143 | | ||||
144 | const qreal strength = static_cast<qreal>(s_strength) / 255.0; | ||||
145 | shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius, | ||||
146 | withOpacity(s_color, params.shadow1.opacity * strength)); | ||||
147 | shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius, | ||||
148 | withOpacity(s_color, params.shadow2.opacity * strength)); | ||||
149 | | ||||
150 | shadowImage = shadowRenderer.render(); | ||||
151 | | ||||
152 | QPainter painter(&shadowImage); | ||||
153 | painter.setRenderHint(QPainter::Antialiasing); | ||||
154 | | ||||
155 | const QRect outerRect = shadowImage.rect(); | ||||
156 | | ||||
157 | QRect boxRect(QPoint(0, 0), boxSize); | ||||
158 | boxRect.moveCenter(outerRect.center()); | ||||
159 | | ||||
160 | // Mask out inner rect. | ||||
161 | padding = QMargins( | ||||
162 | boxRect.left() - outerRect.left() - Metrics::Shadow_Overlap - params.offset.x(), | ||||
163 | boxRect.top() - outerRect.top() - Metrics::Shadow_Overlap - params.offset.y(), | ||||
164 | outerRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x(), | ||||
165 | outerRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y()); | ||||
166 | qDebug() << padding; | ||||
167 | const QRect innerRect = outerRect - padding; | ||||
168 | | ||||
169 | painter.setPen(Qt::NoPen); | ||||
170 | painter.setBrush(Qt::black); | ||||
171 | painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); | ||||
172 | painter.drawRoundedRect( | ||||
173 | innerRect, | ||||
174 | Metrics::Frame_FrameRadius + 0.5, | ||||
175 | Metrics::Frame_FrameRadius + 0.5); | ||||
176 | | ||||
177 | // Draw outline. | ||||
178 | painter.setPen(withOpacity(s_color, 0.2 * strength)); | ||||
179 | painter.setBrush(Qt::NoBrush); | ||||
180 | painter.setCompositionMode(QPainter::CompositionMode_SourceOver); | ||||
181 | painter.drawRoundedRect( | ||||
182 | innerRect, | ||||
183 | Metrics::Frame_FrameRadius - 0.5, | ||||
184 | Metrics::Frame_FrameRadius - 0.5); | ||||
185 | | ||||
186 | painter.end(); | ||||
187 | } | ||||
188 | } | ||||
189 | | ||||
190 | Shadow::Shadow(InternalSettings::EnumShadowSize size, int strength, QColor color) | ||||
191 | { | ||||
192 | createShadowImage(size, strength, color); | ||||
193 | setPadding(padding); | ||||
194 | setInnerShadowRect(QRect(shadowImage.rect().center(), QSize(1, 1))); | ||||
195 | setShadow(shadowImage); | ||||
196 | } | ||||
197 | | ||||
198 | void Shadow::setEdges(bool isRightEdge, bool isLeftEdge, bool isBottomEdge, bool isTopEdge) | ||||
199 | { | ||||
200 | QRect innerRect = QRect(shadowImage.rect().center(), QSize(1, 1)); | ||||
201 | QRect shadowRect(shadowImage.rect()); | ||||
202 | QMargins padding = Shadow::padding; | ||||
203 | if (isRightEdge) { | ||||
204 | shadowRect.setRight(innerRect.right()); | ||||
205 | padding.setRight(0); | ||||
206 | } | ||||
207 | if (isLeftEdge) { | ||||
208 | shadowRect.setLeft(innerRect.left()); | ||||
209 | innerRect.moveLeft(0); | ||||
210 | padding.setLeft(0); | ||||
211 | } | ||||
212 | if (isBottomEdge) { | ||||
213 | shadowRect.setBottom(innerRect.bottom()); | ||||
214 | padding.setBottom(0); | ||||
215 | } | ||||
216 | if (isTopEdge) { | ||||
217 | shadowRect.setTop(innerRect.top()); | ||||
218 | innerRect.moveTop(0); | ||||
219 | padding.setTop(0); | ||||
220 | } | ||||
221 | setInnerShadowRect(innerRect); | ||||
222 | setPadding(padding); | ||||
223 | setShadow(shadowImage.copy(shadowRect)); | ||||
224 | } | ||||
225 | | ||||
226 | |