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

Last change on this file since 1000 was 865, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.3 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 vgSeti(VG_MASKING, VG_FALSE); // disable mask fallback
2337 updateScissor();
2338#else
2339 Q_D(QVGPaintEngine);
2340 QVGPainterState *s = state();
2341 d->dirty |= QPaintEngine::DirtyClipEnabled;
2342 if (s->clipEnabled && s->clipOperation != Qt::NoClip) {
2343 // Replay the entire clip stack to put the mask into the right state.
2344 d->maskValid = false;
2345 d->maskIsSet = true;
2346 d->scissorMask = false;
2347 d->maskRect = QRect();
2348 s->clipRegion = defaultClipRegion();
2349 d->replayClipOperations();
2350 d->transform = s->transform();
2351 d->updateTransform(paintDevice());
2352 } else {
2353 vgSeti(VG_MASKING, VG_FALSE);
2354 d->maskValid = false;
2355 d->maskIsSet = false;
2356 d->scissorMask = false;
2357 d->maskRect = QRect();
2358 }
2359#endif
2360}
2361
2362void QVGPaintEngine::penChanged()
2363{
2364 Q_D(QVGPaintEngine);
2365 d->dirty |= QPaintEngine::DirtyPen;
2366}
2367
2368void QVGPaintEngine::brushChanged()
2369{
2370 Q_D(QVGPaintEngine);
2371 d->dirty |= QPaintEngine::DirtyBrush;
2372}
2373
2374void QVGPaintEngine::brushOriginChanged()
2375{
2376 Q_D(QVGPaintEngine);
2377 d->dirty |= QPaintEngine::DirtyBrushOrigin;
2378 d->brushOrigin = state()->brushOrigin;
2379 d->forcePenChange = true;
2380 d->forceBrushChange = true;
2381}
2382
2383void QVGPaintEngine::opacityChanged()
2384{
2385 Q_D(QVGPaintEngine);
2386 d->dirty |= QPaintEngine::DirtyOpacity;
2387 d->opacity = state()->opacity;
2388 d->forcePenChange = true;
2389 d->forceBrushChange = true;
2390}
2391
2392void QVGPaintEngine::compositionModeChanged()
2393{
2394 Q_D(QVGPaintEngine);
2395 d->dirty |= QPaintEngine::DirtyCompositionMode;
2396
2397 VGint vgMode = VG_BLEND_SRC_OVER;
2398
2399 switch (state()->composition_mode) {
2400 case QPainter::CompositionMode_SourceOver:
2401 vgMode = VG_BLEND_SRC_OVER;
2402 break;
2403 case QPainter::CompositionMode_DestinationOver:
2404 vgMode = VG_BLEND_DST_OVER;
2405 break;
2406 case QPainter::CompositionMode_Source:
2407 vgMode = VG_BLEND_SRC;
2408 break;
2409 case QPainter::CompositionMode_SourceIn:
2410 vgMode = VG_BLEND_SRC_IN;
2411 break;
2412 case QPainter::CompositionMode_DestinationIn:
2413 vgMode = VG_BLEND_DST_IN;
2414 break;
2415 case QPainter::CompositionMode_Plus:
2416 vgMode = VG_BLEND_ADDITIVE;
2417 break;
2418 case QPainter::CompositionMode_Multiply:
2419 vgMode = VG_BLEND_MULTIPLY;
2420 break;
2421 case QPainter::CompositionMode_Screen:
2422 vgMode = VG_BLEND_SCREEN;
2423 break;
2424 case QPainter::CompositionMode_Darken:
2425 vgMode = VG_BLEND_DARKEN;
2426 break;
2427 case QPainter::CompositionMode_Lighten:
2428 vgMode = VG_BLEND_LIGHTEN;
2429 break;
2430 default:
2431 if (d->hasAdvancedBlending) {
2432 switch (state()->composition_mode) {
2433 case QPainter::CompositionMode_Overlay:
2434 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_OVERLAY_KHR;
2435 break;
2436 case QPainter::CompositionMode_ColorDodge:
2437 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORDODGE_KHR;
2438 break;
2439 case QPainter::CompositionMode_ColorBurn:
2440 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORBURN_KHR;
2441 break;
2442 case QPainter::CompositionMode_HardLight:
2443 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_HARDLIGHT_KHR;
2444 break;
2445 case QPainter::CompositionMode_SoftLight:
2446 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SOFTLIGHT_KHR;
2447 break;
2448 case QPainter::CompositionMode_Difference:
2449 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DIFFERENCE_KHR;
2450 break;
2451 case QPainter::CompositionMode_Exclusion:
2452 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_EXCLUSION_KHR;
2453 break;
2454 case QPainter::CompositionMode_SourceOut:
2455 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_OUT_KHR;
2456 break;
2457 case QPainter::CompositionMode_DestinationOut:
2458 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_OUT_KHR;
2459 break;
2460 case QPainter::CompositionMode_SourceAtop:
2461 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_ATOP_KHR;
2462 break;
2463 case QPainter::CompositionMode_DestinationAtop:
2464 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_ATOP_KHR;
2465 break;
2466 case QPainter::CompositionMode_Xor:
2467 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_XOR_KHR;
2468 break;
2469 default: break; // Fall back to VG_BLEND_SRC_OVER.
2470 }
2471 }
2472 if (vgMode == VG_BLEND_SRC_OVER)
2473 qWarning() << "QVGPaintEngine::compositionModeChanged unsupported mode" << state()->composition_mode;
2474 break;
2475 }
2476
2477 d->setBlendMode(VGBlendMode(vgMode));
2478}
2479
2480void QVGPaintEngine::renderHintsChanged()
2481{
2482 Q_D(QVGPaintEngine);
2483 d->dirty |= QPaintEngine::DirtyHints;
2484
2485 QPainter::RenderHints hints = state()->renderHints;
2486
2487 VGRenderingQuality rq =
2488 (hints & QPainter::Antialiasing)
2489 ? VG_RENDERING_QUALITY_BETTER
2490 : VG_RENDERING_QUALITY_NONANTIALIASED;
2491 VGImageQuality iq =
2492 (hints & QPainter::SmoothPixmapTransform)
2493 ? VG_IMAGE_QUALITY_BETTER
2494 : VG_IMAGE_QUALITY_NONANTIALIASED;
2495
2496 d->setRenderingQuality(rq);
2497 d->setImageQuality(iq);
2498}
2499
2500void QVGPaintEngine::transformChanged()
2501{
2502 Q_D(QVGPaintEngine);
2503 QVGPainterState *s = state();
2504 d->dirty |= QPaintEngine::DirtyTransform;
2505 d->transform = s->transform();
2506 qreal oldPenScale = d->penScale;
2507 d->updateTransform(paintDevice());
2508 if (d->penScale != oldPenScale)
2509 d->forcePenChange = true;
2510}
2511
2512bool QVGPaintEngine::clearRect(const QRectF &rect, const QColor &color)
2513{
2514 Q_D(QVGPaintEngine);
2515 QVGPainterState *s = state();
2516 if (!s->clipEnabled || s->clipOperation == Qt::NoClip) {
2517 QRect r = d->transform.mapRect(rect).toRect();
2518 int height = paintDevice()->height();
2519 if (d->clearColor != color || d->clearOpacity != s->opacity) {
2520 VGfloat values[4];
2521 values[0] = color.redF();
2522 values[1] = color.greenF();
2523 values[2] = color.blueF();
2524 values[3] = color.alphaF() * s->opacity;
2525 vgSetfv(VG_CLEAR_COLOR, 4, values);
2526 d->clearColor = color;
2527 d->clearOpacity = s->opacity;
2528 }
2529 vgClear(r.x(), height - r.y() - r.height(),
2530 r.width(), r.height());
2531 return true;
2532 }
2533 return false;
2534}
2535
2536void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
2537{
2538 Q_D(QVGPaintEngine);
2539
2540 if (brush.style() == Qt::NoBrush)
2541 return;
2542
2543 // Check to see if we can use vgClear() for faster filling.
2544 if (brush.style() == Qt::SolidPattern && brush.isOpaque() &&
2545 clipTransformIsSimple(d->transform) && d->opacity == 1.0f &&
2546 clearRect(rect, brush.color())) {
2547 return;
2548 }
2549
2550#if !defined(QVG_NO_MODIFY_PATH)
2551 VGfloat coords[8];
2552 if (d->simpleTransform) {
2553 coords[0] = rect.x();
2554 coords[1] = rect.y();
2555 coords[2] = rect.x() + rect.width();
2556 coords[3] = coords[1];
2557 coords[4] = coords[2];
2558 coords[5] = rect.y() + rect.height();
2559 coords[6] = coords[0];
2560 coords[7] = coords[5];
2561 } else {
2562 QPointF tl = d->transform.map(rect.topLeft());
2563 QPointF tr = d->transform.map(rect.topRight());
2564 QPointF bl = d->transform.map(rect.bottomLeft());
2565 QPointF br = d->transform.map(rect.bottomRight());
2566 coords[0] = tl.x();
2567 coords[1] = tl.y();
2568 coords[2] = tr.x();
2569 coords[3] = tr.y();
2570 coords[4] = br.x();
2571 coords[5] = br.y();
2572 coords[6] = bl.x();
2573 coords[7] = bl.y();
2574 }
2575 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2576 d->fill(d->rectPath, brush);
2577#else
2578 QPaintEngineEx::fillRect(rect, brush);
2579#endif
2580}
2581
2582void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color)
2583{
2584 Q_D(QVGPaintEngine);
2585
2586 // Check to see if we can use vgClear() for faster filling.
2587 if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f && color.alpha() == 255 &&
2588 clearRect(rect, color)) {
2589 return;
2590 }
2591
2592#if !defined(QVG_NO_MODIFY_PATH)
2593 VGfloat coords[8];
2594 if (d->simpleTransform) {
2595 coords[0] = rect.x();
2596 coords[1] = rect.y();
2597 coords[2] = rect.x() + rect.width();
2598 coords[3] = coords[1];
2599 coords[4] = coords[2];
2600 coords[5] = rect.y() + rect.height();
2601 coords[6] = coords[0];
2602 coords[7] = coords[5];
2603 } else {
2604 QPointF tl = d->transform.map(rect.topLeft());
2605 QPointF tr = d->transform.map(rect.topRight());
2606 QPointF bl = d->transform.map(rect.bottomLeft());
2607 QPointF br = d->transform.map(rect.bottomRight());
2608 coords[0] = tl.x();
2609 coords[1] = tl.y();
2610 coords[2] = tr.x();
2611 coords[3] = tr.y();
2612 coords[4] = br.x();
2613 coords[5] = br.y();
2614 coords[6] = bl.x();
2615 coords[7] = bl.y();
2616 }
2617 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2618 d->fill(d->rectPath, QBrush(color));
2619#else
2620 QPaintEngineEx::fillRect(rect, QBrush(color));
2621#endif
2622}
2623
2624void QVGPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode)
2625{
2626 Q_D(QVGPaintEngine);
2627 if (d->simpleTransform) {
2628 QVGPainterState *s = state();
2629 VGPath vgpath = d->roundedRectPath(rect, xrad, yrad, mode);
2630 d->draw(vgpath, s->pen, s->brush);
2631#if defined(QVG_NO_MODIFY_PATH)
2632 vgDestroyPath(vgpath);
2633#endif
2634 } else {
2635 QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
2636 }
2637}
2638
2639void QVGPaintEngine::drawRects(const QRect *rects, int rectCount)
2640{
2641#if !defined(QVG_NO_MODIFY_PATH)
2642 Q_D(QVGPaintEngine);
2643 QVGPainterState *s = state();
2644 for (int i = 0; i < rectCount; ++i, ++rects) {
2645 VGfloat coords[8];
2646 if (d->simpleTransform) {
2647 coords[0] = rects->x();
2648 coords[1] = rects->y();
2649 coords[2] = rects->x() + rects->width();
2650 coords[3] = coords[1];
2651 coords[4] = coords[2];
2652 coords[5] = rects->y() + rects->height();
2653 coords[6] = coords[0];
2654 coords[7] = coords[5];
2655 } else {
2656 QPointF tl = d->transform.map(QPointF(rects->x(), rects->y()));
2657 QPointF tr = d->transform.map(QPointF(rects->x() + rects->width(),
2658 rects->y()));
2659 QPointF bl = d->transform.map(QPointF(rects->x(),
2660 rects->y() + rects->height()));
2661 QPointF br = d->transform.map(QPointF(rects->x() + rects->width(),
2662 rects->y() + rects->height()));
2663 coords[0] = tl.x();
2664 coords[1] = tl.y();
2665 coords[2] = tr.x();
2666 coords[3] = tr.y();
2667 coords[4] = br.x();
2668 coords[5] = br.y();
2669 coords[6] = bl.x();
2670 coords[7] = bl.y();
2671 }
2672 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2673 d->draw(d->rectPath, s->pen, s->brush);
2674 }
2675#else
2676 QPaintEngineEx::drawRects(rects, rectCount);
2677#endif
2678}
2679
2680void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount)
2681{
2682#if !defined(QVG_NO_MODIFY_PATH)
2683 Q_D(QVGPaintEngine);
2684 QVGPainterState *s = state();
2685 for (int i = 0; i < rectCount; ++i, ++rects) {
2686 VGfloat coords[8];
2687 if (d->simpleTransform) {
2688 coords[0] = rects->x();
2689 coords[1] = rects->y();
2690 coords[2] = rects->x() + rects->width();
2691 coords[3] = coords[1];
2692 coords[4] = coords[2];
2693 coords[5] = rects->y() + rects->height();
2694 coords[6] = coords[0];
2695 coords[7] = coords[5];
2696 } else {
2697 QPointF tl = d->transform.map(rects->topLeft());
2698 QPointF tr = d->transform.map(rects->topRight());
2699 QPointF bl = d->transform.map(rects->bottomLeft());
2700 QPointF br = d->transform.map(rects->bottomRight());
2701 coords[0] = tl.x();
2702 coords[1] = tl.y();
2703 coords[2] = tr.x();
2704 coords[3] = tr.y();
2705 coords[4] = br.x();
2706 coords[5] = br.y();
2707 coords[6] = bl.x();
2708 coords[7] = bl.y();
2709 }
2710 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2711 d->draw(d->rectPath, s->pen, s->brush);
2712 }
2713#else
2714 QPaintEngineEx::drawRects(rects, rectCount);
2715#endif
2716}
2717
2718void QVGPaintEngine::drawLines(const QLine *lines, int lineCount)
2719{
2720#if !defined(QVG_NO_MODIFY_PATH)
2721 Q_D(QVGPaintEngine);
2722 QVGPainterState *s = state();
2723 for (int i = 0; i < lineCount; ++i, ++lines) {
2724 VGfloat coords[4];
2725 if (d->simpleTransform) {
2726 coords[0] = lines->x1();
2727 coords[1] = lines->y1();
2728 coords[2] = lines->x2();
2729 coords[3] = lines->y2();
2730 } else {
2731 QPointF p1 = d->transform.map(QPointF(lines->x1(), lines->y1()));
2732 QPointF p2 = d->transform.map(QPointF(lines->x2(), lines->y2()));
2733 coords[0] = p1.x();
2734 coords[1] = p1.y();
2735 coords[2] = p2.x();
2736 coords[3] = p2.y();
2737 }
2738 vgModifyPathCoords(d->linePath, 0, 2, coords);
2739 d->stroke(d->linePath, s->pen);
2740 }
2741#else
2742 QPaintEngineEx::drawLines(lines, lineCount);
2743#endif
2744}
2745
2746void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
2747{
2748#if !defined(QVG_NO_MODIFY_PATH)
2749 Q_D(QVGPaintEngine);
2750 QVGPainterState *s = state();
2751 for (int i = 0; i < lineCount; ++i, ++lines) {
2752 VGfloat coords[4];
2753 if (d->simpleTransform) {
2754 coords[0] = lines->x1();
2755 coords[1] = lines->y1();
2756 coords[2] = lines->x2();
2757 coords[3] = lines->y2();
2758 } else {
2759 QPointF p1 = d->transform.map(lines->p1());
2760 QPointF p2 = d->transform.map(lines->p2());
2761 coords[0] = p1.x();
2762 coords[1] = p1.y();
2763 coords[2] = p2.x();
2764 coords[3] = p2.y();
2765 }
2766 vgModifyPathCoords(d->linePath, 0, 2, coords);
2767 d->stroke(d->linePath, s->pen);
2768 }
2769#else
2770 QPaintEngineEx::drawLines(lines, lineCount);
2771#endif
2772}
2773
2774void QVGPaintEngine::drawEllipse(const QRectF &r)
2775{
2776 // Based on the description of vguEllipse() in the OpenVG specification.
2777 // We don't use vguEllipse(), to avoid unnecessary library dependencies.
2778 Q_D(QVGPaintEngine);
2779 if (d->simpleTransform) {
2780 QVGPainterState *s = state();
2781 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
2782 VG_PATH_DATATYPE_F,
2783 1.0f, // scale
2784 0.0f, // bias
2785 4, // segmentCapacityHint
2786 12, // coordCapacityHint
2787 VG_PATH_CAPABILITY_ALL);
2788 static VGubyte segments[4] = {
2789 VG_MOVE_TO_ABS,
2790 VG_SCCWARC_TO_REL,
2791 VG_SCCWARC_TO_REL,
2792 VG_CLOSE_PATH
2793 };
2794 VGfloat coords[12];
2795 VGfloat halfwid = r.width() / 2;
2796 VGfloat halfht = r.height() / 2;
2797 coords[0] = r.x() + r.width();
2798 coords[1] = r.y() + halfht;
2799 coords[2] = halfwid;
2800 coords[3] = halfht;
2801 coords[4] = 0.0f;
2802 coords[5] = -r.width();
2803 coords[6] = 0.0f;
2804 coords[7] = halfwid;
2805 coords[8] = halfht;
2806 coords[9] = 0.0f;
2807 coords[10] = r.width();
2808 coords[11] = 0.0f;
2809 vgAppendPathData(path, 4, segments, coords);
2810 d->draw(path, s->pen, s->brush);
2811 vgDestroyPath(path);
2812 } else {
2813 // The projective transform version of an ellipse is difficult.
2814 // Generate a QVectorPath containing cubic curves and transform that.
2815 QPaintEngineEx::drawEllipse(r);
2816 }
2817}
2818
2819void QVGPaintEngine::drawEllipse(const QRect &r)
2820{
2821 drawEllipse(QRectF(r));
2822}
2823
2824void QVGPaintEngine::drawPath(const QPainterPath &path)
2825{
2826 // Shortcut past the QPainterPath -> QVectorPath conversion,
2827 // converting the QPainterPath directly into a VGPath.
2828 Q_D(QVGPaintEngine);
2829 QVGPainterState *s = state();
2830 VGPath vgpath = d->painterPathToVGPath(path);
2831 if (path.fillRule() == Qt::OddEvenFill)
2832 d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD);
2833 else
2834 d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
2835 vgDestroyPath(vgpath);
2836}
2837
2838void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
2839{
2840#if !defined(QVG_NO_MODIFY_PATH)
2841 Q_D(QVGPaintEngine);
2842
2843 // Set up a new pen if necessary.
2844 QPen pen = state()->pen;
2845 if (pen.style() == Qt::NoPen)
2846 return;
2847 if (pen.capStyle() == Qt::FlatCap)
2848 pen.setCapStyle(Qt::SquareCap);
2849
2850 for (int i = 0; i < pointCount; ++i, ++points) {
2851 VGfloat coords[4];
2852 if (d->simpleTransform) {
2853 coords[0] = points->x();
2854 coords[1] = points->y();
2855 coords[2] = coords[0];
2856 coords[3] = coords[1];
2857 } else {
2858 QPointF p = d->transform.map(*points);
2859 coords[0] = p.x();
2860 coords[1] = p.y();
2861 coords[2] = coords[0];
2862 coords[3] = coords[1];
2863 }
2864 vgModifyPathCoords(d->linePath, 0, 2, coords);
2865 d->stroke(d->linePath, pen);
2866 }
2867#else
2868 QPaintEngineEx::drawPoints(points, pointCount);
2869#endif
2870}
2871
2872void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
2873{
2874#if !defined(QVG_NO_MODIFY_PATH)
2875 Q_D(QVGPaintEngine);
2876
2877 // Set up a new pen if necessary.
2878 QPen pen = state()->pen;
2879 if (pen.style() == Qt::NoPen)
2880 return;
2881 if (pen.capStyle() == Qt::FlatCap)
2882 pen.setCapStyle(Qt::SquareCap);
2883
2884 for (int i = 0; i < pointCount; ++i, ++points) {
2885 VGfloat coords[4];
2886 if (d->simpleTransform) {
2887 coords[0] = points->x();
2888 coords[1] = points->y();
2889 coords[2] = coords[0];
2890 coords[3] = coords[1];
2891 } else {
2892 QPointF p = d->transform.map(QPointF(*points));
2893 coords[0] = p.x();
2894 coords[1] = p.y();
2895 coords[2] = coords[0];
2896 coords[3] = coords[1];
2897 }
2898 vgModifyPathCoords(d->linePath, 0, 2, coords);
2899 d->stroke(d->linePath, pen);
2900 }
2901#else
2902 QPaintEngineEx::drawPoints(points, pointCount);
2903#endif
2904}
2905
2906void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2907{
2908 Q_D(QVGPaintEngine);
2909 QVGPainterState *s = state();
2910 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
2911 VG_PATH_DATATYPE_F,
2912 1.0f, // scale
2913 0.0f, // bias
2914 pointCount + 1, // segmentCapacityHint
2915 pointCount * 2, // coordCapacityHint
2916 VG_PATH_CAPABILITY_ALL);
2917 QVarLengthArray<VGfloat, 16> coords;
2918 QVarLengthArray<VGubyte, 10> segments;
2919 for (int i = 0; i < pointCount; ++i, ++points) {
2920 if (d->simpleTransform) {
2921 coords.append(points->x());
2922 coords.append(points->y());
2923 } else {
2924 QPointF temp = d->transform.map(*points);
2925 coords.append(temp.x());
2926 coords.append(temp.y());
2927 }
2928 if (i == 0)
2929 segments.append(VG_MOVE_TO_ABS);
2930 else
2931 segments.append(VG_LINE_TO_ABS);
2932 }
2933 if (mode != QPaintEngine::PolylineMode)
2934 segments.append(VG_CLOSE_PATH);
2935 vgAppendPathData(path, segments.count(),
2936 segments.constData(), coords.constData());
2937 switch (mode) {
2938 case QPaintEngine::WindingMode:
2939 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
2940 break;
2941
2942 case QPaintEngine::PolylineMode:
2943 d->stroke(path, s->pen);
2944 break;
2945
2946 default:
2947 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
2948 break;
2949 }
2950 vgDestroyPath(path);
2951}
2952
2953void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2954{
2955 Q_D(QVGPaintEngine);
2956 QVGPainterState *s = state();
2957 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
2958 VG_PATH_DATATYPE_F,
2959 1.0f, // scale
2960 0.0f, // bias
2961 pointCount + 1, // segmentCapacityHint
2962 pointCount * 2, // coordCapacityHint
2963 VG_PATH_CAPABILITY_ALL);
2964 QVarLengthArray<VGfloat, 16> coords;
2965 QVarLengthArray<VGubyte, 10> segments;
2966 for (int i = 0; i < pointCount; ++i, ++points) {
2967 if (d->simpleTransform) {
2968 coords.append(points->x());
2969 coords.append(points->y());
2970 } else {
2971 QPointF temp = d->transform.map(QPointF(*points));
2972 coords.append(temp.x());
2973 coords.append(temp.y());
2974 }
2975 if (i == 0)
2976 segments.append(VG_MOVE_TO_ABS);
2977 else
2978 segments.append(VG_LINE_TO_ABS);
2979 }
2980 if (mode != QPaintEngine::PolylineMode)
2981 segments.append(VG_CLOSE_PATH);
2982 vgAppendPathData(path, segments.count(),
2983 segments.constData(), coords.constData());
2984 switch (mode) {
2985 case QPaintEngine::WindingMode:
2986 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
2987 break;
2988
2989 case QPaintEngine::PolylineMode:
2990 d->stroke(path, s->pen);
2991 break;
2992
2993 default:
2994 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
2995 break;
2996 }
2997 vgDestroyPath(path);
2998}
2999
3000void QVGPaintEnginePrivate::setImageOptions()
3001{
3002 if (opacity != 1.0f && simpleTransform) {
3003 if (opacity != paintOpacity) {
3004 VGfloat values[4];
3005 values[0] = 1.0f;
3006 values[1] = 1.0f;
3007 values[2] = 1.0f;
3008 values[3] = opacity;
3009 vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
3010 paintOpacity = opacity;
3011 }
3012 if (fillPaint != opacityPaint) {
3013 vgSetPaint(opacityPaint, VG_FILL_PATH);
3014 fillPaint = opacityPaint;
3015 }
3016 setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3017 } else {
3018 setImageMode(VG_DRAW_IMAGE_NORMAL);
3019 }
3020}
3021
3022void QVGPaintEnginePrivate::systemStateChanged()
3023{
3024 q->updateScissor();
3025}
3026
3027static void drawVGImage(QVGPaintEnginePrivate *d,
3028 const QRectF& r, VGImage vgImg,
3029 const QSize& imageSize, const QRectF& sr)
3030{
3031 if (vgImg == VG_INVALID_HANDLE)
3032 return;
3033 VGImage child = VG_INVALID_HANDLE;
3034
3035 if (sr.topLeft().isNull() && sr.size() == imageSize) {
3036 child = vgImg;
3037 } else {
3038 QRect src = sr.toRect();
3039#if !defined(QT_SHIVAVG)
3040 child = vgChildImage(vgImg, src.x(), src.y(), src.width(), src.height());
3041#else
3042 child = vgImg; // XXX: ShivaVG doesn't have vgChildImage().
3043#endif
3044 }
3045
3046 QTransform transform(d->imageTransform);
3047 VGfloat scaleX = sr.width() == 0.0f ? 0.0f : r.width() / sr.width();
3048 VGfloat scaleY = sr.height() == 0.0f ? 0.0f : r.height() / sr.height();
3049 transform.translate(r.x(), r.y());
3050 transform.scale(scaleX, scaleY);
3051 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3052
3053 d->setImageOptions();
3054 vgDrawImage(child);
3055
3056 if(child != vgImg)
3057 vgDestroyImage(child);
3058}
3059
3060static void drawVGImage(QVGPaintEnginePrivate *d,
3061 const QPointF& pos, VGImage vgImg)
3062{
3063 if (vgImg == VG_INVALID_HANDLE)
3064 return;
3065
3066 QTransform transform(d->imageTransform);
3067 transform.translate(pos.x(), pos.y());
3068 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3069
3070 d->setImageOptions();
3071 vgDrawImage(vgImg);
3072}
3073
3074// Used by qpixmapfilter_vg.cpp to draw filtered VGImage's.
3075void qt_vg_drawVGImage(QPainter *painter, const QPointF& pos, VGImage vgImg)
3076{
3077 QVGPaintEngine *engine =
3078 static_cast<QVGPaintEngine *>(painter->paintEngine());
3079 drawVGImage(engine->vgPrivate(), pos, vgImg);
3080}
3081
3082// Used by qpixmapfilter_vg.cpp to draw filtered VGImage's as a stencil.
3083void qt_vg_drawVGImageStencil
3084 (QPainter *painter, const QPointF& pos, VGImage vgImg, const QBrush& brush)
3085{
3086 QVGPaintEngine *engine =
3087 static_cast<QVGPaintEngine *>(painter->paintEngine());
3088
3089 QVGPaintEnginePrivate *d = engine->vgPrivate();
3090
3091 QTransform transform(d->imageTransform);
3092 transform.translate(pos.x(), pos.y());
3093 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3094
3095 d->ensureBrush(brush);
3096 d->setImageMode(VG_DRAW_IMAGE_STENCIL);
3097 vgDrawImage(vgImg);
3098}
3099
3100bool QVGPaintEngine::canVgWritePixels(const QImage &image) const
3101{
3102 Q_D(const QVGPaintEngine);
3103 // vgWritePixels ignores masking, blending and xforms so we can only use it if
3104 // ALL of the following conditions are true:
3105 // - It is a simple translate, or a scale of -1 on the y-axis (inverted)
3106 // - The opacity is totally opaque
3107 // - The composition mode is "source" OR "source over" provided the image is opaque
3108 return ( d->imageTransform.type() <= QTransform::TxScale
3109 && d->imageTransform.m11() == 1.0 && qAbs(d->imageTransform.m22()) == 1.0)
3110 && d->opacity == 1.0f
3111 && (d->blendMode == VG_BLEND_SRC || (d->blendMode == VG_BLEND_SRC_OVER &&
3112 !image.hasAlphaChannel()));
3113}
3114
3115void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
3116{
3117 QPixmapData *pd = pm.pixmapData();
3118 if (!pd)
3119 return; // null QPixmap
3120 if (pd->classId() == QPixmapData::OpenVGClass) {
3121 Q_D(QVGPaintEngine);
3122 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3123 if (!vgpd->isValid())
3124 return;
3125 if (d->simpleTransform)
3126 drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr);
3127 else
3128 drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr);
3129
3130 if(!vgpd->failedToAlloc)
3131 return;
3132
3133 // try to reallocate next time if reasonable small pixmap
3134 QSize screenSize = QApplication::desktop()->screenGeometry().size();
3135 if (pm.size().width() <= screenSize.width()
3136 && pm.size().height() <= screenSize.height())
3137 vgpd->failedToAlloc = false;
3138 }
3139
3140 drawImage(r, *(pd->buffer()), sr, Qt::AutoColor);
3141}
3142
3143void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm)
3144{
3145 QPixmapData *pd = pm.pixmapData();
3146 if (!pd)
3147 return; // null QPixmap
3148 if (pd->classId() == QPixmapData::OpenVGClass) {
3149 Q_D(QVGPaintEngine);
3150 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3151 if (!vgpd->isValid())
3152 return;
3153 if (d->simpleTransform)
3154 drawVGImage(d, pos, vgpd->toVGImage());
3155 else
3156 drawVGImage(d, pos, vgpd->toVGImage(d->opacity));
3157
3158 if (!vgpd->failedToAlloc)
3159 return;
3160
3161 // try to reallocate next time if reasonable small pixmap
3162 QSize screenSize = QApplication::desktop()->screenGeometry().size();
3163 if (pm.size().width() <= screenSize.width()
3164 && pm.size().height() <= screenSize.height())
3165 vgpd->failedToAlloc = false;
3166 }
3167
3168 drawImage(pos, *(pd->buffer()));
3169}
3170
3171void QVGPaintEngine::drawImage
3172 (const QRectF &r, const QImage &image, const QRectF &sr,
3173 Qt::ImageConversionFlags flags)
3174{
3175 Q_D(QVGPaintEngine);
3176 VGImage vgImg;
3177 if (d->simpleTransform || d->opacity == 1.0f)
3178 vgImg = toVGImageSubRect(image, sr.toRect(), flags);
3179 else
3180 vgImg = toVGImageWithOpacitySubRect(image, d->opacity, sr.toRect());
3181 if (vgImg != VG_INVALID_HANDLE) {
3182 if (r.size() == sr.size()) {
3183 drawVGImage(d, r.topLeft(), vgImg);
3184 } else {
3185 drawVGImage(d, r, vgImg, sr.size().toSize(),
3186 QRectF(QPointF(0, 0), sr.size()));
3187 }
3188 } else {
3189 if (canVgWritePixels(image) && (r.size() == sr.size()) && !flags) {
3190 // Optimization for straight blits, no blending
3191 int x = sr.x();
3192 int y = sr.y();
3193 int bpp = image.depth() >> 3; // bytes
3194 int offset = 0;
3195 int bpl = image.bytesPerLine();
3196 if (d->imageTransform.m22() < 0) {
3197 // inverted
3198 offset = ((y + sr.height()) * bpl) - ((image.width() - x) * bpp);
3199 bpl = -bpl;
3200 } else {
3201 offset = (y * bpl) + (x * bpp);
3202 }
3203 const uchar *bits = image.constBits() + offset;
3204
3205 QPointF mapped = d->imageTransform.map(r.topLeft());
3206 vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
3207 mapped.x(), mapped.y() - sr.height(), r.width(), r.height());
3208 return;
3209 } else {
3210 // Monochrome images need to use the vgChildImage() path.
3211 vgImg = toVGImage(image, flags);
3212 drawVGImage(d, r, vgImg, image.size(), sr);
3213 }
3214 }
3215 vgDestroyImage(vgImg);
3216}
3217
3218void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image)
3219{
3220 Q_D(QVGPaintEngine);
3221 VGImage vgImg;
3222 if (canVgWritePixels(image)) {
3223 // Optimization for straight blits, no blending
3224 bool inverted = (d->imageTransform.m22() < 0);
3225 const uchar *bits = inverted ? image.constBits() + image.byteCount() : image.constBits();
3226 int bpl = inverted ? -image.bytesPerLine() : image.bytesPerLine();
3227
3228 QPointF mapped = d->imageTransform.map(pos);
3229 vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
3230 mapped.x(), mapped.y() - image.height(), image.width(), image.height());
3231 return;
3232 } else if (d->simpleTransform || d->opacity == 1.0f) {
3233 vgImg = toVGImage(image);
3234 } else {
3235 vgImg = toVGImageWithOpacity(image, d->opacity);
3236 }
3237 drawVGImage(d, pos, vgImg);
3238 vgDestroyImage(vgImg);
3239}
3240
3241void QVGPaintEngine::drawTiledPixmap
3242 (const QRectF &r, const QPixmap &pixmap, const QPointF &s)
3243{
3244 QBrush brush(state()->pen.color(), pixmap);
3245 QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
3246 brush.setTransform(xform);
3247 fillRect(r, brush);
3248}
3249
3250// Best performance will be achieved with QDrawPixmaps::OpaqueHint
3251// (i.e. no opacity), no rotation or scaling, and drawing the full
3252// pixmap rather than parts of the pixmap. Even having just one of
3253// these conditions will improve performance.
3254void QVGPaintEngine::drawPixmapFragments(const QPainter::PixmapFragment *drawingData, int dataCount,
3255 const QPixmap &pixmap, QFlags<QPainter::PixmapFragmentHint> hints)
3256{
3257#if !defined(QT_SHIVAVG)
3258 Q_D(QVGPaintEngine);
3259
3260 // If the pixmap is not VG, or the transformation is projective,
3261 // then fall back to the default implementation.
3262 QPixmapData *pd = pixmap.pixmapData();
3263 if (!pd)
3264 return; // null QPixmap
3265 if (pd->classId() != QPixmapData::OpenVGClass || !d->simpleTransform) {
3266 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3267 return;
3268 }
3269
3270 // Bail out if nothing to do.
3271 if (dataCount <= 0)
3272 return;
3273
3274 // Bail out if we don't have a usable VGImage for the pixmap.
3275 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3276 if (!vgpd->isValid())
3277 return;
3278 VGImage vgImg = vgpd->toVGImage();
3279 if (vgImg == VG_INVALID_HANDLE)
3280 return;
3281
3282 // We cache the results of any vgChildImage() calls because the
3283 // same child is very likely to be used over and over in particle
3284 // systems. However, performance is even better if vgChildImage()
3285 // isn't needed at all, so use full source rects where possible.
3286 QVarLengthArray<VGImage> cachedImages;
3287 QVarLengthArray<QRect> cachedSources;
3288
3289 // Select the opacity paint object.
3290 if ((hints & QPainter::OpaqueHint) != 0 && d->opacity == 1.0f) {
3291 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3292 } else {
3293 hints = 0;
3294 if (d->fillPaint != d->opacityPaint) {
3295 vgSetPaint(d->opacityPaint, VG_FILL_PATH);
3296 d->fillPaint = d->opacityPaint;
3297 }
3298 }
3299
3300 for (int i = 0; i < dataCount; ++i) {
3301 QTransform transform(d->imageTransform);
3302 transform.translate(drawingData[i].x, drawingData[i].y);
3303 transform.rotate(drawingData[i].rotation);
3304
3305 VGImage child;
3306 QSize imageSize = vgpd->size();
3307 QRectF sr(drawingData[i].sourceLeft, drawingData[i].sourceTop,
3308 drawingData[i].width, drawingData[i].height);
3309 if (sr.topLeft().isNull() && sr.size() == imageSize) {
3310 child = vgImg;
3311 } else {
3312 // Look for a previous child with the same source rectangle
3313 // to avoid constantly calling vgChildImage()/vgDestroyImage().
3314 QRect src = sr.toRect();
3315 int j;
3316 for (j = 0; j < cachedSources.size(); ++j) {
3317 if (cachedSources[j] == src)
3318 break;
3319 }
3320 if (j < cachedSources.size()) {
3321 child = cachedImages[j];
3322 } else {
3323 child = vgChildImage
3324 (vgImg, src.x(), src.y(), src.width(), src.height());
3325 cachedImages.append(child);
3326 cachedSources.append(src);
3327 }
3328 }
3329
3330 VGfloat scaleX = drawingData[i].scaleX;
3331 VGfloat scaleY = drawingData[i].scaleY;
3332 transform.translate(-0.5 * scaleX * sr.width(),
3333 -0.5 * scaleY * sr.height());
3334 transform.scale(scaleX, scaleY);
3335 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3336
3337 if ((hints & QPainter::OpaqueHint) == 0) {
3338 qreal opacity = d->opacity * drawingData[i].opacity;
3339 if (opacity != 1.0f) {
3340 if (d->paintOpacity != opacity) {
3341 VGfloat values[4];
3342 values[0] = 1.0f;
3343 values[1] = 1.0f;
3344 values[2] = 1.0f;
3345 values[3] = opacity;
3346 d->paintOpacity = opacity;
3347 vgSetParameterfv
3348 (d->opacityPaint, VG_PAINT_COLOR, 4, values);
3349 }
3350 d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3351 } else {
3352 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3353 }
3354 }
3355
3356 vgDrawImage(child);
3357 }
3358
3359 // Destroy the cached child sub-images.
3360 for (int i = 0; i < cachedImages.size(); ++i)
3361 vgDestroyImage(cachedImages[i]);
3362#else
3363 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3364#endif
3365}
3366
3367QVGFontEngineCleaner::QVGFontEngineCleaner(QVGPaintEnginePrivate *d)
3368 : QObject(), d_ptr(d)
3369{
3370}
3371
3372QVGFontEngineCleaner::~QVGFontEngineCleaner()
3373{
3374}
3375
3376void QVGFontEngineCleaner::fontEngineDestroyed()
3377{
3378#if !defined(QVG_NO_DRAW_GLYPHS)
3379 QFontEngine *engine = static_cast<QFontEngine *>(sender());
3380 QVGFontCache::Iterator it = d_ptr->fontCache.find(engine);
3381 if (it != d_ptr->fontCache.end()) {
3382 delete it.value();
3383 d_ptr->fontCache.erase(it);
3384 }
3385#endif
3386}
3387
3388#if !defined(QVG_NO_DRAW_GLYPHS)
3389
3390QVGFontGlyphCache::QVGFontGlyphCache()
3391{
3392 font = vgCreateFont(0);
3393 scaleX = scaleY = 0.0;
3394 invertedGlyphs = false;
3395 memset(cachedGlyphsMask, 0, sizeof(cachedGlyphsMask));
3396}
3397
3398QVGFontGlyphCache::~QVGFontGlyphCache()
3399{
3400 if (font != VG_INVALID_HANDLE)
3401 vgDestroyFont(font);
3402}
3403
3404void QVGFontGlyphCache::setScaleFromText(const QFont &font, QFontEngine *fontEngine)
3405{
3406 QFontInfo fi(font);
3407 qreal pixelSize = fi.pixelSize();
3408 qreal emSquare = fontEngine->properties().emSquare.toReal();
3409 scaleX = scaleY = static_cast<VGfloat>(pixelSize / emSquare);
3410}
3411
3412void QVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d,
3413 QFontEngine *fontEngine,
3414 const glyph_t *g, int count)
3415{
3416 VGfloat origin[2];
3417 VGfloat escapement[2];
3418 glyph_metrics_t metrics;
3419 // Some Qt font engines don't set yoff in getUnscaledGlyph().
3420 // Zero the metric structure so that everything has a default value.
3421 memset(&metrics, 0, sizeof(metrics));
3422 while (count-- > 0) {
3423 // Skip this glyph if we have already cached it before.
3424 glyph_t glyph = *g++;
3425 if (glyph < 256) {
3426 if ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0)
3427 continue;
3428 cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32));
3429 } else if (cachedGlyphs.contains(glyph)) {
3430 continue;
3431 } else {
3432 cachedGlyphs.insert(glyph);
3433 }
3434#if !defined(QVG_NO_IMAGE_GLYPHS)
3435 Q_UNUSED(d);
3436 QImage scaledImage = fontEngine->alphaMapForGlyph(glyph);
3437 VGImage vgImage = VG_INVALID_HANDLE;
3438 metrics = fontEngine->boundingBox(glyph);
3439 if (!scaledImage.isNull()) { // Not a space character
3440 if (scaledImage.format() == QImage::Format_Indexed8) {
3441 vgImage = vgCreateImage(VG_A_8, scaledImage.width(), scaledImage.height(), VG_IMAGE_QUALITY_FASTER);
3442 vgImageSubData(vgImage, scaledImage.constBits(), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height());
3443 } else if (scaledImage.format() == QImage::Format_Mono) {
3444 QImage img = scaledImage.convertToFormat(QImage::Format_Indexed8);
3445 vgImage = vgCreateImage(VG_A_8, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
3446 vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height());
3447 } else {
3448 QImage img = scaledImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
3449 vgImage = vgCreateImage(VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
3450 vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height());
3451 }
3452 }
3453 origin[0] = -metrics.x.toReal();
3454 origin[1] = -metrics.y.toReal();
3455 escapement[0] = 0;
3456 escapement[1] = 0;
3457 vgSetGlyphToImage(font, glyph, vgImage, origin, escapement);
3458 vgDestroyImage(vgImage); // Reduce reference count.
3459#else
3460 // Calculate the path for the glyph and cache it.
3461 QPainterPath path;
3462 fontEngine->getUnscaledGlyph(glyph, &path, &metrics);
3463 VGPath vgPath;
3464 if (!path.isEmpty()) {
3465 vgPath = d->painterPathToVGPath(path);
3466 } else {
3467 // Probably a "space" character with no visible outline.
3468 vgPath = VG_INVALID_HANDLE;
3469 }
3470 origin[0] = 0;
3471 origin[1] = 0;
3472 escapement[0] = 0;
3473 escapement[1] = 0;
3474 vgSetGlyphToPath(font, glyph, vgPath, VG_FALSE, origin, escapement);
3475 vgDestroyPath(vgPath); // Reduce reference count.
3476#endif // !defined(QVG_NO_IMAGE_GLYPHS)
3477 }
3478}
3479
3480#endif // !defined(QVG_NO_DRAW_GLYPHS)
3481
3482void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3483{
3484#if !defined(QVG_NO_DRAW_GLYPHS)
3485 Q_D(QVGPaintEngine);
3486 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3487
3488 // If we are not using a simple transform, then fall back
3489 // to the default Qt path stroking algorithm.
3490 if (!d->simpleTransform) {
3491 QPaintEngineEx::drawTextItem(p, textItem);
3492 return;
3493 }
3494
3495 // Get the glyphs and positions associated with the text item.
3496 QVarLengthArray<QFixedPoint> positions;
3497 QVarLengthArray<glyph_t> glyphs;
3498 QTransform matrix;
3499 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3500
3501 if (!drawCachedGlyphs(glyphs.size(), glyphs.data(), ti.font(), ti.fontEngine, p, positions.data()))
3502 QPaintEngineEx::drawTextItem(p, textItem);
3503#else
3504 // OpenGL 1.0 does not have support for VGFont and glyphs,
3505 // so fall back to the default Qt path stroking algorithm.
3506 QPaintEngineEx::drawTextItem(p, textItem);
3507#endif
3508}
3509
3510void QVGPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3511{
3512 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->font, textItem->fontEngine(),
3513 QPointF(0, 0), textItem->glyphPositions);
3514}
3515
3516 bool QVGPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFont &font,
3517 QFontEngine *fontEngine, const QPointF &p,
3518 const QFixedPoint *positions)
3519 {
3520#if !defined(QVG_NO_DRAW_GLYPHS)
3521 Q_D(QVGPaintEngine);
3522
3523 // Find the glyph cache for this font.
3524 QVGFontCache::ConstIterator it = d->fontCache.constFind(fontEngine);
3525 QVGFontGlyphCache *glyphCache;
3526 if (it != d->fontCache.constEnd()) {
3527 glyphCache = it.value();
3528 } else {
3529#ifdef Q_OS_SYMBIAN
3530 glyphCache = new QSymbianVGFontGlyphCache();
3531#else
3532 glyphCache = new QVGFontGlyphCache();
3533#endif
3534 if (glyphCache->font == VG_INVALID_HANDLE) {
3535 qWarning("QVGPaintEngine::drawTextItem: OpenVG fonts are not supported by the OpenVG engine");
3536 delete glyphCache;
3537 return false;
3538 }
3539 glyphCache->setScaleFromText(font, fontEngine);
3540 d->fontCache.insert(fontEngine, glyphCache);
3541 if (!d->fontEngineCleaner)
3542 d->fontEngineCleaner = new QVGFontEngineCleaner(d);
3543 QObject::connect(fontEngine, SIGNAL(destroyed()),
3544 d->fontEngineCleaner, SLOT(fontEngineDestroyed()));
3545 }
3546
3547 // Set the transformation to use for drawing the current glyphs.
3548 QTransform glyphTransform(d->pathTransform);
3549 if (d->transform.type() <= QTransform::TxTranslate) {
3550 // Prevent blurriness of unscaled, unrotated text by forcing integer coordinates.
3551 glyphTransform.translate(
3552 floor(p.x() + glyphTransform.dx() + aliasedCoordinateDelta) - glyphTransform.dx(),
3553 floor(p.y() - glyphTransform.dy() + aliasedCoordinateDelta) + glyphTransform.dy());
3554 } else {
3555 glyphTransform.translate(p.x(), p.y());
3556 }
3557#if defined(QVG_NO_IMAGE_GLYPHS)
3558 glyphTransform.scale(glyphCache->scaleX, glyphCache->scaleY);
3559#endif
3560
3561 // Some glyph caches can create the VGImage upright
3562 if (glyphCache->invertedGlyphs)
3563 glyphTransform.scale(1, -1);
3564
3565 d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, glyphTransform);
3566
3567 // Add the glyphs from the text item into the glyph cache.
3568 glyphCache->cacheGlyphs(d, fontEngine, glyphs, numGlyphs);
3569
3570 // Create the array of adjustments between glyphs
3571 QVarLengthArray<VGfloat> adjustments_x(numGlyphs);
3572 QVarLengthArray<VGfloat> adjustments_y(numGlyphs);
3573 for (int i = 1; i < numGlyphs; ++i) {
3574 adjustments_x[i-1] = (positions[i].x - positions[i-1].x).round().toReal();
3575 adjustments_y[i-1] = (positions[i].y - positions[i-1].y).round().toReal();
3576 }
3577
3578 // Set the glyph drawing origin.
3579 VGfloat origin[2];
3580 origin[0] = positions[0].x.round().toReal();
3581 origin[1] = positions[0].y.round().toReal();
3582 vgSetfv(VG_GLYPH_ORIGIN, 2, origin);
3583
3584 // Fast anti-aliasing for paths, better for images.
3585#if !defined(QVG_NO_IMAGE_GLYPHS)
3586 d->setImageQuality(VG_IMAGE_QUALITY_BETTER);
3587 d->setImageMode(VG_DRAW_IMAGE_STENCIL);
3588#else
3589 d->setRenderingQuality(VG_RENDERING_QUALITY_FASTER);
3590#endif
3591
3592 // Draw the glyphs. We need to fill with the brush associated with
3593 // the Qt pen, not the Qt brush.
3594 d->ensureBrush(state()->pen.brush());
3595 vgDrawGlyphs(glyphCache->font, numGlyphs, (VGuint*)glyphs,
3596 adjustments_x.data(), adjustments_y.data(), VG_FILL_PATH, VG_TRUE);
3597 return true;
3598#else
3599 Q_UNUSED(numGlyphs);
3600 Q_UNUSED(glyphs);
3601 Q_UNUSED(font);
3602 Q_UNUSED(fontEngine);
3603 Q_UNUSED(p);
3604 Q_UNUSED(positions);
3605 return false;
3606#endif
3607}
3608
3609void QVGPaintEngine::setState(QPainterState *s)
3610{
3611 Q_D(QVGPaintEngine);
3612 QPaintEngineEx::setState(s);
3613 QVGPainterState *ps = static_cast<QVGPainterState *>(s);
3614 if (ps->isNew) {
3615 // Newly created state object. The call to setState()
3616 // will either be followed by a call to begin(), or we are
3617 // setting the state as part of a save().
3618 ps->isNew = false;
3619 } else {
3620 // This state object was set as part of a restore().
3621 restoreState(d->dirty);
3622 d->dirty = ps->savedDirty;
3623 }
3624}
3625
3626void QVGPaintEngine::beginNativePainting()
3627{
3628 Q_D(QVGPaintEngine);
3629
3630 // About to enter raw VG mode: flush pending changes and make
3631 // sure that all matrices are set to the current transformation.
3632 QVGPainterState *s = this->state();
3633 d->ensurePen(s->pen);
3634 d->ensureBrush(s->brush);
3635 d->ensurePathTransform();
3636 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform);
3637#if !defined(QVG_NO_DRAW_GLYPHS)
3638 d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform);
3639#endif
3640 d->rawVG = true;
3641}
3642
3643void QVGPaintEngine::endNativePainting()
3644{
3645 Q_D(QVGPaintEngine);
3646 // Exiting raw VG mode: force all state values to be
3647 // explicitly set on the VG engine to undo any changes
3648 // that were made by the raw VG function calls.
3649 QPaintEngine::DirtyFlags dirty = d->dirty;
3650 d->clearModes();
3651 d->forcePenChange = true;
3652 d->forceBrushChange = true;
3653 d->penType = (VGPaintType)0;
3654 d->brushType = (VGPaintType)0;
3655 d->clearColor = QColor();
3656 d->fillPaint = d->brushPaint;
3657 d->scissorDirty = true;
3658 restoreState(QPaintEngine::AllDirty);
3659 d->dirty = dirty;
3660 d->rawVG = false;
3661 vgSetPaint(d->penPaint, VG_STROKE_PATH);
3662 vgSetPaint(d->brushPaint, VG_FILL_PATH);
3663}
3664
3665QPixmapFilter *QVGPaintEngine::pixmapFilter(int type, const QPixmapFilter *prototype)
3666{
3667#if !defined(QT_SHIVAVG)
3668 Q_D(QVGPaintEngine);
3669 switch (type) {
3670 case QPixmapFilter::ConvolutionFilter:
3671 if (!d->convolutionFilter)
3672 d->convolutionFilter.reset(new QVGPixmapConvolutionFilter);
3673 return d->convolutionFilter.data();
3674 case QPixmapFilter::ColorizeFilter:
3675 if (!d->colorizeFilter)
3676 d->colorizeFilter.reset(new QVGPixmapColorizeFilter);
3677 return d->colorizeFilter.data();
3678 case QPixmapFilter::DropShadowFilter:
3679 if (!d->dropShadowFilter)
3680 d->dropShadowFilter.reset(new QVGPixmapDropShadowFilter);
3681 return d->dropShadowFilter.data();
3682 case QPixmapFilter::BlurFilter:
3683 if (!d->blurFilter)
3684 d->blurFilter.reset(new QVGPixmapBlurFilter);
3685 return d->blurFilter.data();
3686 default: break;
3687 }
3688#endif
3689 return QPaintEngineEx::pixmapFilter(type, prototype);
3690}
3691
3692void QVGPaintEngine::restoreState(QPaintEngine::DirtyFlags dirty)
3693{
3694 Q_D(QVGPaintEngine);
3695
3696 // Restore the pen, brush, and other settings.
3697 if ((dirty & QPaintEngine::DirtyBrushOrigin) != 0)
3698 brushOriginChanged();
3699 d->fillRule = 0;
3700 if ((dirty & QPaintEngine::DirtyOpacity) != 0)
3701 opacityChanged();
3702 if ((dirty & QPaintEngine::DirtyTransform) != 0)
3703 transformChanged();
3704 if ((dirty & QPaintEngine::DirtyCompositionMode) != 0)
3705 compositionModeChanged();
3706 if ((dirty & QPaintEngine::DirtyHints) != 0)
3707 renderHintsChanged();
3708 if ((dirty & (QPaintEngine::DirtyClipRegion |
3709 QPaintEngine::DirtyClipPath |
3710 QPaintEngine::DirtyClipEnabled)) != 0) {
3711 d->maskValid = false;
3712 d->maskIsSet = false;
3713 d->scissorMask = false;
3714 d->maskRect = QRect();
3715 d->scissorDirty = true;
3716 clipEnabledChanged();
3717 }
3718
3719#if defined(QVG_SCISSOR_CLIP)
3720 if ((dirty & (QPaintEngine::DirtyClipRegion |
3721 QPaintEngine::DirtyClipPath |
3722 QPaintEngine::DirtyClipEnabled)) == 0) {
3723 updateScissor();
3724 }
3725#else
3726 updateScissor();
3727#endif
3728}
3729
3730void QVGPaintEngine::fillRegion
3731 (const QRegion& region, const QColor& color, const QSize& surfaceSize)
3732{
3733 Q_D(QVGPaintEngine);
3734 if (d->clearColor != color || d->clearOpacity != 1.0f) {
3735 VGfloat values[4];
3736 values[0] = color.redF();
3737 values[1] = color.greenF();
3738 values[2] = color.blueF();
3739 values[3] = color.alphaF();
3740 vgSetfv(VG_CLEAR_COLOR, 4, values);
3741 d->clearColor = color;
3742 d->clearOpacity = 1.0f;
3743 }
3744 if (region.rectCount() == 1) {
3745 QRect r = region.boundingRect();
3746 vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
3747 r.width(), r.height());
3748 } else {
3749 const QVector<QRect> rects = region.rects();
3750 for (int i = 0; i < rects.size(); ++i) {
3751 QRect r = rects.at(i);
3752 vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
3753 r.width(), r.height());
3754 }
3755 }
3756}
3757
3758#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL)
3759
3760QVGCompositionHelper::QVGCompositionHelper()
3761{
3762 d = qt_vg_create_paint_engine()->vgPrivate();
3763}
3764
3765QVGCompositionHelper::~QVGCompositionHelper()
3766{
3767}
3768
3769void QVGCompositionHelper::startCompositing(const QSize& screenSize)
3770{
3771 this->screenSize = screenSize;
3772 clearScissor();
3773 d->setBlendMode(VG_BLEND_SRC_OVER);
3774}
3775
3776void QVGCompositionHelper::endCompositing()
3777{
3778 clearScissor();
3779}
3780
3781void QVGCompositionHelper::blitWindow
3782 (VGImage image, const QSize& imageSize,
3783 const QRect& rect, const QPoint& topLeft, int opacity)
3784{
3785 if (image == VG_INVALID_HANDLE)
3786 return;
3787
3788 // Determine which sub rectangle of the window to draw.
3789 QRect sr = rect.translated(-topLeft);
3790
3791 if (opacity >= 255) {
3792 // Fully opaque: use vgSetPixels() to directly copy the sub-region.
3793 int y = screenSize.height() - (rect.bottom() + 1);
3794 vgSetPixels(rect.x(), y, image, sr.x(),
3795 imageSize.height() - (sr.y() + sr.height()),
3796 sr.width(), sr.height());
3797 } else {
3798 // Extract the child image that we want to draw.
3799 VGImage child;
3800 if (sr.topLeft().isNull() && sr.size() == imageSize)
3801 child = image;
3802 else {
3803 child = vgChildImage
3804 (image, sr.x(), imageSize.height() - (sr.y() + sr.height()),
3805 sr.width(), sr.height());
3806 }
3807
3808 // Set the image transform.
3809 QTransform transform;
3810 int y = screenSize.height() - (rect.bottom() + 1);
3811 transform.translate(rect.x() - 0.5f, y - 0.5f);
3812 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3813
3814 // Enable opacity for image drawing if necessary.
3815 if (opacity != d->paintOpacity) {
3816 VGfloat values[4];
3817 values[0] = 1.0f;
3818 values[1] = 1.0f;
3819 values[2] = 1.0f;
3820 values[3] = ((VGfloat)opacity) / 255.0f;
3821 vgSetParameterfv(d->opacityPaint, VG_PAINT_COLOR, 4, values);
3822 d->paintOpacity = values[3];
3823 }
3824 if (d->fillPaint != d->opacityPaint) {
3825 vgSetPaint(d->opacityPaint, VG_FILL_PATH);
3826 d->fillPaint = d->opacityPaint;
3827 }
3828 d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3829
3830 // Draw the child image.
3831 vgDrawImage(child);
3832
3833 // Destroy the child image.
3834 if(child != image)
3835 vgDestroyImage(child);
3836 }
3837}
3838
3839static void fillBackgroundRect(const QRect& rect, QVGPaintEnginePrivate *d)
3840{
3841 VGfloat coords[8];
3842 coords[0] = rect.x();
3843 coords[1] = rect.y();
3844 coords[2] = rect.x() + rect.width();
3845 coords[3] = coords[1];
3846 coords[4] = coords[2];
3847 coords[5] = rect.y() + rect.height();
3848 coords[6] = coords[0];
3849 coords[7] = coords[5];
3850#if !defined(QVG_NO_MODIFY_PATH)
3851 vgModifyPathCoords(d->rectPath, 0, 4, coords);
3852 vgDrawPath(d->rectPath, VG_FILL_PATH);
3853#else
3854 Q_UNUSED(d);
3855 VGPath rectPath = vgCreatePath
3856 (VG_PATH_FORMAT_STANDARD,
3857 VG_PATH_DATATYPE_F,
3858 1.0f, // scale
3859 0.0f, // bias
3860 5, // segmentCapacityHint
3861 8, // coordCapacityHint
3862 VG_PATH_CAPABILITY_ALL);
3863 static VGubyte const segments[5] = {
3864 VG_MOVE_TO_ABS,
3865 VG_LINE_TO_ABS,
3866 VG_LINE_TO_ABS,
3867 VG_LINE_TO_ABS,
3868 VG_CLOSE_PATH
3869 };
3870 vgAppendPathData(rectPath, 5, segments, coords);
3871 vgDrawPath(rectPath, VG_FILL_PATH);
3872 vgDestroyPath(rectPath);
3873#endif
3874}
3875
3876void QVGCompositionHelper::fillBackground
3877 (const QRegion& region, const QBrush& brush)
3878{
3879 if (brush.style() == Qt::SolidPattern) {
3880 // Use vgClear() to quickly fill the background.
3881 QColor color = brush.color();
3882 if (d->clearColor != color || d->clearOpacity != 1.0f) {
3883 VGfloat values[4];
3884 values[0] = color.redF();
3885 values[1] = color.greenF();
3886 values[2] = color.blueF();
3887 values[3] = color.alphaF();
3888 vgSetfv(VG_CLEAR_COLOR, 4, values);
3889 d->clearColor = color;
3890 d->clearOpacity = 1.0f;
3891 }
3892 if (region.rectCount() == 1) {
3893 QRect r = region.boundingRect();
3894 vgClear(r.x(), screenSize.height() - r.y() - r.height(),
3895 r.width(), r.height());
3896 } else {
3897 const QVector<QRect> rects = region.rects();
3898 for (int i = 0; i < rects.size(); ++i) {
3899 QRect r = rects.at(i);
3900 vgClear(r.x(), screenSize.height() - r.y() - r.height(),
3901 r.width(), r.height());
3902 }
3903 }
3904
3905 } else {
3906 // Set the path transform to the default viewport transformation.
3907 VGfloat devh = screenSize.height();
3908 QTransform viewport(1.0f, 0.0f, 0.0f,
3909 0.0f, -1.0f, 0.0f,
3910 0.0f, devh, 1.0f);
3911 d->setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, viewport);
3912
3913 // Set the brush to use to fill the background.
3914 d->ensureBrush(brush);
3915 d->setFillRule(VG_EVEN_ODD);
3916
3917 if (region.rectCount() == 1) {
3918 fillBackgroundRect(region.boundingRect(), d);
3919 } else {
3920 const QVector<QRect> rects = region.rects();
3921 for (int i = 0; i < rects.size(); ++i)
3922 fillBackgroundRect(rects.at(i), d);
3923 }
3924
3925 // We will need to reset the path transform during the next paint.
3926 d->pathTransformSet = false;
3927 }
3928}
3929
3930void QVGCompositionHelper::drawCursorPixmap
3931 (const QPixmap& pixmap, const QPoint& offset)
3932{
3933 VGImage vgImage = VG_INVALID_HANDLE;
3934
3935 // Fetch the VGImage from the pixmap if possible.
3936 QPixmapData *pd = pixmap.pixmapData();
3937 if (!pd)
3938 return; // null QPixmap
3939 if (pd->classId() == QPixmapData::OpenVGClass) {
3940 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
3941 if (vgpd->isValid())
3942 vgImage = vgpd->toVGImage();
3943 }
3944
3945 // Set the image transformation and modes.
3946 VGfloat devh = screenSize.height();
3947 QTransform transform(1.0f, 0.0f, 0.0f,
3948 0.0f, -1.0f, 0.0f,
3949 0.0f, devh, 1.0f);
3950 transform.translate(offset.x(), offset.y());
3951 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3952 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3953
3954 // Draw the VGImage.
3955 if (vgImage != VG_INVALID_HANDLE) {
3956 vgDrawImage(vgImage);
3957 } else {
3958 QImage img = pixmap.toImage().convertToFormat
3959 (QImage::Format_ARGB32_Premultiplied);
3960
3961 vgImage = vgCreateImage
3962 (VG_sARGB_8888_PRE, img.width(), img.height(),
3963 VG_IMAGE_QUALITY_FASTER);
3964 if (vgImage == VG_INVALID_HANDLE)
3965 return;
3966 vgImageSubData
3967 (vgImage, img.constBits() + img.bytesPerLine() * (img.height() - 1),
3968 -(img.bytesPerLine()), VG_sARGB_8888_PRE, 0, 0,
3969 img.width(), img.height());
3970
3971 vgDrawImage(vgImage);
3972 vgDestroyImage(vgImage);
3973 }
3974}
3975
3976void QVGCompositionHelper::setScissor(const QRegion& region)
3977{
3978 QVector<QRect> rects = region.rects();
3979 int count = rects.count();
3980 if (count > d->maxScissorRects)
3981 count = d->maxScissorRects;
3982 QVarLengthArray<VGint> params(count * 4);
3983 int height = screenSize.height();
3984 for (int i = 0; i < count; ++i) {
3985 params[i * 4 + 0] = rects[i].x();
3986 params[i * 4 + 1] = height - rects[i].y() - rects[i].height();
3987 params[i * 4 + 2] = rects[i].width();
3988 params[i * 4 + 3] = rects[i].height();
3989 }
3990
3991 vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
3992 vgSeti(VG_SCISSORING, VG_TRUE);
3993 d->scissorDirty = false;
3994 d->scissorActive = true;
3995 d->scissorRegion = region;
3996}
3997
3998void QVGCompositionHelper::clearScissor()
3999{
4000 if (d->scissorActive || d->scissorDirty) {
4001 vgSeti(VG_SCISSORING, VG_FALSE);
4002 d->scissorActive = false;
4003 d->scissorDirty = false;
4004 }
4005}
4006
4007#endif // !QVG_NO_SINGLE_CONTEXT && !QT_NO_EGL
4008
4009VGImageFormat qt_vg_image_to_vg_format(QImage::Format format)
4010{
4011 switch (format) {
4012 case QImage::Format_MonoLSB:
4013 return VG_BW_1;
4014 case QImage::Format_ARGB32_Premultiplied:
4015 return VG_sARGB_8888_PRE;
4016 case QImage::Format_RGB32:
4017 return VG_sXRGB_8888;
4018 case QImage::Format_ARGB32:
4019 return VG_sARGB_8888;
4020 case QImage::Format_RGB16:
4021 return VG_sRGB_565;
4022 case QImage::Format_ARGB4444_Premultiplied:
4023 return VG_sARGB_4444;
4024 default: break;
4025 }
4026 return VG_sARGB_8888; // XXX
4027}
4028
4029QT_END_NAMESPACE
4030
4031#include "qpaintengine_vg.moc"
Note: See TracBrowser for help on using the repository browser.