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 &region, 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 &region, 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