source: trunk/src/openvg/qpaintengine_vg.cpp@ 817

Last change on this file since 817 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

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