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

Last change on this file since 577 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

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