|
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 "hbvgmaskeffect_p.h" |
|
27 #include "hbvgmaskeffect_p_p.h" |
|
28 #include <QPainter> |
|
29 #include <QPaintEngine> |
|
30 #include <QPaintDevice> |
|
31 |
|
32 /*! |
|
33 * \class HbVgMaskEffect |
|
34 * |
|
35 * \brief OpenVG-based mask effect. |
|
36 * |
|
37 * The mask effect can be used to make a rectangular area or an area |
|
38 * defined by a pixmap's alpha channel transparent. The opacity effect |
|
39 * parameter is ignored. |
|
40 * |
|
41 * \internal |
|
42 */ |
|
43 |
|
44 HbVgMaskEffectPrivate::HbVgMaskEffectPrivate() |
|
45 : maskRectIsInDeviceCoords(false), maskCallback(0) |
|
46 { |
|
47 } |
|
48 |
|
49 HbVgMaskEffect::HbVgMaskEffect(QObject *parent) |
|
50 : HbVgEffect(*new HbVgMaskEffectPrivate, parent) |
|
51 { |
|
52 } |
|
53 |
|
54 HbVgMaskEffect::HbVgMaskEffect(HbVgMaskEffectPrivate &dd, QObject *parent) |
|
55 : HbVgEffect(dd, parent) |
|
56 { |
|
57 } |
|
58 |
|
59 HbVgMaskEffect::~HbVgMaskEffect() |
|
60 { |
|
61 } |
|
62 |
|
63 /*! |
|
64 * Turns off masking. Any previously set mask pixmap or rectangle will not be |
|
65 * effective anymore. |
|
66 */ |
|
67 void HbVgMaskEffect::clear() |
|
68 { |
|
69 Q_D(HbVgMaskEffect); |
|
70 d->mask = d->scaledMask = d->callbackResult = QPixmap(); |
|
71 d->maskRect = QRectF(); |
|
72 d->maskRectIsInDeviceCoords = false; |
|
73 d->maskCallback = 0; |
|
74 d->maskCallbackParam = 0; |
|
75 } |
|
76 |
|
77 /*! |
|
78 * Returns the mask pixmap set by the previous setMask() call. |
|
79 */ |
|
80 QPixmap HbVgMaskEffect::mask() const |
|
81 { |
|
82 Q_D(const HbVgMaskEffect); |
|
83 return d->mask; |
|
84 } |
|
85 |
|
86 /*! |
|
87 * Returns the scaled version of the mask that was used during the previous |
|
88 * paint. Will return a null pixmap if no painting took place since the last |
|
89 * setMask() call. |
|
90 */ |
|
91 QPixmap HbVgMaskEffect::scaledMask() const |
|
92 { |
|
93 Q_D(const HbVgMaskEffect); |
|
94 return d->scaledMask; |
|
95 } |
|
96 |
|
97 /*! |
|
98 * Sets the mask pixmap. Only the alpha channel is relevant. Pixels where alpha |
|
99 * is 0 will be set transparent. The pixmap is subject to scaling and therefore |
|
100 * distortion may occur. If this is not acceptable then use the callback |
|
101 * version. Any previously set mask pixmap or rectangle will not be effective |
|
102 * anymore. |
|
103 */ |
|
104 void HbVgMaskEffect::setMask(const QPixmap &mask) |
|
105 { |
|
106 Q_D(HbVgMaskEffect); |
|
107 clear(); |
|
108 d->mask = mask; |
|
109 updateEffect(); |
|
110 emit maskChanged(mask); |
|
111 } |
|
112 |
|
113 /*! |
|
114 * Returns the currently set mask callback pointer. |
|
115 */ |
|
116 HbVgMaskEffect::MaskCallback HbVgMaskEffect::maskCallback() const |
|
117 { |
|
118 Q_D(const HbVgMaskEffect); |
|
119 return d->maskCallback; |
|
120 } |
|
121 |
|
122 /*! |
|
123 * Returns the currently set custom parameter that will be passed to the |
|
124 * callback. |
|
125 */ |
|
126 void *HbVgMaskEffect::maskCallbackParam() const |
|
127 { |
|
128 Q_D(const HbVgMaskEffect); |
|
129 return d->maskCallbackParam; |
|
130 } |
|
131 |
|
132 /*! |
|
133 * If the user of the API is able to generate a mask pixmap for a given size, |
|
134 * use this function instead of setMask(). The callback will be invoked during |
|
135 * painting to get the mask pixmap. It should return a pixmap that has a size |
|
136 * that is same as the size that has been passed to the callback. The effect |
|
137 * tries to reduce the number of calls by calling only when a new callback is |
|
138 * set or when the size of the source item is different than before. Any |
|
139 * previously set mask pixmap or rectangle will not be effective anymore. |
|
140 */ |
|
141 void HbVgMaskEffect::setMaskCallback(MaskCallback callback, void *param) |
|
142 { |
|
143 Q_D(HbVgMaskEffect); |
|
144 if (d->maskCallback == callback) |
|
145 return; |
|
146 clear(); |
|
147 d->maskCallback = callback; |
|
148 d->maskCallbackParam = param; |
|
149 updateEffect(); |
|
150 emit maskCallbackChanged(callback); |
|
151 } |
|
152 |
|
153 /*! |
|
154 * Returns the mask rectangle set by the previous setMaskRect() call. |
|
155 */ |
|
156 QRectF HbVgMaskEffect::maskRect() const |
|
157 { |
|
158 Q_D(const HbVgMaskEffect); |
|
159 return d->maskRectIsInDeviceCoords ? QRectF() : d->maskRect; |
|
160 } |
|
161 |
|
162 /*! |
|
163 * Returns the mask rectangle set by the previous setMaskDeviceRect() call. |
|
164 */ |
|
165 QRectF HbVgMaskEffect::maskDeviceRect() const |
|
166 { |
|
167 Q_D(const HbVgMaskEffect); |
|
168 return d->maskRectIsInDeviceCoords ? d->maskRect : QRectF(); |
|
169 } |
|
170 |
|
171 /*! |
|
172 * Sets the mask rectangle. The area defined by the rectangle will be made |
|
173 * transparent inside the source item. Any previously set mask pixmap or |
|
174 * rectangle will not be effective anymore. |
|
175 */ |
|
176 void HbVgMaskEffect::setMaskRect(const QRectF &rect) |
|
177 { |
|
178 Q_D(HbVgMaskEffect); |
|
179 if (rect == d->maskRect && !d->maskRectIsInDeviceCoords) |
|
180 return; |
|
181 clear(); |
|
182 d->maskRect = rect; |
|
183 d->maskRectIsInDeviceCoords = false; |
|
184 updateEffect(); |
|
185 emit maskRectChanged(rect); |
|
186 } |
|
187 |
|
188 /*! |
|
189 * Similar to setMask() but the rectangle is assumed to be in device coordinates |
|
190 * (i.e. relative to the entire screen instead of the source item), meaning that |
|
191 * the source item will be clipped where it intersects with \a rect. |
|
192 */ |
|
193 void HbVgMaskEffect::setMaskDeviceRect(const QRectF &rect) |
|
194 { |
|
195 Q_D(HbVgMaskEffect); |
|
196 if (rect == d->maskRect && d->maskRectIsInDeviceCoords) |
|
197 return; |
|
198 clear(); |
|
199 d->maskRect = rect; |
|
200 d->maskRectIsInDeviceCoords = true; |
|
201 updateEffect(); |
|
202 emit maskRectChanged(rect); |
|
203 } |
|
204 |
|
205 /*! |
|
206 * \reimp |
|
207 */ |
|
208 QRectF HbVgMaskEffect::boundingRectFor(const QRectF &rect) const |
|
209 { |
|
210 return rect; |
|
211 } |
|
212 |
|
213 /*! |
|
214 * Returns the OpenVG Y coordinate for a rectangle with the given Qt Y |
|
215 * coordinate and height. |
|
216 * |
|
217 * \internal |
|
218 */ |
|
219 inline int toVgYH(int y, int h, QPaintDevice *pdev) |
|
220 { |
|
221 return pdev->height() - 1 - y - h; |
|
222 } |
|
223 |
|
224 /*! |
|
225 * \reimp |
|
226 */ |
|
227 void HbVgMaskEffect::performEffect(QPainter *painter, |
|
228 const QPointF &offset, |
|
229 const QVariant &vgImage, |
|
230 const QSize &vgImageSize) |
|
231 { |
|
232 #ifdef HB_EFFECTS_OPENVG |
|
233 Q_UNUSED(vgImage); |
|
234 Q_D(HbVgMaskEffect); |
|
235 |
|
236 // Initialize scaledMask if the mask has changed or the size of the source |
|
237 // is different than before. |
|
238 if (!d->mask.isNull()) { |
|
239 if (d->scaledMask.isNull()) |
|
240 d->scaledMask = d->mask; |
|
241 // Scale only when really needed, i.e. when the size is different than |
|
242 // before (or there is a new mask). |
|
243 if (d->scaledMask.size() != vgImageSize) |
|
244 d->scaledMask = d->mask.scaled(vgImageSize); |
|
245 } |
|
246 |
|
247 vgSeti(VG_MASKING, VG_TRUE); |
|
248 QPaintDevice *pdev = painter->paintEngine()->paintDevice(); |
|
249 // Set the mask for the entire surface to 1 (i.e. nothing is transparent). |
|
250 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, |
|
251 0, 0, pdev->width(), pdev->height()); |
|
252 |
|
253 // If there is a pixmap then use that, otherwise use the rectangle. If none |
|
254 // of these is set then try the callback, if that is not set either then |
|
255 // just draw the source normally. |
|
256 QPixmap *maskPtr = 0; |
|
257 int ox = (int) offset.x(); |
|
258 int oy = (int) offset.y(); |
|
259 if (d->scaledMask.isNull() && !d->maskRect.isNull()) { |
|
260 int x1 = (int) d->maskRect.x(); |
|
261 int y1 = (int) d->maskRect.y(); |
|
262 int w = (int) d->maskRect.width(); |
|
263 int h = (int) d->maskRect.height(); |
|
264 if (!d->maskRectIsInDeviceCoords) { |
|
265 x1 += ox; |
|
266 y1 += oy; |
|
267 } |
|
268 // Make the area defined by the rectangle transparent. Passing |
|
269 // VG_CLEAR_MASK results in writing 0 to the mask which results in |
|
270 // transparent pixels at that position. |
|
271 vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, |
|
272 x1, toVgYH(y1, h, pdev), |
|
273 w, h); |
|
274 } else if (!d->scaledMask.isNull()) { |
|
275 maskPtr = &d->scaledMask; |
|
276 } else if (d->maskCallback) { |
|
277 // Invoke the callback but only if it has just been set or the size of |
|
278 // the source is different than before. |
|
279 if (d->callbackResult.isNull() || d->callbackResult.size() != vgImageSize) |
|
280 d->callbackResult = d->maskCallback(vgImageSize, d->maskCallbackParam); |
|
281 maskPtr = &d->callbackResult; |
|
282 } |
|
283 |
|
284 if (maskPtr) { |
|
285 int w = vgImageSize.width(); |
|
286 int h = vgImageSize.height(); |
|
287 // Will use the alpha channel from the image, alpha=0 => 0 in the mask |
|
288 // => transparent pixel, alpha=255 => 1 in the mask => opaque pixel. |
|
289 vgMask(qPixmapToVGImage(*maskPtr), VG_SET_MASK, |
|
290 ox, toVgYH(oy, h, pdev), |
|
291 w, h); |
|
292 } |
|
293 |
|
294 // Draw the source item with masking enabled. |
|
295 painter->drawPixmap(offset, d->srcPixmap); |
|
296 vgSeti(VG_MASKING, VG_FALSE); |
|
297 |
|
298 #else |
|
299 Q_UNUSED(painter); |
|
300 Q_UNUSED(offset); |
|
301 Q_UNUSED(vgImage); |
|
302 Q_UNUSED(vgImageSize); |
|
303 #endif |
|
304 } |