1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtOpenVG module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
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.
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.
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.
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.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
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"
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>
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
71 #if defined(QVG_SCISSOR_CLIP) && !defined(QVG_NO_RENDER_TO_MASK)
72 #define QVG_NO_RENDER_TO_MASK 1
75 // use the same rounding as in qrasterizer.cpp (6 bit fixed point)
76 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
78 #if !defined(QVG_NO_DRAW_GLYPHS)
80 Q_DECL_IMPORT extern int qt_defaultDpiX();
81 Q_DECL_IMPORT extern int qt_defaultDpiY();
83 class QVGPaintEnginePrivate;
85 typedef QHash<QFontEngine*, QVGFontGlyphCache*> QVGFontCache;
89 class QVGFontEngineCleaner : public QObject
93 QVGFontEngineCleaner(QVGPaintEnginePrivate *d);
94 ~QVGFontEngineCleaner();
97 void fontEngineDestroyed();
100 QVGPaintEnginePrivate *d_ptr;
103 class QVGPaintEnginePrivate : public QPaintEngineExPrivate
105 Q_DECLARE_PUBLIC(QVGPaintEngine)
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
136 QVGPaintEnginePrivate(QVGPaintEngine *q_ptr);
137 ~QVGPaintEnginePrivate();
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);
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);
161 (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region);
163 (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect);
166 VGint maxScissorRects; // Maximum scissor rectangles for clipping.
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.
173 QPen currentPen; // Current pen set in "penPaint".
174 QBrush currentBrush; // Current brush set in "brushPaint".
176 bool forcePenChange; // Force a pen change, even if the same.
177 bool forceBrushChange; // Force a brush change, even if the same.
179 VGPaintType penType; // Type of the last pen that was set.
180 VGPaintType brushType; // Type of the last brush that was set.
182 QPointF brushOrigin; // Current brush origin.
184 VGint fillRule; // Last fill rule that was set.
186 qreal opacity; // Current drawing opacity.
187 qreal paintOpacity; // Opacity in opacityPaint.
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.
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".
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.
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.
208 QRect maskRect; // Rectangle version of mask if it is simple.
210 QTransform penTransform; // Transform for the pen.
211 QTransform brushTransform; // Transform for the brush.
213 VGMatrixMode matrixMode; // Last matrix mode that was set.
214 VGImageMode imageMode; // Last image mode that was set.
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.
220 QPaintEngine::DirtyFlags dirty;
222 QColor clearColor; // Last clear color that was set.
223 VGfloat clearOpacity; // Opacity during the last clear.
225 VGBlendMode blendMode; // Active blend mode.
226 VGRenderingQuality renderingQuality; // Active rendering quality.
227 VGImageQuality imageQuality; // Active image quality.
229 #if !defined(QVG_NO_DRAW_GLYPHS)
230 QVGFontCache fontCache;
231 QVGFontEngineCleaner *fontEngineCleaner;
234 bool hasAdvancedBlending;
236 QScopedPointer<QPixmapFilter> convolutionFilter;
237 QScopedPointer<QPixmapFilter> colorizeFilter;
238 QScopedPointer<QPixmapFilter> dropShadowFilter;
239 QScopedPointer<QPixmapFilter> blurFilter;
241 // Ensure that the path transform is properly set in the VG context
242 // before we perform a vgDrawPath() operation.
243 inline void ensurePathTransform()
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;
255 // Ensure that a specific pen has been set into penPaint.
256 inline void ensurePen(const QPen& pen) {
257 if (forcePenChange || pen != currentPen) {
259 forcePenChange = false;
261 (penPaint, pen.brush(),
262 VG_MATRIX_STROKE_PAINT_TO_USER, penType);
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;
273 (brushPaint, brush, VG_MATRIX_FILL_PAINT_TO_USER, brushType);
275 if (fillPaint != brushPaint) {
276 vgSetPaint(brushPaint, VG_FILL_PATH);
277 fillPaint = brushPaint;
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);
288 // Clear all lazily-set modes.
295 inline void QVGPaintEnginePrivate::setImageMode(VGImageMode mode)
297 if (imageMode != mode) {
299 vgSeti(VG_IMAGE_MODE, mode);
303 inline void QVGPaintEnginePrivate::setRenderingQuality(VGRenderingQuality mode)
305 if (renderingQuality != mode) {
306 vgSeti(VG_RENDERING_QUALITY, mode);
307 renderingQuality = mode;
308 pathTransformSet = false; // need to tweak transform for aliased stroking
312 inline void QVGPaintEnginePrivate::setImageQuality(VGImageQuality mode)
314 if (imageQuality != mode) {
315 vgSeti(VG_IMAGE_QUALITY, mode);
320 inline void QVGPaintEnginePrivate::setBlendMode(VGBlendMode mode)
322 if (blendMode != mode) {
323 vgSeti(VG_BLEND_MODE, mode);
328 inline void QVGPaintEnginePrivate::setFillRule(VGint mode)
330 if (fillRule != mode) {
332 vgSeti(VG_FILL_RULE, mode);
336 void QVGPaintEnginePrivate::clearModes()
338 matrixMode = (VGMatrixMode)0;
339 imageMode = (VGImageMode)0;
340 blendMode = (VGBlendMode)0;
341 renderingQuality = (VGRenderingQuality)0;
342 imageQuality = (VGImageQuality)0;
345 QVGPaintEnginePrivate::QVGPaintEnginePrivate(QVGPaintEngine *q_ptr) : q(q_ptr)
350 void QVGPaintEnginePrivate::init()
359 forcePenChange = true;
360 forceBrushChange = true;
361 penType = (VGPaintType)0;
362 brushType = (VGPaintType)0;
364 brushOrigin = QPointF(0.0f, 0.0f);
371 #if !defined(QVG_NO_MODIFY_PATH)
377 simpleTransform = true;
378 pathTransformSet = false;
386 scissorActive = false;
387 scissorDirty = false;
393 #if !defined(QVG_NO_DRAW_GLYPHS)
394 fontEngineCleaner = 0;
397 hasAdvancedBlending = false;
402 QVGPaintEnginePrivate::~QVGPaintEnginePrivate()
407 void QVGPaintEnginePrivate::initObjects()
409 maxScissorRects = vgGeti(VG_MAX_SCISSOR_RECTS);
411 penPaint = vgCreatePaint();
412 vgSetParameteri(penPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
413 vgSetPaint(penPaint, VG_STROKE_PATH);
415 vgSeti(VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
418 brushPaint = vgCreatePaint();
419 vgSetParameteri(brushPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
420 vgSetPaint(brushPaint, VG_FILL_PATH);
421 fillPaint = brushPaint;
423 vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
425 matrixMode = VG_MATRIX_FILL_PAINT_TO_USER;
427 opacityPaint = vgCreatePaint();
428 vgSetParameteri(opacityPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
433 values[3] = paintOpacity;
434 vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
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,
444 5, // segmentCapacityHint
445 8, // coordCapacityHint
446 VG_PATH_CAPABILITY_ALL);
447 static VGubyte const segments[5] = {
458 coords[3] = coords[1];
459 coords[4] = coords[2];
461 coords[6] = coords[0];
462 coords[7] = coords[5];
463 vgAppendPathData(rectPath, 5, segments, coords);
465 // Create a dummy line drawing path as well.
466 linePath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
470 2, // segmentCapacityHint
471 4, // coordCapacityHint
472 VG_PATH_CAPABILITY_ALL);
473 vgAppendPathData(linePath, 2, segments, coords);
476 const char *extensions = reinterpret_cast<const char *>(vgGetString(VG_EXTENSIONS));
478 hasAdvancedBlending = strstr(extensions, "VG_KHR_advanced_blending") != 0;
481 void QVGPaintEnginePrivate::destroy()
484 vgDestroyPaint(penPaint);
486 vgDestroyPaint(brushPaint);
488 vgDestroyPaint(opacityPaint);
490 #if !defined(QVG_NO_MODIFY_PATH)
492 vgDestroyPath(rectPath);
494 vgDestroyPath(linePath);
496 vgDestroyPath(roundRectPath);
499 #if !defined(QVG_NO_DRAW_GLYPHS)
500 QVGFontCache::Iterator it;
501 for (it = fontCache.begin(); it != fontCache.end(); ++it)
504 delete fontEngineCleaner;
508 // Set a specific VG transformation matrix in the current VG context.
509 void QVGPaintEnginePrivate::setTransform
510 (VGMatrixMode mode, const QTransform& transform)
513 if (mode != matrixMode) {
514 vgSeti(VG_MATRIX_MODE, mode);
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();
529 Q_DECL_IMPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
531 void QVGPaintEnginePrivate::updateTransform(QPaintDevice *pdev)
533 VGfloat devh = pdev->height();
535 // Construct the VG transform by combining the Qt transform with
536 // the following viewport transformation:
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,
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);
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;
559 simpleTransform = true;
561 pathTransformSet = false;
563 // The image transform is always the full transformation,
564 imageTransform = transform * viewport;
566 // Calculate the scaling factor to use for turning cosmetic pens
567 // into ordinary non-cosmetic pens.
568 qt_scaleForTransform(transform, &penScale);
571 VGPath QVGPaintEnginePrivate::vectorPathToVGPath(const QVectorPath& path)
573 int count = path.elementCount();
574 const qreal *points = path.points();
575 const QPainterPath::ElementType *elements = path.elements();
577 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
581 count + 1, // segmentCapacityHint
582 count * 2, // coordCapacityHint
583 VG_PATH_CAPABILITY_ALL);
585 // Size is sufficient segments for drawRoundedRect() paths.
586 QVarLengthArray<VGubyte, 20> segments;
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]) {
595 case QPainterPath::MoveToElement:
596 segments.append(VG_MOVE_TO_ABS); break;
598 case QPainterPath::LineToElement:
599 segments.append(VG_LINE_TO_ABS); break;
601 case QPainterPath::CurveToElement:
602 segments.append(VG_CUBIC_TO_ABS); break;
604 case QPainterPath::CurveToDataElement: break;
608 if (path.hasImplicitClose())
609 segments.append(VG_CLOSE_PATH);
611 vgAppendPathData(vgpath, segments.count(), segments.constData(),
612 reinterpret_cast<const VGfloat *>(points));
617 // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
618 QVarLengthArray<VGfloat, 48> coords;
623 if (elements && simpleTransform) {
624 // Convert the members of the element array.
625 for (int i = 0; i < count; ++i) {
626 switch (elements[i]) {
628 case QPainterPath::MoveToElement:
630 coords.append(points[0]);
631 coords.append(points[1]);
632 segments.append(VG_MOVE_TO_ABS);
636 case QPainterPath::LineToElement:
638 coords.append(points[0]);
639 coords.append(points[1]);
640 segments.append(VG_LINE_TO_ABS);
644 case QPainterPath::CurveToElement:
646 coords.append(points[0]);
647 coords.append(points[1]);
652 case QPainterPath::CurveToDataElement:
654 coords.append(points[0]);
655 coords.append(points[1]);
659 segments.append(VG_CUBIC_TO_ABS);
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]) {
673 case QPainterPath::MoveToElement:
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);
682 case QPainterPath::LineToElement:
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);
691 case QPainterPath::CurveToElement:
693 temp = transform.map(QPointF(points[0], points[1]));
694 coords.append(temp.x());
695 coords.append(temp.y());
700 case QPainterPath::CurveToDataElement:
702 temp = transform.map(QPointF(points[0], points[1]));
703 coords.append(temp.x());
704 coords.append(temp.y());
708 segments.append(VG_CUBIC_TO_ABS);
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);
724 coords.append(points[0]);
725 coords.append(points[1]);
726 segments.append(VG_LINE_TO_ABS);
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);
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);
745 // Close the path if specified.
746 if (path.hasImplicitClose())
747 segments.append(VG_CLOSE_PATH);
749 vgAppendPathData(vgpath, segments.count(),
750 segments.constData(), coords.constData());
755 VGPath QVGPaintEnginePrivate::painterPathToVGPath(const QPainterPath& path)
757 int count = path.elementCount();
759 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
763 count + 1, // segmentCapacityHint
764 count * 2, // coordCapacityHint
765 VG_PATH_CAPABILITY_ALL);
770 const QPainterPath::Element *elements = &(path.elementAt(0));
772 // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
773 QVarLengthArray<VGfloat, 48> coords;
774 QVarLengthArray<VGubyte, 20> segments;
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.
787 bool haveStart = false;
788 bool haveEnd = false;
790 if (simpleTransform) {
791 // Convert the members of the element array.
792 for (int i = 0; i < count; ++i) {
793 switch (elements[i].type) {
795 case QPainterPath::MoveToElement:
797 if (haveStart && haveEnd && startx == endx && starty == endy) {
798 // Implicitly close the previous sub-path.
799 segments.append(VG_CLOSE_PATH);
801 startx = elements[i].x;
802 starty = elements[i].y;
803 coords.append(startx);
804 coords.append(starty);
807 segments.append(VG_MOVE_TO_ABS);
811 case QPainterPath::LineToElement:
813 endx = elements[i].x;
814 endy = elements[i].y;
818 segments.append(VG_LINE_TO_ABS);
822 case QPainterPath::CurveToElement:
824 coords.append(elements[i].x);
825 coords.append(elements[i].y);
831 case QPainterPath::CurveToDataElement:
833 coords.append(elements[i].x);
834 coords.append(elements[i].y);
839 segments.append(VG_CUBIC_TO_ABS);
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) {
852 case QPainterPath::MoveToElement:
854 if (haveStart && haveEnd && startx == endx && starty == endy) {
855 // Implicitly close the previous sub-path.
856 segments.append(VG_CLOSE_PATH);
858 temp = transform.map(QPointF(elements[i].x, elements[i].y));
861 coords.append(startx);
862 coords.append(starty);
865 segments.append(VG_MOVE_TO_ABS);
869 case QPainterPath::LineToElement:
871 temp = transform.map(QPointF(elements[i].x, elements[i].y));
877 segments.append(VG_LINE_TO_ABS);
881 case QPainterPath::CurveToElement:
883 temp = transform.map(QPointF(elements[i].x, elements[i].y));
884 coords.append(temp.x());
885 coords.append(temp.y());
891 case QPainterPath::CurveToDataElement:
893 temp = transform.map(QPointF(elements[i].x, elements[i].y));
894 coords.append(temp.x());
895 coords.append(temp.y());
900 segments.append(VG_CUBIC_TO_ABS);
909 if (haveStart && haveEnd && startx == endx && starty == endy) {
910 // Implicitly close the last sub-path.
911 segments.append(VG_CLOSE_PATH);
914 vgAppendPathData(vgpath, segments.count(),
915 segments.constData(), coords.constData());
920 VGPath QVGPaintEnginePrivate::roundedRectPath(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
922 static VGubyte roundedrect_types[] = {
935 qreal x1 = rect.left();
936 qreal x2 = rect.right();
937 qreal y1 = rect.top();
938 qreal y2 = rect.bottom();
940 if (mode == Qt::RelativeSize) {
941 xRadius = xRadius * rect.width() / 200.;
942 yRadius = yRadius * rect.height() / 200.;
945 xRadius = qMin(xRadius, rect.width() / 2);
946 yRadius = qMin(yRadius, rect.height() / 2);
949 x1 + xRadius, y1, // MoveTo
950 x2 - xRadius, y1, // LineTo
951 x2 - (1 - KAPPA) * xRadius, y1, // CurveTo
952 x2, y1 + (1 - KAPPA) * yRadius,
954 x2, y2 - yRadius, // LineTo
955 x2, y2 - (1 - KAPPA) * yRadius, // CurveTo
956 x2 - (1 - KAPPA) * xRadius, y2,
958 x1 + xRadius, y2, // LineTo
959 x1 + (1 - KAPPA) * xRadius, y2, // CurveTo
960 x1, y2 - (1 - KAPPA) * yRadius,
962 x1, y1 + yRadius, // LineTo
963 x1, y1 + (1 - KAPPA) * yRadius, // CurveTo
964 x1 + (1 - KAPPA) * xRadius, y1,
968 #if !defined(QVG_NO_MODIFY_PATH)
969 VGPath vgpath = roundRectPath;
971 vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
975 10, // segmentCapacityHint
976 17 * 2, // coordCapacityHint
977 VG_PATH_CAPABILITY_ALL);
978 vgAppendPathData(vgpath, 10, roundedrect_types, pts);
979 roundRectPath = vgpath;
981 vgModifyPathCoords(vgpath, 0, 9, pts);
984 VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
988 10, // segmentCapacityHint
989 17 * 2, // coordCapacityHint
990 VG_PATH_CAPABILITY_ALL);
991 vgAppendPathData(vgpath, 10, roundedrect_types, pts);
997 Q_DECL_IMPORT extern QImage qt_imageForBrush(int style, bool invert);
999 static QImage colorizeBitmap(const QImage &image, const QColor &color)
1001 QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
1002 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
1004 QRgb fg = PREMUL(color.rgba());
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;
1018 static VGImage toVGImage
1019 (const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor)
1023 VGImageFormat format;
1024 switch (img.format()) {
1025 case QImage::Format_Mono:
1026 img = image.convertToFormat(QImage::Format_MonoLSB, flags);
1029 case QImage::Format_MonoLSB:
1032 case QImage::Format_RGB32:
1033 format = VG_sXRGB_8888;
1035 case QImage::Format_ARGB32:
1036 format = VG_sARGB_8888;
1038 case QImage::Format_ARGB32_Premultiplied:
1039 format = VG_sARGB_8888_PRE;
1041 case QImage::Format_RGB16:
1042 format = VG_sRGB_565;
1045 // Convert everything else into ARGB32_Premultiplied.
1046 img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
1047 format = VG_sARGB_8888_PRE;
1051 const uchar *pixels = img.constBits();
1053 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1054 (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1056 (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
1057 img.width(), img.height());
1062 static VGImage toVGImageSubRect
1063 (const QImage & image, const QRect& sr,
1064 Qt::ImageConversionFlags flags = Qt::AutoColor)
1068 VGImageFormat format;
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;
1078 case QImage::Format_ARGB32:
1079 format = VG_sARGB_8888;
1081 case QImage::Format_ARGB32_Premultiplied:
1082 format = VG_sARGB_8888_PRE;
1084 case QImage::Format_RGB16:
1085 format = VG_sRGB_565;
1089 // Convert everything else into ARGB32_Premultiplied.
1090 img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
1091 format = VG_sARGB_8888_PRE;
1095 const uchar *pixels = img.constBits() + bpp * sr.x() +
1096 img.bytesPerLine() * sr.y();
1098 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1099 (format, sr.width(), sr.height(), VG_IMAGE_QUALITY_FASTER);
1101 (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
1102 sr.width(), sr.height());
1107 static VGImage toVGImageWithOpacity(const QImage & image, qreal opacity)
1109 QImage img(image.size(), QImage::Format_ARGB32_Premultiplied);
1112 painter.begin(&img);
1113 painter.setOpacity(opacity);
1114 painter.drawImage(0, 0, image);
1117 const uchar *pixels = img.constBits();
1119 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1120 (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1122 (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
1123 img.width(), img.height());
1128 static VGImage toVGImageWithOpacitySubRect
1129 (const QImage & image, qreal opacity, const QRect& sr)
1131 QImage img(sr.size(), QImage::Format_ARGB32_Premultiplied);
1134 painter.begin(&img);
1135 painter.setOpacity(opacity);
1136 painter.drawImage(QPoint(0, 0), image, sr);
1139 const uchar *pixels = img.constBits();
1141 VGImage vgImg = QVGImagePool::instance()->createPermanentImage
1142 (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
1144 (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
1145 img.width(), img.height());
1150 VGPaintType QVGPaintEnginePrivate::setBrush
1151 (VGPaint paint, const QBrush& brush, VGMatrixMode mode,
1152 VGPaintType prevType)
1155 setBrushTransform(brush, mode);
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);
1162 switch (brush.style()) {
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;
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;
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;
1210 case Qt::TexturePattern: {
1211 // The brush is a texture specified by a QPixmap/QImage.
1212 QPixmapData *pd = brush.texture().pixmapData();
1214 break; // null QPixmap
1217 if (pd->pixelType() == QPixmapData::BitmapType) {
1218 // Colorize bitmaps using the brush color and opacity.
1219 QColor color = brush.color();
1221 color.setAlphaF(color.alphaF() * opacity);
1222 QImage image = colorizeBitmap(*(pd->buffer()), color);
1223 vgImg = toVGImage(image);
1225 } else if (opacity == 1.0) {
1226 if (pd->classId() == QPixmapData::OpenVGClass) {
1227 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
1228 vgImg = vgpd->toVGImage();
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();
1236 vgImg = toVGImage(*(pd->buffer()));
1239 } else if (pd->classId() == QPixmapData::OpenVGClass) {
1240 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
1241 vgImg = vgpd->toVGImage(opacity);
1242 vgpd->detachImageFromPool();
1244 vgImg = toVGImageWithOpacity(*(pd->buffer()), opacity);
1247 if (vgImg == VG_INVALID_HANDLE)
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);
1254 vgDestroyImage(vgImg); // Will be valid until pattern is destroyed.
1255 return VG_PAINT_TYPE_PATTERN;
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();
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;
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();
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;
1308 return (VGPaintType)0;
1311 void QVGPaintEnginePrivate::setPenParams(const QPen& pen)
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();
1318 if (pen.isCosmetic()) {
1319 if (penScale != 1.0 && penScale != 0.0)
1322 vgSetf(VG_STROKE_LINE_WIDTH, width);
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);
1329 vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
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);
1337 vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
1340 if (pen.style() == Qt::SolidLine) {
1341 vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL);
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);
1353 void QVGPaintEnginePrivate::setBrushTransform
1354 (const QBrush& brush, VGMatrixMode mode)
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());
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)
1369 brushTransform = transform;
1371 if (!rawVG && transform == penTransform)
1373 penTransform = transform;
1376 // Set the brush transformation matrix.
1377 if (mode != matrixMode) {
1378 vgSeti(VG_MATRIX_MODE, mode);
1381 if (transform.isIdentity()) {
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();
1398 void QVGPaintEnginePrivate::setupColorRamp(const QGradient *grad, VGPaint paint)
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;
1407 spreadMode = VG_COLOR_RAMP_SPREAD_PAD;
1409 const QGradientStops stops = grad->stops();
1410 int n = 5*stops.size();
1411 QVector<VGfloat> fill_stops(n);
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;
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());
1427 QVGPainterState::QVGPainterState(QVGPainterState& other)
1428 : QPainterState(other),
1429 isNew(true), clipRegion(other.clipRegion),
1434 QVGPainterState::QVGPainterState()
1435 : isNew(true), savedDirty(0)
1439 QVGPainterState::~QVGPainterState()
1443 QVGPaintEngine::QVGPaintEngine()
1444 : QPaintEngineEx(*new QVGPaintEnginePrivate(this))
1448 QVGPaintEngine::QVGPaintEngine(QVGPaintEnginePrivate &data)
1449 : QPaintEngineEx(data)
1453 QVGPaintEngine::~QVGPaintEngine()
1457 QPainterState *QVGPaintEngine::createState(QPainterState *orig) const
1460 return new QVGPainterState();
1462 Q_D(const QVGPaintEngine);
1463 QVGPaintEnginePrivate *d2 = const_cast<QVGPaintEnginePrivate*>(d);
1464 QVGPainterState *origState = static_cast<QVGPainterState *>(orig);
1465 origState->savedDirty = d2->dirty;
1467 return new QVGPainterState(*origState);
1471 void QVGPaintEnginePrivate::draw
1472 (VGPath path, const QPen& pen, const QBrush& brush, VGint rule)
1474 VGbitfield mode = 0;
1475 if (pen.style() != Qt::NoPen) {
1477 mode |= VG_STROKE_PATH;
1479 if (brush.style() != Qt::NoBrush) {
1482 mode |= VG_FILL_PATH;
1485 ensurePathTransform();
1486 vgDrawPath(path, mode);
1490 void QVGPaintEnginePrivate::stroke(VGPath path, const QPen& pen)
1492 if (pen.style() == Qt::NoPen)
1495 ensurePathTransform();
1496 vgDrawPath(path, VG_STROKE_PATH);
1499 void QVGPaintEnginePrivate::fill(VGPath path, const QBrush& brush, VGint rule)
1501 if (brush.style() == Qt::NoBrush)
1505 QPen savedPen = currentPen;
1506 currentPen = Qt::NoPen;
1507 ensurePathTransform();
1508 currentPen = savedPen;
1509 vgDrawPath(path, VG_FILL_PATH);
1512 bool QVGPaintEngine::begin(QPaintDevice *pdev)
1515 Q_D(QVGPaintEngine);
1517 // Initialize the VG painting objects if we haven't done it yet.
1521 // The initial clip region is the entire device area.
1522 QVGPainterState *s = state();
1523 s->clipRegion = defaultClipRegion();
1525 // Initialize the VG state for this paint operation.
1526 restoreState(QPaintEngine::AllDirty);
1532 bool QVGPaintEngine::end()
1537 void QVGPaintEngine::draw(const QVectorPath &path)
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);
1545 d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
1546 vgDestroyPath(vgpath);
1549 void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1551 Q_D(QVGPaintEngine);
1552 VGPath vgpath = d->vectorPathToVGPath(path);
1553 if (!path.hasWindingFill())
1554 d->fill(vgpath, brush, VG_EVEN_ODD);
1556 d->fill(vgpath, brush, VG_NON_ZERO);
1557 vgDestroyPath(vgpath);
1560 void QVGPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1562 Q_D(QVGPaintEngine);
1563 VGPath vgpath = d->vectorPathToVGPath(path);
1564 d->stroke(vgpath, pen);
1565 vgDestroyPath(vgpath);
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.
1571 static inline bool clipTransformIsSimple(const QTransform& transform)
1573 QTransform::TransformationType type = transform.type();
1574 if (type == QTransform::TxNone || type == QTransform::TxTranslate)
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.
1598 #if defined(QVG_SCISSOR_CLIP)
1600 void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1602 Q_D(QVGPaintEngine);
1603 QVGPainterState *s = state();
1605 d->dirty |= QPaintEngine::DirtyClipRegion;
1607 if (op == Qt::NoClip) {
1608 s->clipRegion = defaultClipRegion();
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);
1625 // Try converting the path into a QRegion that tightly follows
1626 // the outline of the path we want to clip with.
1628 if (!path.isEmpty())
1629 region = QRegion(path.convertToPainterPath().toFillPolygon(QTransform()).toPolygon());
1634 region = defaultClipRegion();
1638 case Qt::ReplaceClip:
1640 region = d->transform.map(region);
1644 case Qt::IntersectClip:
1646 region = s->clipRegion.intersect(d->transform.map(region));
1652 region = s->clipRegion.unite(d->transform.map(region));
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;
1664 // The best we can do is clip to the bounding rectangle
1665 // of all control points.
1666 clip(path.controlPointRect().toRect(), op);
1669 void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1671 Q_D(QVGPaintEngine);
1672 QVGPainterState *s = state();
1674 d->dirty |= QPaintEngine::DirtyClipRegion;
1679 s->clipRegion = defaultClipRegion();
1683 case Qt::ReplaceClip:
1685 s->clipRegion = d->transform.map(QRegion(rect));
1689 case Qt::IntersectClip:
1691 s->clipRegion = s->clipRegion.intersect(d->transform.map(QRegion(rect)));
1697 s->clipRegion = s->clipRegion.unite(d->transform.map(QRegion(rect)));
1705 void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1707 Q_D(QVGPaintEngine);
1708 QVGPainterState *s = state();
1710 d->dirty |= QPaintEngine::DirtyClipRegion;
1715 s->clipRegion = defaultClipRegion();
1719 case Qt::ReplaceClip:
1721 s->clipRegion = d->transform.map(region);
1725 case Qt::IntersectClip:
1727 s->clipRegion = s->clipRegion.intersect(d->transform.map(region));
1733 s->clipRegion = s->clipRegion.unite(d->transform.map(region));
1741 void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
1743 QPaintEngineEx::clip(path, op);
1746 #else // !QVG_SCISSOR_CLIP
1748 void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1750 Q_D(QVGPaintEngine);
1752 d->dirty |= QPaintEngine::DirtyClipRegion;
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);
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);
1775 #if !defined(QVG_NO_RENDER_TO_MASK)
1776 QPaintDevice *pdev = paintDevice();
1777 int width = pdev->width();
1778 int height = pdev->height();
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);
1787 d->ensurePathTransform();
1788 VGPath vgpath = d->vectorPathToVGPath(path);
1790 case Qt::ReplaceClip:
1792 vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
1795 case Qt::IntersectClip:
1796 vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
1801 vgDestroyPath(vgpath);
1803 vgSeti(VG_MASKING, VG_TRUE);
1804 d->maskValid = true;
1805 d->maskIsSet = false;
1806 d->scissorMask = false;
1810 void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1812 Q_D(QVGPaintEngine);
1814 d->dirty |= QPaintEngine::DirtyClipRegion;
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);
1825 d->maskValid = false;
1826 d->maskIsSet = true;
1827 d->scissorMask = false;
1828 d->maskRect = QRect();
1829 vgSeti(VG_MASKING, VG_FALSE);
1833 case Qt::ReplaceClip:
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.
1840 vgSeti(VG_MASKING, VG_FALSE);
1841 d->maskValid = false;
1842 d->maskIsSet = true;
1843 d->scissorMask = false;
1844 d->maskRect = QRect();
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.
1852 if (clip.rectCount() == 1) {
1853 clipRect = clip.boundingRect().intersected(r);
1854 } else if (clip.isEmpty()) {
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);
1866 clipRect = clip.boundingRect();
1868 d->maskValid = false;
1869 d->maskIsSet = false;
1870 d->scissorMask = true;
1871 d->maskRect = clipRect;
1872 vgSeti(VG_MASKING, VG_FALSE);
1878 case Qt::IntersectClip:
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.
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();
1891 // Activate the scissor on a smaller maskRect.
1892 d->maskIsSet = false;
1895 d->scissorMask = true;
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.
1901 vgSeti(VG_MASKING, VG_FALSE);
1902 d->maskValid = false;
1903 d->maskIsSet = true;
1904 d->scissorMask = false;
1905 d->maskRect = QRect();
1907 d->modifyMask(this, VG_INTERSECT_MASK, r);
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));
1923 void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1925 Q_D(QVGPaintEngine);
1927 // Use the QRect case if the region consists of a single rectangle.
1928 if (region.rectCount() == 1) {
1929 clip(region.boundingRect(), op);
1933 d->dirty |= QPaintEngine::DirtyClipRegion;
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);
1944 d->maskValid = false;
1945 d->maskIsSet = true;
1946 d->scissorMask = false;
1947 d->maskRect = QRect();
1948 vgSeti(VG_MASKING, VG_FALSE);
1952 case Qt::ReplaceClip:
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.
1959 vgSeti(VG_MASKING, VG_FALSE);
1960 d->maskValid = false;
1961 d->maskIsSet = true;
1962 d->scissorMask = false;
1963 d->maskRect = QRect();
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;
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);
1981 d->maskValid = false;
1982 d->maskIsSet = false;
1983 d->scissorMask = false;
1984 d->maskRect = QRect();
1985 d->modifyMask(this, VG_FILL_MASK, r);
1991 case Qt::IntersectClip:
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);
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.
2005 vgSeti(VG_MASKING, VG_FALSE);
2006 d->maskValid = false;
2007 d->maskIsSet = true;
2008 d->scissorMask = false;
2009 d->maskRect = QRect();
2011 d->modifyMask(this, VG_INTERSECT_MASK, r);
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));
2027 #if !defined(QVG_NO_RENDER_TO_MASK)
2029 // Copied from qpathclipper.cpp.
2030 static bool qt_vg_pathToRect(const QPainterPath &path, QRectF *rect)
2032 if (path.elementCount() != 5)
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();
2044 const qreal x1 = path.elementAt(0).x;
2045 const qreal y1 = path.elementAt(0).y;
2047 const qreal x2 = path.elementAt(1).x;
2048 const qreal y2 = path.elementAt(2).y;
2050 if (path.elementAt(1).y != y1)
2053 if (path.elementAt(2).x != x2)
2056 if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
2059 if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
2063 *rect = QRectF(QPointF(x1, y1), QPointF(x2, y2));
2070 void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
2072 #if !defined(QVG_NO_RENDER_TO_MASK)
2073 Q_D(QVGPaintEngine);
2075 // If the path is a simple rectangle, then use clip(QRect) instead.
2077 if (qt_vg_pathToRect(path, &simpleRect)) {
2078 clip(simpleRect.toRect(), op);
2082 d->dirty |= QPaintEngine::DirtyClipRegion;
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);
2093 QPaintDevice *pdev = paintDevice();
2094 int width = pdev->width();
2095 int height = pdev->height();
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);
2104 d->ensurePathTransform();
2105 VGPath vgpath = d->painterPathToVGPath(path);
2107 case Qt::ReplaceClip:
2109 vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
2112 case Qt::IntersectClip:
2113 vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
2118 vgDestroyPath(vgpath);
2120 vgSeti(VG_MASKING, VG_TRUE);
2121 d->maskValid = true;
2122 d->maskIsSet = false;
2123 d->scissorMask = false;
2125 QPaintEngineEx::clip(path, op);
2129 void QVGPaintEnginePrivate::ensureMask
2130 (QVGPaintEngine *engine, int width, int height)
2132 scissorMask = false;
2134 vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, width, height);
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());
2143 engine->updateScissor();
2148 void QVGPaintEnginePrivate::modifyMask
2149 (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region)
2151 QPaintDevice *pdev = engine->paintDevice();
2152 int width = pdev->width();
2153 int height = pdev->height();
2156 ensureMask(engine, width, height);
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());
2165 vgSeti(VG_MASKING, VG_TRUE);
2168 scissorMask = false;
2171 void QVGPaintEnginePrivate::modifyMask
2172 (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect)
2174 QPaintDevice *pdev = engine->paintDevice();
2175 int width = pdev->width();
2176 int height = pdev->height();
2179 ensureMask(engine, width, height);
2181 if (rect.isValid()) {
2182 vgMask(VG_INVALID_HANDLE, op,
2183 rect.x(), height - rect.y() - rect.height(),
2184 rect.width(), rect.height());
2187 vgSeti(VG_MASKING, VG_TRUE);
2190 scissorMask = false;
2193 #endif // !QVG_SCISSOR_CLIP
2195 void QVGPaintEngine::updateScissor()
2197 Q_D(QVGPaintEngine);
2199 QRegion region = d->systemClip;
2201 #if defined(QVG_SCISSOR_CLIP)
2202 // Using the scissor to do clipping, so combine the systemClip
2203 // with the current painting clipRegion.
2206 vgSeti(VG_MASKING, VG_FALSE);
2207 d->maskValid = false;
2210 QVGPainterState *s = state();
2211 if (s->clipEnabled) {
2212 if (region.isEmpty())
2213 region = s->clipRegion;
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;
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;
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;
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;
2254 if (d->scissorActive && region == d->scissorRegion && !d->scissorDirty)
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;
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());
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;
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();
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;
2302 QRegion QVGPaintEngine::defaultClipRegion()
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());
2309 bool QVGPaintEngine::isDefaultClipRegion(const QRegion& region)
2311 if (region.rectCount() != 1)
2314 QPaintDevice *pdev = paintDevice();
2315 int width = pdev->width();
2316 int height = pdev->height();
2318 QRect rect = region.boundingRect();
2319 return (rect.x() == 0 && rect.y() == 0 &&
2320 rect.width() == width && rect.height() == height);
2323 bool QVGPaintEngine::isDefaultClipRect(const QRect& rect)
2325 QPaintDevice *pdev = paintDevice();
2326 int width = pdev->width();
2327 int height = pdev->height();
2329 return (rect.x() == 0 && rect.y() == 0 &&
2330 rect.width() == width && rect.height() == height);
2333 void QVGPaintEngine::clipEnabledChanged()
2335 #if defined(QVG_SCISSOR_CLIP)
2336 vgSeti(VG_MASKING, VG_FALSE); // disable mask fallback
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());
2353 vgSeti(VG_MASKING, VG_FALSE);
2354 d->maskValid = false;
2355 d->maskIsSet = false;
2356 d->scissorMask = false;
2357 d->maskRect = QRect();
2362 void QVGPaintEngine::penChanged()
2364 Q_D(QVGPaintEngine);
2365 d->dirty |= QPaintEngine::DirtyPen;
2368 void QVGPaintEngine::brushChanged()
2370 Q_D(QVGPaintEngine);
2371 d->dirty |= QPaintEngine::DirtyBrush;
2374 void QVGPaintEngine::brushOriginChanged()
2376 Q_D(QVGPaintEngine);
2377 d->dirty |= QPaintEngine::DirtyBrushOrigin;
2378 d->brushOrigin = state()->brushOrigin;
2379 d->forcePenChange = true;
2380 d->forceBrushChange = true;
2383 void QVGPaintEngine::opacityChanged()
2385 Q_D(QVGPaintEngine);
2386 d->dirty |= QPaintEngine::DirtyOpacity;
2387 d->opacity = state()->opacity;
2388 d->forcePenChange = true;
2389 d->forceBrushChange = true;
2392 void QVGPaintEngine::compositionModeChanged()
2394 Q_D(QVGPaintEngine);
2395 d->dirty |= QPaintEngine::DirtyCompositionMode;
2397 VGint vgMode = VG_BLEND_SRC_OVER;
2399 switch (state()->composition_mode) {
2400 case QPainter::CompositionMode_SourceOver:
2401 vgMode = VG_BLEND_SRC_OVER;
2403 case QPainter::CompositionMode_DestinationOver:
2404 vgMode = VG_BLEND_DST_OVER;
2406 case QPainter::CompositionMode_Source:
2407 vgMode = VG_BLEND_SRC;
2409 case QPainter::CompositionMode_SourceIn:
2410 vgMode = VG_BLEND_SRC_IN;
2412 case QPainter::CompositionMode_DestinationIn:
2413 vgMode = VG_BLEND_DST_IN;
2415 case QPainter::CompositionMode_Plus:
2416 vgMode = VG_BLEND_ADDITIVE;
2418 case QPainter::CompositionMode_Multiply:
2419 vgMode = VG_BLEND_MULTIPLY;
2421 case QPainter::CompositionMode_Screen:
2422 vgMode = VG_BLEND_SCREEN;
2424 case QPainter::CompositionMode_Darken:
2425 vgMode = VG_BLEND_DARKEN;
2427 case QPainter::CompositionMode_Lighten:
2428 vgMode = VG_BLEND_LIGHTEN;
2431 if (d->hasAdvancedBlending) {
2432 switch (state()->composition_mode) {
2433 case QPainter::CompositionMode_Overlay:
2434 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_OVERLAY_KHR;
2436 case QPainter::CompositionMode_ColorDodge:
2437 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORDODGE_KHR;
2439 case QPainter::CompositionMode_ColorBurn:
2440 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORBURN_KHR;
2442 case QPainter::CompositionMode_HardLight:
2443 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_HARDLIGHT_KHR;
2445 case QPainter::CompositionMode_SoftLight:
2446 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SOFTLIGHT_KHR;
2448 case QPainter::CompositionMode_Difference:
2449 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DIFFERENCE_KHR;
2451 case QPainter::CompositionMode_Exclusion:
2452 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_EXCLUSION_KHR;
2454 case QPainter::CompositionMode_SourceOut:
2455 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_OUT_KHR;
2457 case QPainter::CompositionMode_DestinationOut:
2458 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_OUT_KHR;
2460 case QPainter::CompositionMode_SourceAtop:
2461 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_ATOP_KHR;
2463 case QPainter::CompositionMode_DestinationAtop:
2464 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_ATOP_KHR;
2466 case QPainter::CompositionMode_Xor:
2467 vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_XOR_KHR;
2469 default: break; // Fall back to VG_BLEND_SRC_OVER.
2472 if (vgMode == VG_BLEND_SRC_OVER)
2473 qWarning() << "QVGPaintEngine::compositionModeChanged unsupported mode" << state()->composition_mode;
2477 d->setBlendMode(VGBlendMode(vgMode));
2480 void QVGPaintEngine::renderHintsChanged()
2482 Q_D(QVGPaintEngine);
2483 d->dirty |= QPaintEngine::DirtyHints;
2485 QPainter::RenderHints hints = state()->renderHints;
2487 VGRenderingQuality rq =
2488 (hints & QPainter::Antialiasing)
2489 ? VG_RENDERING_QUALITY_BETTER
2490 : VG_RENDERING_QUALITY_NONANTIALIASED;
2492 (hints & QPainter::SmoothPixmapTransform)
2493 ? VG_IMAGE_QUALITY_BETTER
2494 : VG_IMAGE_QUALITY_NONANTIALIASED;
2496 d->setRenderingQuality(rq);
2497 d->setImageQuality(iq);
2500 void QVGPaintEngine::transformChanged()
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;
2512 bool QVGPaintEngine::clearRect(const QRectF &rect, const QColor &color)
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) {
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;
2529 vgClear(r.x(), height - r.y() - r.height(),
2530 r.width(), r.height());
2536 void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
2538 Q_D(QVGPaintEngine);
2540 if (brush.style() == Qt::NoBrush)
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())) {
2550 #if !defined(QVG_NO_MODIFY_PATH)
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];
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());
2575 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2576 d->fill(d->rectPath, brush);
2578 QPaintEngineEx::fillRect(rect, brush);
2582 void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color)
2584 Q_D(QVGPaintEngine);
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)) {
2592 #if !defined(QVG_NO_MODIFY_PATH)
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];
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());
2617 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2618 d->fill(d->rectPath, QBrush(color));
2620 QPaintEngineEx::fillRect(rect, QBrush(color));
2624 void QVGPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode)
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);
2635 QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
2639 void QVGPaintEngine::drawRects(const QRect *rects, int rectCount)
2641 #if !defined(QVG_NO_MODIFY_PATH)
2642 Q_D(QVGPaintEngine);
2643 QVGPainterState *s = state();
2644 for (int i = 0; i < rectCount; ++i, ++rects) {
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];
2656 QPointF tl = d->transform.map(QPointF(rects->x(), rects->y()));
2657 QPointF tr = d->transform.map(QPointF(rects->x() + rects->width(),
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()));
2672 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2673 d->draw(d->rectPath, s->pen, s->brush);
2676 QPaintEngineEx::drawRects(rects, rectCount);
2680 void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount)
2682 #if !defined(QVG_NO_MODIFY_PATH)
2683 Q_D(QVGPaintEngine);
2684 QVGPainterState *s = state();
2685 for (int i = 0; i < rectCount; ++i, ++rects) {
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];
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());
2710 vgModifyPathCoords(d->rectPath, 0, 4, coords);
2711 d->draw(d->rectPath, s->pen, s->brush);
2714 QPaintEngineEx::drawRects(rects, rectCount);
2718 void QVGPaintEngine::drawLines(const QLine *lines, int lineCount)
2720 #if !defined(QVG_NO_MODIFY_PATH)
2721 Q_D(QVGPaintEngine);
2722 QVGPainterState *s = state();
2723 for (int i = 0; i < lineCount; ++i, ++lines) {
2725 if (d->simpleTransform) {
2726 coords[0] = lines->x1();
2727 coords[1] = lines->y1();
2728 coords[2] = lines->x2();
2729 coords[3] = lines->y2();
2731 QPointF p1 = d->transform.map(QPointF(lines->x1(), lines->y1()));
2732 QPointF p2 = d->transform.map(QPointF(lines->x2(), lines->y2()));
2738 vgModifyPathCoords(d->linePath, 0, 2, coords);
2739 d->stroke(d->linePath, s->pen);
2742 QPaintEngineEx::drawLines(lines, lineCount);
2746 void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
2748 #if !defined(QVG_NO_MODIFY_PATH)
2749 Q_D(QVGPaintEngine);
2750 QVGPainterState *s = state();
2751 for (int i = 0; i < lineCount; ++i, ++lines) {
2753 if (d->simpleTransform) {
2754 coords[0] = lines->x1();
2755 coords[1] = lines->y1();
2756 coords[2] = lines->x2();
2757 coords[3] = lines->y2();
2759 QPointF p1 = d->transform.map(lines->p1());
2760 QPointF p2 = d->transform.map(lines->p2());
2766 vgModifyPathCoords(d->linePath, 0, 2, coords);
2767 d->stroke(d->linePath, s->pen);
2770 QPaintEngineEx::drawLines(lines, lineCount);
2774 void QVGPaintEngine::drawEllipse(const QRectF &r)
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,
2785 4, // segmentCapacityHint
2786 12, // coordCapacityHint
2787 VG_PATH_CAPABILITY_ALL);
2788 static VGubyte segments[4] = {
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;
2802 coords[5] = -r.width();
2804 coords[7] = halfwid;
2807 coords[10] = r.width();
2809 vgAppendPathData(path, 4, segments, coords);
2810 d->draw(path, s->pen, s->brush);
2811 vgDestroyPath(path);
2813 // The projective transform version of an ellipse is difficult.
2814 // Generate a QVectorPath containing cubic curves and transform that.
2815 QPaintEngineEx::drawEllipse(r);
2819 void QVGPaintEngine::drawEllipse(const QRect &r)
2821 drawEllipse(QRectF(r));
2824 void QVGPaintEngine::drawPath(const QPainterPath &path)
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);
2834 d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
2835 vgDestroyPath(vgpath);
2838 void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
2840 #if !defined(QVG_NO_MODIFY_PATH)
2841 Q_D(QVGPaintEngine);
2843 // Set up a new pen if necessary.
2844 QPen pen = state()->pen;
2845 if (pen.style() == Qt::NoPen)
2847 if (pen.capStyle() == Qt::FlatCap)
2848 pen.setCapStyle(Qt::SquareCap);
2850 for (int i = 0; i < pointCount; ++i, ++points) {
2852 if (d->simpleTransform) {
2853 coords[0] = points->x();
2854 coords[1] = points->y();
2855 coords[2] = coords[0];
2856 coords[3] = coords[1];
2858 QPointF p = d->transform.map(*points);
2861 coords[2] = coords[0];
2862 coords[3] = coords[1];
2864 vgModifyPathCoords(d->linePath, 0, 2, coords);
2865 d->stroke(d->linePath, pen);
2868 QPaintEngineEx::drawPoints(points, pointCount);
2872 void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
2874 #if !defined(QVG_NO_MODIFY_PATH)
2875 Q_D(QVGPaintEngine);
2877 // Set up a new pen if necessary.
2878 QPen pen = state()->pen;
2879 if (pen.style() == Qt::NoPen)
2881 if (pen.capStyle() == Qt::FlatCap)
2882 pen.setCapStyle(Qt::SquareCap);
2884 for (int i = 0; i < pointCount; ++i, ++points) {
2886 if (d->simpleTransform) {
2887 coords[0] = points->x();
2888 coords[1] = points->y();
2889 coords[2] = coords[0];
2890 coords[3] = coords[1];
2892 QPointF p = d->transform.map(QPointF(*points));
2895 coords[2] = coords[0];
2896 coords[3] = coords[1];
2898 vgModifyPathCoords(d->linePath, 0, 2, coords);
2899 d->stroke(d->linePath, pen);
2902 QPaintEngineEx::drawPoints(points, pointCount);
2906 void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
2908 Q_D(QVGPaintEngine);
2909 QVGPainterState *s = state();
2910 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
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());
2924 QPointF temp = d->transform.map(*points);
2925 coords.append(temp.x());
2926 coords.append(temp.y());
2929 segments.append(VG_MOVE_TO_ABS);
2931 segments.append(VG_LINE_TO_ABS);
2933 if (mode != QPaintEngine::PolylineMode)
2934 segments.append(VG_CLOSE_PATH);
2935 vgAppendPathData(path, segments.count(),
2936 segments.constData(), coords.constData());
2938 case QPaintEngine::WindingMode:
2939 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
2942 case QPaintEngine::PolylineMode:
2943 d->stroke(path, s->pen);
2947 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
2950 vgDestroyPath(path);
2953 void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2955 Q_D(QVGPaintEngine);
2956 QVGPainterState *s = state();
2957 VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
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());
2971 QPointF temp = d->transform.map(QPointF(*points));
2972 coords.append(temp.x());
2973 coords.append(temp.y());
2976 segments.append(VG_MOVE_TO_ABS);
2978 segments.append(VG_LINE_TO_ABS);
2980 if (mode != QPaintEngine::PolylineMode)
2981 segments.append(VG_CLOSE_PATH);
2982 vgAppendPathData(path, segments.count(),
2983 segments.constData(), coords.constData());
2985 case QPaintEngine::WindingMode:
2986 d->draw(path, s->pen, s->brush, VG_NON_ZERO);
2989 case QPaintEngine::PolylineMode:
2990 d->stroke(path, s->pen);
2994 d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
2997 vgDestroyPath(path);
3000 void QVGPaintEnginePrivate::setImageOptions()
3002 if (opacity != 1.0f && simpleTransform) {
3003 if (opacity != paintOpacity) {
3008 values[3] = opacity;
3009 vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
3010 paintOpacity = opacity;
3012 if (fillPaint != opacityPaint) {
3013 vgSetPaint(opacityPaint, VG_FILL_PATH);
3014 fillPaint = opacityPaint;
3016 setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3018 setImageMode(VG_DRAW_IMAGE_NORMAL);
3022 void QVGPaintEnginePrivate::systemStateChanged()
3027 static void drawVGImage(QVGPaintEnginePrivate *d,
3028 const QRectF& r, VGImage vgImg,
3029 const QSize& imageSize, const QRectF& sr)
3031 if (vgImg == VG_INVALID_HANDLE)
3033 VGImage child = VG_INVALID_HANDLE;
3035 if (sr.topLeft().isNull() && sr.size() == imageSize) {
3038 QRect src = sr.toRect();
3039 #if !defined(QT_SHIVAVG)
3040 child = vgChildImage(vgImg, src.x(), src.y(), src.width(), src.height());
3042 child = vgImg; // XXX: ShivaVG doesn't have vgChildImage().
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);
3053 d->setImageOptions();
3057 vgDestroyImage(child);
3060 static void drawVGImage(QVGPaintEnginePrivate *d,
3061 const QPointF& pos, VGImage vgImg)
3063 if (vgImg == VG_INVALID_HANDLE)
3066 QTransform transform(d->imageTransform);
3067 transform.translate(pos.x(), pos.y());
3068 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3070 d->setImageOptions();
3074 // Used by qpixmapfilter_vg.cpp to draw filtered VGImage's.
3075 void qt_vg_drawVGImage(QPainter *painter, const QPointF& pos, VGImage vgImg)
3077 QVGPaintEngine *engine =
3078 static_cast<QVGPaintEngine *>(painter->paintEngine());
3079 drawVGImage(engine->vgPrivate(), pos, vgImg);
3082 // Used by qpixmapfilter_vg.cpp to draw filtered VGImage's as a stencil.
3083 void qt_vg_drawVGImageStencil
3084 (QPainter *painter, const QPointF& pos, VGImage vgImg, const QBrush& brush)
3086 QVGPaintEngine *engine =
3087 static_cast<QVGPaintEngine *>(painter->paintEngine());
3089 QVGPaintEnginePrivate *d = engine->vgPrivate();
3091 QTransform transform(d->imageTransform);
3092 transform.translate(pos.x(), pos.y());
3093 d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
3095 d->ensureBrush(brush);
3096 d->setImageMode(VG_DRAW_IMAGE_STENCIL);
3100 bool QVGPaintEngine::canVgWritePixels(const QImage &image) const
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()));
3115 void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
3117 QPixmapData *pd = pm.pixmapData();
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())
3125 if (d->simpleTransform)
3126 drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr);
3128 drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr);
3130 if(!vgpd->failedToAlloc)
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;
3140 drawImage(r, *(pd->buffer()), sr, Qt::AutoColor);
3143 void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm)
3145 QPixmapData *pd = pm.pixmapData();
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())
3153 if (d->simpleTransform)
3154 drawVGImage(d, pos, vgpd->toVGImage());
3156 drawVGImage(d, pos, vgpd->toVGImage(d->opacity));
3158 if (!vgpd->failedToAlloc)
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;
3168 drawImage(pos, *(pd->buffer()));
3171 void QVGPaintEngine::drawImage
3172 (const QRectF &r, const QImage &image, const QRectF &sr,
3173 Qt::ImageConversionFlags flags)
3175 Q_D(QVGPaintEngine);
3177 if (d->simpleTransform || d->opacity == 1.0f)
3178 vgImg = toVGImageSubRect(image, sr.toRect(), flags);
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);
3185 drawVGImage(d, r, vgImg, sr.size().toSize(),
3186 QRectF(QPointF(0, 0), sr.size()));
3189 if (canVgWritePixels(image) && (r.size() == sr.size()) && !flags) {
3190 // Optimization for straight blits, no blending
3193 int bpp = image.depth() >> 3; // bytes
3195 int bpl = image.bytesPerLine();
3196 if (d->imageTransform.m22() < 0) {
3198 offset = ((y + sr.height()) * bpl) - ((image.width() - x) * bpp);
3201 offset = (y * bpl) + (x * bpp);
3203 const uchar *bits = image.constBits() + offset;
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());
3210 // Monochrome images need to use the vgChildImage() path.
3211 vgImg = toVGImage(image, flags);
3212 drawVGImage(d, r, vgImg, image.size(), sr);
3215 vgDestroyImage(vgImg);
3218 void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image)
3220 Q_D(QVGPaintEngine);
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();
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());
3232 } else if (d->simpleTransform || d->opacity == 1.0f) {
3233 vgImg = toVGImage(image);
3235 vgImg = toVGImageWithOpacity(image, d->opacity);
3237 drawVGImage(d, pos, vgImg);
3238 vgDestroyImage(vgImg);
3241 void QVGPaintEngine::drawTiledPixmap
3242 (const QRectF &r, const QPixmap &pixmap, const QPointF &s)
3244 QBrush brush(state()->pen.color(), pixmap);
3245 QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
3246 brush.setTransform(xform);
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.
3254 void QVGPaintEngine::drawPixmapFragments(const QPainter::PixmapFragment *drawingData, int dataCount,
3255 const QPixmap &pixmap, QFlags<QPainter::PixmapFragmentHint> hints)
3257 #if !defined(QT_SHIVAVG)
3258 Q_D(QVGPaintEngine);
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();
3264 return; // null QPixmap
3265 if (pd->classId() != QPixmapData::OpenVGClass || !d->simpleTransform) {
3266 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3270 // Bail out if nothing to do.
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())
3278 VGImage vgImg = vgpd->toVGImage();
3279 if (vgImg == VG_INVALID_HANDLE)
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;
3289 // Select the opacity paint object.
3290 if ((hints & QPainter::OpaqueHint) != 0 && d->opacity == 1.0f) {
3291 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3294 if (d->fillPaint != d->opacityPaint) {
3295 vgSetPaint(d->opacityPaint, VG_FILL_PATH);
3296 d->fillPaint = d->opacityPaint;
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);
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) {
3312 // Look for a previous child with the same source rectangle
3313 // to avoid constantly calling vgChildImage()/vgDestroyImage().
3314 QRect src = sr.toRect();
3316 for (j = 0; j < cachedSources.size(); ++j) {
3317 if (cachedSources[j] == src)
3320 if (j < cachedSources.size()) {
3321 child = cachedImages[j];
3323 child = vgChildImage
3324 (vgImg, src.x(), src.y(), src.width(), src.height());
3325 cachedImages.append(child);
3326 cachedSources.append(src);
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);
3337 if ((hints & QPainter::OpaqueHint) == 0) {
3338 qreal opacity = d->opacity * drawingData[i].opacity;
3339 if (opacity != 1.0f) {
3340 if (d->paintOpacity != opacity) {
3345 values[3] = opacity;
3346 d->paintOpacity = opacity;
3348 (d->opacityPaint, VG_PAINT_COLOR, 4, values);
3350 d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
3352 d->setImageMode(VG_DRAW_IMAGE_NORMAL);
3359 // Destroy the cached child sub-images.
3360 for (int i = 0; i < cachedImages.size(); ++i)
3361 vgDestroyImage(cachedImages[i]);
3363 QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
3367 QVGFontEngineCleaner::QVGFontEngineCleaner(QVGPaintEnginePrivate *d)
3368 : QObject(), d_ptr(d)
3372 QVGFontEngineCleaner::~QVGFontEngineCleaner()
3376 void QVGFontEngineCleaner::fontEngineDestroyed()
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()) {
3383 d_ptr->fontCache.erase(it);
3388 #if !defined(QVG_NO_DRAW_GLYPHS)
3390 QVGFontGlyphCache::QVGFontGlyphCache()
3392 font = vgCreateFont(0);
3393 scaleX = scaleY = 0.0;
3394 invertedGlyphs = false;
3395 memset(cachedGlyphsMask, 0, sizeof(cachedGlyphsMask));
3398 QVGFontGlyphCache::~QVGFontGlyphCache()
3400 if (font != VG_INVALID_HANDLE)
3401 vgDestroyFont(font);
3404 void QVGFontGlyphCache::setScaleFromText(const QFont &font, QFontEngine *fontEngine)
3407 qreal pixelSize = fi.pixelSize();
3408 qreal emSquare = fontEngine->properties().emSquare.toReal();
3409 scaleX = scaleY = static_cast<VGfloat>(pixelSize / emSquare);
3412 void QVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d,
3413 QFontEngine *fontEngine,
3414 const glyph_t *g, int count)
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++;
3426 if ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0)
3428 cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32));
3429 } else if (cachedGlyphs.contains(glyph)) {
3432 cachedGlyphs.insert(glyph);
3434 #if !defined(QVG_NO_IMAGE_GLYPHS)
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());
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());
3453 origin[0] = -metrics.x.toReal();
3454 origin[1] = -metrics.y.toReal();
3457 vgSetGlyphToImage(font, glyph, vgImage, origin, escapement);
3458 vgDestroyImage(vgImage); // Reduce reference count.
3460 // Calculate the path for the glyph and cache it.
3462 fontEngine->getUnscaledGlyph(glyph, &path, &metrics);
3464 if (!path.isEmpty()) {
3465 vgPath = d->painterPathToVGPath(path);
3467 // Probably a "space" character with no visible outline.
3468 vgPath = VG_INVALID_HANDLE;
3474 vgSetGlyphToPath(font, glyph, vgPath, VG_FALSE, origin, escapement);
3475 vgDestroyPath(vgPath); // Reduce reference count.
3476 #endif // !defined(QVG_NO_IMAGE_GLYPHS)
3480 #endif // !defined(QVG_NO_DRAW_GLYPHS)
3482 void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3484 #if !defined(QVG_NO_DRAW_GLYPHS)
3485 Q_D(QVGPaintEngine);
3486 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
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);
3495 // Get the glyphs and positions associated with the text item.
3496 QVarLengthArray<QFixedPoint> positions;
3497 QVarLengthArray<glyph_t> glyphs;
3499 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3501 if (!drawCachedGlyphs(glyphs.size(), glyphs.data(), ti.font(), ti.fontEngine, p, positions.data()))
3502 QPaintEngineEx::drawTextItem(p, textItem);
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);
3510 void QVGPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)