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

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

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

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