|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbCore module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbvgreflectioneffect_p.h" |
|
27 #include "hbvgreflectioneffect_p_p.h" |
|
28 #include "hbvgcolorizeeffect_p_p.h" // for getColorMatrix |
|
29 #include "hbvgblureffect_p_p.h" // for identityLUT |
|
30 #include <QPainter> |
|
31 #include <QGraphicsItem> |
|
32 #include <QDebug> |
|
33 |
|
34 /*! |
|
35 * \class HbVgReflectionEffect |
|
36 * |
|
37 * \brief OpenVG-based reflection effect. |
|
38 * |
|
39 * \internal |
|
40 */ |
|
41 |
|
42 HbVgReflectionEffectPrivate::HbVgReflectionEffectPrivate() |
|
43 : fade(0), fadeInited(false) |
|
44 { |
|
45 } |
|
46 |
|
47 HbVgReflectionEffectPrivate::~HbVgReflectionEffectPrivate() |
|
48 { |
|
49 #ifdef HB_EFFECTS_OPENVG |
|
50 if (fadeInited) { |
|
51 vgDestroyPaint(fadePaint); |
|
52 } |
|
53 #endif |
|
54 } |
|
55 |
|
56 HbVgReflectionEffect::HbVgReflectionEffect(QObject *parent) |
|
57 : HbVgFrameEffect(*new HbVgReflectionEffectPrivate, parent) |
|
58 { |
|
59 } |
|
60 |
|
61 HbVgReflectionEffect::HbVgReflectionEffect(HbVgReflectionEffectPrivate &dd, QObject *parent) |
|
62 : HbVgFrameEffect(dd, parent) |
|
63 { |
|
64 } |
|
65 |
|
66 HbVgReflectionEffect::~HbVgReflectionEffect() |
|
67 { |
|
68 } |
|
69 |
|
70 QPointF HbVgReflectionEffect::offset() const |
|
71 { |
|
72 Q_D(const HbVgReflectionEffect); |
|
73 return d->offset; |
|
74 } |
|
75 |
|
76 void HbVgReflectionEffect::setOffset(const QPointF &offset) |
|
77 { |
|
78 Q_D(HbVgReflectionEffect); |
|
79 if (offset == d->offset) |
|
80 return; |
|
81 d->offset = offset; |
|
82 updateEffectBoundingRect(); |
|
83 emit offsetChanged(offset); |
|
84 } |
|
85 |
|
86 qreal HbVgReflectionEffect::fade() const |
|
87 { |
|
88 Q_D(const HbVgReflectionEffect); |
|
89 return d->fade; |
|
90 } |
|
91 |
|
92 /*! |
|
93 * The fade parameter tells where the opacity in the gradual fade-out effect reaches zero, |
|
94 * i.e. total transparency, when moving upwards from the bottom of the reflection. The |
|
95 * default 0 is a special value, it disables the fade-out effect on the reflection |
|
96 * completely. Examples: 0.5 causes the lower half of the reflection to be completely |
|
97 * invisible (and the upper half linearly faded), 0.9 causes almost the entire reflection |
|
98 * to be invisible, 1 shows nothing from the reflection (it is completely faded out). |
|
99 */ |
|
100 void HbVgReflectionEffect::setFade(qreal fade) |
|
101 { |
|
102 Q_D(HbVgReflectionEffect); |
|
103 if (fade == d->fade) |
|
104 return; |
|
105 d->fade = fade; |
|
106 updateEffect(); |
|
107 emit fadeChanged(fade); |
|
108 } |
|
109 |
|
110 QColor HbVgReflectionEffect::color() const |
|
111 { |
|
112 Q_D(const HbVgReflectionEffect); |
|
113 return d->color; |
|
114 } |
|
115 |
|
116 void HbVgReflectionEffect::setColor(const QColor &color) |
|
117 { |
|
118 Q_D(HbVgReflectionEffect); |
|
119 if (color == d->color) |
|
120 return; |
|
121 d->color = color; |
|
122 updateEffect(); |
|
123 emit colorChanged(color); |
|
124 } |
|
125 |
|
126 QRectF HbVgReflectionEffect::boundingRectFor(const QRectF &rect) const |
|
127 { |
|
128 // Double the height of the rectangle but take also the offset into account. |
|
129 Q_D(const HbVgReflectionEffect); |
|
130 QRectF r(rect); |
|
131 QPointF mappedOffset = d->mapOffset(d->offset); |
|
132 qreal rotationAngle = d->mainWindowRotation(); |
|
133 |
|
134 if (rotationAngle == 0) |
|
135 r.adjust(0, 0, 0, r.height()); |
|
136 else if (rotationAngle == 90 || rotationAngle == -270) |
|
137 r.adjust(-r.width(), 0, 0, 0); |
|
138 else if (rotationAngle == -90 || rotationAngle == 270) |
|
139 r.adjust(0, 0, r.width(), 0); |
|
140 |
|
141 qreal x1 = qMin(r.left(), r.left() + mappedOffset.x()); |
|
142 qreal y1 = qMin(r.top(), r.top() + mappedOffset.y()); |
|
143 qreal x2 = qMax(r.right(), r.right() + mappedOffset.x()); |
|
144 qreal y2 = qMax(r.bottom(), r.bottom() + mappedOffset.y()); |
|
145 |
|
146 return QRectF(x1, y1, x2 - x1 + 1, y2 - y1 + 1); |
|
147 } |
|
148 |
|
149 void HbVgReflectionEffect::performEffect(QPainter *painter, |
|
150 const QPointF &offset, |
|
151 const QVariant &vgImage, |
|
152 const QSize &vgImageSize) |
|
153 { |
|
154 #ifdef HB_EFFECTS_OPENVG |
|
155 Q_D(HbVgReflectionEffect); |
|
156 |
|
157 if (d->hints & ForceFrameHint) { |
|
158 painter->save(); |
|
159 HbVgFrameEffect::performEffect(painter, offset, vgImage, vgImageSize); |
|
160 painter->restore(); |
|
161 } |
|
162 |
|
163 QPaintDevice *pdev = painter->device(); |
|
164 QRectF rectWithChildren = d->deviceRectForSource( |
|
165 HbVgFrameEffectPrivate::IncludeChildren, |
|
166 pdev); |
|
167 QRectF rectWithoutChildren = d->deviceRectForSource( |
|
168 HbVgFrameEffectPrivate::ExcludeChildren, |
|
169 pdev); |
|
170 VGImage srcImage = vgImage.value<VGImage>(); |
|
171 VGImage dstImage = d->ensurePixmap(&d->dstPixmap, vgImageSize); |
|
172 |
|
173 // Draw the source pixmap using the painter, this will also set up the |
|
174 // IMAGE_USER_TO_SURFACE matrix. |
|
175 painter->drawPixmap(offset, d->srcPixmap); |
|
176 |
|
177 // Prepare the mirrored image. |
|
178 qreal rotationAngle = d->mainWindowRotation(); |
|
179 qreal absRotationAngle = qAbs(rotationAngle); |
|
180 |
|
181 VGfloat m[9]; |
|
182 vgGetMatrix(m); |
|
183 vgLoadIdentity(); |
|
184 if (absRotationAngle == 0) |
|
185 m[4] *= -1.0f; |
|
186 else if (absRotationAngle == 90 || absRotationAngle == 270) |
|
187 m[0] *= -1.0f; |
|
188 vgMultMatrix(m); |
|
189 |
|
190 // Must move the mirrored image to have it on top of the original and then down |
|
191 // again to have it below in portrait-mode. Rotation angles -90 or 270 causes image to be moved to right, |
|
192 // and in rotation angles -90 and 270, image is in correct place initially. |
|
193 // Try to take the exclude-children hint into account when performing the second move. |
|
194 |
|
195 VGfloat trans; |
|
196 if (absRotationAngle == 0) { |
|
197 if (d->hints & ExcludeChildrenHint) |
|
198 trans = -rectWithChildren.height() - rectWithoutChildren.height(); |
|
199 else |
|
200 trans = -2.0f * rectWithChildren.height(); |
|
201 |
|
202 vgTranslate(0.0f, trans); |
|
203 } |
|
204 else if (absRotationAngle == 90 || absRotationAngle == 270) { |
|
205 if (d->hints & ExcludeChildrenHint) |
|
206 trans = -rectWithChildren.width() - rectWithoutChildren.width(); |
|
207 else |
|
208 trans = -2.0f * rectWithChildren.width(); |
|
209 |
|
210 vgTranslate(trans, 0.0f); |
|
211 } |
|
212 |
|
213 // Apply the additional offset. Note: down = minus, right = plus. |
|
214 QPointF mappedOffset = d->mapOffset(d->offset); |
|
215 VGfloat ox = (VGfloat) mappedOffset.x(); |
|
216 VGfloat oy = (VGfloat) mappedOffset.y(); |
|
217 |
|
218 if (rotationAngle == 0) |
|
219 vgTranslate(ox, -oy); |
|
220 else if (rotationAngle == 90 || rotationAngle == -270) |
|
221 vgTranslate(-ox, oy); |
|
222 else if (rotationAngle == -90 || rotationAngle == 270) |
|
223 vgTranslate(-ox, oy); |
|
224 |
|
225 // Apply the opacity and the color. When no color was set and the opacity is 1, the |
|
226 // source image will be used as it is. This is the only place where we can try to use |
|
227 // the pixmap cache. |
|
228 VGImage imgToDraw = srcImage; |
|
229 QPixmap cachedPm = cached(vgImageSize); |
|
230 if (cachedPm.isNull()) { |
|
231 VGImage tmpImage = VG_INVALID_HANDLE; |
|
232 if (d->color.isValid()) { |
|
233 // Perform a colorize effect (ignore the opacity here because it must be set for |
|
234 // the full image, not just the color overlay). |
|
235 tmpImage = d->ensurePixmap(&d->tmpPixmap, vgImageSize); |
|
236 VGfloat colorMatrix[20]; |
|
237 HbVgColorizeEffectPrivate::getColorMatrix(colorMatrix, d->color, 1.0f); |
|
238 vgColorMatrix(tmpImage, srcImage, colorMatrix); |
|
239 imgToDraw = tmpImage; |
|
240 } |
|
241 qreal opacity = clamp(d->opacity, 0.0f, 1.0f); |
|
242 if (d->opacity < 1.0f - HBVG_EPSILON) { |
|
243 // Apply the opacity, i.e. modify the alpha channel. |
|
244 if (d->paramsChanged) { |
|
245 for (int i = 0; i < 256; ++i) |
|
246 d->alphaLUT[i] = (VGubyte) (i * opacity); |
|
247 } |
|
248 vgLookup(dstImage, imgToDraw, |
|
249 identityLUT, identityLUT, identityLUT, d->alphaLUT, |
|
250 VG_TRUE, VG_FALSE); |
|
251 imgToDraw = dstImage; |
|
252 } |
|
253 // If colorize and/or opacity was used then try to cache the result. |
|
254 if (imgToDraw == tmpImage) |
|
255 tryCache(d->tmpPixmap); |
|
256 else if (imgToDraw == dstImage) |
|
257 tryCache(d->dstPixmap); |
|
258 } else { |
|
259 imgToDraw = qPixmapToVGImage(cachedPm); |
|
260 } |
|
261 |
|
262 // Fade out the lower part of the mirrored image. Skip this if 'fade' is 0, i.e. there |
|
263 // is no fade-out effect. |
|
264 VGfloat fade = (VGfloat) clamp(d->fade, 0.0f, 1.0f); |
|
265 if (fade > HBVG_EPSILON) { |
|
266 // Prepare the linear gradient used for fading if not yet done. |
|
267 if (!d->fadeInited) { |
|
268 d->fadePaint = vgCreatePaint(); |
|
269 vgSetParameteri(d->fadePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); |
|
270 vgSetParameteri(d->fadePaint, |
|
271 VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_REFLECT); |
|
272 d->fadeInited = true; |
|
273 } |
|
274 |
|
275 if (d->paramsChanged) { |
|
276 // Prepare the color ramp, take the value of 'fade' into account. (It tells |
|
277 // where the opacity reaches zero, i.e. total transparency.) The RGB values |
|
278 // are set to 1 because the mirrored image's RGB values must not be changed, |
|
279 // only the alpha channel needs a little manipulation (to get the gradual |
|
280 // fade-out effect). |
|
281 VGfloat stops[] = { |
|
282 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, |
|
283 1.0f - fade, 1.0f, 1.0f, 1.0f, 0.0f, |
|
284 1.0f, 1.0f, 1.0f, 1.0f, 0.0f |
|
285 }; |
|
286 vgSetParameterfv(d->fadePaint, VG_PAINT_COLOR_RAMP_STOPS, 15, stops); |
|
287 } |
|
288 |
|
289 // Set up the linear gradient based on the (transformed) size of the source. |
|
290 VGfloat sw = (VGfloat) rectWithChildren.width(); |
|
291 VGfloat sh = (VGfloat) rectWithChildren.height(); |
|
292 // must be bottom-up to get the proper effect |
|
293 if (rotationAngle == 0) { |
|
294 VGfloat grad[] = { sw / 2.0f, sh, |
|
295 sw / 2.0f, 0.0f }; |
|
296 vgSetParameterfv(d->fadePaint, VG_PAINT_LINEAR_GRADIENT, 4, grad); |
|
297 } |
|
298 else if (rotationAngle == -90 || rotationAngle == 270){ |
|
299 VGfloat grad[] = { sw, sh / 2.0f, |
|
300 0.0f, sh / 2.0f }; |
|
301 vgSetParameterfv(d->fadePaint, VG_PAINT_LINEAR_GRADIENT, 4, grad); |
|
302 } |
|
303 else if (rotationAngle == 90 || rotationAngle == -270){ |
|
304 VGfloat grad[] = { 0.0f, sh / 2.0f, |
|
305 sw, sh / 2.0f }; |
|
306 vgSetParameterfv(d->fadePaint, VG_PAINT_LINEAR_GRADIENT, 4, grad); |
|
307 } |
|
308 |
|
309 // Draw the mirrored image by using the paint to get a gradual fade-out effect. |
|
310 vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); |
|
311 vgLoadIdentity(); |
|
312 vgSetPaint(d->fadePaint, VG_FILL_PATH); |
|
313 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY); |
|
314 vgDrawImage(imgToDraw); |
|
315 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); |
|
316 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
317 vgSetPaint(VG_INVALID_HANDLE, VG_FILL_PATH); |
|
318 } else { |
|
319 // Just draw the mirrored image normally. |
|
320 vgDrawImage(imgToDraw); |
|
321 } |
|
322 |
|
323 #else |
|
324 Q_UNUSED(painter); |
|
325 Q_UNUSED(offset); |
|
326 Q_UNUSED(vgImage); |
|
327 Q_UNUSED(vgImageSize); |
|
328 #endif |
|
329 } |