1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtOpenVG module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qpaintengine_vg_p.h"
43 #include "qpixmapdata_vg_p.h"
44 #include "qpixmapfilter_vg_p.h"
45 #include "qvgcompositionhelper_p.h"
46 #include "qvgimagepool_p.h"
47 #include "qvgfontglyphcache_p.h"
48 #if !defined(QT_NO_EGL)
49 #include <QtGui/private/qeglcontext_p.h>
50 #include "qwindowsurface_vgegl_p.h"
51 #endif
52 #include <QtCore/qvarlengtharray.h>
53 #include <QtGui/private/qdrawhelper_p.h>
54 #include <QtGui/private/qtextengine_p.h>
55 #include <QtGui/private/qfontengine_p.h>
56 #include <QtGui/private/qpainterpath_p.h>
57 #include <QtGui/private/qstatictext_p.h>
58 #include <QtGui/QApplication>
59 #include <QtGui/QDesktopWidget>
60 #include <QtCore/qmath.h>
61 #include <QDebug>
62 #include <QSet>
63
64 QT_BEGIN_NAMESPACE
65
66 // vgRenderToMask() only exists in OpenVG 1.1 and higher.
67 // Also, disable masking completely if we are using the scissor to clip.
68 #if !defined(OPENVG_VERSION_1_1) && !defined(QVG_NO_RENDER_TO_MASK)
69 #define QVG_NO_RENDER_TO_MASK 1
70 #endif
71 #if defined(QVG_SCISSOR_CLIP) && !defined(QVG_NO_RENDER_TO_MASK)
72 #define QVG_NO_RENDER_TO_MASK 1
73 #endif
74
75 // use the same rounding as in qrasterizer.cpp (6 bit fixed point)
76 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
77
78 #if !defined(QVG_NO_DRAW_GLYPHS)
79
80 class QVGPaintEnginePrivate;
81
82 typedef QHash<QFontEngine*, QVGFontGlyphCache*> QVGFontCache;
83
84 #endif
85
86 class QVGFontEngineCleaner : public QObject
87 {
88 Q_OBJECT
89 public:
90 QVGFontEngineCleaner(QVGPaintEnginePrivate *d);
91 ~QVGFontEngineCleaner();
92
93 public slots:
94 void fontEngineDestroyed();
95
96 private:
97 QVGPaintEnginePrivate *d_ptr;
98 };
99
100 class QVGPaintEnginePrivate : public QPaintEngineExPrivate
101 {
102 Q_DECLARE_PUBLIC(QVGPaintEngine)
103 public:
104 // Extra blending modes from VG_KHR_advanced_blending extension.
105 // Use the QT_VG prefix to avoid conflicts with any definitions
106 // that may come in via <VG/vgext.h>.
107 enum AdvancedBlending {
108 QT_VG_BLEND_OVERLAY_KHR = 0x2010,
109 QT_VG_BLEND_HARDLIGHT_KHR = 0x2011,
110 QT_VG_BLEND_SOFTLIGHT_SVG_KHR = 0x2012,
111 QT_VG_BLEND_SOFTLIGHT_KHR = 0x2013,
112 QT_VG_BLEND_COLORDODGE_KHR = 0x2014,
113 QT_VG_BLEND_COLORBURN_KHR = 0x2015,
114 QT_VG_BLEND_DIFFERENCE_KHR = 0x2016,
115 QT_VG_BLEND_SUBTRACT_KHR = 0x2017,
116 QT_VG_BLEND_INVERT_KHR = 0x2018,
117 QT_VG_BLEND_EXCLUSION_KHR = 0x2019,
118 QT_VG_BLEND_LINEARDODGE_KHR = 0x201a,
119 QT_VG_BLEND_LINEARBURN_KHR = 0x201b,
120 QT_VG_BLEND_VIVIDLIGHT_KHR = 0x201c,
121 QT_VG_BLEND_LINEARLIGHT_KHR = 0x201d,
122 QT_VG_BLEND_PINLIGHT_KHR = 0x201e,
123 QT_VG_BLEND_HARDMIX_KHR = 0x201f,
124 QT_VG_BLEND_CLEAR_KHR = 0x2020,
125 QT_VG_BLEND_DST_KHR = 0x2021,
126 QT_VG_BLEND_SRC_OUT_KHR = 0x2022,
127 QT_VG_BLEND_DST_OUT_KHR = 0x2023,
128 QT_VG_BLEND_SRC_ATOP_KHR = 0x2024,
129 QT_VG_BLEND_DST_ATOP_KHR = 0x2025,
130 QT_VG_BLEND_XOR_KHR = 0x2026
131 };
132
133 QVGPaintEnginePrivate(QVGPaintEngine *q_ptr);
134 ~QVGPaintEnginePrivate();
135
136 void init();
137 void initObjects();
138 void destroy();
139 void setTransform(VGMatrixMode mode, const QTransform& transform);
140 void updateTransform(QPaintDevice *pdev);
141 void draw(VGPath path, const QPen& pen, const QBrush& brush, VGint rule = VG_EVEN_ODD);
142 void stroke(VGPath path, const QPen& pen);
143 void fill(VGPath path, const QBrush& brush, VGint rule = VG_EVEN_ODD);
144 VGPath vectorPathToVGPath(const QVectorPath& path);
145 VGPath painterPathToVGPath(const QPainterPath& path);
146 VGPath roundedRectPath(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode);
147 VGPaintType setBrush
148 (VGPaint paint, const QBrush& brush, VGMatrixMode mode,
149 VGPaintType prevPaintType);
150 void setPenParams(const QPen& pen);
151 void setBrushTransform(const QBrush& brush, VGMatrixMode mode);
152 void setupColorRamp(const QGradient *grad, VGPaint paint);
153 void setImageOptions();
154 void systemStateChanged();
155 #if !defined(QVG_SCISSOR_CLIP)
156 void ensureMask(QVGPaintEngine *engine, int width, int height);
157 void modifyMask
158 (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region);
159 void modifyMask
160 (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect);
161 #endif
162
163 VGint maxScissorRects; // Maximum scissor rectangles for clipping.
164
165 VGPaint penPaint; // Paint for currently active pen.
166 VGPaint brushPaint; // Paint for currently active brush.
167 VGPaint opacityPaint; // Paint for drawing images with opacity.
168 VGPaint fillPaint; // Current fill paint that is active.
169
170 QPen currentPen; // Current pen set in "penPaint".
171 QBrush currentBrush; // Current brush set in "brushPaint".
172
173 bool forcePenChange; // Force a pen change, even if the same.
174 bool forceBrushChange; // Force a brush change, even if the same.
175
176 bool hasExtendedRadialGradientPen; // Current pen's brush is extended radial gradient.
177 bool hasExtendedRadialGradientBrush; // Current brush is extended radial gradient.
178
179 VGPaintType penType; // Type of the last pen that was set.
180 VGPaintType brushType; // Type of the last brush that was set.
181
182 QPointF brushOrigin; // Current brush origin.
183
184 VGint fillRule; // Last fill rule that was set.
185
186 qreal opacity; // Current drawing opacity.
187 qreal paintOpacity; // Opacity in opacityPaint.
188
189 #if !defined(QVG_NO_MODIFY_PATH)
190 VGPath rectPath; // Cached path for quick drawing of rectangles.
191 VGPath linePath; // Cached path for quick drawing of lines.
192 VGPath roundRectPath; // Cached path for quick drawing of rounded rects.
193 #endif
194
195 QTransform transform; // Currently active transform.
196 bool simpleTransform; // True if the transform is simple (non-projective).
197 qreal penScale; // Pen scaling factor from "transform".
198
199 QTransform pathTransform; // Calculated VG path transformation.
200 QTransform imageTransform; // Calculated VG image transformation.
201 bool pathTransformSet; // True if path transform set in the VG context.
202
203 bool maskValid; // True if vgMask() contains valid data.
204 bool maskIsSet; // True if mask would be fully set if it was valid.
205 bool scissorMask; // True if scissor is used in place of the mask.
206 bool rawVG; // True if processing a raw VG escape.
207
208 QRect maskRect; // Rectangle version of mask if it is simple.
209
210 QTransform penTransform; // Transform for the pen.
211 QTransform brushTransform; // Transform for the brush.
212
213 VGMatrixMode matrixMode; // Last matrix mode that was set.
214 VGImageMode imageMode; // Last image mode that was set.
215
216 QRegion scissorRegion; // Currently active scissor region.
217 bool scissorActive; // True if scissor region is active.
218 bool scissorDirty; // True if scissor is dirty after native painting.
219
220 QPaintEngine::DirtyFlags dirty;
221
222 QColor clearColor; // Last clear color that was set.
223 VGfloat clearOpacity; // Opacity during the last clear.
224
225 VGBlendMode blendMode; // Active blend mode.
226 VGRenderingQuality renderingQuality; // Active rendering quality.
227 VGImageQuality imageQuality; // Active image quality.
228
229 #if !defined(QVG_NO_DRAW_GLYPHS)
230 QVGFontCache fontCache;
231 QVGFontEngineCleaner *fontEngineCleaner;
232 #endif
233
234 bool hasAdvancedBlending;
235
236 QScopedPointer<QPixmapFilter> convolutionFilter;
237 QScopedPointer<QPixmapFilter> colorizeFilter;
238 QScopedPointer<QPixmapFilter> dropShadowFilter;
239 QScopedPointer<QPixmapFilter> blurFilter;
240
241 // Ensure that the path transform is properly set in the VG context
242 // before we perform a vgDrawPath() operation.
ensurePathTransform()243 inline void ensurePathTransform()
244 {
245 if (!pathTransformSet) {
246 QTransform aliasedTransform = pathTransform;
247 if (renderingQuality == VG_RENDERING_QUALITY_NONANTIALIASED && currentPen != Qt::NoPen)
248 aliasedTransform = aliasedTransform
249 * QTransform::fromTranslate(aliasedCoordinateDelta, -aliasedCoordinateDelta);
250 setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, aliasedTransform);
251 pathTransformSet = true;
252 }
253 }
254
255 // Ensure that a specific pen has been set into penPaint.
ensurePen(const QPen & pen)256 inline void ensurePen(const QPen& pen) {
257 if (forcePenChange || pen != currentPen) {
258 currentPen = pen;
259 forcePenChange = false;
260 penType = setBrush
261 (penPaint, pen.brush(),
262 VG_MATRIX_STROKE_PAINT_TO_USER, penType);
263 setPenParams(pen);
264 }
265 }
266
267 // Ensure that a specific brush has been set into brushPaint.
ensureBrush(const QBrush & brush)268 inline void ensureBrush(const QBrush& brush) {
269 if (forceBrushChange || brush != currentBrush) {
270 currentBrush = brush;
271 forceBrushChange = false;
272 brushType = setBrush
273 (brushPaint, brush, VG_MATRIX_FILL_PAINT_TO_USER, brushType);
274 }
275 if (fillPaint != brushPaint) {
276 vgSetPaint(brushPaint, VG_FILL_PATH);
277 fillPaint = brushPaint;
278 }
279 }
280
needsEmulation(const QBrush & brush) const281 inline bool needsEmulation(const QBrush &brush) const
282 {
283 Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush);
284 return qt_isExtendedRadialGradient(brush);
285 }
286
needsEmulation() const287 inline bool needsEmulation() const
288 {
289 return hasExtendedRadialGradientPen || hasExtendedRadialGradientBrush;
290 }
291
needsPenEmulation() const292 inline bool needsPenEmulation() const
293 {
294 return hasExtendedRadialGradientPen;
295 }
296
needsBrushEmulation() const297 inline bool needsBrushEmulation() const
298 {
299 return hasExtendedRadialGradientBrush;
300 }
301
302 // Set various modes, but only if different.
303 inline void setImageMode(VGImageMode mode);
304 inline void setRenderingQuality(VGRenderingQuality mode);
305 inline void setImageQuality(VGImageQuality mode);
306 inline void setBlendMode(VGBlendMode mode);
307 inline void setFillRule(VGint mode);
308
309 // Clear all lazily-set modes.
310 void clearModes();
311
312 private:
313 QVGPaintEngine *q;
314 };
315
setImageMode(VGImageMode mode)316 inline void QVGPaintEnginePrivate::setImageMode(VGImageMode mode)
317 {
318 if (imageMode != mode) {
319 imageMode = mode;
320 vgSeti(VG_IMAGE_MODE, mode);
321 }
322 }
323
setRenderingQuality(VGRenderingQuality mode)324 inline void QVGPaintEnginePrivate::setRenderingQuality(VGRenderingQuality mode)
325 {
326 if (renderingQuality != mode) {
327 vgSeti(VG_RENDERING_QUALITY, mode);
328 renderingQuality = mode;
329 pathTransformSet = false; // need to tweak transform for aliased stroking
330 }
331 }
332
setImageQuality(VGImageQuality mode)333 inline void QVGPaintEnginePrivate::setImageQuality(VGImageQuality mode)
334 {
335 if (imageQuality != mode) {
336 vgSeti(VG_IMAGE_QUALITY, mode);
337 imageQuality = mode;
338 }
339 }
340
setBlendMode(VGBlendMode mode)341 inline void QVGPaintEnginePrivate::setBlendMode(VGBlendMode mode)
342 {
343 if (blendMode != mode) {
344 vgSeti(VG_BLEND_MODE, mode);
345 blendMode = mode;
346 }
347 }
348
setFillRule(VGint mode)349 inline void QVGPaintEnginePrivate::setFillRule(VGint mode)
350 {
351 if (fillRule != mode) {
352 fillRule = mode;
353 vgSeti(VG_FILL_RULE, mode);
354 }
355 }
356
clearModes()357 void QVGPaintEnginePrivate::clearModes()
358 {
359 matrixMode = (VGMatrixMode)0;
360 imageMode = (VGImageMode)0;
361 blendMode = (VGBlendMode)0;
362 renderingQuality = (VGRenderingQuality)0;
363 imageQuality = (VGImageQuality)0;
364 }
365
QVGPaintEnginePrivate(QVGPaintEngine * q_ptr)366 QVGPaintEnginePrivate::QVGPaintEnginePrivate(QVGPaintEngine *q_ptr) : q(q_ptr)
367 {
368 init();
369 }
370
init()371 void QVGPaintEnginePrivate::init()
372 {
373 maxScissorRects = 0;
374
375 penPaint = 0;
376 brushPaint = 0;
377 opacityPaint = 0;
378 fillPaint = 0;
379
380 forcePenChange = true;
381 forceBrushChange = true;
382
383 hasExtendedRadialGradientPen = false;
384 hasExtendedRadialGradientBrush = false;
385
386 penType = (VGPaintType)0;
387 brushType = (VGPaintType)0;
388
389 brushOrigin = QPointF(0.0f, 0.0f);
390
391 fillRule = 0;
392
393 opacity = 1.0;
394 paintOpacity = 1.0f;
395
396 #if !defined(QVG_NO_MODIFY_PATH)
397 rectPath = 0;
398 linePath = 0;
399 roundRectPath = 0;
400 #endif
401
402 simpleTransform = true;
403 pathTransformSet = false;
404 penScale = 1.0;
405
406 maskValid = false;
407 maskIsSet = false;
408 scissorMask = false;
409 rawVG = false;
410
411 scissorActive = false;
412 scissorDirty = false;
413
414 dirty = 0;
415
416 clearOpacity = 1.0f;
417
418 #if !defined(QVG_NO_DRAW_GLYPHS)
419 fontEngineCleaner = 0;
420 #endif
421
422 hasAdvancedBlending = false;
423
424 clearModes();
425 }
426
~QVGPaintEnginePrivate()427 QVGPaintEnginePrivate::~QVGPaintEnginePrivate()
428 {
429 destroy();
430 }
431
initObjects()432 void QVGPaintEnginePrivate::initObjects()
433 {
434 maxScissorRects = vgGeti(VG_MAX_SCISSOR_RECTS);
435
436 penPaint = vgCreatePaint();
437 vgSetParameteri(penPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
438 vgSetPaint(penPaint, VG_STROKE_PATH);
439
440 vgSeti(VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
441 vgLoadIdentity();
442
443 brushPaint = vgCreatePaint();
444 vgSetParameteri(brushPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
445 vgSetPaint(brushPaint, VG_FILL_PATH);
446 fillPaint = brushPaint;
447
448 vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
449 vgLoadIdentity();
450 matrixMode = VG_MATRIX_FILL_PAINT_TO_USER;
451
452 opacityPaint = vgCreatePaint();
453 vgSetParameteri(opacityPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
454 VGfloat values[4];
455 values[0] = 1.0f;
456 values[1] = 1.0f;
457 values[2] = 1.0f;
458 values[3] = paintOpacity;
459 vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
460
461 #if !defined(QVG_NO_MODIFY_PATH)
462 // Create a dummy path for rectangle drawing, which we can
463 // modify later with vgModifyPathCoords(). This should be
464 // faster than constantly creating and destroying paths.
465 rectPath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
466 VG_PATH_DATATYPE_F,
467 1.0f, // scale
468 0.0f, // bias
469 5, // segmentCapacityHint
470 8, // coordCapacityHint
471 VG_PATH_CAPABILITY_ALL);
472 static VGubyte const segments[5] = {
473 VG_MOVE_TO_ABS,
474 VG_LINE_TO_ABS,
475 VG_LINE_TO_ABS,
476 VG_LINE_TO_ABS,
477 VG_CLOSE_PATH
478 };
479 VGfloat coords[8];
480 coords[0] = 0.0f;
481 coords[1] = 0.0f;
482 coords[2] = 100.0f;
483 coords[3] = coords[1];
484 coords[4] = coords[2];
485 coords[5] = 100.0f;
486 coords[6] = coords[0];
487 coords[7] = coords[5];
488 vgAppendPathData(rectPath, 5, segments, coords);
489
490 // Create a dummy line drawing path as well.
491 linePath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
492 VG_PATH_DATATYPE_F,
493 1.0f, // scale
494 0.0f, // bias
495 2, // segmentCapacityHint
496 4, // coordCapacityHint
497 VG_PATH_CAPABILITY_ALL);
498 vgAppendPathData(linePath, 2, segments, coords);
499 #endif
500
501 const char *extensions = reinterpret_cast<const char *>(vgGetString(VG_EXTENSIONS));
502 if (extensions)
503 hasAdvancedBlending = strstr(extensions, "VG_KHR_advanced_blending") != 0;
504 }
505
destroy()506 void QVGPaintEnginePrivate::destroy()
507 {
508 if (penPaint)
509 vgDestroyPaint(penPaint);
510 if (brushPaint)
511 vgDestroyPaint(brushPaint);
512 if (opacityPaint)
513 vgDestroyPaint(opacityPaint);
514
515 #if !defined(QVG_NO_MODIFY_PATH)
516 if (rectPath)
517 vgDestroyPath(rectPath);
518 if (linePath)
519 vgDestroyPath(linePath);
520 if (roundRectPath)
521 vgDestroyPath(roundRectPath);
522 #endif
523
524 #if !defined(QVG_NO_DRAW_GLYPHS)
525 QVGFontCache::Iterator it;
526 for (it = fontCache.begin(); it != fontCache.end(); ++it)
527 delete it.value();
528 fontCache.clear();
529 delete fontEngineCleaner;
530 #endif
531 }
532
533 // Set a specific VG transformation matrix in the current VG context.
setTransform(VGMatrixMode mode,const QTransform & transform)534 void QVGPaintEnginePrivate::setTransform
535 (VGMatrixMode mode, const QTransform& transform)
536 {
537 VGfloat mat[9];
538 if (mode != matrixMode) {
539 vgSeti(VG_MATRIX_MODE, mode);
540 matrixMode = mode;
541 }
542 mat[0] = transform.m11();
543 mat[1] = transform.m12();
544 mat[2] = transform.m13();
545 mat[3] = transform.m21();
546 mat[4] = transform.m22();
547 mat[5] = transform.m23();
548 mat[6] = transform.m31();
549 mat[7] = transform.m32();
550 mat[8] = transform.m33();
551 vgLoadMatrix(mat);
552 }
553
554 Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
555
updateTransform(QPaintDevice * pdev)556 void QVGPaintEnginePrivate::updateTransform(QPaintDevice *pdev)
557 {
558 VGfloat devh = pdev->height();
559
560 // Construct the VG transform by combining the Qt transform with
561 // the following viewport transformation:
562 // | 1 0 0 |
563 // | 0 -1 devh |
564 // | 0 0 1 |
565 // The full VG transform is effectively:
566 // 1. Apply the user's transformation matrix.
567 // 2. Flip the co-ordinate system upside down.
568 QTransform viewport(1.0f, 0.0f, 0.0f,
569 0.0f, -1.0f, 0.0f,
570 0.0f, devh, 1.0f);
571
572 // Compute the path transform and determine if it is projective.
573 pathTransform = transform * viewport;
574 bool projective = (pathTransform.m13() != 0.0f ||
575 pathTransform.m23() != 0.0f ||
576 pathTransform.m33() != 1.0f);
577 if (projective) {
578 // The engine cannot do projective path transforms for us,
579 // so we will have to convert the co-ordinates ourselves.
580 // Change the matrix to just the viewport transformation.
581 pathTransform = viewport;
582 simpleTransform = false;
583 } else {
584 simpleTransform = true;
585 }
586 pathTransformSet = false;
587
588 // The image transform is always the full transformation,
589 imageTransform = transform * viewport;
590
591 // Calculate the scaling factor to use for turning cosmetic pens
592 // into ordinary non-cosmetic pens.
593 qt_scaleForTransform(transform, &penScale);
594 }
595
vectorPathToVGPath(const QVectorPath & path)596 VGPath QVGPaintEnginePrivate::vectorPathToVGPath(const QVectorPath& path)
597 {
598 int count = path.elementCount();
599 const qreal *points = path.points();
600 const QPainterPath::ElementType *elements = path.elements();
601
602 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
603 VG_PATH_DATATYPE_F,
604 1.0f, // scale
605 0.0f, // bias
606 count + 1, // segmentCapacityHint
607 count * 2, // coordCapacityHint
608 VG_PATH_CAPABILITY_ALL);
609
610 // Size is sufficient segments for drawRoundedRect() paths.
611 QVarLengthArray<VGubyte, 20> segments;
612
613 if (sizeof(qreal) == sizeof(VGfloat) && elements && simpleTransform) {
614 // If Qt was compiled with qreal the same size as VGfloat,
615 // then convert the segment types and use the incoming
616 // points array directly.
617 for (int i = 0; i < count; ++i) {
618 switch (elements[i]) {
619
620 case QPainterPath::MoveToElement:
621 segments.append(VG_MOVE_TO_ABS); break;
622
623 case QPainterPath::LineToElement:
624 segments.append(VG_LINE_TO_ABS); break;
625
626 case QPainterPath::CurveToElement:
627 segments.append(VG_CUBIC_TO_ABS); break;
628
629 case QPainterPath::CurveToDataElement: break;
630
631 }
632 }
633 if (path.hasImplicitClose())
634 segments.append(VG_CLOSE_PATH);
635
636 vgAppendPathData(vgpath, segments.count(), segments.constData(),
637 reinterpret_cast<const VGfloat *>(points));
638
639 return vgpath;
640 }
641
642 // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
643 QVarLengthArray<VGfloat, 48> coords;
644
645 int curvePos = 0;
646 QPointF temp;
647
648 if (elements && simpleTransform) {
649 // Convert the members of the element array.
650 for (int i = 0; i < count; ++i) {
651 switch (elements[i]) {
652
653 case QPainterPath::MoveToElement:
654 {
655 coords.append(points[0]);
656 coords.append(points[1]);
657 segments.append(VG_MOVE_TO_ABS);
658 }
659 break;
660
661 case QPainterPath::LineToElement:
662 {
663 coords.append(points[0]);
664 coords.append(points[1]);
665 segments.append(VG_LINE_TO_ABS);
666 }
667 break;
668
669 case QPainterPath::CurveToElement:
670 {
671 coords.append(points[0]);
672 coords.append(points[1]);
673 curvePos = 2;
674 }
675 break;
676
677 case QPainterPath::CurveToDataElement:
678 {
679 coords.append(points[0]);
680 coords.append(points[1]);
681 curvePos += 2;
682 if (curvePos == 6) {
683 curvePos = 0;
684 segments.append(VG_CUBIC_TO_ABS);
685 }
686 }
687 break;
688
689 }
690 points += 2;
691 }
692 } else if (elements && !simpleTransform) {
693 // Convert the members of the element array after applying the
694 // current transform to the path locally.
695 for (int i = 0; i < count; ++i) {
696 switch (elements[i]) {
697
698 case QPainterPath::MoveToElement:
699 {
700 temp = transform.map(QPointF(points[0], points[1]));
701 coords.append(temp.x());
702 coords.append(temp.y());
703 segments.append(VG_MOVE_TO_ABS);
704 }
705 break;
706
707 case QPainterPath::LineToElement:
708 {
709 temp = transform.map(QPointF(points[0], points[1]));
710 coords.append(temp.x());
711 coords.append(temp.y());
712 segments.append(VG_LINE_TO_ABS);
713 }
714 break;
715
716 case QPainterPath::CurveToElement:
717 {
718 temp = transform.map(QPointF(points[0], points[1]));
719 coords.append(temp.x());
720 coords.append(temp.y());
721 curvePos = 2;
722 }
723 break;
724
725 case QPainterPath::CurveToDataElement:
726 {
727 temp = transform.map(QPointF(points[0], points[1]));
728 coords.append(temp.x());
729 coords.append(temp.y());
730 curvePos += 2;
731 if (curvePos == 6) {
732 curvePos = 0;
733 segments.append(VG_CUBIC_TO_ABS);
734 }
735 }
736 break;
737
738 }
739 points += 2;
740 }
741 } else if (count > 0 && simpleTransform) {
742 // If there is no element array, then the path is assumed
743 // to be a MoveTo followed by several LineTo's.
744 coords.append(points[0]);
745 coords.append(points[1]);
746 segments.append(VG_MOVE_TO_ABS);
747 while (count > 1) {
748 points += 2;
749 coords.append(points[0]);
750 coords.append(points[1]);
751 segments.append(VG_LINE_TO_ABS);
752 --count;
753 }
754 } else if (count > 0 && !simpleTransform) {
755 // Convert a simple path, and apply the transform locally.
756 temp = transform.map(QPointF(points[0], points[1]));
757 coords.append(temp.x());
758 coords.append(temp.y());
759 segments.append(VG_MOVE_TO_ABS);
760 while (count > 1) {
761 points += 2;
762 temp = transform.map(QPointF(points[0], points[1]));
763 coords.append(temp.x());
764 coords.append(temp.y());
765 segments.append(VG_LINE_TO_ABS);
766 --count;
767 }
768 }
769
770 // Close the path if specified.
771 if (path.hasImplicitClose())
772 segments.append(VG_CLOSE_PATH);
773
774 vgAppendPathData(vgpath, segments.count(),
775 segments.constData(), coords.constData());
776
777 return vgpath;
778 }
779
painterPathToVGPath(const QPainterPath & path)780 VGPath QVGPaintEnginePrivate::painterPathToVGPath(const QPainterPath& path)
781 {
782 int count = path.elementCount();
783
784 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
785 VG_PATH_DATATYPE_F,
786 1.0f, // scale
787 0.0f, // bias
788 count + 1, // segmentCapacityHint
789 count * 2, // coordCapacityHint
790 VG_PATH_CAPABILITY_ALL);
791
792 if (count == 0)
793 return vgpath;
794
795 const QPainterPath::Element *elements = &(path.elementAt(0));
796
797 // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
798 QVarLengthArray<VGfloat, 48> coords;
799 QVarLengthArray<VGubyte, 20> segments;
800
801 int curvePos = 0;
802 QPointF temp;
803
804 // Keep track of the start and end of each sub-path. QPainterPath
805 // does not have an "implicit close" flag like QVectorPath does.
806 // We therefore have to detect closed paths by looking for a LineTo
807 // element that connects back to the initial MoveTo element.
808 qreal startx = 0.0;
809 qreal starty = 0.0;
810 qreal endx = 0.0;
811 qreal endy = 0.0;
812 bool haveStart = false;
813 bool haveEnd = false;
814
815 if (simpleTransform) {
816 // Convert the members of the element array.
817 for (int i = 0; i < count; ++i) {
818 switch (elements[i].type) {
819
820 case QPainterPath::MoveToElement:
821 {
822 if (haveStart && haveEnd && startx == endx && starty == endy) {
823 // Implicitly close the previous sub-path.
824 segments.append(VG_CLOSE_PATH);
825 }
826 startx = elements[i].x;
827 starty = elements[i].y;
828 coords.append(startx);
829 coords.append(starty);
830 haveStart = true;
831 haveEnd = false;
832 segments.append(VG_MOVE_TO_ABS);
833 }
834 break;
835
836 case QPainterPath::LineToElement:
837 {
838 endx = elements[i].x;
839 endy = elements[i].y;
840 coords.append(endx);
841 coords.append(endy);
842 haveEnd = true;
843 segments.append(VG_LINE_TO_ABS);
844 }
845 break;
846
847 case QPainterPath::CurveToElement:
848 {
849 coords.append(elements[i].x);
850 coords.append(elements[i].y);
851 haveEnd = false;
852 curvePos = 2;
853 }
854 break;
855
856 case QPainterPath::CurveToDataElement:
857 {
858 coords.append(elements[i].x);
859 coords.append(elements[i].y);
860 haveEnd = false;
861 curvePos += 2;
862 if (curvePos == 6) {
863 curvePos = 0;
864 segments.append(VG_CUBIC_TO_ABS);
865 }
866 }
867 break;
868
869 }
870 }
871 } else {
872 // Convert the members of the element array after applying the
873 // current transform to the path locally.
874 for (int i = 0; i < count; ++i) {
875 switch (elements[i].type) {
876
877 case QPainterPath::MoveToElement:
878 {
879 if (haveStart && haveEnd && startx == endx && starty == endy) {
880 // Implicitly close the previous sub-path.
881 segments.append(VG_CLOSE_PATH);
882 }
883 temp = transform.map(QPointF(elements[i].x, elements[i].y));
884 startx = temp.x();
885 starty = temp.y();
886 coords.append(startx);
887 coords.append(starty);
888 haveStart = true;
889 haveEnd = false;
890 segments.append(VG_MOVE_TO_ABS);
891 }
892 break;
893
894 case QPainterPath::LineToElement:
895 {
896 temp = transform.map(QPointF(elements[i].x, elements[i].y));
897 endx = temp.x();
898 endy = temp.y();
899 coords.append(endx);
900 coords.append(endy);
901 haveEnd = true;
902 segments.append(VG_LINE_TO_ABS);
903 }
904 break;
905
906 case QPainterPath::CurveToElement:
907 {
908 temp = transform.map(QPointF(elements[i].x, elements[i].y));
909 coords.append(temp.x());
910 coords.append(temp.y());
911 haveEnd = false;
912 curvePos = 2;
913 }
914 break;
915
916 case QPainterPath::CurveToDataElement:
917 {
918 temp = transform.map(QPointF(elements[i].x, elements[i].y));
919 coords.append(temp.x());
920 coords.append(temp.y());
921 haveEnd = false;
922 curvePos += 2;
923 if (curvePos == 6) {
924 curvePos = 0;
925 segments.append(VG_CUBIC_TO_ABS);
926 }
927 }
928 break;
929
930 }
931 }
932 }
933
934 if (haveStart && haveEnd && startx == endx && starty == endy) {
935 // Implicitly close the last sub-path.
936 segments.append(VG_CLOSE_PATH);
937 }
938
939 vgAppendPathData(vgpath, segments.count(),
940 segments.constData(), coords.constData());
941
942 return vgpath;
943 }
944
roundedRectPath(const QRectF & rect,qreal xRadius,qreal yRadius,Qt::SizeMode mode)945 VGPath QVGPaintEnginePrivate::roundedRectPath(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
946 {
947 static VGubyte roundedrect_types[] = {
948 VG_MOVE_TO_ABS,
949 VG_LINE_TO_ABS,
950 VG_CUBIC_TO_ABS,
951 VG_LINE_TO_ABS,
952 VG_CUBIC_TO_ABS,
953 VG_LINE_TO_ABS,
954 VG_CUBIC_TO_ABS,
955 VG_LINE_TO_ABS,
956 VG_CUBIC_TO_ABS,
957 VG_CLOSE_PATH
958 };
959
960 qreal x1 = rect.left();
961 qreal x2 = rect.right();
962 qreal y1 = rect.top();
963 qreal y2 = rect.bottom();
964
965 if (mode == Qt::RelativeSize) {
966 xRadius = xRadius * rect.width() / 200.;
967 yRadius = yRadius * rect.height() / 200.;
968 }
969
970 xRadius = qMin(xRadius, rect.width() / 2);
971 yRadius = qMin(yRadius, rect.height() / 2);
972
973 VGfloat pts[] = {
974 x1 + xRadius, y1, // MoveTo
975 x2 - xRadius, y1, // LineTo
976 x2 - (1 - KAPPA) * xRadius, y1, // CurveTo
977 x2, y1 + (1 - KAPPA) * yRadius,
978 x2, y1 + yRadius,
979 x2, y2 - yRadius, // LineTo
980 x2, y2 - (1 - KAPPA) * yRadius, // CurveTo
981 x2 - (1 - KAPPA) * xRadius, y2,
982 x2 - xRadius, y2,
983 x1 + xRadius, y2, // LineTo
984 x1 + (1 - KAPPA) * xRadius, y2, // CurveTo
985 x1, y2 - (1 - KAPPA) * yRadius,
986 x1, y2 - yRadius,
987 x1, y1 + yRadius, // LineTo
988 x1, y1 + (1 - KAPPA) * yRadius, // CurveTo
989 x1 + (1 - KAPPA) * xRadius, y1,
990 x1 + xRadius, y1
991 };
992
993 #if !defined(QVG_NO_MODIFY_PATH)
994 VGPath vgpath = roundRectPath;
995 if (!vgpath) {
996 vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
997 VG_PATH_DATATYPE_F,
998 1.0f, // scale
999 0.0f, // bias
1000 10, // segmentCapacityHint
1001 17 * 2, // coordCapacityHint
1002 VG_PATH_CAPABILITY_ALL);
1003 vgAppendPathData(vgpath, 10, roundedrect_types, pts);
1004 roundRectPath = vgpath;
1005 } else {
1006 vgModifyPathCoords(vgpath, 0, 9, pts);
1007 }
1008 #else
1009 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
1010 VG_PATH_DATATYPE_F,
1011 1.0f, // scale
1012 0.0f, // bias
1013 10, // segmentCapacityHint
1014 17 * 2, // coordCapacityHint
1015 VG_PATH_CAPABILITY_ALL);
1016 vgAppendPathData(vgpath, 10, roundedrect_types, pts);
1017 #endif
1018
1019 return vgpath;
1020 }
1021
1022 Q_GUI_EXPORT QImage qt_imageForBrush(int style, bool invert);
1023
colorizeBitmap(const QImage & image,const QColor & color)1024 static QImage colorizeBitmap(const QImage &image, const QColor &color)
1025 {
1026 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
1027 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
1028
1029 QRgb fg = PREMUL(color.rgba());
1030 QRgb bg = 0;
1031
1032 int height = sourceImage.height();
1033 int width = sourceImage.width();
1034 for (int y=0; y<height; ++y) {
1035 const uchar *source = sourceImage.constScanLine(y);
1036 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
1037 for (int x=0; x < width; ++x)
1038 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
1039 }
1040 return dest;
1041 }
1042
toVGImage(const QImage & image,Qt::ImageConversionFlags flags=Qt::AutoColor)1043 static VGImage toVGImage
1044 (const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor)
1045 {
1046 QImage img(image);
1047
1048 VGImageFormat format;
1049 switch (img.format()) {
1050 case QImage::Format_Mono:
1051 img = image.convertToFormat(QImage::Format_MonoLSB, flags);
1052 img.invertPixels();
1053 format = VG_BW_1;
1054 break;
1055 case QImage::Format_MonoLSB:
1056 img.invertPixels();
1057 format = VG_BW_1;
1058 break;
1059 case QImage::Format_RGB32:
1060 format = VG_sXRGB_8888;
1061 break;
1062 case QImage::Format_ARGB32:
1063 format = VG_sARGB_8888;
1064 break;
1065 case QImage::Format_ARGB32_Premultiplied:
1066 format = VG_sARGB_8888_PRE;
1067 break;
1068 case QImage::Format_RGB16:
1069 format = VG_sRGB_565;
1070 break;
1071 default:
1072 // Convert everything else into ARGB32_Premultiplied.
1073 img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
1074 format = VG_sARGB_8888_PRE;
1075 break;
1076 }
1077
1078 const uchar *pixels = img.constBits();
1079
1080 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1081 (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1082 vgImageSubData
1083 (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
1084 img.width(), img.height());
1085
1086 return vgImg;
1087 }
1088
toVGImageSubRect(const QImage & image,const QRect & sr,Qt::ImageConversionFlags flags=Qt::AutoColor)1089 static VGImage toVGImageSubRect
1090 (const QImage & image, const QRect& sr,
1091 Qt::ImageConversionFlags flags = Qt::AutoColor)
1092 {
1093 QImage img(image);
1094
1095 VGImageFormat format;
1096 int bpp = 4;
1097
1098 switch (img.format()) {
1099 case QImage::Format_Mono:
1100 case QImage::Format_MonoLSB:
1101 return VG_INVALID_HANDLE;
1102 case QImage::Format_RGB32:
1103 format = VG_sXRGB_8888;
1104 break;
1105 case QImage::Format_ARGB32:
1106 format = VG_sARGB_8888;
1107 break;
1108 case QImage::Format_ARGB32_Premultiplied:
1109 format = VG_sARGB_8888_PRE;
1110 break;
1111 case QImage::Format_RGB16:
1112 format = VG_sRGB_565;
1113 bpp = 2;
1114 break;
1115 default:
1116 // Convert everything else into ARGB32_Premultiplied.
1117 img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
1118 format = VG_sARGB_8888_PRE;
1119 break;
1120 }
1121
1122 const uchar *pixels = img.constBits() + bpp * sr.x() +
1123 img.bytesPerLine() * sr.y();
1124
1125 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1126 (format, sr.width(), sr.height(), VG_IMAGE_QUALITY_FASTER);
1127 vgImageSubData
1128 (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
1129 sr.width(), sr.height());
1130
1131 return vgImg;
1132 }
1133
toVGImageWithOpacity(const QImage & image,qreal opacity)1134 static VGImage toVGImageWithOpacity(const QImage & image, qreal opacity)
1135 {
1136 QImage img(image.size(), QImage::Format_ARGB32_Premultiplied);
1137 img.fill(0);
1138 QPainter painter;
1139 painter.begin(&img);
1140 painter.setOpacity(opacity);
1141 painter.drawImage(0, 0, image);
1142 painter.end();
1143
1144 const uchar *pixels = img.constBits();
1145
1146 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1147 (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1148 vgImageSubData
1149 (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
1150 img.width(), img.height());
1151
1152 return vgImg;
1153 }
1154
toVGImageWithOpacitySubRect(const QImage & image,qreal opacity,const QRect & sr)1155 static VGImage toVGImageWithOpacitySubRect
1156 (const QImage & image, qreal opacity, const QRect& sr)
1157 {
1158 QImage img(sr.size(), QImage::Format_ARGB32_Premultiplied);
1159 img.fill(0);
1160 QPainter painter;
1161 painter.begin(&img);
1162 painter.setOpacity(opacity);
1163 painter.drawImage(QPoint(0, 0), image, sr);
1164 painter.end();
1165
1166 const uchar *pixels = img.constBits();
1167
1168 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1169 (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1170 vgImageSubData
1171 (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
1172 img.width(), img.height());
1173
1174 return vgImg;
1175 }
1176
setBrush(VGPaint paint,const QBrush & brush,VGMatrixMode mode,VGPaintType prevType)1177 VGPaintType QVGPaintEnginePrivate::setBrush
1178 (VGPaint paint, const QBrush& brush, VGMatrixMode mode,
1179 VGPaintType prevType)
1180 {
1181 VGfloat values[5];
1182 setBrushTransform(brush, mode);
1183
1184 // Reset the paint pattern on the brush, which will discard
1185 // the previous VGImage if one was set.
1186 if (prevType == VG_PAINT_TYPE_PATTERN || prevType == (VGPaintType)0)
1187 vgPaintPattern(paint, VG_INVALID_HANDLE);
1188
1189 switch (brush.style()) {
1190
1191 case Qt::SolidPattern: {
1192 // The brush is a solid color.
1193 QColor color(brush.color());
1194 values[0] = color.redF();
1195 values[1] = color.greenF();
1196 values[2] = color.blueF();
1197 values[3] = color.alphaF() * opacity;
1198 if (prevType != VG_PAINT_TYPE_COLOR)
1199 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
1200 vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values);
1201 return VG_PAINT_TYPE_COLOR;
1202 }
1203
1204 case Qt::LinearGradientPattern: {
1205 // The brush is a linear gradient.
1206 Q_ASSERT(brush.gradient()->type() == QGradient::LinearGradient);
1207 const QLinearGradient *grad =
1208 static_cast<const QLinearGradient*>(brush.gradient());
1209 values[0] = grad->start().x();
1210 values[1] = grad->start().y();
1211 values[2] = grad->finalStop().x();
1212 values[3] = grad->finalStop().y();
1213 if (prevType != VG_PAINT_TYPE_LINEAR_GRADIENT)
1214 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
1215 vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, values);
1216 setupColorRamp(grad, paint);
1217 return VG_PAINT_TYPE_LINEAR_GRADIENT;
1218 }
1219
1220 case Qt::RadialGradientPattern: {
1221 // The brush is a radial gradient.
1222 Q_ASSERT(brush.gradient()->type() == QGradient::RadialGradient);
1223 const QRadialGradient *grad =
1224 static_cast<const QRadialGradient*>(brush.gradient());
1225 values[0] = grad->center().x();
1226 values[1] = grad->center().y();
1227 values[2] = grad->focalPoint().x();
1228 values[3] = grad->focalPoint().y();
1229 values[4] = grad->radius();
1230 if (prevType != VG_PAINT_TYPE_RADIAL_GRADIENT)
1231 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
1232 vgSetParameterfv(paint, VG_PAINT_RADIAL_GRADIENT, 5, values);
1233 setupColorRamp(grad, paint);
1234 return VG_PAINT_TYPE_RADIAL_GRADIENT;
1235 }
1236
1237 case Qt::TexturePattern: {
1238 // The brush is a texture specified by a QPixmap/QImage.
1239 QPixmapData *pd = brush.texture().pixmapData();
1240 if (!pd)
1241 break; // null QPixmap
1242 VGImage vgImg;
1243 bool deref = false;
1244 if (pd->pixelType() == QPixmapData::BitmapType) {
1245 // Colorize bitmaps using the brush color and opacity.
1246 QColor color = brush.color();
1247 if (opacity != 1.0)
1248 color.setAlphaF(color.alphaF() * opacity);
1249 QImage image = colorizeBitmap(*(pd->buffer()), color);
1250 vgImg = toVGImage(image);
1251 deref = true;
1252 } else if (opacity == 1.0) {
1253 if (pd->classId() == QPixmapData::OpenVGClass) {
1254 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
1255 vgImg = vgpd->toVGImage();
1256
1257 // We don't want the pool to reclaim this image
1258 // because we cannot predict when the paint object
1259 // will stop using it. Replacing the image with
1260 // new data will make the paint object invalid.
1261 vgpd->detachImageFromPool();
1262 } else {
1263 vgImg = toVGImage(*(pd->buffer()));
1264 deref = true;
1265 }
1266 } else if (pd->classId() == QPixmapData::OpenVGClass) {
1267 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
1268 vgImg = vgpd->toVGImage(opacity);
1269 vgpd->detachImageFromPool();
1270 } else {
1271 vgImg = toVGImageWithOpacity(*(pd->buffer()), opacity);
1272 deref = true;
1273 }
1274 if (vgImg == VG_INVALID_HANDLE)
1275 break;
1276 if (prevType != VG_PAINT_TYPE_PATTERN)
1277 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
1278 vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT);
1279 vgPaintPattern(paint, vgImg);
1280 if (deref)
1281 vgDestroyImage(vgImg); // Will be valid until pattern is destroyed.
1282 return VG_PAINT_TYPE_PATTERN;
1283 }
1284
1285 case Qt::ConicalGradientPattern: {
1286 // Convert conical gradients into the first stop color.
1287 qWarning() << "QVGPaintEnginePrivate::setBrush: conical gradients are not supported by OpenVG";
1288 Q_ASSERT(brush.gradient()->type() == QGradient::ConicalGradient);
1289 const QConicalGradient *grad =
1290 static_cast<const QConicalGradient*>(brush.gradient());
1291 const QGradientStops stops = grad->stops();
1292 QColor color;
1293 if (stops.size() > 0)
1294 color = stops[0].second;
1295 values[0] = color.redF();
1296 values[1] = color.greenF();
1297 values[2] = color.blueF();
1298 values[3] = color.alphaF() * opacity;
1299 if (prevType != VG_PAINT_TYPE_COLOR)
1300 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
1301 vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values);
1302 return VG_PAINT_TYPE_COLOR;
1303 }
1304
1305 case Qt::Dense1Pattern:
1306 case Qt::Dense2Pattern:
1307 case Qt::Dense3Pattern:
1308 case Qt::Dense4Pattern:
1309 case Qt::Dense5Pattern:
1310 case Qt::Dense6Pattern:
1311 case Qt::Dense7Pattern:
1312 case Qt::HorPattern:
1313 case Qt::VerPattern:
1314 case Qt::CrossPattern:
1315 case Qt::BDiagPattern:
1316 case Qt::FDiagPattern:
1317 case Qt::DiagCrossPattern: {
1318 // The brush is a traditional dotted or cross-hatched pattern brush.
1319 QColor color = brush.color();
1320 if (opacity != 1.0)
1321 color.setAlphaF(color.alphaF() * opacity);
1322 QImage image = colorizeBitmap
1323 (qt_imageForBrush(brush.style(), true), color);
1324 VGImage vgImg = toVGImage(image);
1325 if (prevType != VG_PAINT_TYPE_PATTERN)
1326 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
1327 vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT);
1328 vgPaintPattern(paint, vgImg);
1329 vgDestroyImage(vgImg); // Will stay valid until pattern is destroyed.
1330 return VG_PAINT_TYPE_PATTERN;
1331 }
1332
1333 default: break;
1334 }
1335 return (VGPaintType)0;
1336 }
1337
setPenParams(const QPen & pen)1338 void QVGPaintEnginePrivate::setPenParams(const QPen& pen)
1339 {
1340 // Note: OpenVG does not support zero-width or cosmetic pens,
1341 // so we have to simulate cosmetic pens by reversing the scale.
1342 VGfloat width = pen.widthF();
1343 if (width <= 0.0f)
1344 width = 1.0f;
1345 if (pen.isCosmetic()) {
1346 if (penScale != 1.0 && penScale != 0.0)
1347 width /= penScale;
1348 }
1349 vgSetf(VG_STROKE_LINE_WIDTH, width);
1350
1351 if (pen.capStyle() == Qt::FlatCap)
1352 vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_BUTT);
1353 else if (pen.capStyle() == Qt::SquareCap)
1354 vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_SQUARE);
1355 else
1356 vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
1357
1358 if (pen.joinStyle() == Qt::MiterJoin) {
1359 vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER);
1360 vgSetf(VG_STROKE_MITER_LIMIT, pen.miterLimit());
1361 } else if (pen.joinStyle() == Qt::BevelJoin) {
1362 vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL);
1363 } else {
1364 vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
1365 }
1366
1367 if (pen.style() == Qt::SolidLine) {
1368 vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL);
1369 } else {
1370 const QVector<qreal> dashPattern = pen.dashPattern();
1371 QVector<VGfloat> currentDashPattern(dashPattern.count());
1372 for (int i = 0; i < dashPattern.count(); ++i)
1373 currentDashPattern[i] = dashPattern[i] * width;
1374 vgSetfv(VG_STROKE_DASH_PATTERN, currentDashPattern.count(), currentDashPattern.data());
1375 vgSetf(VG_STROKE_DASH_PHASE, pen.dashOffset());
1376 vgSetf(VG_STROKE_DASH_PHASE_RESET, VG_FALSE);
1377 }
1378 }
1379
setBrushTransform(const QBrush & brush,VGMatrixMode mode)1380 void QVGPaintEnginePrivate::setBrushTransform
1381 (const QBrush& brush, VGMatrixMode mode)
1382 {
1383 // Compute the new brush transformation matrix.
1384 QTransform transform(brush.transform());
1385 if (brushOrigin.x() != 0.0f || brushOrigin.y() != 0.0f)
1386 transform.translate(brushOrigin.x(), brushOrigin.y());
1387
1388 // Bail out if the matrix is the same as last time, to avoid
1389 // updating the VG context state unless absolutely necessary.
1390 // Most applications won't have a brush transformation set,
1391 // which will leave the VG setting at its default of identity.
1392 // Always change the transform if coming out of raw VG mode.
1393 if (mode == VG_MATRIX_FILL_PAINT_TO_USER) {
1394 if (!rawVG && transform == brushTransform)
1395 return;
1396 brushTransform = transform;
1397 } else {
1398 if (!rawVG && transform == penTransform)
1399 return;
1400 penTransform = transform;
1401 }
1402
1403 // Set the brush transformation matrix.
1404 if (mode != matrixMode) {
1405 vgSeti(VG_MATRIX_MODE, mode);
1406 matrixMode = mode;
1407 }
1408 if (transform.isIdentity()) {
1409 vgLoadIdentity();
1410 } else {
1411 VGfloat mat[9];
1412 mat[0] = transform.m11();
1413 mat[1] = transform.m12();
1414 mat[2] = transform.m13();
1415 mat[3] = transform.m21();
1416 mat[4] = transform.m22();
1417 mat[5] = transform.m23();
1418 mat[6] = transform.m31();
1419 mat[7] = transform.m32();
1420 mat[8] = transform.m33();
1421 vgLoadMatrix(mat);
1422 }
1423 }
1424
setupColorRamp(const QGradient * grad,VGPaint paint)1425 void QVGPaintEnginePrivate::setupColorRamp(const QGradient *grad, VGPaint paint)
1426 {
1427 QGradient::Spread spread = grad->spread();
1428 VGColorRampSpreadMode spreadMode;
1429 if (spread == QGradient::ReflectSpread)
1430 spreadMode = VG_COLOR_RAMP_SPREAD_REFLECT;
1431 else if (spread == QGradient::RepeatSpread)
1432 spreadMode = VG_COLOR_RAMP_SPREAD_REPEAT;
1433 else
1434 spreadMode = VG_COLOR_RAMP_SPREAD_PAD;
1435
1436 const QGradientStops stops = grad->stops();
1437 int n = 5*stops.size();
1438 QVector<VGfloat> fill_stops(n);
1439
1440 for (int i = 0; i < stops.size(); ++i ) {
1441 QColor col = stops[i].second;
1442 fill_stops[i*5] = stops[i].first;
1443 fill_stops[i*5 + 1] = col.redF();
1444 fill_stops[i*5 + 2] = col.greenF();
1445 fill_stops[i*5 + 3] = col.blueF();
1446 fill_stops[i*5 + 4] = col.alphaF() * opacity;
1447 }
1448
1449 vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, spreadMode);
1450 vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, VG_FALSE);
1451 vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, n, fill_stops.data());
1452 }
1453
QVGPainterState(QVGPainterState & other)1454 QVGPainterState::QVGPainterState(QVGPainterState& other)
1455 : QPainterState(other),
1456 isNew(true), clipRegion(other.clipRegion),
1457 savedDirty(0)
1458 {
1459 }
1460
QVGPainterState()1461 QVGPainterState::QVGPainterState()
1462 : isNew(true), savedDirty(0)
1463 {
1464 }
1465
~QVGPainterState()1466 QVGPainterState::~QVGPainterState()
1467 {
1468 }
1469
QVGPaintEngine()1470 QVGPaintEngine::QVGPaintEngine()
1471 : QPaintEngineEx(*new QVGPaintEnginePrivate(this))
1472 {
1473 }
1474
QVGPaintEngine(QVGPaintEnginePrivate & data)1475 QVGPaintEngine::QVGPaintEngine(QVGPaintEnginePrivate &data)
1476 : QPaintEngineEx(data)
1477 {
1478 }
1479
~QVGPaintEngine()1480 QVGPaintEngine::~QVGPaintEngine()
1481 {
1482 }
1483
createState(QPainterState * orig) const1484 QPainterState *QVGPaintEngine::createState(QPainterState *orig) const
1485 {
1486 if (!orig) {
1487 return new QVGPainterState();
1488 } else {
1489 Q_D(const QVGPaintEngine);
1490 QVGPaintEnginePrivate *d2 = const_cast<QVGPaintEnginePrivate*>(d);
1491 QVGPainterState *origState = static_cast<QVGPainterState *>(orig);
1492 origState->savedDirty = d2->dirty;
1493 d2->dirty = 0;
1494 return new QVGPainterState(*origState);
1495 }
1496 }
1497
draw(VGPath path,const QPen & pen,const QBrush & brush,VGint rule)1498 void QVGPaintEnginePrivate::draw
1499 (VGPath path, const QPen& pen, const QBrush& brush, VGint rule)
1500 {
1501 VGbitfield mode = 0;
1502 if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) {
1503 ensurePen(pen);
1504 mode |= VG_STROKE_PATH;
1505 }
1506 if (brush.style() != Qt::NoBrush) {
1507 ensureBrush(brush);
1508 setFillRule(rule);
1509 mode |= VG_FILL_PATH;
1510 }
1511 if (mode != 0) {
1512 ensurePathTransform();
1513 vgDrawPath(path, mode);
1514 }
1515 }
1516
stroke(VGPath path,const QPen & pen)1517 void QVGPaintEnginePrivate::stroke(VGPath path, const QPen& pen)
1518 {
1519 if (pen.style() == Qt::NoPen)
1520 return;
1521 ensurePen(pen);
1522 ensurePathTransform();
1523 vgDrawPath(path, VG_STROKE_PATH);
1524 }
1525
fill(VGPath path,const QBrush & brush,VGint rule)1526 void QVGPaintEnginePrivate::fill(VGPath path, const QBrush& brush, VGint rule)
1527 {
1528 if (brush.style() == Qt::NoBrush)
1529 return;
1530 ensureBrush(brush);
1531 setFillRule(rule);
1532 QPen savedPen = currentPen;
1533 currentPen = Qt::NoPen;
1534 ensurePathTransform();
1535 currentPen = savedPen;
1536 vgDrawPath(path, VG_FILL_PATH);
1537 }
1538
begin(QPaintDevice * pdev)1539 bool QVGPaintEngine::begin(QPaintDevice *pdev)
1540 {
1541 Q_UNUSED(pdev);
1542 Q_D(QVGPaintEngine);
1543
1544 // Initialize the VG painting objects if we haven't done it yet.
1545 if (!d->penPaint)
1546 d->initObjects();
1547
1548 // The initial clip region is the entire device area.
1549 QVGPainterState *s = state();
1550 s->clipRegion = defaultClipRegion();
1551
1552 // Initialize the VG state for this paint operation.
1553 restoreState(QPaintEngine::AllDirty);
1554 d->dirty = 0;
1555 d->rawVG = false;
1556 return true;
1557 }
1558
end()1559 bool QVGPaintEngine::end()
1560 {
1561 vgSeti(VG_SCISSORING, VG_FALSE);
1562 vgSeti(VG_MASKING, VG_FALSE);
1563 return true;
1564 }
1565
draw(const QVectorPath & path)1566 void QVGPaintEngine::draw(const QVectorPath &path)
1567 {
1568 Q_D(QVGPaintEngine);
1569 if (d->needsEmulation()) {
1570 QPaintEngineEx::draw(path);
1571 return;
1572 }
1573 QVGPainterState *s = state();
1574 VGPath vgpath = d->vectorPathToVGPath(path);
1575 if (!path.hasWindingFill())
1576 d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD);
1577 else
1578 d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
1579 vgDestroyPath(vgpath);
1580 }
1581
1582 Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path);
1583
fill(const QVectorPath & path,const QBrush & brush)1584 void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1585 {
1586 Q_D(QVGPaintEngine);
1587 if (d->needsEmulation(brush)) {
1588 QPainter *p = painter();
1589 QBrush oldBrush = p->brush();
1590 p->setBrush(brush);
1591 qt_draw_helper(p->d_ptr.data(), qt_painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
1592 p->setBrush(oldBrush);
1593 return;
1594 }
1595 VGPath vgpath = d->vectorPathToVGPath(path);
1596 if (!path.hasWindingFill())
1597 d->fill(vgpath, brush, VG_EVEN_ODD);
1598 else
1599 d->fill(vgpath, brush, VG_NON_ZERO);
1600 vgDestroyPath(vgpath);
1601 }
1602
stroke(const QVectorPath & path,const QPen & pen)1603 void QVGPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1604 {
1605 Q_D(QVGPaintEngine);
1606 if (d->needsEmulation(pen.brush())) {
1607 QPaintEngineEx::stroke(path, pen);
1608 return;
1609 }
1610 VGPath vgpath = d->vectorPathToVGPath(path);
1611 d->stroke(vgpath, pen);
1612 vgDestroyPath(vgpath);
1613 }
1614
1615 // Determine if a co-ordinate transform is simple enough to allow
1616 // rectangle-based clipping with vgMask(). Simple transforms most
1617 // often result from origin translations.
clipTransformIsSimple(const QTransform & transform)1618 static inline bool clipTransformIsSimple(const QTransform& transform)
1619 {
1620 QTransform::TransformationType type = transform.type();
1621 if (type == QTransform::TxNone || type == QTransform::TxTranslate)
1622 return true;
1623 if (type == QTransform::TxRotate) {
1624 // Check for 0, 90, 180, and 270 degree rotations.
1625 // (0 might happen after 4 rotations of 90 degrees).
1626 qreal m11 = transform.m11();
1627 qreal m12 = transform.m12();
1628 qreal m21 = transform.m21();
1629 qreal m22 = transform.m22();
1630 if (m11 == 0.0f && m22 == 0.0f) {
1631 if (m12 == 1.0f && m21 == -1.0f)
1632 return true; // 90 degrees.
1633 else if (m12 == -1.0f && m21 == 1.0f)
1634 return true; // 270 degrees.
1635 } else if (m12 == 0.0f && m21 == 0.0f) {
1636 if (m11 == -1.0f && m22 == -1.0f)
1637 return true; // 180 degrees.
1638 else if (m11 == 1.0f && m22 == 1.0f)
1639 return true; // 0 degrees.
1640 }
1641 }
1642 return false;
1643 }
1644
1645 #if defined(QVG_SCISSOR_CLIP)
1646
clip(const QVectorPath & path,Qt::ClipOperation op)1647 void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1648 {
1649 Q_D(QVGPaintEngine);
1650 QVGPainterState *s = state();
1651
1652 d->dirty |= QPaintEngine::DirtyClipRegion;
1653
1654 if (op == Qt::NoClip) {
1655 s->clipRegion = defaultClipRegion();
1656 updateScissor();
1657 return;
1658 }
1659
1660 // We aren't using masking, so handle simple QRectF's only.
1661 if (path.shape() == QVectorPath::RectangleHint &&
1662 path.elementCount() == 4 && clipTransformIsSimple(d->transform)) {
1663 // Clipping region that resulted from QPainter::setClipRect(QRectF).
1664 // Convert it into a QRect and apply.
1665 const qreal *points = path.points();
1666 QRectF rect(points[0], points[1], points[2] - points[0],
1667 points[5] - points[1]);
1668 clip(rect.toRect(), op);
1669 return;
1670 }
1671
1672 // Try converting the path into a QRegion that tightly follows
1673 // the outline of the path we want to clip with.
1674 QRegion region;
1675 if (!path.isEmpty())
1676 region = QRegion(path.convertToPainterPath().toFillPolygon(QTransform()).toPolygon());
1677
1678 switch (op) {
1679 case Qt::NoClip:
1680 {
1681 region = defaultClipRegion();
1682 }
1683 break;
1684
1685 case Qt::ReplaceClip:
1686 {
1687 region = d->transform.map(region);
1688 }
1689 break;
1690
1691 case Qt::IntersectClip:
1692 {
1693 region = s->clipRegion.intersect(d->transform.map(region));
1694 }
1695 break;
1696
1697 case Qt::UniteClip:
1698 {
1699 region = s->clipRegion.unite(d->transform.map(region));
1700 }
1701 break;
1702 }
1703 if (region.numRects() <= d->maxScissorRects) {
1704 // We haven't reached the maximum scissor count yet, so we can
1705 // still make use of this region.
1706 s->clipRegion = region;
1707 updateScissor();
1708 return;
1709 }
1710
1711 // The best we can do is clip to the bounding rectangle
1712 // of all control points.
1713 clip(path.controlPointRect().toRect(), op);
1714 }
1715
clip(const QRect & rect,Qt::ClipOperation op)1716 void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1717 {
1718 Q_D(QVGPaintEngine);
1719 QVGPainterState *s = state();
1720
1721 d->dirty |= QPaintEngine::DirtyClipRegion;
1722
1723 switch (op) {
1724 case Qt::NoClip:
1725 {
1726 s->clipRegion = defaultClipRegion();
1727 }
1728 break;
1729
1730 case Qt::ReplaceClip:
1731 {
1732 s->clipRegion = d->transform.map(QRegion(rect));
1733 }
1734 break;
1735
1736 case Qt::IntersectClip:
1737 {
1738 s->clipRegion = s->clipRegion.intersect(d->transform.map(QRegion(rect)));
1739 }
1740 break;
1741
1742 case Qt::UniteClip:
1743 {
1744 s->clipRegion = s->clipRegion.unite(d->transform.map(QRegion(rect)));
1745 }
1746 break;
1747 }
1748
1749 updateScissor();
1750 }
1751
clip(const QRegion & region,Qt::ClipOperation op)1752 void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1753 {
1754 Q_D(QVGPaintEngine);
1755 QVGPainterState *s = state();
1756
1757 d->dirty |= QPaintEngine::DirtyClipRegion;
1758
1759 switch (op) {
1760 case Qt::NoClip:
1761 {
1762 s->clipRegion = defaultClipRegion();
1763 }
1764 break;
1765
1766 case Qt::ReplaceClip:
1767 {
1768 s->clipRegion = d->transform.map(region);
1769 }
1770 break;
1771
1772 case Qt::IntersectClip:
1773 {
1774 s->clipRegion = s->clipRegion.intersect(d->transform.map(region));
1775 }
1776 break;
1777
1778 case Qt::UniteClip:
1779 {
1780 s->clipRegion = s->clipRegion.unite(d->transform.map(region));
1781 }
1782 break;
1783 }
1784
1785 updateScissor();
1786 }
1787
clip(const QPainterPath & path,Qt::ClipOperation op)1788 void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
1789 {
1790 QPaintEngineEx::clip(path, op);
1791 }
1792
1793 #else // !QVG_SCISSOR_CLIP
1794
clip(const QVectorPath & path,Qt::ClipOperation op)1795 void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1796 {
1797 Q_D(QVGPaintEngine);
1798
1799 d->dirty |= QPaintEngine::DirtyClipRegion;
1800
1801 if (op == Qt::NoClip) {
1802 d->maskValid = false;
1803 d->maskIsSet = true;
1804 d->scissorMask = false;
1805 d->maskRect = QRect();
1806 vgSeti(VG_MASKING, VG_FALSE);
1807 return;
1808 }
1809
1810 // We don't have vgRenderToMask(), so handle simple QRectF's only.
1811 if (path.shape() == QVectorPath::RectangleHint &&
1812 path.elementCount() == 4 && clipTransformIsSimple(d->transform)) {
1813 // Clipping region that resulted from QPainter::setClipRect(QRectF).
1814 // Convert it into a QRect and apply.
1815 const qreal *points = path.points();
1816 QRectF rect(points[0], points[1], points[2] - points[0],
1817 points[5] - points[1]);
1818 clip(rect.toRect(), op);
1819 return;
1820 }
1821
1822 #if !defined(QVG_NO_RENDER_TO_MASK)
1823 QPaintDevice *pdev = paintDevice();
1824 int width = pdev->width();
1825 int height = pdev->height();
1826
1827 if (op == Qt::ReplaceClip) {
1828 vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
1829 d->maskRect = QRect();
1830 } else if (!d->maskValid) {
1831 d->ensureMask(this, width, height);
1832 }
1833
1834 d->ensurePathTransform();
1835 VGPath vgpath = d->vectorPathToVGPath(path);
1836 switch (op) {
1837 case Qt::ReplaceClip:
1838 case Qt::UniteClip:
1839 vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
1840 break;
1841
1842 case Qt::IntersectClip:
1843 vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
1844 break;
1845
1846 default: break;
1847 }
1848 vgDestroyPath(vgpath);
1849
1850 vgSeti(VG_MASKING, VG_TRUE);
1851 d->maskValid = true;
1852 d->maskIsSet = false;
1853 d->scissorMask = false;
1854 #endif
1855 }
1856
clip(const QRect & rect,Qt::ClipOperation op)1857 void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1858 {
1859 Q_D(QVGPaintEngine);
1860
1861 d->dirty |= QPaintEngine::DirtyClipRegion;
1862
1863 // If we have a non-simple transform, then use path-based clipping.
1864 if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) {
1865 QPaintEngineEx::clip(rect, op);
1866 return;
1867 }
1868
1869 switch (op) {
1870 case Qt::NoClip:
1871 {
1872 d->maskValid = false;
1873 d->maskIsSet = true;
1874 d->scissorMask = false;
1875 d->maskRect = QRect();
1876 vgSeti(VG_MASKING, VG_FALSE);
1877 }
1878 break;
1879
1880 case Qt::ReplaceClip:
1881 {
1882 QRect r = d->transform.mapRect(rect);
1883 if (isDefaultClipRect(r)) {
1884 // Replacing the clip with a full-window region is the
1885 // same as turning off clipping.
1886 if (d->maskValid)
1887 vgSeti(VG_MASKING, VG_FALSE);
1888 d->maskValid = false;
1889 d->maskIsSet = true;
1890 d->scissorMask = false;
1891 d->maskRect = QRect();
1892 } else {
1893 // Special case: if the intersection of the system
1894 // clip and "r" is a single rectangle, then use the
1895 // scissor for clipping. We try to avoid allocating a
1896 // QRegion copy on the heap for the test if we can.
1897 QRegion clip = d->systemClip; // Reference-counted, no alloc.
1898 QRect clipRect;
1899 if (clip.rectCount() == 1) {
1900 clipRect = clip.boundingRect().intersected(r);
1901 } else if (clip.isEmpty()) {
1902 clipRect = r;
1903 } else {
1904 clip = clip.intersect(r);
1905 if (clip.rectCount() != 1) {
1906 d->maskValid = false;
1907 d->maskIsSet = false;
1908 d->scissorMask = false;
1909 d->maskRect = QRect();
1910 d->modifyMask(this, VG_FILL_MASK, r);
1911 break;
1912 }
1913 clipRect = clip.boundingRect();
1914 }
1915 d->maskValid = false;
1916 d->maskIsSet = false;
1917 d->scissorMask = true;
1918 d->maskRect = clipRect;
1919 vgSeti(VG_MASKING, VG_FALSE);
1920 updateScissor();
1921 }
1922 }
1923 break;
1924
1925 case Qt::IntersectClip:
1926 {
1927 QRect r = d->transform.mapRect(rect);
1928 if (!d->maskValid) {
1929 // Mask has not been used yet, so intersect with
1930 // the previous scissor-based region in maskRect.
1931 if (d->scissorMask)
1932 r = r.intersect(d->maskRect);
1933 if (isDefaultClipRect(r)) {
1934 // The clip is the full window, so turn off clipping.
1935 d->maskIsSet = true;
1936 d->maskRect = QRect();
1937 } else {
1938 // Activate the scissor on a smaller maskRect.
1939 d->maskIsSet = false;
1940 d->maskRect = r;
1941 }
1942 d->scissorMask = true;
1943 updateScissor();
1944 } else if (d->maskIsSet && isDefaultClipRect(r)) {
1945 // Intersecting a full-window clip with a full-window
1946 // region is the same as turning off clipping.
1947 if (d->maskValid)
1948 vgSeti(VG_MASKING, VG_FALSE);
1949 d->maskValid = false;
1950 d->maskIsSet = true;
1951 d->scissorMask = false;
1952 d->maskRect = QRect();
1953 } else {
1954 d->modifyMask(this, VG_INTERSECT_MASK, r);
1955 }
1956 }
1957 break;
1958
1959 case Qt::UniteClip:
1960 {
1961 // If we already have a full-window clip, then uniting a
1962 // region with it will do nothing. Otherwise union.
1963 if (!(d->maskIsSet))
1964 d->modifyMask(this, VG_UNION_MASK, d->transform.mapRect(rect));
1965 }
1966 break;
1967 }
1968 }
1969
clip(const QRegion & region,Qt::ClipOperation op)1970 void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1971 {
1972 Q_D(QVGPaintEngine);
1973
1974 // Use the QRect case if the region consists of a single rectangle.
1975 if (region.rectCount() == 1) {
1976 clip(region.boundingRect(), op);
1977 return;
1978 }
1979
1980 d->dirty |= QPaintEngine::DirtyClipRegion;
1981
1982 // If we have a non-simple transform, then use path-based clipping.
1983 if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) {
1984 QPaintEngineEx::clip(region, op);
1985 return;
1986 }
1987
1988 switch (op) {
1989 case Qt::NoClip:
1990 {
1991 d->maskValid = false;
1992 d->maskIsSet = true;
1993 d->scissorMask = false;
1994 d->maskRect = QRect();
1995 vgSeti(VG_MASKING, VG_FALSE);
1996 }
1997 break;
1998
1999 case Qt::ReplaceClip:
2000 {
2001 QRegion r = d->transform.map(region);
2002 if (isDefaultClipRegion(r)) {
2003 // Replacing the clip with a full-window region is the
2004 // same as turning off clipping.
2005 if (d->maskValid)
2006 vgSeti(VG_MASKING, VG_FALSE);
2007 d->maskValid = false;
2008 d->maskIsSet = true;
2009 d->scissorMask = false;
2010 d->maskRect = QRect();
2011 } else {
2012 // Special case: if the intersection of the system
2013 // clip and the region is a single rectangle, then
2014 // use the scissor for clipping.
2015 QRegion clip = d->systemClip;
2016 if (clip.isEmpty())
2017 clip = r;
2018 else
2019 clip = clip.intersect(r);
2020 if (clip.rectCount() == 1) {
2021 d->maskValid = false;
2022 d->maskIsSet = false;
2023 d->scissorMask = true;
2024 d->maskRect = clip.boundingRect();
2025 vgSeti(VG_MASKING, VG_FALSE);
2026 updateScissor();
2027 } else {
2028 d->maskValid = false;
2029 d->maskIsSet = false;
2030 d->scissorMask = false;
2031 d->maskRect = QRect();
2032 d->modifyMask(this, VG_FILL_MASK, r);
2033 }
2034 }
2035 }
2036 break;
2037
2038 case Qt::IntersectClip:
2039 {
2040 if (region.rectCount() != 1) {
2041 // If there is more than one rectangle, then intersecting
2042 // the rectangles one by one in modifyMask() will not give
2043 // the desired result. So fall back to path-based clipping.
2044 QPaintEngineEx::clip(region, op);
2045 return;
2046 }
2047 QRegion r = d->transform.map(region);
2048 if (d->maskIsSet && isDefaultClipRegion(r)) {
2049 // Intersecting a full-window clip with a full-window
2050 // region is the same as turning off clipping.
2051 if (d->maskValid)
2052 vgSeti(VG_MASKING, VG_FALSE);
2053 d->maskValid = false;
2054 d->maskIsSet = true;
2055 d->scissorMask = false;
2056 d->maskRect = QRect();
2057 } else {
2058 d->modifyMask(this, VG_INTERSECT_MASK, r);
2059 }
2060 }
2061 break;
2062
2063 case Qt::UniteClip:
2064 {
2065 // If we already have a full-window clip, then uniting a
2066 // region with it will do nothing. Otherwise union.
2067 if (!(d->maskIsSet))
2068 d->modifyMask(this, VG_UNION_MASK, d->transform.map(region));
2069 }
2070 break;
2071 }
2072 }
2073
2074 #if !defined(QVG_NO_RENDER_TO_MASK)
2075
2076 // Copied from qpathclipper.cpp.
qt_vg_pathToRect(const QPainterPath & path,QRectF * rect)2077 static bool qt_vg_pathToRect(const QPainterPath &path, QRectF *rect)
2078 {
2079 if (path.elementCount() != 5)
2080 return false;
2081
2082 const bool mightBeRect = path.elementAt(0).isMoveTo()
2083 && path.elementAt(1).isLineTo()
2084 && path.elementAt(2).isLineTo()
2085 && path.elementAt(3).isLineTo()
2086 && path.elementAt(4).isLineTo();
2087
2088 if (!mightBeRect)
2089 return false;
2090
2091 const qreal x1 = path.elementAt(0).x;
2092 const qreal y1 = path.elementAt(0).y;
2093
2094 const qreal x2 = path.elementAt(1).x;
2095 const qreal y2 = path.elementAt(2).y;
2096
2097 if (path.elementAt(1).y != y1)
2098 return false;
2099
2100 if (path.elementAt(2).x != x2)
2101 return false;
2102
2103 if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
2104 return false;
2105
2106 if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
2107 return false;
2108
2109 if (rect)
2110 *rect = QRectF(QPointF(x1, y1), QPointF(x2, y2));
2111
2112 return true;
2113 }
2114
2115 #endif
2116
clip(const QPainterPath & path,Qt::ClipOperation op)2117 void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
2118 {
2119 #if !defined(QVG_NO_RENDER_TO_MASK)
2120 Q_D(QVGPaintEngine);
2121
2122 // If the path is a simple rectangle, then use clip(QRect) instead.
2123 QRectF simpleRect;
2124 if (qt_vg_pathToRect(path, &simpleRect)) {
2125 clip(simpleRect.toRect(), op);
2126 return;
2127 }
2128
2129 d->dirty |= QPaintEngine::DirtyClipRegion;
2130
2131 if (op == Qt::NoClip) {
2132 d->maskValid = false;
2133 d->maskIsSet = true;
2134 d->scissorMask = false;
2135 d->maskRect = QRect();
2136 vgSeti(VG_MASKING, VG_FALSE);
2137 return;
2138 }
2139
2140 QPaintDevice *pdev = paintDevice();
2141 int width = pdev->width();
2142 int height = pdev->height();
2143
2144 if (op == Qt::ReplaceClip) {
2145 vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
2146 d->maskRect = QRect();
2147 } else if (!d->maskValid) {
2148 d->ensureMask(this, width, height);
2149 }
2150
2151 d->ensurePathTransform();
2152 VGPath vgpath = d->painterPathToVGPath(path);
2153 switch (op) {
2154 case Qt::ReplaceClip:
2155 case Qt::UniteClip:
2156 vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
2157 break;
2158
2159 case Qt::IntersectClip:
2160 vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
2161 break;
2162
2163 default: break;
2164 }
2165 vgDestroyPath(vgpath);
2166
2167 vgSeti(VG_MASKING, VG_TRUE);
2168 d->maskValid = true;
2169 d->maskIsSet = false;
2170 d->scissorMask = false;
2171 #else
2172 QPaintEngineEx::clip(path, op);
2173 #endif
2174 }
2175
ensureMask(QVGPaintEngine * engine,int width,int height)2176 void QVGPaintEnginePrivate::ensureMask
2177 (QVGPaintEngine *engine, int width, int height)
2178 {
2179 scissorMask = false;
2180 if (maskIsSet) {
2181 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, width, height);
2182 maskRect = QRect();
2183 } else {
2184 vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
2185 if (maskRect.isValid()) {
2186 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK,
2187 maskRect.x(), height - maskRect.y() - maskRect.height(),
2188 maskRect.width(), maskRect.height());
2189 maskRect = QRect();
2190 engine->updateScissor();
2191 }
2192 }
2193 }
2194
modifyMask(QVGPaintEngine * engine,VGMaskOperation op,const QRegion & region)2195 void QVGPaintEnginePrivate::modifyMask
2196 (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region)
2197 {
2198 QPaintDevice *pdev = engine->paintDevice();
2199 int width = pdev->width();
2200 int height = pdev->height();
2201
2202 if (!maskValid)
2203 ensureMask(engine, width, height);
2204
2205 QVector<QRect> rects = region.rects();
2206 for (int i = 0; i < rects.size(); ++i) {
2207 vgMask(VG_INVALID_HANDLE, op,
2208 rects[i].x(), height - rects[i].y() - rects[i].height(),
2209 rects[i].width(), rects[i].height());
2210 }
2211
2212 vgSeti(VG_MASKING, VG_TRUE);
2213 maskValid = true;
2214 maskIsSet = false;
2215 scissorMask = false;
2216 }
2217
modifyMask(QVGPaintEngine * engine,VGMaskOperation op,const QRect & rect)2218 void QVGPaintEnginePrivate::modifyMask
2219 (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect)
2220 {
2221 QPaintDevice *pdev = engine->paintDevice();
2222 int width = pdev->width();
2223 int height = pdev->height();
2224
2225 if (!maskValid)
2226 ensureMask(engine, width, height);
2227
2228 if (rect.isValid()) {
2229 vgMask(VG_INVALID_HANDLE, op,
2230 rect.x(), height - rect.y() - rect.height(),
2231 rect.width(), rect.height());
2232 }
2233
2234 vgSeti(VG_MASKING, VG_TRUE);
2235 maskValid = true;
2236 maskIsSet = false;
2237 scissorMask = false;
2238 }
2239
2240 #endif // !QVG_SCISSOR_CLIP
2241
updateScissor()2242 void QVGPaintEngine::updateScissor()
2243 {
2244 Q_D(QVGPaintEngine);
2245
2246 QRegion region = d->systemClip;
2247
2248 #if defined(QVG_SCISSOR_CLIP)
2249 // Using the scissor to do clipping, so combine the systemClip
2250 // with the current painting clipRegion.
2251
2252 if (d->maskValid) {
2253 vgSeti(VG_MASKING, VG_FALSE);
2254 d->maskValid = false;
2255 }
2256
2257 QVGPainterState *s = state();
2258 if (s->clipEnabled) {
2259 if (region.isEmpty())
2260 region = s->clipRegion;
2261 else
2262 region = region.intersect(s->clipRegion);
2263 if (isDefaultClipRegion(region)) {
2264 // The scissor region is the entire drawing surface,
2265 // so there is no point doing any scissoring.
2266 vgSeti(VG_SCISSORING, VG_FALSE);
2267 d->scissorActive = false;
2268 d->scissorDirty = false;
2269 return;
2270 }
2271 } else
2272 #endif
2273 {
2274 #if !defined(QVG_SCISSOR_CLIP)
2275 // Combine the system clip with the simple mask rectangle.
2276 if (d->scissorMask) {
2277 if (region.isEmpty())
2278 region = d->maskRect;
2279 else
2280 region = region.intersect(d->maskRect);
2281 if (isDefaultClipRegion(region)) {
2282 // The scissor region is the entire drawing surface,
2283 // so there is no point doing any scissoring.
2284 vgSeti(VG_SCISSORING, VG_FALSE);
2285 d->scissorActive = false;
2286 d->scissorDirty = false;
2287 return;
2288 }
2289 } else
2290 #endif
2291
2292 // Disable the scissor completely if the system clip is empty.
2293 if (region.isEmpty()) {
2294 vgSeti(VG_SCISSORING, VG_FALSE);
2295 d->scissorActive = false;
2296 d->scissorDirty = false;
2297 return;
2298 }
2299 }
2300
2301 if (d->scissorActive && region == d->scissorRegion && !d->scissorDirty)
2302 return;
2303
2304 QVector<QRect> rects = region.rects();
2305 int count = rects.count();
2306 if (count > d->maxScissorRects) {
2307 #if !defined(QVG_SCISSOR_CLIP)
2308 count = d->maxScissorRects;
2309 #else
2310 // Use masking
2311 int width = paintDevice()->width();
2312 int height = paintDevice()->height();
2313 vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK,
2314 0, 0, width, height);
2315 for (int i = 0; i < rects.size(); ++i) {
2316 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK,
2317 rects[i].x(), height - rects[i].y() - rects[i].height(),
2318 rects[i].width(), rects[i].height());
2319 }
2320
2321 vgSeti(VG_SCISSORING, VG_FALSE);
2322 vgSeti(VG_MASKING, VG_TRUE);
2323 d->maskValid = true;
2324 d->maskIsSet = false;
2325 d->scissorMask = false;
2326 d->scissorActive = false;
2327 d->scissorDirty = false;
2328 d->scissorRegion = region;
2329 return;
2330 #endif
2331 }
2332
2333 QVarLengthArray<VGint> params(count * 4);
2334 int height = paintDevice()->height();
2335 for (int i = 0; i < count; ++i) {
2336 params[i * 4 + 0] = rects[i].x();
2337 params[i * 4 + 1] = height - rects[i].y() - rects[i].height();
2338 params[i * 4 + 2] = rects[i].width();
2339 params[i * 4 + 3] = rects[i].height();
2340 }
2341
2342 vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
2343 vgSeti(VG_SCISSORING, VG_TRUE);
2344 d->scissorDirty = false;
2345 d->scissorActive = true;
2346 d->scissorRegion = region;
2347 }
2348
defaultClipRegion()2349 QRegion QVGPaintEngine::defaultClipRegion()
2350 {
2351 // The default clip region for a paint device is the whole drawing area.
2352 QPaintDevice *pdev = paintDevice();
2353 return QRegion(0, 0, pdev->width(), pdev->height());
2354 }
2355
isDefaultClipRegion(const QRegion & region)2356 bool QVGPaintEngine::isDefaultClipRegion(const QRegion& region)
2357 {
2358 if (region.rectCount() != 1)
2359 return false;
2360
2361 QPaintDevice *pdev = paintDevice();
2362 int width = pdev->width();
2363 int height = pdev->height();
2364
2365 QRect rect = region.boundingRect();
2366 return (rect.x() == 0 && rect.y() == 0 &&
2367 rect.width() == width && rect.height() == height);
2368 }
2369
isDefaultClipRect(const QRect & rect)2370 bool QVGPaintEngine::isDefaultClipRect(const QRect& rect)
2371 {
2372 QPaintDevice *pdev = paintDevice();
2373 int width = pdev->width();
2374 int height = pdev->height();
2375
2376 return (rect.x() == 0 && rect.y() == 0 &&
2377 rect.width() == width && rect.height() == height);
2378 }
2379
clipEnabledChanged()2380 void QVGPaintEngine::clipEnabledChanged()
2381 {
2382 #if defined(QVG_SCISSOR_CLIP)
2383 vgSeti(VG_MASKING, VG_FALSE); // disable mask fallback
2384 updateScissor();
2385 #else
2386 Q_D(QVGPaintEngine);
2387 QVGPainterState *s = state();
2388 d->dirty |= QPaintEngine::DirtyClipEnabled;
2389 if (s->clipEnabled && s->clipOperation != Qt::NoClip) {
2390 // Replay the entire clip stack to put the mask into the right state.
2391 d->maskValid = false;
2392 d->maskIsSet = true;
2393 d->scissorMask = false;
2394 d->maskRect = QRect();
2395 s->clipRegion = defaultClipRegion();
2396 d->replayClipOperations();
2397 d->transform = s->transform();
2398 d->updateTransform(paintDevice());
2399 } else {
2400 vgSeti(VG_MASKING, VG_FALSE);
2401 d->maskValid = false;
2402 d->maskIsSet = false;
2403 d->scissorMask = false;
2404 d->maskRect = QRect();
2405 }
2406 #endif
2407 }
2408
penChanged()2409 void QVGPaintEngine::penChanged()
2410 {
2411 Q_D(QVGPaintEngine);
2412 d->dirty |= QPaintEngine::DirtyPen;
2413
2414 d->hasExtendedRadialGradientPen =
2415 state()->pen.style() != Qt::NoPen && d->needsEmulation(state()->pen.brush());
2416 }
2417
brushChanged()2418 void QVGPaintEngine::brushChanged()
2419 {
2420 Q_D(QVGPaintEngine);
2421 d->dirty |= QPaintEngine::DirtyBrush;
2422
2423 d->hasExtendedRadialGradientPen = d->needsEmulation(state()->brush);
2424 }
2425
brushOriginChanged()2426 void QVGPaintEngine::brushOriginChanged()
2427 {
2428 Q_D(QVGPaintEngine);
2429 d->dirty |= QPaintEngine::DirtyBrushOrigin;
2430 d->brushOrigin = state()->brushOrigin;
2431 d->forcePenChange = true;
2432 d->forceBrushChange = true;
2433 }
2434
opacityChanged()2435 void QVGPaintEngine::opacityChanged()
2436 {
2437 Q_D(QVGPaintEngine);
2438 d->dirty |= QPaintEngine::DirtyOpacity;
2439 d->opacity = state()->opacity;
2440 d->forcePenChange = true;
2441 d->forceBrushChange = true;
2442 }
2443
compositionModeChanged()2444 void QVGPaintEngine::compositionModeChanged()
2445 {
2446 Q_D(QVGPaintEngine);
2447 d->dirty |= QPaintEngine::DirtyCompositionMode;
2448
2449 VGint vgMode = VG_BLEND_SRC_OVER;
2450
2451 switch (state()->composition_mode) {
2452 case QPainter::CompositionMode_SourceOver:
2453 vgMode = VG_BLEND_SRC_OVER;
2454 break;
2455 case QPainter::CompositionMode_DestinationOver:
2456 vgMode = VG_BLEND_DST_OVER;
2457 break;
2458 case QPainter::CompositionMode_Source:
2459 vgMode = VG_BLEND_SRC;
2460 break;
2461 case QPainter::CompositionMode_SourceIn:
2462 vgMode = VG_BLEND_SRC_IN;
2463 break;
2464 case QPainter::CompositionMode_DestinationIn:
2465 vgMode = VG_BLEND_DST_IN;
2466 break;
2467 case QPainter::CompositionMode_Plus:
2468 vgMode = VG_BLEND_ADDITIVE;
2469 break;
2470 case QPainter::CompositionMode_Multiply:
2471 vgMode = VG_BLEND_MULTIPLY;
2472 break;
2473 case QPainter::CompositionMode_Screen:
2474 vgMode = VG_BLEND_SCREEN;
2475 break;
2476 case QPainter::CompositionMode_Darken:
2477 vgMode = VG_BLEND_DARKEN;
2478 break;
2479 case QPainter::CompositionMode_Lighten:
2480 vgMode = VG_BLEND_LIGHTEN;
2481 break;
2482 default:
2483 if (d->hasAdvancedBlending) {
2484 switch (state()->composition_mode) {
2485 case QPainter::CompositionMode_Overlay:
2486 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_OVERLAY_KHR;
2487 break;
2488 case QPainter::CompositionMode_ColorDodge:
2489 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORDODGE_KHR;
2490 break;
2491 case QPainter::CompositionMode_ColorBurn:
2492 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORBURN_KHR;
2493 break;
2494 case QPainter::CompositionMode_HardLight:
2495 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_HARDLIGHT_KHR;
2496 break;
2497 case QPainter::CompositionMode_SoftLight:
2498 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SOFTLIGHT_KHR;
2499 break;
2500 case QPainter::CompositionMode_Difference:
2501 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DIFFERENCE_KHR;
2502 break;
2503 case QPainter::CompositionMode_Exclusion:
2504 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_EXCLUSION_KHR;
2505 break;
2506 case QPainter::CompositionMode_SourceOut:
2507 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_OUT_KHR;
2508 break;
2509 case QPainter::CompositionMode_DestinationOut:
2510 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_OUT_KHR;
2511 break;
2512 case QPainter::CompositionMode_SourceAtop:
2513 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_ATOP_KHR;
2514 break;
2515 case QPainter::CompositionMode_DestinationAtop:
2516 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_ATOP_KHR;
2517 break;
2518 case QPainter::CompositionMode_Xor:
2519 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_XOR_KHR;
2520 break;
2521 default: break; // Fall back to VG_BLEND_SRC_OVER.
2522 }
2523 }
2524 if (vgMode == VG_BLEND_SRC_OVER)
2525 qWarning() << "QVGPaintEngine::compositionModeChanged unsupported mode" << state()->composition_mode;
2526 break;
2527 }
2528
2529 d->setBlendMode(VGBlendMode(vgMode));
2530 }
2531
renderHintsChanged()2532 void QVGPaintEngine::renderHintsChanged()
2533 {
2534 Q_D(QVGPaintEngine);
2535 d->dirty |= QPaintEngine::DirtyHints;
2536
2537 QPainter::RenderHints hints = state()->renderHints;
2538
2539 VGRenderingQuality rq =
2540 (hints & QPainter::Antialiasing)
2541 ? VG_RENDERING_QUALITY_BETTER
2542 : VG_RENDERING_QUALITY_NONANTIALIASED;
2543 VGImageQuality iq =
2544 (hints & QPainter::SmoothPixmapTransform)
2545 ? VG_IMAGE_QUALITY_BETTER
2546 : VG_IMAGE_QUALITY_NONANTIALIASED;
2547
2548 d->setRenderingQuality(rq);
2549 d->setImageQuality(iq);
2550 }
2551
transformChanged()2552 void QVGPaintEngine::transformChanged()
2553 {
2554 Q_D(QVGPaintEngine);
2555 QVGPainterState *s = state();
2556 d->dirty |= QPaintEngine::DirtyTransform;
2557 d->transform = s->transform();
2558 qreal oldPenScale = d->penScale;
2559 d->updateTransform(paintDevice());
2560 if (d->penScale != oldPenScale)
2561 d->forcePenChange = true;
2562 }
2563
clearRect(const QRectF & rect,const QColor & color)2564 bool QVGPaintEngine::clearRect(const QRectF &rect, const QColor &color)
2565 {
2566 Q_D(QVGPaintEngine);
2567 QVGPainterState *s = state();
2568 if (!s->clipEnabled || s->clipOperation == Qt::NoClip) {
2569 QRect r = d->transform.mapRect(rect).toRect();
2570 int height = paintDevice()->height();
2571 if (d->clearColor != color || d->clearOpacity != s->opacity) {
2572 VGfloat values[4];
2573 values[0] = color.redF();
2574 values[1] = color.greenF();
2575 values[2] = color.blueF();
2576 values[3] = color.alphaF() * s->opacity;
2577 vgSetfv(VG_CLEAR_COLOR, 4, values);
2578 d->clearColor = color;
2579 d->clearOpacity = s->opacity;
2580 }
2581 vgClear(r.x(), height - r.y() - r.height(),
2582 r.width(), r.height());
2583 return true;
2584 }
2585 return false;
2586 }
2587
fillRect(const QRectF & rect,const QBrush & brush)2588 void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
2589 {
2590 Q_D(QVGPaintEngine);
2591
2592 if (brush.style() == Qt::NoBrush)
2593 return;
2594
2595 // Check to see if we can use vgClear() for faster filling.
2596 if (brush.style() == Qt::SolidPattern && brush.isOpaque() &&
2597 clipTransformIsSimple(d->transform) && d->opacity == 1.0f &&
2598 clearRect(rect, brush.color())) {
2599 return;
2600 }
2601
2602 if (d->needsEmulation(brush)) {
2603 QPaintEngineEx::fillRect(rect, brush);
2604 return;
2605 }
2606
2607 #if !defined(QVG_NO_MODIFY_PATH)
2608 VGfloat coords[8];
2609 if (d->simpleTransform) {
2610 coords[0] = rect.x();
2611 coords[1] = rect.y();
2612 coords[2] = rect.x() + rect.width();
2613 coords[3] = coords[1];
2614 coords[4] = coords[2];
2615 coords[5] = rect.y() + rect.height();
2616 coords[6] = coords[0];
2617 coords[7] = coords[5];
2618 } else {
2619 QPointF tl = d->transform.map(rect.topLeft());
2620 QPointF tr = d->transform.map(rect.topRight());
2621 QPointF bl = d->transform.map(rect.bottomLeft());
2622 QPointF br = d->transform.map(rect.bottomRight());
2623 coords[0] = tl.x();
2624 coords[1] = tl.y();
2625 coords[2] = tr.x();
2626 coords[3] = tr.y();
2627 coords[4] = br.x();
2628 coords[5] = br.y();
2629 coords[6] = bl.x();
2630 coords[7] = bl.y();
2631 }
2632 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2633 d->fill(d->rectPath, brush);
2634 #else
2635 QPaintEngineEx::fillRect(rect, brush);
2636 #endif
2637 }
2638
fillRect(const QRectF & rect,const QColor & color)2639 void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color)
2640 {
2641 Q_D(QVGPaintEngine);
2642
2643 // Check to see if we can use vgClear() for faster filling.
2644 if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f && color.alpha() == 255 &&
2645 clearRect(rect, color)) {
2646 return;
2647 }
2648
2649 #if !defined(QVG_NO_MODIFY_PATH)
2650 VGfloat coords[8];
2651 if (d->simpleTransform) {
2652 coords[0] = rect.x();
2653 coords[1] = rect.y();
2654 coords[2] = rect.x() + rect.width();
2655 coords[3] = coords[1];
2656 coords[4] = coords[2];
2657 coords[5] = rect.y() + rect.height();
2658 coords[6] = coords[0];
2659 coords[7] = coords[5];
2660 } else {
2661 QPointF tl = d->transform.map(rect.topLeft());
2662 QPointF tr = d->transform.map(rect.topRight());
2663 QPointF bl = d->transform.map(rect.bottomLeft());
2664 QPointF br = d->transform.map(rect.bottomRight());
2665 coords[0] = tl.x();
2666 coords[1] = tl.y();
2667 coords[2] = tr.x();
2668 coords[3] = tr.y();
2669 coords[4] = br.x();
2670 coords[5] = br.y();
2671 coords[6] = bl.x();
2672 coords[7] = bl.y();
2673 }
2674 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2675 d->fill(d->rectPath, QBrush(color));
2676 #else
2677 QPaintEngineEx::fillRect(rect, QBrush(color));
2678 #endif
2679 }
2680
drawRoundedRect(const QRectF & rect,qreal xrad,qreal yrad,Qt::SizeMode mode)2681 void QVGPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode)
2682 {
2683 Q_D(QVGPaintEngine);
2684 if (d->needsEmulation()) {
2685 QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
2686 return;
2687 }
2688 if (d->simpleTransform) {
2689 QVGPainterState *s = state();
2690 VGPath vgpath = d->roundedRectPath(rect, xrad, yrad, mode);
2691 d->draw(vgpath, s->pen, s->brush);
2692 #if defined(QVG_NO_MODIFY_PATH)
2693 vgDestroyPath(vgpath);
2694 #endif
2695 } else {
2696 QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
2697 }
2698 }
2699
drawRects(const QRect * rects,int rectCount)2700 void QVGPaintEngine::drawRects(const QRect *rects, int rectCount)
2701 {
2702 #if !defined(QVG_NO_MODIFY_PATH)
2703 Q_D(QVGPaintEngine);
2704 if (d->needsEmulation()) {
2705 QPaintEngineEx::drawRects(rects, rectCount);
2706 return;
2707 }
2708 QVGPainterState *s = state();
2709 for (int i = 0; i < rectCount; ++i, ++rects) {
2710 VGfloat coords[8];
2711 if (d->simpleTransform) {
2712 coords[0] = rects->x();
2713 coords[1] = rects->y();
2714 coords[2] = rects->x() + rects->width();
2715 coords[3] = coords[1];
2716 coords[4] = coords[2];
2717 coords[5] = rects->y() + rects->height();
2718 coords[6] = coords[0];
2719 coords[7] = coords[5];
2720 } else {
2721 QPointF tl = d->transform.map(QPointF(rects->x(), rects->y()));
2722 QPointF tr = d->transform.map(QPointF(rects->x() + rects->width(),
2723 rects->y()));
2724 QPointF bl = d->transform.map(QPointF(rects->x(),
2725 rects->y() + rects->height()));
2726 QPointF br = d->transform.map(QPointF(rects->x() + rects->width(),
2727 rects->y() + rects->height()));
2728 coords[0] = tl.x();
2729 coords[1] = tl.y();
2730 coords[2] = tr.x();
2731 coords[3] = tr.y();
2732 coords[4] = br.x();
2733 coords[5] = br.y();
2734 coords[6] = bl.x();
2735 coords[7] = bl.y();
2736 }
2737 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2738 d->draw(d->rectPath, s->pen, s->brush);
2739 }
2740 #else
2741 QPaintEngineEx::drawRects(rects, rectCount);
2742 #endif
2743 }
2744
drawRects(const QRectF * rects,int rectCount)2745 void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount)
2746 {
2747 #if !defined(QVG_NO_MODIFY_PATH)
2748 Q_D(QVGPaintEngine);
2749 if (d->needsEmulation()) {
2750 QPaintEngineEx::drawRects(rects, rectCount);
2751 return;
2752 }
2753 QVGPainterState *s = state();
2754 for (int i = 0; i < rectCount; ++i, ++rects) {
2755 VGfloat coords[8];
2756 if (d->simpleTransform) {
2757 coords[0] = rects->x();
2758 coords[1] = rects->y();
2759 coords[2] = rects->x() + rects->width();
2760 coords[3] = coords[1];
2761 coords[4] = coords[2];
2762 coords[5] = rects->y() + rects->height();
2763 coords[6] = coords[0];
2764 coords[7] = coords[5];
2765 } else {
2766 QPointF tl = d->transform.map(rects->topLeft());
2767 QPointF tr = d->transform.map(rects->topRight());
2768 QPointF bl = d->transform.map(rects->bottomLeft());
2769 QPointF br = d->transform.map(rects->bottomRight());
2770 coords[0] = tl.x();
2771 coords[1] = tl.y();
2772 coords[2] = tr.x();
2773 coords[3] = tr.y();
2774 coords[4] = br.x();
2775 coords[5] = br.y();
2776 coords[6] = bl.x();
2777 coords[7] = bl.y();
2778 }
2779 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2780 d->draw(d->rectPath, s->pen, s->brush);
2781 }
2782 #else
2783 QPaintEngineEx::drawRects(rects, rectCount);
2784 #endif
2785 }
2786
drawLines(const QLine * lines,int lineCount)2787 void QVGPaintEngine::drawLines(const QLine *lines, int lineCount)
2788 {
2789 #if !defined(QVG_NO_MODIFY_PATH)
2790 Q_D(QVGPaintEngine);
2791 if (d->needsEmulation()) {
2792 QPaintEngineEx::drawLines(lines, lineCount);
2793 return;
2794 }
2795 QVGPainterState *s = state();
2796 for (int i = 0; i < lineCount; ++i, ++lines) {
2797 VGfloat coords[4];
2798 if (d->simpleTransform) {
2799 coords[0] = lines->x1();
2800 coords[1] = lines->y1();
2801 coords[2] = lines->x2();
2802 coords[3] = lines->y2();
2803 } else {
2804 QPointF p1 = d->transform.map(QPointF(lines->x1(), lines->y1()));
2805 QPointF p2 = d->transform.map(QPointF(lines->x2(), lines->y2()));
2806 coords[0] = p1.x();
2807 coords[1] = p1.y();
2808 coords[2] = p2.x();
2809 coords[3] = p2.y();
2810 }
2811 vgModifyPathCoords(d->linePath, 0, 2, coords);
2812 d->stroke(d->linePath, s->pen);
2813 }
2814 #else
2815 QPaintEngineEx::drawLines(lines, lineCount);
2816 #endif
2817 }
2818
drawLines(const QLineF * lines,int lineCount)2819 void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
2820 {
2821 #if !defined(QVG_NO_MODIFY_PATH)
2822 Q_D(QVGPaintEngine);
2823 if (d->needsEmulation()) {
2824 QPaintEngineEx::drawLines(lines, lineCount);
2825 return;
2826 }
2827 QVGPainterState *s = state();
2828 for (int i = 0; i < lineCount; ++i, ++lines) {
2829 VGfloat coords[4];
2830 if (d->simpleTransform) {
2831 coords[0] = lines->x1();
2832 coords[1] = lines->y1();
2833 coords[2] = lines->x2();
2834 coords[3] = lines->y2();
2835 } else {
2836 QPointF p1 = d->transform.map(lines->p1());
2837 QPointF p2 = d->transform.map(lines->p2());
2838 coords[0] = p1.x();
2839 coords[1] = p1.y();
2840 coords[2] = p2.x();
2841 coords[3] = p2.y();
2842 }
2843 vgModifyPathCoords(d->linePath, 0, 2, coords);
2844 d->stroke(d->linePath, s->pen);
2845 }
2846 #else
2847 QPaintEngineEx::drawLines(lines, lineCount);
2848 #endif
2849 }
2850
drawEllipse(const QRectF & r)2851 void QVGPaintEngine::drawEllipse(const QRectF &r)
2852 {
2853 // Based on the description of vguEllipse() in the OpenVG specification.
2854 // We don't use vguEllipse(), to avoid unnecessary library dependencies.
2855 Q_D(QVGPaintEngine);
2856 if (d->needsEmulation()) {
2857 QPaintEngineEx::drawEllipse(r);
2858 return;
2859 }
2860 if (d->simpleTransform) {
2861 QVGPainterState *s = state();
2862 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
2863 VG_PATH_DATATYPE_F,
2864 1.0f, // scale
2865 0.0f, // bias
2866 4, // segmentCapacityHint
2867 12, // coordCapacityHint
2868 VG_PATH_CAPABILITY_ALL);
2869 static VGubyte segments[4] = {
2870 VG_MOVE_TO_ABS,
2871 VG_SCCWARC_TO_REL,
2872 VG_SCCWARC_TO_REL,
2873 VG_CLOSE_PATH
2874 };
2875 VGfloat coords[12];
2876 VGfloat halfwid = r.width() / 2;
2877 VGfloat halfht = r.height() / 2;
2878 coords[0] = r.x() + r.width();
2879 coords[1] = r.y() + halfht;
2880 coords[2] = halfwid;
2881 coords[3] = halfht;
2882 coords[4] = 0.0f;
2883 coords[5] = -r.width();
2884 coords[6] = 0.0f;
2885 coords[7] = halfwid;
2886 coords[8] = halfht;
2887 coords[9] = 0.0f;
2888 coords[10] = r.width();
2889 coords[11] = 0.0f;
2890 vgAppendPathData(path, 4, segments, coords);
2891 d->draw(path, s->pen, s->brush);
2892 vgDestroyPath(path);
2893 } else {
2894 // The projective transform version of an ellipse is difficult.
2895 // Generate a QVectorPath containing cubic curves and transform that.
2896 QPaintEngineEx::drawEllipse(r);
2897 }
2898 }
2899
drawEllipse(const QRect & r)2900 void QVGPaintEngine::drawEllipse(const QRect &r)
2901 {
2902 drawEllipse(QRectF(r));
2903 }
2904
drawPath(const QPainterPath & path)2905 void QVGPaintEngine::drawPath(const QPainterPath &path)
2906 {
2907 // Shortcut past the QPainterPath -> QVectorPath conversion,
2908 // converting the QPainterPath directly into a VGPath.
2909 Q_D(QVGPaintEngine);
2910 if (d->needsEmulation()) {
2911 QPaintEngineEx::drawPath(path);
2912 return;
2913 }
2914 QVGPainterState *s = state();
2915 VGPath vgpath = d->painterPathToVGPath(path);
2916 if (path.fillRule() == Qt::OddEvenFill)
2917 d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD);
2918 else
2919 d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
2920 vgDestroyPath(vgpath);
2921 }
2922
drawPoints(const QPointF * points,int pointCount)2923 void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
2924 {
2925 #if !defined(QVG_NO_MODIFY_PATH)
2926 Q_D(QVGPaintEngine);
2927
2928 if (d->needsPenEmulation()) {
2929 QPaintEngineEx::drawPoints(points, pointCount);
2930 return;
2931 }
2932
2933 // Set up a new pen if necessary.
2934 QPen pen = state()->pen;
2935 if (pen.style() == Qt::NoPen)
2936 return;
2937 if (pen.capStyle() == Qt::FlatCap)
2938 pen.setCapStyle(Qt::SquareCap);
2939
2940 for (int i = 0; i < pointCount; ++i, ++points) {
2941 VGfloat coords[4];
2942 if (d->simpleTransform) {
2943 coords[0] = points->x();
2944 coords[1] = points->y();
2945 coords[2] = coords[0];
2946 coords[3] = coords[1];
2947 } else {
2948 QPointF p = d->transform.map(*points);
2949 coords[0] = p.x();
2950 coords[1] = p.y();
2951 coords[2] = coords[0];
2952 coords[3] = coords[1];
2953 }
2954 vgModifyPathCoords(d->linePath, 0, 2, coords);
2955 d->stroke(d->linePath, pen);
2956 }
2957 #else
2958 QPaintEngineEx::drawPoints(points, pointCount);
2959 #endif
2960 }
2961
drawPoints(const QPoint * points,int pointCount)2962 void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
2963 {
2964 #if !defined(QVG_NO_MODIFY_PATH)
2965 Q_D(QVGPaintEngine);
2966
2967 if (d->needsEmulation()) {
2968 QPaintEngineEx::drawPoints(points, pointCount);
2969 return;
2970 }
2971
2972 // Set up a new pen if necessary.
2973 QPen pen = state()->pen;
2974 if (pen.style() == Qt::NoPen)
2975 return;
2976 if (pen.capStyle() == Qt::FlatCap)
2977 pen.setCapStyle(Qt::SquareCap);
2978
2979 for (int i = 0; i < pointCount; ++i, ++points) {
2980 VGfloat coords[4];
2981 if (d->simpleTransform) {
2982 coords[0] = points->x();
2983 coords[1] = points->y();
2984 coords[2] = coords[0];
2985 coords[3] = coords[1];
2986 } else {
2987 QPointF p = d->transform.map(QPointF(*points));
2988 coords[0] = p.x();
2989 coords[1] = p.y();
2990 coords[2] = coords[0];
2991 coords[3] = coords[1];
2992 }
2993 vgModifyPathCoords(d->linePath, 0, 2, coords);
2994 d->stroke(d->linePath, pen);
2995 }
2996 #else
2997 QPaintEngineEx::drawPoints(points, pointCount);
2998 #endif
2999 }
3000
drawPolygon(const QPointF * points,int pointCount,PolygonDrawMode mode)3001 void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
3002 {
3003 Q_D(QVGPaintEngine);
3004
3005 if (d->needsEmulation()) {
3006 QPaintEngineEx::drawPolygon(points, pointCount, mode);
3007 return;
3008 }
3009
3010 QVGPainterState *s = state();
3011 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
3012 VG_PATH_DATATYPE_F,
3013 1.0f, // scale
3014 0.0f, // bias
3015 pointCount + 1, // segmentCapacityHint
3016 pointCount * 2, // coordCapacityHint
3017 VG_PATH_CAPABILITY_ALL);
3018 QVarLengthArray<VGfloat, 16> coords;
3019 QVarLengthArray<VGubyte, 10> segments;
3020 for (int i = 0; i < pointCount; ++i, ++points) {
3021 if (d->simpleTransform) {
3022 coords.append(points->x());
3023 coords.append(points->y());
3024 } else {
3025 QPointF temp = d->transform.map(*points);
3026 coords.append(temp.x());
3027 coords.append(temp.y());
3028 }
3029 if (i == 0)
3030 segments.append(VG_MOVE_TO_ABS);
3031 else
3032 segments.append(VG_LINE_TO_ABS);
3033 }
3034 if (mode != QPaintEngine::PolylineMode)
3035 segments.append(VG_CLOSE_PATH);
3036 vgAppendPathData(path, segments.count(),
3037 segments.constData(), coords.constData());
3038 switch (mode) {
3039 case QPaintEngine::WindingMode:
3040 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
3041 break;
3042
3043 case QPaintEngine::PolylineMode:
3044 d->stroke(path, s->pen);
3045 break;
3046
3047 default:
3048 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
3049 break;
3050 }
3051 vgDestroyPath(path);
3052 }
3053
drawPolygon(const QPoint * points,int pointCount,PolygonDrawMode mode)3054 void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
3055 {
3056 Q_D(QVGPaintEngine);
3057
3058 if (d->needsEmulation()) {
3059 QPaintEngineEx::drawPolygon(points, pointCount, mode);
3060 return;
3061 }
3062
3063 QVGPainterState *s = state();
3064 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
3065 VG_PATH_DATATYPE_F,
3066 1.0f, // scale
3067 0.0f, // bias
3068 pointCount + 1, // segmentCapacityHint
3069 pointCount * 2, // coordCapacityHint
3070 VG_PATH_CAPABILITY_ALL);
3071 QVarLengthArray<VGfloat, 16> coords;
3072 QVarLengthArray<VGubyte, 10> segments;
3073 for (int i = 0; i < pointCount; ++i, ++points) {
3074 if (d->simpleTransform) {
3075 coords.append(points->x());
3076 coords.append(points->y());
3077 } else {
3078 QPointF temp = d->transform.map(QPointF(*points));
3079 coords.append(temp.x());
3080 coords.append(temp.y());
3081 }
3082 if (i == 0)
3083 segments.append(VG_MOVE_TO_ABS);
3084 else
3085 segments.append(VG_LINE_TO_ABS);
3086 }
3087 if (mode != QPaintEngine::PolylineMode)
3088 segments.append(VG_CLOSE_PATH);
3089 vgAppendPathData(path, segments.count(),
3090 segments.constData(), coords.constData());
3091 switch (mode) {
3092 case QPaintEngine::WindingMode:
3093 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
3094 break;
3095
3096 case QPaintEngine::PolylineMode:
3097 d->stroke(path, s->pen);
3098 break;
3099
3100 default:
3101 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
3102 break;
3103 }
3104 vgDestroyPath(path);
3105 }
3106
setImageOptions()3107 void QVGPaintEnginePrivate::setImageOptions()
3108 {
3109 if (opacity != 1.0f && simpleTransform) {
3110 if (opacity != paintOpacity) {
3111 VGfloat values[4];
3112 values[0] = 1.0f;
3113 values[1] = 1.0f;
3114 values[2] = 1.0f;
3115 values[3] = opacity;
3116 vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
3117 paintOpacity = opacity;
3118 }
3119 if (fillPaint != opacityPaint) {
3120 vgSetPaint(opacityPaint, VG_FILL_PATH);
3121 fillPaint = opacityPaint;
3122 }
3123 setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3124 } else {
3125 setImageMode(VG_DRAW_IMAGE_NORMAL);
3126 }
3127 }
3128
systemStateChanged()3129 void QVGPaintEnginePrivate::systemStateChanged()
3130 {
3131 q->updateScissor();
3132 }
3133
drawVGImage(QVGPaintEnginePrivate * d,const QRectF & r,VGImage vgImg,const QSize & imageSize,const QRectF & sr)3134 static void drawVGImage(QVGPaintEnginePrivate *d,
3135 const QRectF& r, VGImage vgImg,
3136 const QSize& imageSize, const QRectF& sr)
3137 {
3138 if (vgImg == VG_INVALID_HANDLE)
3139 return;
3140 VGImage child = VG_INVALID_HANDLE;
3141
3142 if (sr.topLeft().isNull() && sr.size() == imageSize) {
3143 child = vgImg;
3144 } else {
3145 QRect src = sr.toRect();
3146 #if !defined(QT_SHIVAVG)
3147 child = vgChildImage(vgImg, src.x(), src.y(), src.width(), src.height());
3148 #else
3149 child = vgImg; // XXX: ShivaVG doesn't have vgChildImage().
3150 #endif
3151 }
3152
3153 QTransform transform(d->imageTransform);
3154 VGfloat scaleX = sr.width() == 0.0f ? 0.0f : r.width() / sr.width();
3155 VGfloat scaleY = sr.height() == 0.0f ? 0.0f : r.height() / sr.height();
3156 transform.translate(r.x(), r.y());
3157 transform.scale(scaleX, scaleY);
3158 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3159
3160 d->setImageOptions();
3161 vgDrawImage(child);
3162
3163 if(child != vgImg)
3164 vgDestroyImage(child);
3165 }
3166
drawVGImage(QVGPaintEnginePrivate * d,const QPointF & pos,VGImage vgImg)3167 static void drawVGImage(QVGPaintEnginePrivate *d,
3168 const QPointF& pos, VGImage vgImg)
3169 {
3170 if (vgImg == VG_INVALID_HANDLE)
3171 return;
3172
3173 QTransform transform(d->imageTransform);
3174 transform.translate(pos.x(), pos.y());
3175 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3176
3177 d->setImageOptions();
3178 vgDrawImage(vgImg);
3179 }
3180
drawImageTiled(QVGPaintEnginePrivate * d,const QRectF & r,const QImage & image,const QRectF & sr=QRectF ())3181 static void drawImageTiled(QVGPaintEnginePrivate *d,
3182 const QRectF &r,
3183 const QImage &image,
3184 const QRectF &sr = QRectF())
3185 {
3186 const int minTileSize = 16;
3187 int tileWidth = 512;
3188 int tileHeight = tileWidth;
3189
3190 VGImageFormat tileFormat = qt_vg_image_to_vg_format(image.format());
3191 VGImage tile = VG_INVALID_HANDLE;
3192 QVGImagePool *pool = QVGImagePool::instance();
3193 while (tile == VG_INVALID_HANDLE && tileWidth >= minTileSize) {
3194 tile = pool->createPermanentImage(tileFormat, tileWidth, tileHeight,
3195 VG_IMAGE_QUALITY_FASTER);
3196 if (tile == VG_INVALID_HANDLE) {
3197 tileWidth /= 2;
3198 tileHeight /= 2;
3199 }
3200 }
3201 if (tile == VG_INVALID_HANDLE) {
3202 qWarning("drawImageTiled: Failed to create %dx%d tile, giving up", tileWidth, tileHeight);
3203 return;
3204 }
3205
3206 VGfloat opacityMatrix[20] = {
3207 1.0f, 0.0f, 0.0f, 0.0f,
3208 0.0f, 1.0f, 0.0f, 0.0f,
3209 0.0f, 0.0f, 1.0f, 0.0f,
3210 0.0f, 0.0f, 0.0f, d->opacity,
3211 0.0f, 0.0f, 0.0f, 0.0f
3212 };
3213 VGImage tileWithOpacity = VG_INVALID_HANDLE;
3214 if (d->opacity != 1) {
3215 tileWithOpacity = pool->createPermanentImage(VG_sARGB_8888_PRE,
3216 tileWidth, tileHeight, VG_IMAGE_QUALITY_NONANTIALIASED);
3217 if (tileWithOpacity == VG_INVALID_HANDLE)
3218 qWarning("drawImageTiled: Failed to create extra tile, ignoring opacity");
3219 }
3220
3221 QRect sourceRect = sr.toRect();
3222 if (sourceRect.isNull())
3223 sourceRect = QRect(0, 0, image.width(), image.height());
3224
3225 VGfloat scaleX = r.width() / sourceRect.width();
3226 VGfloat scaleY = r.height() / sourceRect.height();
3227
3228 d->setImageOptions();
3229 VGImageQuality oldImageQuality = d->imageQuality;
3230 VGRenderingQuality oldRenderingQuality = d->renderingQuality;
3231 d->setImageQuality(VG_IMAGE_QUALITY_NONANTIALIASED);
3232 d->setRenderingQuality(VG_RENDERING_QUALITY_NONANTIALIASED);
3233
3234 for (int y = sourceRect.y(); y < sourceRect.height(); y += tileHeight) {
3235 int h = qMin(tileHeight, sourceRect.height() - y);
3236 if (h < 1)
3237 break;
3238 for (int x = sourceRect.x(); x < sourceRect.width(); x += tileWidth) {
3239 int w = qMin(tileWidth, sourceRect.width() - x);
3240 if (w < 1)
3241 break;
3242
3243 int bytesPerPixel = image.depth() / 8;
3244 const uchar *sptr = image.constBits() + x * bytesPerPixel + y * image.bytesPerLine();
3245 vgImageSubData(tile, sptr, image.bytesPerLine(), tileFormat, 0, 0, w, h);
3246
3247 QTransform transform(d->imageTransform);
3248 transform.translate(r.x() + x, r.y() + y);
3249 transform.scale(scaleX, scaleY);
3250 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3251
3252 VGImage actualTile = tile;
3253 if (tileWithOpacity != VG_INVALID_HANDLE) {
3254 vgColorMatrix(tileWithOpacity, actualTile, opacityMatrix);
3255 if (w < tileWidth || h < tileHeight)
3256 actualTile = vgChildImage(tileWithOpacity, 0, 0, w, h);
3257 else
3258 actualTile = tileWithOpacity;
3259 } else if (w < tileWidth || h < tileHeight) {
3260 actualTile = vgChildImage(tile, 0, 0, w, h);
3261 }
3262 vgDrawImage(actualTile);
3263
3264 if (actualTile != tile && actualTile != tileWithOpacity)
3265 vgDestroyImage(actualTile);
3266 }
3267 }
3268
3269 vgDestroyImage(tile);
3270 if (tileWithOpacity != VG_INVALID_HANDLE)
3271 vgDestroyImage(tileWithOpacity);
3272
3273 d->setImageQuality(oldImageQuality);
3274 d->setRenderingQuality(oldRenderingQuality);
3275 }
3276
3277 // Used by qpixmapfilter_vg.cpp to draw filtered VGImage's.
qt_vg_drawVGImage(QPainter * painter,const QPointF & pos,VGImage vgImg)3278 void qt_vg_drawVGImage(QPainter *painter, const QPointF& pos, VGImage vgImg)
3279 {
3280 QVGPaintEngine *engine =
3281 static_cast<QVGPaintEngine *>(painter->paintEngine());
3282 drawVGImage(engine->vgPrivate(), pos, vgImg);
3283 }
3284
3285 // Used by qpixmapfilter_vg.cpp to draw filtered VGImage's as a stencil.
qt_vg_drawVGImageStencil(QPainter * painter,const QPointF & pos,VGImage vgImg,const QBrush & brush)3286 void qt_vg_drawVGImageStencil
3287 (QPainter *painter, const QPointF& pos, VGImage vgImg, const QBrush& brush)
3288 {
3289 QVGPaintEngine *engine =
3290 static_cast<QVGPaintEngine *>(painter->paintEngine());
3291
3292 QVGPaintEnginePrivate *d = engine->vgPrivate();
3293
3294 QTransform transform(d->imageTransform);
3295 transform.translate(pos.x(), pos.y());
3296 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3297
3298 d->ensureBrush(brush);
3299 d->setImageMode(VG_DRAW_IMAGE_STENCIL);
3300 vgDrawImage(vgImg);
3301 }
3302
canVgWritePixels(const QImage & image) const3303 bool QVGPaintEngine::canVgWritePixels(const QImage &image) const
3304 {
3305 Q_D(const QVGPaintEngine);
3306
3307 // qt_vg_image_to_vg_format returns VG_sARGB_8888 as
3308 // fallback case if no matching VG format is found.
3309 // If given image format is not Format_ARGB32 and returned
3310 // format is VG_sARGB_8888, it means that no match was
3311 // found. In that case vgWritePixels cannot be used.
3312 // Also 1-bit formats cannot be used directly either.
3313 if ((image.format() != QImage::Format_ARGB32
3314 && qt_vg_image_to_vg_format(image.format()) == VG_sARGB_8888)
3315 || image.depth() == 1) {
3316 return false;
3317 }
3318
3319 // vgWritePixels ignores masking, blending and xforms so we can only use it if
3320 // ALL of the following conditions are true:
3321 // - It is a simple translate, or a scale of -1 on the y-axis (inverted)
3322 // - The opacity is totally opaque
3323 // - The composition mode is "source" OR "source over" provided the image is opaque
3324 return ( d->imageTransform.type() <= QTransform::TxScale
3325 && d->imageTransform.m11() == 1.0 && qAbs(d->imageTransform.m22()) == 1.0)
3326 && d->opacity == 1.0f
3327 && (d->blendMode == VG_BLEND_SRC || (d->blendMode == VG_BLEND_SRC_OVER &&
3328 !image.hasAlphaChannel()));
3329 }
3330
drawPixmap(const QRectF & r,const QPixmap & pm,const QRectF & sr)3331 void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
3332 {
3333 QPixmapData *pd = pm.pixmapData();
3334 if (!pd)
3335 return; // null QPixmap
3336 if (pd->classId() == QPixmapData::OpenVGClass) {
3337 Q_D(QVGPaintEngine);
3338 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3339 if (!vgpd->isValid())
3340 return;
3341 if (d->simpleTransform)
3342 drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr);
3343 else
3344 drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr);
3345
3346 if (vgpd->vgImage != VG_INVALID_HANDLE)
3347 return;
3348
3349 vgpd->source.beginDataAccess();
3350 drawImage(r, vgpd->source.imageRef(), sr, Qt::AutoColor);
3351 vgpd->source.endDataAccess(true);
3352 } else {
3353 drawImage(r, *(pd->buffer()), sr, Qt::AutoColor);
3354 }
3355 }
3356
drawPixmap(const QPointF & pos,const QPixmap & pm)3357 void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm)
3358 {
3359 QPixmapData *pd = pm.pixmapData();
3360 if (!pd)
3361 return; // null QPixmap
3362 if (pd->classId() == QPixmapData::OpenVGClass) {
3363 Q_D(QVGPaintEngine);
3364 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3365 if (!vgpd->isValid())
3366 return;
3367 if (d->simpleTransform)
3368 drawVGImage(d, pos, vgpd->toVGImage());
3369 else
3370 drawVGImage(d, pos, vgpd->toVGImage(d->opacity));
3371
3372 if (vgpd->vgImage != VG_INVALID_HANDLE)
3373 return;
3374
3375 vgpd->source.beginDataAccess();
3376 drawImage(pos, vgpd->source.imageRef());
3377 vgpd->source.endDataAccess(true);
3378 } else {
3379 drawImage(pos, *(pd->buffer()));
3380 }
3381 }
3382
drawImage(const QRectF & r,const QImage & image,const QRectF & sr,Qt::ImageConversionFlags flags)3383 void QVGPaintEngine::drawImage
3384 (const QRectF &r, const QImage &image, const QRectF &sr,
3385 Qt::ImageConversionFlags flags)
3386 {
3387 Q_D(QVGPaintEngine);
3388 if (image.isNull())
3389 return;
3390 VGImage vgImg;
3391 if (d->simpleTransform || d->opacity == 1.0f)
3392 vgImg = toVGImageSubRect(image, sr.toRect(), flags);
3393 else
3394 vgImg = toVGImageWithOpacitySubRect(image, d->opacity, sr.toRect());
3395 if (vgImg != VG_INVALID_HANDLE) {
3396 if (r.size() == sr.size()) {
3397 drawVGImage(d, r.topLeft(), vgImg);
3398 } else {
3399 drawVGImage(d, r, vgImg, sr.size().toSize(),
3400 QRectF(QPointF(0, 0), sr.size()));
3401 }
3402 } else {
3403 if (canVgWritePixels(image) && (r.size() == sr.size()) && !flags) {
3404 // Optimization for straight blits, no blending
3405 int x = sr.x();
3406 int y = sr.y();
3407 int bpp = image.depth() >> 3; // bytes
3408 int offset = 0;
3409 int bpl = image.bytesPerLine();
3410 if (d->imageTransform.m22() < 0) {
3411 // inverted
3412 offset = ((y + sr.height()) * bpl) - ((image.width() - x) * bpp);
3413 bpl = -bpl;
3414 } else {
3415 offset = (y * bpl) + (x * bpp);
3416 }
3417 const uchar *bits = image.constBits() + offset;
3418
3419 QPointF mapped = d->imageTransform.map(r.topLeft());
3420 vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
3421 mapped.x(), mapped.y() - sr.height(), r.width(), r.height());
3422 return;
3423 } else {
3424 // Monochrome images need to use the vgChildImage() path.
3425 vgImg = toVGImage(image, flags);
3426 if (vgImg == VG_INVALID_HANDLE)
3427 drawImageTiled(d, r, image, sr);
3428 else
3429 drawVGImage(d, r, vgImg, image.size(), sr);
3430 }
3431 }
3432 vgDestroyImage(vgImg);
3433 }
3434
drawImage(const QPointF & pos,const QImage & image)3435 void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image)
3436 {
3437 Q_D(QVGPaintEngine);
3438 if (image.isNull())
3439 return;
3440 VGImage vgImg;
3441 if (canVgWritePixels(image)) {
3442 // Optimization for straight blits, no blending
3443 bool inverted = (d->imageTransform.m22() < 0);
3444 const uchar *bits = inverted ? image.constBits() + image.byteCount() - image.bytesPerLine() : image.constBits();
3445 int bpl = inverted ? -image.bytesPerLine() : image.bytesPerLine();
3446
3447 QPointF mapped = d->imageTransform.map(pos);
3448 vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
3449 mapped.x(), mapped.y() - image.height(), image.width(), image.height());
3450 return;
3451 } else if (d->simpleTransform || d->opacity == 1.0f) {
3452 vgImg = toVGImage(image);
3453 } else {
3454 vgImg = toVGImageWithOpacity(image, d->opacity);
3455 }
3456 if (vgImg == VG_INVALID_HANDLE)
3457 drawImageTiled(d, QRectF(pos, image.size()), image);
3458 else
3459 drawVGImage(d, pos, vgImg);
3460 vgDestroyImage(vgImg);
3461 }
3462
drawTiledPixmap(const QRectF & r,const QPixmap & pixmap,const QPointF & s)3463 void QVGPaintEngine::drawTiledPixmap
3464 (const QRectF &r, const QPixmap &pixmap, const QPointF &s)
3465 {
3466 QBrush brush(state()->pen.color(), pixmap);
3467 QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
3468 brush.setTransform(xform);
3469 fillRect(r, brush);
3470 }
3471
3472 // Best performance will be achieved with QDrawPixmaps::OpaqueHint
3473 // (i.e. no opacity), no rotation or scaling, and drawing the full
3474 // pixmap rather than parts of the pixmap. Even having just one of
3475 // these conditions will improve performance.
drawPixmapFragments(const QPainter::PixmapFragment * drawingData,int dataCount,const QPixmap & pixmap,QFlags<QPainter::PixmapFragmentHint> hints)3476 void QVGPaintEngine::drawPixmapFragments(const QPainter::PixmapFragment *drawingData, int dataCount,
3477 const QPixmap &pixmap, QFlags<QPainter::PixmapFragmentHint> hints)
3478 {
3479 #if !defined(QT_SHIVAVG)
3480 Q_D(QVGPaintEngine);
3481
3482 // If the pixmap is not VG, or the transformation is projective,
3483 // then fall back to the default implementation.
3484 QPixmapData *pd = pixmap.pixmapData();
3485 if (!pd)
3486 return; // null QPixmap
3487 if (pd->classId() != QPixmapData::OpenVGClass || !d->simpleTransform) {
3488 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3489 return;
3490 }
3491
3492 // Bail out if nothing to do.
3493 if (dataCount <= 0)
3494 return;
3495
3496 // Bail out if we don't have a usable VGImage for the pixmap.
3497 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3498 if (!vgpd->isValid())
3499 return;
3500 VGImage vgImg = vgpd->toVGImage();
3501 if (vgImg == VG_INVALID_HANDLE)
3502 return;
3503
3504 // We cache the results of any vgChildImage() calls because the
3505 // same child is very likely to be used over and over in particle
3506 // systems. However, performance is even better if vgChildImage()
3507 // isn't needed at all, so use full source rects where possible.
3508 QVarLengthArray<VGImage> cachedImages;
3509 QVarLengthArray<QRect> cachedSources;
3510
3511 // Select the opacity paint object.
3512 if ((hints & QPainter::OpaqueHint) != 0 && d->opacity == 1.0f) {
3513 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3514 } else {
3515 hints = 0;
3516 if (d->fillPaint != d->opacityPaint) {
3517 vgSetPaint(d->opacityPaint, VG_FILL_PATH);
3518 d->fillPaint = d->opacityPaint;
3519 }
3520 }
3521
3522 for (int i = 0; i < dataCount; ++i) {
3523 QTransform transform(d->imageTransform);
3524 transform.translate(drawingData[i].x, drawingData[i].y);
3525 transform.rotate(drawingData[i].rotation);
3526
3527 VGImage child;
3528 QSize imageSize = vgpd->size();
3529 QRectF sr(drawingData[i].sourceLeft, drawingData[i].sourceTop,
3530 drawingData[i].width, drawingData[i].height);
3531 if (sr.topLeft().isNull() && sr.size() == imageSize) {
3532 child = vgImg;
3533 } else {
3534 // Look for a previous child with the same source rectangle
3535 // to avoid constantly calling vgChildImage()/vgDestroyImage().
3536 QRect src = sr.toRect();
3537 int j;
3538 for (j = 0; j < cachedSources.size(); ++j) {
3539 if (cachedSources[j] == src)
3540 break;
3541 }
3542 if (j < cachedSources.size()) {
3543 child = cachedImages[j];
3544 } else {
3545 child = vgChildImage
3546 (vgImg, src.x(), src.y(), src.width(), src.height());
3547 cachedImages.append(child);
3548 cachedSources.append(src);
3549 }
3550 }
3551
3552 VGfloat scaleX = drawingData[i].scaleX;
3553 VGfloat scaleY = drawingData[i].scaleY;
3554 transform.translate(-0.5 * scaleX * sr.width(),
3555 -0.5 * scaleY * sr.height());
3556 transform.scale(scaleX, scaleY);
3557 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3558
3559 if ((hints & QPainter::OpaqueHint) == 0) {
3560 qreal opacity = d->opacity * drawingData[i].opacity;
3561 if (opacity != 1.0f) {
3562 if (d->paintOpacity != opacity) {
3563 VGfloat values[4];
3564 values[0] = 1.0f;
3565 values[1] = 1.0f;
3566 values[2] = 1.0f;
3567 values[3] = opacity;
3568 d->paintOpacity = opacity;
3569 vgSetParameterfv
3570 (d->opacityPaint, VG_PAINT_COLOR, 4, values);
3571 }
3572 d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3573 } else {
3574 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3575 }
3576 }
3577
3578 vgDrawImage(child);
3579 }
3580
3581 // Destroy the cached child sub-images.
3582 for (int i = 0; i < cachedImages.size(); ++i)
3583 vgDestroyImage(cachedImages[i]);
3584 #else
3585 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3586 #endif
3587 }
3588
QVGFontEngineCleaner(QVGPaintEnginePrivate * d)3589 QVGFontEngineCleaner::QVGFontEngineCleaner(QVGPaintEnginePrivate *d)
3590 : QObject(), d_ptr(d)
3591 {
3592 }
3593
~QVGFontEngineCleaner()3594 QVGFontEngineCleaner::~QVGFontEngineCleaner()
3595 {
3596 }
3597
fontEngineDestroyed()3598 void QVGFontEngineCleaner::fontEngineDestroyed()
3599 {
3600 #if !defined(QVG_NO_DRAW_GLYPHS)
3601 QFontEngine *engine = static_cast<QFontEngine *>(sender());
3602 QVGFontCache::Iterator it = d_ptr->fontCache.find(engine);
3603 if (it != d_ptr->fontCache.end()) {
3604 delete it.value();
3605 d_ptr->fontCache.erase(it);
3606 }
3607 #endif
3608 }
3609
3610 #if !defined(QVG_NO_DRAW_GLYPHS)
3611
QVGFontGlyphCache()3612 QVGFontGlyphCache::QVGFontGlyphCache()
3613 {
3614 font = vgCreateFont(0);
3615 scaleX = scaleY = 0.0;
3616 invertedGlyphs = false;
3617 memset(cachedGlyphsMask, 0, sizeof(cachedGlyphsMask));
3618 }
3619
~QVGFontGlyphCache()3620 QVGFontGlyphCache::~QVGFontGlyphCache()
3621 {
3622 if (font != VG_INVALID_HANDLE)
3623 vgDestroyFont(font);
3624 }
3625
setScaleFromText(const QFont & font,QFontEngine * fontEngine)3626 void QVGFontGlyphCache::setScaleFromText(const QFont &font, QFontEngine *fontEngine)
3627 {
3628 QFontInfo fi(font);
3629 qreal pixelSize = fi.pixelSize();
3630 qreal emSquare = fontEngine->properties().emSquare.toReal();
3631 scaleX = scaleY = static_cast<VGfloat>(pixelSize / emSquare);
3632 }
3633
cacheGlyphs(QVGPaintEnginePrivate * d,QFontEngine * fontEngine,const glyph_t * g,int count)3634 void QVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d,
3635 QFontEngine *fontEngine,
3636 const glyph_t *g, int count)
3637 {
3638 VGfloat origin[2];
3639 VGfloat escapement[2];
3640 glyph_metrics_t metrics;
3641 // Some Qt font engines don't set yoff in getUnscaledGlyph().
3642 // Zero the metric structure so that everything has a default value.
3643 memset(&metrics, 0, sizeof(metrics));
3644 while (count-- > 0) {
3645 // Skip this glyph if we have already cached it before.
3646 glyph_t glyph = *g++;
3647 if (glyph < 256) {
3648 if ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0)
3649 continue;
3650 cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32));
3651 } else if (cachedGlyphs.contains(glyph)) {
3652 continue;
3653 } else {
3654 cachedGlyphs.insert(glyph);
3655 }
3656 #if !defined(QVG_NO_IMAGE_GLYPHS)
3657 Q_UNUSED(d);
3658 QImage scaledImage = fontEngine->alphaMapForGlyph(glyph);
3659 VGImage vgImage = VG_INVALID_HANDLE;
3660 metrics = fontEngine->boundingBox(glyph);
3661 if (!scaledImage.isNull()) { // Not a space character
3662 if (scaledImage.format() == QImage::Format_Indexed8) {
3663 vgImage = vgCreateImage(VG_A_8, scaledImage.width(), scaledImage.height(), VG_IMAGE_QUALITY_FASTER);
3664 vgImageSubData(vgImage, scaledImage.constBits(), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height());
3665 } else if (scaledImage.format() == QImage::Format_Mono) {
3666 QImage img = scaledImage.convertToFormat(QImage::Format_Indexed8);
3667 vgImage = vgCreateImage(VG_A_8, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
3668 vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height());
3669 } else {
3670 QImage img = scaledImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
3671 vgImage = vgCreateImage(VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
3672 vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height());
3673 }
3674 }
3675 origin[0] = -metrics.x.toReal();
3676 origin[1] = -metrics.y.toReal();
3677 escapement[0] = 0;
3678 escapement[1] = 0;
3679 vgSetGlyphToImage(font, glyph, vgImage, origin, escapement);
3680 vgDestroyImage(vgImage); // Reduce reference count.
3681 #else
3682 // Calculate the path for the glyph and cache it.
3683 QPainterPath path;
3684 fontEngine->getUnscaledGlyph(glyph, &path, &metrics);
3685 VGPath vgPath;
3686 if (!path.isEmpty()) {
3687 vgPath = d->painterPathToVGPath(path);
3688 } else {
3689 // Probably a "space" character with no visible outline.
3690 vgPath = VG_INVALID_HANDLE;
3691 }
3692 origin[0] = 0;
3693 origin[1] = 0;
3694 escapement[0] = 0;
3695 escapement[1] = 0;
3696 vgSetGlyphToPath(font, glyph, vgPath, VG_FALSE, origin, escapement);
3697 vgDestroyPath(vgPath); // Reduce reference count.
3698 #endif // !defined(QVG_NO_IMAGE_GLYPHS)
3699 }
3700 }
3701
3702 #endif // !defined(QVG_NO_DRAW_GLYPHS)
3703
drawTextItem(const QPointF & p,const QTextItem & textItem)3704 void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3705 {
3706 #if !defined(QVG_NO_DRAW_GLYPHS)
3707 Q_D(QVGPaintEngine);
3708 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3709
3710 // If we are not using a simple transform, then fall back
3711 // to the default Qt path stroking algorithm.
3712 if (!d->simpleTransform) {
3713 QPaintEngineEx::drawTextItem(p, textItem);
3714 return;
3715 }
3716
3717 if (d->needsPenEmulation()) {
3718 QPaintEngineEx::drawTextItem(p, textItem);
3719 return;
3720 }
3721
3722 // Get the glyphs and positions associated with the text item.
3723 QVarLengthArray<QFixedPoint> positions;
3724 QVarLengthArray<glyph_t> glyphs;
3725 QTransform matrix;
3726 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3727
3728 if (!drawCachedGlyphs(glyphs.size(), glyphs.data(), ti.font(), ti.fontEngine, p, positions.data()))
3729 QPaintEngineEx::drawTextItem(p, textItem);
3730 #else
3731 // OpenGL 1.0 does not have support for VGFont and glyphs,
3732 // so fall back to the default Qt path stroking algorithm.
3733 QPaintEngineEx::drawTextItem(p, textItem);
3734 #endif
3735 }
3736
drawStaticTextItem(QStaticTextItem * textItem)3737 void QVGPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3738 {
3739 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->font, textItem->fontEngine(),
3740 QPointF(0, 0), textItem->glyphPositions);
3741 }
3742
drawCachedGlyphs(int numGlyphs,const glyph_t * glyphs,const QFont & font,QFontEngine * fontEngine,const QPointF & p,const QFixedPoint * positions)3743 bool QVGPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFont &font,
3744 QFontEngine *fontEngine, const QPointF &p,
3745 const QFixedPoint *positions)
3746 {
3747 #if !defined(QVG_NO_DRAW_GLYPHS)
3748 Q_D(QVGPaintEngine);
3749
3750 // Find the glyph cache for this font.
3751 QVGFontCache::ConstIterator it = d->fontCache.constFind(fontEngine);
3752 QVGFontGlyphCache *glyphCache;
3753 if (it != d->fontCache.constEnd()) {
3754 glyphCache = it.value();
3755 } else {
3756 #ifdef Q_OS_SYMBIAN
3757 glyphCache = new QSymbianVGFontGlyphCache();
3758 #else
3759 glyphCache = new QVGFontGlyphCache();
3760 #endif
3761 if (glyphCache->font == VG_INVALID_HANDLE) {
3762 qWarning("QVGPaintEngine::drawTextItem: OpenVG fonts are not supported by the OpenVG engine");
3763 delete glyphCache;
3764 return false;
3765 }
3766 glyphCache->setScaleFromText(font, fontEngine);
3767 d->fontCache.insert(fontEngine, glyphCache);
3768 if (!d->fontEngineCleaner)
3769 d->fontEngineCleaner = new QVGFontEngineCleaner(d);
3770 QObject::connect(fontEngine, SIGNAL(destroyed()),
3771 d->fontEngineCleaner, SLOT(fontEngineDestroyed()));
3772 }
3773
3774 // Set the transformation to use for drawing the current glyphs.
3775 QTransform glyphTransform(d->pathTransform);
3776 if (d->transform.type() <= QTransform::TxTranslate) {
3777 // Prevent blurriness of unscaled, unrotated text by forcing integer coordinates.
3778 glyphTransform.translate(
3779 floor(p.x() + glyphTransform.dx() + aliasedCoordinateDelta) - glyphTransform.dx(),
3780 floor(p.y() - glyphTransform.dy() + aliasedCoordinateDelta) + glyphTransform.dy());
3781 } else {
3782 glyphTransform.translate(p.x(), p.y());
3783 }
3784 #if defined(QVG_NO_IMAGE_GLYPHS)
3785 glyphTransform.scale(glyphCache->scaleX, glyphCache->scaleY);
3786 #endif
3787
3788 // Some glyph caches can create the VGImage upright
3789 if (glyphCache->invertedGlyphs)
3790 glyphTransform.scale(1, -1);
3791
3792 d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, glyphTransform);
3793
3794 // Add the glyphs from the text item into the glyph cache.
3795 glyphCache->cacheGlyphs(d, fontEngine, glyphs, numGlyphs);
3796
3797 // Create the array of adjustments between glyphs
3798 QVarLengthArray<VGfloat> adjustments_x(numGlyphs);
3799 QVarLengthArray<VGfloat> adjustments_y(numGlyphs);
3800 for (int i = 1; i < numGlyphs; ++i) {
3801 adjustments_x[i-1] = (positions[i].x - positions[i-1].x).round().toReal();
3802 adjustments_y[i-1] = (positions[i].y - positions[i-1].y).round().toReal();
3803 }
3804
3805 // Set the glyph drawing origin.
3806 VGfloat origin[2];
3807 origin[0] = positions[0].x.round().toReal();
3808 origin[1] = positions[0].y.round().toReal();
3809 vgSetfv(VG_GLYPH_ORIGIN, 2, origin);
3810
3811 // Fast anti-aliasing for paths, better for images.
3812 #if !defined(QVG_NO_IMAGE_GLYPHS)
3813 d->setImageQuality(VG_IMAGE_QUALITY_BETTER);
3814 d->setImageMode(VG_DRAW_IMAGE_STENCIL);
3815 #else
3816 d->setRenderingQuality(VG_RENDERING_QUALITY_FASTER);
3817 #endif
3818
3819 // Draw the glyphs. We need to fill with the brush associated with
3820 // the Qt pen, not the Qt brush.
3821 d->ensureBrush(state()->pen.brush());
3822 vgDrawGlyphs(glyphCache->font, numGlyphs, (VGuint*)glyphs,
3823 adjustments_x.data(), adjustments_y.data(), VG_FILL_PATH, VG_TRUE);
3824 return true;
3825 #else
3826 Q_UNUSED(numGlyphs);
3827 Q_UNUSED(glyphs);
3828 Q_UNUSED(font);
3829 Q_UNUSED(fontEngine);
3830 Q_UNUSED(p);
3831 Q_UNUSED(positions);
3832 return false;
3833 #endif
3834 }
3835
setState(QPainterState * s)3836 void QVGPaintEngine::setState(QPainterState *s)
3837 {
3838 Q_D(QVGPaintEngine);
3839 QPaintEngineEx::setState(s);
3840 QVGPainterState *ps = static_cast<QVGPainterState *>(s);
3841 if (ps->isNew) {
3842 // Newly created state object. The call to setState()
3843 // will either be followed by a call to begin(), or we are
3844 // setting the state as part of a save().
3845 ps->isNew = false;
3846 } else {
3847 // This state object was set as part of a restore().
3848 restoreState(d->dirty);
3849 d->dirty = ps->savedDirty;
3850 }
3851 }
3852
beginNativePainting()3853 void QVGPaintEngine::beginNativePainting()
3854 {
3855 Q_D(QVGPaintEngine);
3856
3857 // About to enter raw VG mode: flush pending changes and make
3858 // sure that all matrices are set to the current transformation.
3859 QVGPainterState *s = this->state();
3860 d->ensurePen(s->pen);
3861 d->ensureBrush(s->brush);
3862 d->ensurePathTransform();
3863 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform);
3864 #if !defined(QVG_NO_DRAW_GLYPHS)
3865 d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform);
3866 #endif
3867 vgSeti(VG_SCISSORING, VG_FALSE);
3868 vgSeti(VG_MASKING, VG_FALSE);
3869 d->rawVG = true;
3870 }
3871
endNativePainting()3872 void QVGPaintEngine::endNativePainting()
3873 {
3874 Q_D(QVGPaintEngine);
3875 // Exiting raw VG mode: force all state values to be
3876 // explicitly set on the VG engine to undo any changes
3877 // that were made by the raw VG function calls.
3878 QPaintEngine::DirtyFlags dirty = d->dirty;
3879 d->clearModes();
3880 d->forcePenChange = true;
3881 d->forceBrushChange = true;
3882 d->penType = (VGPaintType)0;
3883 d->brushType = (VGPaintType)0;
3884 d->clearColor = QColor();
3885 d->fillPaint = d->brushPaint;
3886 d->scissorDirty = true;
3887 restoreState(QPaintEngine::AllDirty);
3888 d->dirty = dirty;
3889 d->rawVG = false;
3890 vgSetPaint(d->penPaint, VG_STROKE_PATH);
3891 vgSetPaint(d->brushPaint, VG_FILL_PATH);
3892 }
3893
pixmapFilter(int type,const QPixmapFilter * prototype)3894 QPixmapFilter *QVGPaintEngine::pixmapFilter(int type, const QPixmapFilter *prototype)
3895 {
3896 #if !defined(QT_SHIVAVG)
3897 Q_D(QVGPaintEngine);
3898 switch (type) {
3899 case QPixmapFilter::ConvolutionFilter:
3900 if (!d->convolutionFilter)
3901 d->convolutionFilter.reset(new QVGPixmapConvolutionFilter);
3902 return d->convolutionFilter.data();
3903 case QPixmapFilter::ColorizeFilter:
3904 if (!d->colorizeFilter)
3905 d->colorizeFilter.reset(new QVGPixmapColorizeFilter);
3906 return d->colorizeFilter.data();
3907 case QPixmapFilter::DropShadowFilter:
3908 if (!d->dropShadowFilter)
3909 d->dropShadowFilter.reset(new QVGPixmapDropShadowFilter);
3910 return d->dropShadowFilter.data();
3911 case QPixmapFilter::BlurFilter:
3912 if (!d->blurFilter)
3913 d->blurFilter.reset(new QVGPixmapBlurFilter);
3914 return d->blurFilter.data();
3915 default: break;
3916 }
3917 #endif
3918 return QPaintEngineEx::pixmapFilter(type, prototype);
3919 }
3920
restoreState(QPaintEngine::DirtyFlags dirty)3921 void QVGPaintEngine::restoreState(QPaintEngine::DirtyFlags dirty)
3922 {
3923 Q_D(QVGPaintEngine);
3924
3925 // Restore the pen, brush, and other settings.
3926 if ((dirty & QPaintEngine::DirtyBrushOrigin) != 0)
3927 brushOriginChanged();
3928 d->fillRule = 0;
3929 d->clearColor = QColor();
3930 if ((dirty & QPaintEngine::DirtyOpacity) != 0)
3931 opacityChanged();
3932 if ((dirty & QPaintEngine::DirtyTransform) != 0)
3933 transformChanged();
3934 if ((dirty & QPaintEngine::DirtyCompositionMode) != 0)
3935 compositionModeChanged();
3936 if ((dirty & QPaintEngine::DirtyHints) != 0)
3937 renderHintsChanged();
3938 if ((dirty & (QPaintEngine::DirtyClipRegion |
3939 QPaintEngine::DirtyClipPath |
3940 QPaintEngine::DirtyClipEnabled)) != 0) {
3941 d->maskValid = false;
3942 d->maskIsSet = false;
3943 d->scissorMask = false;
3944 d->maskRect = QRect();
3945 d->scissorDirty = true;
3946 clipEnabledChanged();
3947 }
3948
3949 #if defined(QVG_SCISSOR_CLIP)
3950 if ((dirty & (QPaintEngine::DirtyClipRegion |
3951 QPaintEngine::DirtyClipPath |
3952 QPaintEngine::DirtyClipEnabled)) == 0) {
3953 updateScissor();
3954 }
3955 #else
3956 updateScissor();
3957 #endif
3958 }
3959
fillRegion(const QRegion & region,const QColor & color,const QSize & surfaceSize)3960 void QVGPaintEngine::fillRegion
3961 (const QRegion& region, const QColor& color, const QSize& surfaceSize)
3962 {
3963 Q_D(QVGPaintEngine);
3964 if (d->clearColor != color || d->clearOpacity != 1.0f) {
3965 VGfloat values[4];
3966 values[0] = color.redF();
3967 values[1] = color.greenF();
3968 values[2] = color.blueF();
3969 values[3] = color.alphaF();
3970 vgSetfv(VG_CLEAR_COLOR, 4, values);
3971 d->clearColor = color;
3972 d->clearOpacity = 1.0f;
3973 }
3974 if (region.rectCount() == 1) {
3975 QRect r = region.boundingRect();
3976 vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
3977 r.width(), r.height());
3978 } else {
3979 const QVector<QRect> rects = region.rects();
3980 for (int i = 0; i < rects.size(); ++i) {
3981 QRect r = rects.at(i);
3982 vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
3983 r.width(), r.height());
3984 }
3985 }
3986 }
3987
3988 #if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL)
3989
QVGCompositionHelper()3990 QVGCompositionHelper::QVGCompositionHelper()
3991 {
3992 d = qt_vg_create_paint_engine()->vgPrivate();
3993 }
3994
~QVGCompositionHelper()3995 QVGCompositionHelper::~QVGCompositionHelper()
3996 {
3997 }
3998
startCompositing(const QSize & screenSize)3999 void QVGCompositionHelper::startCompositing(const QSize& screenSize)
4000 {
4001 this->screenSize = screenSize;
4002 clearScissor();
4003 d->setBlendMode(VG_BLEND_SRC_OVER);
4004 }
4005
endCompositing()4006 void QVGCompositionHelper::endCompositing()
4007 {
4008 clearScissor();
4009 }
4010
blitWindow(VGImage image,const QSize & imageSize,const QRect & rect,const QPoint & topLeft,int opacity)4011 void QVGCompositionHelper::blitWindow
4012 (VGImage image, const QSize& imageSize,
4013 const QRect& rect, const QPoint& topLeft, int opacity)
4014 {
4015 if (image == VG_INVALID_HANDLE)
4016 return;
4017
4018 // Determine which sub rectangle of the window to draw.
4019 QRect sr = rect.translated(-topLeft);
4020
4021 if (opacity >= 255) {
4022 // Fully opaque: use vgSetPixels() to directly copy the sub-region.
4023 int y = screenSize.height() - (rect.bottom() + 1);
4024 vgSetPixels(rect.x(), y, image, sr.x(),
4025 imageSize.height() - (sr.y() + sr.height()),
4026 sr.width(), sr.height());
4027 } else {
4028 // Extract the child image that we want to draw.
4029 VGImage child;
4030 if (sr.topLeft().isNull() && sr.size() == imageSize)
4031 child = image;
4032 else {
4033 child = vgChildImage
4034 (image, sr.x(), imageSize.height() - (sr.y() + sr.height()),
4035 sr.width(), sr.height());
4036 }
4037
4038 // Set the image transform.
4039 QTransform transform;
4040 int y = screenSize.height() - (rect.bottom() + 1);
4041 transform.translate(rect.x() - 0.5f, y - 0.5f);
4042 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
4043
4044 // Enable opacity for image drawing if necessary.
4045 if (opacity != d->paintOpacity) {
4046 VGfloat values[4];
4047 values[0] = 1.0f;
4048 values[1] = 1.0f;
4049 values[2] = 1.0f;
4050 values[3] = ((VGfloat)opacity) / 255.0f;
4051 vgSetParameterfv(d->opacityPaint, VG_PAINT_COLOR, 4, values);
4052 d->paintOpacity = values[3];
4053 }
4054 if (d->fillPaint != d->opacityPaint) {
4055 vgSetPaint(d->opacityPaint, VG_FILL_PATH);
4056 d->fillPaint = d->opacityPaint;
4057 }
4058 d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
4059
4060 // Draw the child image.
4061 vgDrawImage(child);
4062
4063 // Destroy the child image.
4064 if(child != image)
4065 vgDestroyImage(child);
4066 }
4067 }
4068
fillBackgroundRect(const QRect & rect,QVGPaintEnginePrivate * d)4069 static void fillBackgroundRect(const QRect& rect, QVGPaintEnginePrivate *d)
4070 {
4071 VGfloat coords[8];
4072 coords[0] = rect.x();
4073 coords[1] = rect.y();
4074 coords[2] = rect.x() + rect.width();
4075 coords[3] = coords[1];
4076 coords[4] = coords[2];
4077 coords[5] = rect.y() + rect.height();
4078 coords[6] = coords[0];
4079 coords[7] = coords[5];
4080 #if !defined(QVG_NO_MODIFY_PATH)
4081 vgModifyPathCoords(d->rectPath, 0, 4, coords);
4082 vgDrawPath(d->rectPath, VG_FILL_PATH);
4083 #else
4084 Q_UNUSED(d);
4085 VGPath rectPath = vgCreatePath
4086 (VG_PATH_FORMAT_STANDARD,
4087 VG_PATH_DATATYPE_F,
4088 1.0f, // scale
4089 0.0f, // bias
4090 5, // segmentCapacityHint
4091 8, // coordCapacityHint
4092 VG_PATH_CAPABILITY_ALL);
4093 static VGubyte const segments[5] = {
4094 VG_MOVE_TO_ABS,
4095 VG_LINE_TO_ABS,
4096 VG_LINE_TO_ABS,
4097 VG_LINE_TO_ABS,
4098 VG_CLOSE_PATH
4099 };
4100 vgAppendPathData(rectPath, 5, segments, coords);
4101 vgDrawPath(rectPath, VG_FILL_PATH);
4102 vgDestroyPath(rectPath);
4103 #endif
4104 }
4105
fillBackground(const QRegion & region,const QBrush & brush)4106 void QVGCompositionHelper::fillBackground
4107 (const QRegion& region, const QBrush& brush)
4108 {
4109 if (brush.style() == Qt::SolidPattern) {
4110 // Use vgClear() to quickly fill the background.
4111 QColor color = brush.color();
4112 if (d->clearColor != color || d->clearOpacity != 1.0f) {
4113 VGfloat values[4];
4114 values[0] = color.redF();
4115 values[1] = color.greenF();
4116 values[2] = color.blueF();
4117 values[3] = color.alphaF();
4118 vgSetfv(VG_CLEAR_COLOR, 4, values);
4119 d->clearColor = color;
4120 d->clearOpacity = 1.0f;
4121 }
4122 if (region.rectCount() == 1) {
4123 QRect r = region.boundingRect();
4124 vgClear(r.x(), screenSize.height() - r.y() - r.height(),
4125 r.width(), r.height());
4126 } else {
4127 const QVector<QRect> rects = region.rects();
4128 for (int i = 0; i < rects.size(); ++i) {
4129 QRect r = rects.at(i);
4130 vgClear(r.x(), screenSize.height() - r.y() - r.height(),
4131 r.width(), r.height());
4132 }
4133 }
4134
4135 } else {
4136 // Set the path transform to the default viewport transformation.
4137 VGfloat devh = screenSize.height();
4138 QTransform viewport(1.0f, 0.0f, 0.0f,
4139 0.0f, -1.0f, 0.0f,
4140 0.0f, devh, 1.0f);
4141 d->setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, viewport);
4142
4143 // Set the brush to use to fill the background.
4144 d->ensureBrush(brush);
4145 d->setFillRule(VG_EVEN_ODD);
4146
4147 if (region.rectCount() == 1) {
4148 fillBackgroundRect(region.boundingRect(), d);
4149 } else {
4150 const QVector<QRect> rects = region.rects();
4151 for (int i = 0; i < rects.size(); ++i)
4152 fillBackgroundRect(rects.at(i), d);
4153 }
4154
4155 // We will need to reset the path transform during the next paint.
4156 d->pathTransformSet = false;
4157 }
4158 }
4159
drawCursorPixmap(const QPixmap & pixmap,const QPoint & offset)4160 void QVGCompositionHelper::drawCursorPixmap
4161 (const QPixmap& pixmap, const QPoint& offset)
4162 {
4163 VGImage vgImage = VG_INVALID_HANDLE;
4164
4165 // Fetch the VGImage from the pixmap if possible.
4166 QPixmapData *pd = pixmap.pixmapData();
4167 if (!pd)
4168 return; // null QPixmap
4169 if (pd->classId() == QPixmapData::OpenVGClass) {
4170 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
4171 if (vgpd->isValid())
4172 vgImage = vgpd->toVGImage();
4173 }
4174
4175 // Set the image transformation and modes.
4176 VGfloat devh = screenSize.height();
4177 QTransform transform(1.0f, 0.0f, 0.0f,
4178 0.0f, -1.0f, 0.0f,
4179 0.0f, devh, 1.0f);
4180 transform.translate(offset.x(), offset.y());
4181 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
4182 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
4183
4184 // Draw the VGImage.
4185 if (vgImage != VG_INVALID_HANDLE) {
4186 vgDrawImage(vgImage);
4187 } else {
4188 QImage img = pixmap.toImage().convertToFormat
4189 (QImage::Format_ARGB32_Premultiplied);
4190
4191 vgImage = vgCreateImage
4192 (VG_sARGB_8888_PRE, img.width(), img.height(),
4193 VG_IMAGE_QUALITY_FASTER);
4194 if (vgImage == VG_INVALID_HANDLE)
4195 return;
4196 vgImageSubData
4197 (vgImage, img.constBits() + img.bytesPerLine() * (img.height() - 1),
4198 -(img.bytesPerLine()), VG_sARGB_8888_PRE, 0, 0,
4199 img.width(), img.height());
4200
4201 vgDrawImage(vgImage);
4202 vgDestroyImage(vgImage);
4203 }
4204 }
4205
setScissor(const QRegion & region)4206 void QVGCompositionHelper::setScissor(const QRegion& region)
4207 {
4208 QVector<QRect> rects = region.rects();
4209 int count = rects.count();
4210 if (count > d->maxScissorRects)
4211 count = d->maxScissorRects;
4212 QVarLengthArray<VGint> params(count * 4);
4213 int height = screenSize.height();
4214 for (int i = 0; i < count; ++i) {
4215 params[i * 4 + 0] = rects[i].x();
4216 params[i * 4 + 1] = height - rects[i].y() - rects[i].height();
4217 params[i * 4 + 2] = rects[i].width();
4218 params[i * 4 + 3] = rects[i].height();
4219 }
4220
4221 vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
4222 vgSeti(VG_SCISSORING, VG_TRUE);
4223 d->scissorDirty = false;
4224 d->scissorActive = true;
4225 d->scissorRegion = region;
4226 }
4227
clearScissor()4228 void QVGCompositionHelper::clearScissor()
4229 {
4230 if (d->scissorActive || d->scissorDirty) {
4231 vgSeti(VG_SCISSORING, VG_FALSE);
4232 d->scissorActive = false;
4233 d->scissorDirty = false;
4234 }
4235 }
4236
4237 #endif // !QVG_NO_SINGLE_CONTEXT && !QT_NO_EGL
4238
qt_vg_image_to_vg_format(QImage::Format format)4239 VGImageFormat qt_vg_image_to_vg_format(QImage::Format format)
4240 {
4241 switch (format) {
4242 case QImage::Format_MonoLSB:
4243 return VG_BW_1;
4244 case QImage::Format_Indexed8:
4245 return VG_sL_8;
4246 case QImage::Format_ARGB32_Premultiplied:
4247 return VG_sARGB_8888_PRE;
4248 case QImage::Format_RGB32:
4249 return VG_sXRGB_8888;
4250 case QImage::Format_ARGB32:
4251 return VG_sARGB_8888;
4252 case QImage::Format_RGB16:
4253 return VG_sRGB_565;
4254 case QImage::Format_ARGB4444_Premultiplied:
4255 return VG_sARGB_4444;
4256 default:
4257 break;
4258 }
4259 return VG_sARGB_8888; // XXX
4260 }
4261
4262 QT_END_NAMESPACE
4263
4264 #include "qpaintengine_vg.moc"
4265