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

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

trunk: Merged in qt 4.6.2 sources.

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