summaryrefslogtreecommitdiffstats
path: root/src/openvg
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/openvg
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/openvg')
-rw-r--r--src/openvg/openvg.pro71
-rw-r--r--src/openvg/qpaintengine_vg.cpp4153
-rw-r--r--src/openvg/qpaintengine_vg_p.h178
-rw-r--r--src/openvg/qpixmapdata_vg.cpp575
-rw-r--r--src/openvg/qpixmapdata_vg_p.h202
-rw-r--r--src/openvg/qpixmapfilter_vg.cpp356
-rw-r--r--src/openvg/qpixmapfilter_vg_p.h108
-rw-r--r--src/openvg/qvg.h65
-rw-r--r--src/openvg/qvg_p.h112
-rw-r--r--src/openvg/qvg_symbian.cpp366
-rw-r--r--src/openvg/qvgcompositionhelper_p.h90
-rw-r--r--src/openvg/qvgfontglyphcache_p.h101
-rw-r--r--src/openvg/qvgimagepool.cpp233
-rw-r--r--src/openvg/qvgimagepool_p.h157
-rw-r--r--src/openvg/qwindowsurface_vg.cpp137
-rw-r--r--src/openvg/qwindowsurface_vg_p.h94
-rw-r--r--src/openvg/qwindowsurface_vgegl.cpp782
-rw-r--r--src/openvg/qwindowsurface_vgegl_p.h148
18 files changed, 7928 insertions, 0 deletions
diff --git a/src/openvg/openvg.pro b/src/openvg/openvg.pro
new file mode 100644
index 0000000000..e185949bef
--- /dev/null
+++ b/src/openvg/openvg.pro
@@ -0,0 +1,71 @@
+TARGET = QtOpenVG
+QT += core \
+ gui
+
+DEFINES+=QT_BUILD_OPENVG_LIB
+
+contains(QT_CONFIG, shivavg) {
+ DEFINES += QVG_NO_DRAW_GLYPHS
+ DEFINES += QVG_NO_RENDER_TO_MASK
+ DEFINES += QVG_SCISSOR_CLIP
+}
+
+HEADERS += \
+ qvg.h \
+ qvg_p.h \
+ qpaintengine_vg_p.h \
+ qpixmapdata_vg_p.h \
+ qpixmapfilter_vg_p.h \
+ qvgcompositionhelper_p.h \
+ qvgimagepool_p.h \
+ qvgfontglyphcache_p.h
+SOURCES += \
+ qpaintengine_vg.cpp \
+ qpixmapdata_vg.cpp \
+ qpixmapfilter_vg.cpp \
+ qvgimagepool.cpp
+
+contains(QT_CONFIG, egl) {
+ HEADERS += \
+ qwindowsurface_vgegl_p.h \
+ qwindowsurface_vg_p.h
+ SOURCES += \
+ qwindowsurface_vg.cpp \
+ qwindowsurface_vgegl.cpp
+}
+
+symbian {
+ DEFINES += QVG_RECREATE_ON_SIZE_CHANGE QVG_BUFFER_SCROLLING QVG_SCISSOR_CLIP
+ SOURCES += \
+ qvg_symbian.cpp
+
+ contains(QT_CONFIG, freetype) {
+ DEFINES += QT_NO_FONTCONFIG
+ INCLUDEPATH += \
+ ../3rdparty/freetype/src \
+ ../3rdparty/freetype/include
+ }
+}
+
+include(../qbase.pri)
+
+unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
+symbian:TARGET.UID3 = 0x2001E62F
+
+!isEmpty(QMAKE_INCDIR_OPENVG): INCLUDEPATH += $$QMAKE_INCDIR_OPENVG
+!isEmpty(QMAKE_LIBDIR_OPENVG): LIBS_PRIVATE += -L$$QMAKE_LIBDIR_OPENVG
+!isEmpty(QMAKE_LIBS_OPENVG): LIBS_PRIVATE += $$QMAKE_LIBS_OPENVG
+
+contains(QT_CONFIG, egl) {
+ !isEmpty(QMAKE_INCDIR_EGL): INCLUDEPATH += $$QMAKE_INCDIR_EGL
+ !isEmpty(QMAKE_LIBDIR_EGL): LIBS_PRIVATE += -L$$QMAKE_LIBDIR_EGL
+ !isEmpty(QMAKE_LIBS_EGL): LIBS_PRIVATE += $$QMAKE_LIBS_EGL
+}
+
+contains(QT_CONFIG, openvg_on_opengl) {
+ !isEmpty(QMAKE_INCDIR_OPENGL): INCLUDEPATH += $$QMAKE_INCDIR_OPENGL
+ !isEmpty(QMAKE_LIBDIR_OPENGL): LIBS_PRIVATE += -L$$QMAKE_LIBDIR_OPENGL
+ !isEmpty(QMAKE_LIBS_OPENGL): LIBS_PRIVATE += $$QMAKE_LIBS_OPENGL
+}
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src
diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp
new file mode 100644
index 0000000000..588c35ac5b
--- /dev/null
+++ b/src/openvg/qpaintengine_vg.cpp
@@ -0,0 +1,4153 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintengine_vg_p.h"
+#include "qpixmapdata_vg_p.h"
+#include "qpixmapfilter_vg_p.h"
+#include "qvgcompositionhelper_p.h"
+#include "qvgimagepool_p.h"
+#include "qvgfontglyphcache_p.h"
+#if !defined(QT_NO_EGL)
+#include <QtGui/private/qeglcontext_p.h>
+#include "qwindowsurface_vgegl_p.h"
+#endif
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/private/qdrawhelper_p.h>
+#include <QtGui/private/qtextengine_p.h>
+#include <QtGui/private/qfontengine_p.h>
+#include <QtGui/private/qpainterpath_p.h>
+#include <QtGui/private/qstatictext_p.h>
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopWidget>
+#include <QtCore/qmath.h>
+#include <QDebug>
+#include <QSet>
+
+QT_BEGIN_NAMESPACE
+
+// vgRenderToMask() only exists in OpenVG 1.1 and higher.
+// Also, disable masking completely if we are using the scissor to clip.
+#if !defined(OPENVG_VERSION_1_1) && !defined(QVG_NO_RENDER_TO_MASK)
+#define QVG_NO_RENDER_TO_MASK 1
+#endif
+#if defined(QVG_SCISSOR_CLIP) && !defined(QVG_NO_RENDER_TO_MASK)
+#define QVG_NO_RENDER_TO_MASK 1
+#endif
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#if !defined(QVG_NO_DRAW_GLYPHS)
+
+class QVGPaintEnginePrivate;
+
+typedef QHash<QFontEngine*, QVGFontGlyphCache*> QVGFontCache;
+
+#endif
+
+class QVGFontEngineCleaner : public QObject
+{
+ Q_OBJECT
+public:
+ QVGFontEngineCleaner(QVGPaintEnginePrivate *d);
+ ~QVGFontEngineCleaner();
+
+public slots:
+ void fontEngineDestroyed();
+
+private:
+ QVGPaintEnginePrivate *d_ptr;
+};
+
+class QVGPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QVGPaintEngine)
+public:
+ // Extra blending modes from VG_KHR_advanced_blending extension.
+ // Use the QT_VG prefix to avoid conflicts with any definitions
+ // that may come in via <VG/vgext.h>.
+ enum AdvancedBlending {
+ QT_VG_BLEND_OVERLAY_KHR = 0x2010,
+ QT_VG_BLEND_HARDLIGHT_KHR = 0x2011,
+ QT_VG_BLEND_SOFTLIGHT_SVG_KHR = 0x2012,
+ QT_VG_BLEND_SOFTLIGHT_KHR = 0x2013,
+ QT_VG_BLEND_COLORDODGE_KHR = 0x2014,
+ QT_VG_BLEND_COLORBURN_KHR = 0x2015,
+ QT_VG_BLEND_DIFFERENCE_KHR = 0x2016,
+ QT_VG_BLEND_SUBTRACT_KHR = 0x2017,
+ QT_VG_BLEND_INVERT_KHR = 0x2018,
+ QT_VG_BLEND_EXCLUSION_KHR = 0x2019,
+ QT_VG_BLEND_LINEARDODGE_KHR = 0x201a,
+ QT_VG_BLEND_LINEARBURN_KHR = 0x201b,
+ QT_VG_BLEND_VIVIDLIGHT_KHR = 0x201c,
+ QT_VG_BLEND_LINEARLIGHT_KHR = 0x201d,
+ QT_VG_BLEND_PINLIGHT_KHR = 0x201e,
+ QT_VG_BLEND_HARDMIX_KHR = 0x201f,
+ QT_VG_BLEND_CLEAR_KHR = 0x2020,
+ QT_VG_BLEND_DST_KHR = 0x2021,
+ QT_VG_BLEND_SRC_OUT_KHR = 0x2022,
+ QT_VG_BLEND_DST_OUT_KHR = 0x2023,
+ QT_VG_BLEND_SRC_ATOP_KHR = 0x2024,
+ QT_VG_BLEND_DST_ATOP_KHR = 0x2025,
+ QT_VG_BLEND_XOR_KHR = 0x2026
+ };
+
+ QVGPaintEnginePrivate(QVGPaintEngine *q_ptr);
+ ~QVGPaintEnginePrivate();
+
+ void init();
+ void initObjects();
+ void destroy();
+ void setTransform(VGMatrixMode mode, const QTransform& transform);
+ void updateTransform(QPaintDevice *pdev);
+ void draw(VGPath path, const QPen& pen, const QBrush& brush, VGint rule = VG_EVEN_ODD);
+ void stroke(VGPath path, const QPen& pen);
+ void fill(VGPath path, const QBrush& brush, VGint rule = VG_EVEN_ODD);
+ VGPath vectorPathToVGPath(const QVectorPath& path);
+ VGPath painterPathToVGPath(const QPainterPath& path);
+ VGPath roundedRectPath(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode);
+ VGPaintType setBrush
+ (VGPaint paint, const QBrush& brush, VGMatrixMode mode,
+ VGPaintType prevPaintType);
+ void setPenParams(const QPen& pen);
+ void setBrushTransform(const QBrush& brush, VGMatrixMode mode);
+ void setupColorRamp(const QGradient *grad, VGPaint paint);
+ void setImageOptions();
+ void systemStateChanged();
+#if !defined(QVG_SCISSOR_CLIP)
+ void ensureMask(QVGPaintEngine *engine, int width, int height);
+ void modifyMask
+ (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region);
+ void modifyMask
+ (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect);
+#endif
+
+ VGint maxScissorRects; // Maximum scissor rectangles for clipping.
+
+ VGPaint penPaint; // Paint for currently active pen.
+ VGPaint brushPaint; // Paint for currently active brush.
+ VGPaint opacityPaint; // Paint for drawing images with opacity.
+ VGPaint fillPaint; // Current fill paint that is active.
+
+ QPen currentPen; // Current pen set in "penPaint".
+ QBrush currentBrush; // Current brush set in "brushPaint".
+
+ bool forcePenChange; // Force a pen change, even if the same.
+ bool forceBrushChange; // Force a brush change, even if the same.
+
+ VGPaintType penType; // Type of the last pen that was set.
+ VGPaintType brushType; // Type of the last brush that was set.
+
+ QPointF brushOrigin; // Current brush origin.
+
+ VGint fillRule; // Last fill rule that was set.
+
+ qreal opacity; // Current drawing opacity.
+ qreal paintOpacity; // Opacity in opacityPaint.
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ VGPath rectPath; // Cached path for quick drawing of rectangles.
+ VGPath linePath; // Cached path for quick drawing of lines.
+ VGPath roundRectPath; // Cached path for quick drawing of rounded rects.
+#endif
+
+ QTransform transform; // Currently active transform.
+ bool simpleTransform; // True if the transform is simple (non-projective).
+ qreal penScale; // Pen scaling factor from "transform".
+
+ QTransform pathTransform; // Calculated VG path transformation.
+ QTransform imageTransform; // Calculated VG image transformation.
+ bool pathTransformSet; // True if path transform set in the VG context.
+
+ bool maskValid; // True if vgMask() contains valid data.
+ bool maskIsSet; // True if mask would be fully set if it was valid.
+ bool scissorMask; // True if scissor is used in place of the mask.
+ bool rawVG; // True if processing a raw VG escape.
+
+ QRect maskRect; // Rectangle version of mask if it is simple.
+
+ QTransform penTransform; // Transform for the pen.
+ QTransform brushTransform; // Transform for the brush.
+
+ VGMatrixMode matrixMode; // Last matrix mode that was set.
+ VGImageMode imageMode; // Last image mode that was set.
+
+ QRegion scissorRegion; // Currently active scissor region.
+ bool scissorActive; // True if scissor region is active.
+ bool scissorDirty; // True if scissor is dirty after native painting.
+
+ QPaintEngine::DirtyFlags dirty;
+
+ QColor clearColor; // Last clear color that was set.
+ VGfloat clearOpacity; // Opacity during the last clear.
+
+ VGBlendMode blendMode; // Active blend mode.
+ VGRenderingQuality renderingQuality; // Active rendering quality.
+ VGImageQuality imageQuality; // Active image quality.
+
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ QVGFontCache fontCache;
+ QVGFontEngineCleaner *fontEngineCleaner;
+#endif
+
+ bool hasAdvancedBlending;
+
+ QScopedPointer<QPixmapFilter> convolutionFilter;
+ QScopedPointer<QPixmapFilter> colorizeFilter;
+ QScopedPointer<QPixmapFilter> dropShadowFilter;
+ QScopedPointer<QPixmapFilter> blurFilter;
+
+ // Ensure that the path transform is properly set in the VG context
+ // before we perform a vgDrawPath() operation.
+ inline void ensurePathTransform()
+ {
+ if (!pathTransformSet) {
+ QTransform aliasedTransform = pathTransform;
+ if (renderingQuality == VG_RENDERING_QUALITY_NONANTIALIASED && currentPen != Qt::NoPen)
+ aliasedTransform = aliasedTransform
+ * QTransform::fromTranslate(aliasedCoordinateDelta, -aliasedCoordinateDelta);
+ setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, aliasedTransform);
+ pathTransformSet = true;
+ }
+ }
+
+ // Ensure that a specific pen has been set into penPaint.
+ inline void ensurePen(const QPen& pen) {
+ if (forcePenChange || pen != currentPen) {
+ currentPen = pen;
+ forcePenChange = false;
+ penType = setBrush
+ (penPaint, pen.brush(),
+ VG_MATRIX_STROKE_PAINT_TO_USER, penType);
+ setPenParams(pen);
+ }
+ }
+
+ // Ensure that a specific brush has been set into brushPaint.
+ inline void ensureBrush(const QBrush& brush) {
+ if (forceBrushChange || brush != currentBrush) {
+ currentBrush = brush;
+ forceBrushChange = false;
+ brushType = setBrush
+ (brushPaint, brush, VG_MATRIX_FILL_PAINT_TO_USER, brushType);
+ }
+ if (fillPaint != brushPaint) {
+ vgSetPaint(brushPaint, VG_FILL_PATH);
+ fillPaint = brushPaint;
+ }
+ }
+
+ // Set various modes, but only if different.
+ inline void setImageMode(VGImageMode mode);
+ inline void setRenderingQuality(VGRenderingQuality mode);
+ inline void setImageQuality(VGImageQuality mode);
+ inline void setBlendMode(VGBlendMode mode);
+ inline void setFillRule(VGint mode);
+
+ // Clear all lazily-set modes.
+ void clearModes();
+
+private:
+ QVGPaintEngine *q;
+};
+
+inline void QVGPaintEnginePrivate::setImageMode(VGImageMode mode)
+{
+ if (imageMode != mode) {
+ imageMode = mode;
+ vgSeti(VG_IMAGE_MODE, mode);
+ }
+}
+
+inline void QVGPaintEnginePrivate::setRenderingQuality(VGRenderingQuality mode)
+{
+ if (renderingQuality != mode) {
+ vgSeti(VG_RENDERING_QUALITY, mode);
+ renderingQuality = mode;
+ pathTransformSet = false; // need to tweak transform for aliased stroking
+ }
+}
+
+inline void QVGPaintEnginePrivate::setImageQuality(VGImageQuality mode)
+{
+ if (imageQuality != mode) {
+ vgSeti(VG_IMAGE_QUALITY, mode);
+ imageQuality = mode;
+ }
+}
+
+inline void QVGPaintEnginePrivate::setBlendMode(VGBlendMode mode)
+{
+ if (blendMode != mode) {
+ vgSeti(VG_BLEND_MODE, mode);
+ blendMode = mode;
+ }
+}
+
+inline void QVGPaintEnginePrivate::setFillRule(VGint mode)
+{
+ if (fillRule != mode) {
+ fillRule = mode;
+ vgSeti(VG_FILL_RULE, mode);
+ }
+}
+
+void QVGPaintEnginePrivate::clearModes()
+{
+ matrixMode = (VGMatrixMode)0;
+ imageMode = (VGImageMode)0;
+ blendMode = (VGBlendMode)0;
+ renderingQuality = (VGRenderingQuality)0;
+ imageQuality = (VGImageQuality)0;
+}
+
+QVGPaintEnginePrivate::QVGPaintEnginePrivate(QVGPaintEngine *q_ptr) : q(q_ptr)
+{
+ init();
+}
+
+void QVGPaintEnginePrivate::init()
+{
+ maxScissorRects = 0;
+
+ penPaint = 0;
+ brushPaint = 0;
+ opacityPaint = 0;
+ fillPaint = 0;
+
+ forcePenChange = true;
+ forceBrushChange = true;
+ penType = (VGPaintType)0;
+ brushType = (VGPaintType)0;
+
+ brushOrigin = QPointF(0.0f, 0.0f);
+
+ fillRule = 0;
+
+ opacity = 1.0;
+ paintOpacity = 1.0f;
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ rectPath = 0;
+ linePath = 0;
+ roundRectPath = 0;
+#endif
+
+ simpleTransform = true;
+ pathTransformSet = false;
+ penScale = 1.0;
+
+ maskValid = false;
+ maskIsSet = false;
+ scissorMask = false;
+ rawVG = false;
+
+ scissorActive = false;
+ scissorDirty = false;
+
+ dirty = 0;
+
+ clearOpacity = 1.0f;
+
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ fontEngineCleaner = 0;
+#endif
+
+ hasAdvancedBlending = false;
+
+ clearModes();
+}
+
+QVGPaintEnginePrivate::~QVGPaintEnginePrivate()
+{
+ destroy();
+}
+
+void QVGPaintEnginePrivate::initObjects()
+{
+ maxScissorRects = vgGeti(VG_MAX_SCISSOR_RECTS);
+
+ penPaint = vgCreatePaint();
+ vgSetParameteri(penPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetPaint(penPaint, VG_STROKE_PATH);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
+ vgLoadIdentity();
+
+ brushPaint = vgCreatePaint();
+ vgSetParameteri(brushPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetPaint(brushPaint, VG_FILL_PATH);
+ fillPaint = brushPaint;
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
+ vgLoadIdentity();
+ matrixMode = VG_MATRIX_FILL_PAINT_TO_USER;
+
+ opacityPaint = vgCreatePaint();
+ vgSetParameteri(opacityPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ VGfloat values[4];
+ values[0] = 1.0f;
+ values[1] = 1.0f;
+ values[2] = 1.0f;
+ values[3] = paintOpacity;
+ vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ // Create a dummy path for rectangle drawing, which we can
+ // modify later with vgModifyPathCoords(). This should be
+ // faster than constantly creating and destroying paths.
+ rectPath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 5, // segmentCapacityHint
+ 8, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ static VGubyte const segments[5] = {
+ VG_MOVE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CLOSE_PATH
+ };
+ VGfloat coords[8];
+ coords[0] = 0.0f;
+ coords[1] = 0.0f;
+ coords[2] = 100.0f;
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = 100.0f;
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+ vgAppendPathData(rectPath, 5, segments, coords);
+
+ // Create a dummy line drawing path as well.
+ linePath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 2, // segmentCapacityHint
+ 4, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ vgAppendPathData(linePath, 2, segments, coords);
+#endif
+
+ const char *extensions = reinterpret_cast<const char *>(vgGetString(VG_EXTENSIONS));
+ if (extensions)
+ hasAdvancedBlending = strstr(extensions, "VG_KHR_advanced_blending") != 0;
+}
+
+void QVGPaintEnginePrivate::destroy()
+{
+ if (penPaint)
+ vgDestroyPaint(penPaint);
+ if (brushPaint)
+ vgDestroyPaint(brushPaint);
+ if (opacityPaint)
+ vgDestroyPaint(opacityPaint);
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ if (rectPath)
+ vgDestroyPath(rectPath);
+ if (linePath)
+ vgDestroyPath(linePath);
+ if (roundRectPath)
+ vgDestroyPath(roundRectPath);
+#endif
+
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ QVGFontCache::Iterator it;
+ for (it = fontCache.begin(); it != fontCache.end(); ++it)
+ delete it.value();
+ fontCache.clear();
+ delete fontEngineCleaner;
+#endif
+}
+
+// Set a specific VG transformation matrix in the current VG context.
+void QVGPaintEnginePrivate::setTransform
+ (VGMatrixMode mode, const QTransform& transform)
+{
+ VGfloat mat[9];
+ if (mode != matrixMode) {
+ vgSeti(VG_MATRIX_MODE, mode);
+ matrixMode = mode;
+ }
+ mat[0] = transform.m11();
+ mat[1] = transform.m12();
+ mat[2] = transform.m13();
+ mat[3] = transform.m21();
+ mat[4] = transform.m22();
+ mat[5] = transform.m23();
+ mat[6] = transform.m31();
+ mat[7] = transform.m32();
+ mat[8] = transform.m33();
+ vgLoadMatrix(mat);
+}
+
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+
+void QVGPaintEnginePrivate::updateTransform(QPaintDevice *pdev)
+{
+ VGfloat devh = pdev->height();
+
+ // Construct the VG transform by combining the Qt transform with
+ // the following viewport transformation:
+ // | 1 0 0 |
+ // | 0 -1 devh |
+ // | 0 0 1 |
+ // The full VG transform is effectively:
+ // 1. Apply the user's transformation matrix.
+ // 2. Flip the co-ordinate system upside down.
+ QTransform viewport(1.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f,
+ 0.0f, devh, 1.0f);
+
+ // Compute the path transform and determine if it is projective.
+ pathTransform = transform * viewport;
+ bool projective = (pathTransform.m13() != 0.0f ||
+ pathTransform.m23() != 0.0f ||
+ pathTransform.m33() != 1.0f);
+ if (projective) {
+ // The engine cannot do projective path transforms for us,
+ // so we will have to convert the co-ordinates ourselves.
+ // Change the matrix to just the viewport transformation.
+ pathTransform = viewport;
+ simpleTransform = false;
+ } else {
+ simpleTransform = true;
+ }
+ pathTransformSet = false;
+
+ // The image transform is always the full transformation,
+ imageTransform = transform * viewport;
+
+ // Calculate the scaling factor to use for turning cosmetic pens
+ // into ordinary non-cosmetic pens.
+ qt_scaleForTransform(transform, &penScale);
+}
+
+VGPath QVGPaintEnginePrivate::vectorPathToVGPath(const QVectorPath& path)
+{
+ int count = path.elementCount();
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *elements = path.elements();
+
+ VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ count + 1, // segmentCapacityHint
+ count * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+
+ // Size is sufficient segments for drawRoundedRect() paths.
+ QVarLengthArray<VGubyte, 20> segments;
+
+ if (sizeof(qreal) == sizeof(VGfloat) && elements && simpleTransform) {
+ // If Qt was compiled with qreal the same size as VGfloat,
+ // then convert the segment types and use the incoming
+ // points array directly.
+ for (int i = 0; i < count; ++i) {
+ switch (elements[i]) {
+
+ case QPainterPath::MoveToElement:
+ segments.append(VG_MOVE_TO_ABS); break;
+
+ case QPainterPath::LineToElement:
+ segments.append(VG_LINE_TO_ABS); break;
+
+ case QPainterPath::CurveToElement:
+ segments.append(VG_CUBIC_TO_ABS); break;
+
+ case QPainterPath::CurveToDataElement: break;
+
+ }
+ }
+ if (path.hasImplicitClose())
+ segments.append(VG_CLOSE_PATH);
+
+ vgAppendPathData(vgpath, segments.count(), segments.constData(),
+ reinterpret_cast<const VGfloat *>(points));
+
+ return vgpath;
+ }
+
+ // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
+ QVarLengthArray<VGfloat, 48> coords;
+
+ int curvePos = 0;
+ QPointF temp;
+
+ if (elements && simpleTransform) {
+ // Convert the members of the element array.
+ for (int i = 0; i < count; ++i) {
+ switch (elements[i]) {
+
+ case QPainterPath::MoveToElement:
+ {
+ coords.append(points[0]);
+ coords.append(points[1]);
+ segments.append(VG_MOVE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ coords.append(points[0]);
+ coords.append(points[1]);
+ segments.append(VG_LINE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ coords.append(points[0]);
+ coords.append(points[1]);
+ curvePos = 2;
+ }
+ break;
+
+ case QPainterPath::CurveToDataElement:
+ {
+ coords.append(points[0]);
+ coords.append(points[1]);
+ curvePos += 2;
+ if (curvePos == 6) {
+ curvePos = 0;
+ segments.append(VG_CUBIC_TO_ABS);
+ }
+ }
+ break;
+
+ }
+ points += 2;
+ }
+ } else if (elements && !simpleTransform) {
+ // Convert the members of the element array after applying the
+ // current transform to the path locally.
+ for (int i = 0; i < count; ++i) {
+ switch (elements[i]) {
+
+ case QPainterPath::MoveToElement:
+ {
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ segments.append(VG_MOVE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ segments.append(VG_LINE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ curvePos = 2;
+ }
+ break;
+
+ case QPainterPath::CurveToDataElement:
+ {
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ curvePos += 2;
+ if (curvePos == 6) {
+ curvePos = 0;
+ segments.append(VG_CUBIC_TO_ABS);
+ }
+ }
+ break;
+
+ }
+ points += 2;
+ }
+ } else if (count > 0 && simpleTransform) {
+ // If there is no element array, then the path is assumed
+ // to be a MoveTo followed by several LineTo's.
+ coords.append(points[0]);
+ coords.append(points[1]);
+ segments.append(VG_MOVE_TO_ABS);
+ while (count > 1) {
+ points += 2;
+ coords.append(points[0]);
+ coords.append(points[1]);
+ segments.append(VG_LINE_TO_ABS);
+ --count;
+ }
+ } else if (count > 0 && !simpleTransform) {
+ // Convert a simple path, and apply the transform locally.
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ segments.append(VG_MOVE_TO_ABS);
+ while (count > 1) {
+ points += 2;
+ temp = transform.map(QPointF(points[0], points[1]));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ segments.append(VG_LINE_TO_ABS);
+ --count;
+ }
+ }
+
+ // Close the path if specified.
+ if (path.hasImplicitClose())
+ segments.append(VG_CLOSE_PATH);
+
+ vgAppendPathData(vgpath, segments.count(),
+ segments.constData(), coords.constData());
+
+ return vgpath;
+}
+
+VGPath QVGPaintEnginePrivate::painterPathToVGPath(const QPainterPath& path)
+{
+ int count = path.elementCount();
+
+ VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ count + 1, // segmentCapacityHint
+ count * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+
+ if (count == 0)
+ return vgpath;
+
+ const QPainterPath::Element *elements = &(path.elementAt(0));
+
+ // Sizes chosen so that drawRoundedRect() paths fit in these arrays.
+ QVarLengthArray<VGfloat, 48> coords;
+ QVarLengthArray<VGubyte, 20> segments;
+
+ int curvePos = 0;
+ QPointF temp;
+
+ // Keep track of the start and end of each sub-path. QPainterPath
+ // does not have an "implicit close" flag like QVectorPath does.
+ // We therefore have to detect closed paths by looking for a LineTo
+ // element that connects back to the initial MoveTo element.
+ qreal startx = 0.0;
+ qreal starty = 0.0;
+ qreal endx = 0.0;
+ qreal endy = 0.0;
+ bool haveStart = false;
+ bool haveEnd = false;
+
+ if (simpleTransform) {
+ // Convert the members of the element array.
+ for (int i = 0; i < count; ++i) {
+ switch (elements[i].type) {
+
+ case QPainterPath::MoveToElement:
+ {
+ if (haveStart && haveEnd && startx == endx && starty == endy) {
+ // Implicitly close the previous sub-path.
+ segments.append(VG_CLOSE_PATH);
+ }
+ startx = elements[i].x;
+ starty = elements[i].y;
+ coords.append(startx);
+ coords.append(starty);
+ haveStart = true;
+ haveEnd = false;
+ segments.append(VG_MOVE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ endx = elements[i].x;
+ endy = elements[i].y;
+ coords.append(endx);
+ coords.append(endy);
+ haveEnd = true;
+ segments.append(VG_LINE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ coords.append(elements[i].x);
+ coords.append(elements[i].y);
+ haveEnd = false;
+ curvePos = 2;
+ }
+ break;
+
+ case QPainterPath::CurveToDataElement:
+ {
+ coords.append(elements[i].x);
+ coords.append(elements[i].y);
+ haveEnd = false;
+ curvePos += 2;
+ if (curvePos == 6) {
+ curvePos = 0;
+ segments.append(VG_CUBIC_TO_ABS);
+ }
+ }
+ break;
+
+ }
+ }
+ } else {
+ // Convert the members of the element array after applying the
+ // current transform to the path locally.
+ for (int i = 0; i < count; ++i) {
+ switch (elements[i].type) {
+
+ case QPainterPath::MoveToElement:
+ {
+ if (haveStart && haveEnd && startx == endx && starty == endy) {
+ // Implicitly close the previous sub-path.
+ segments.append(VG_CLOSE_PATH);
+ }
+ temp = transform.map(QPointF(elements[i].x, elements[i].y));
+ startx = temp.x();
+ starty = temp.y();
+ coords.append(startx);
+ coords.append(starty);
+ haveStart = true;
+ haveEnd = false;
+ segments.append(VG_MOVE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ temp = transform.map(QPointF(elements[i].x, elements[i].y));
+ endx = temp.x();
+ endy = temp.y();
+ coords.append(endx);
+ coords.append(endy);
+ haveEnd = true;
+ segments.append(VG_LINE_TO_ABS);
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ temp = transform.map(QPointF(elements[i].x, elements[i].y));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ haveEnd = false;
+ curvePos = 2;
+ }
+ break;
+
+ case QPainterPath::CurveToDataElement:
+ {
+ temp = transform.map(QPointF(elements[i].x, elements[i].y));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ haveEnd = false;
+ curvePos += 2;
+ if (curvePos == 6) {
+ curvePos = 0;
+ segments.append(VG_CUBIC_TO_ABS);
+ }
+ }
+ break;
+
+ }
+ }
+ }
+
+ if (haveStart && haveEnd && startx == endx && starty == endy) {
+ // Implicitly close the last sub-path.
+ segments.append(VG_CLOSE_PATH);
+ }
+
+ vgAppendPathData(vgpath, segments.count(),
+ segments.constData(), coords.constData());
+
+ return vgpath;
+}
+
+VGPath QVGPaintEnginePrivate::roundedRectPath(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
+{
+ static VGubyte roundedrect_types[] = {
+ VG_MOVE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CUBIC_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CUBIC_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CUBIC_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CUBIC_TO_ABS,
+ VG_CLOSE_PATH
+ };
+
+ qreal x1 = rect.left();
+ qreal x2 = rect.right();
+ qreal y1 = rect.top();
+ qreal y2 = rect.bottom();
+
+ if (mode == Qt::RelativeSize) {
+ xRadius = xRadius * rect.width() / 200.;
+ yRadius = yRadius * rect.height() / 200.;
+ }
+
+ xRadius = qMin(xRadius, rect.width() / 2);
+ yRadius = qMin(yRadius, rect.height() / 2);
+
+ VGfloat pts[] = {
+ x1 + xRadius, y1, // MoveTo
+ x2 - xRadius, y1, // LineTo
+ x2 - (1 - KAPPA) * xRadius, y1, // CurveTo
+ x2, y1 + (1 - KAPPA) * yRadius,
+ x2, y1 + yRadius,
+ x2, y2 - yRadius, // LineTo
+ x2, y2 - (1 - KAPPA) * yRadius, // CurveTo
+ x2 - (1 - KAPPA) * xRadius, y2,
+ x2 - xRadius, y2,
+ x1 + xRadius, y2, // LineTo
+ x1 + (1 - KAPPA) * xRadius, y2, // CurveTo
+ x1, y2 - (1 - KAPPA) * yRadius,
+ x1, y2 - yRadius,
+ x1, y1 + yRadius, // LineTo
+ x1, y1 + (1 - KAPPA) * yRadius, // CurveTo
+ x1 + (1 - KAPPA) * xRadius, y1,
+ x1 + xRadius, y1
+ };
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ VGPath vgpath = roundRectPath;
+ if (!vgpath) {
+ vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 10, // segmentCapacityHint
+ 17 * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ vgAppendPathData(vgpath, 10, roundedrect_types, pts);
+ roundRectPath = vgpath;
+ } else {
+ vgModifyPathCoords(vgpath, 0, 9, pts);
+ }
+#else
+ VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 10, // segmentCapacityHint
+ 17 * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ vgAppendPathData(vgpath, 10, roundedrect_types, pts);
+#endif
+
+ return vgpath;
+}
+
+Q_GUI_EXPORT QImage qt_imageForBrush(int style, bool invert);
+
+static QImage colorizeBitmap(const QImage &image, const QColor &color)
+{
+ QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
+ QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
+
+ QRgb fg = PREMUL(color.rgba());
+ QRgb bg = 0;
+
+ int height = sourceImage.height();
+ int width = sourceImage.width();
+ for (int y=0; y<height; ++y) {
+ const uchar *source = sourceImage.constScanLine(y);
+ QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
+ for (int x=0; x < width; ++x)
+ target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
+ }
+ return dest;
+}
+
+static VGImage toVGImage
+ (const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor)
+{
+ QImage img(image);
+
+ VGImageFormat format;
+ switch (img.format()) {
+ case QImage::Format_Mono:
+ img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ img.invertPixels();
+ format = VG_BW_1;
+ break;
+ case QImage::Format_MonoLSB:
+ img.invertPixels();
+ format = VG_BW_1;
+ break;
+ case QImage::Format_RGB32:
+ format = VG_sXRGB_8888;
+ break;
+ case QImage::Format_ARGB32:
+ format = VG_sARGB_8888;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ format = VG_sARGB_8888_PRE;
+ break;
+ case QImage::Format_RGB16:
+ format = VG_sRGB_565;
+ break;
+ default:
+ // Convert everything else into ARGB32_Premultiplied.
+ img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
+ format = VG_sARGB_8888_PRE;
+ break;
+ }
+
+ const uchar *pixels = img.constBits();
+
+ VGImage vgImg = QVGImagePool::instance()->createPermanentImage
+ (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData
+ (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
+ img.width(), img.height());
+
+ return vgImg;
+}
+
+static VGImage toVGImageSubRect
+ (const QImage & image, const QRect& sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor)
+{
+ QImage img(image);
+
+ VGImageFormat format;
+ int bpp = 4;
+
+ switch (img.format()) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ return VG_INVALID_HANDLE;
+ case QImage::Format_RGB32:
+ format = VG_sXRGB_8888;
+ break;
+ case QImage::Format_ARGB32:
+ format = VG_sARGB_8888;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ format = VG_sARGB_8888_PRE;
+ break;
+ case QImage::Format_RGB16:
+ format = VG_sRGB_565;
+ bpp = 2;
+ break;
+ default:
+ // Convert everything else into ARGB32_Premultiplied.
+ img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags);
+ format = VG_sARGB_8888_PRE;
+ break;
+ }
+
+ const uchar *pixels = img.constBits() + bpp * sr.x() +
+ img.bytesPerLine() * sr.y();
+
+ VGImage vgImg = QVGImagePool::instance()->createPermanentImage
+ (format, sr.width(), sr.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData
+ (vgImg, pixels, img.bytesPerLine(), format, 0, 0,
+ sr.width(), sr.height());
+
+ return vgImg;
+}
+
+static VGImage toVGImageWithOpacity(const QImage & image, qreal opacity)
+{
+ QImage img(image.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ QPainter painter;
+ painter.begin(&img);
+ painter.setOpacity(opacity);
+ painter.drawImage(0, 0, image);
+ painter.end();
+
+ const uchar *pixels = img.constBits();
+
+ VGImage vgImg = QVGImagePool::instance()->createPermanentImage
+ (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData
+ (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
+ img.width(), img.height());
+
+ return vgImg;
+}
+
+static VGImage toVGImageWithOpacitySubRect
+ (const QImage & image, qreal opacity, const QRect& sr)
+{
+ QImage img(sr.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ QPainter painter;
+ painter.begin(&img);
+ painter.setOpacity(opacity);
+ painter.drawImage(QPoint(0, 0), image, sr);
+ painter.end();
+
+ const uchar *pixels = img.constBits();
+
+ VGImage vgImg = QVGImagePool::instance()->createPermanentImage
+ (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData
+ (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0,
+ img.width(), img.height());
+
+ return vgImg;
+}
+
+VGPaintType QVGPaintEnginePrivate::setBrush
+ (VGPaint paint, const QBrush& brush, VGMatrixMode mode,
+ VGPaintType prevType)
+{
+ VGfloat values[5];
+ setBrushTransform(brush, mode);
+
+ // Reset the paint pattern on the brush, which will discard
+ // the previous VGImage if one was set.
+ if (prevType == VG_PAINT_TYPE_PATTERN || prevType == (VGPaintType)0)
+ vgPaintPattern(paint, VG_INVALID_HANDLE);
+
+ switch (brush.style()) {
+
+ case Qt::SolidPattern: {
+ // The brush is a solid color.
+ QColor color(brush.color());
+ values[0] = color.redF();
+ values[1] = color.greenF();
+ values[2] = color.blueF();
+ values[3] = color.alphaF() * opacity;
+ if (prevType != VG_PAINT_TYPE_COLOR)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values);
+ return VG_PAINT_TYPE_COLOR;
+ }
+
+ case Qt::LinearGradientPattern: {
+ // The brush is a linear gradient.
+ Q_ASSERT(brush.gradient()->type() == QGradient::LinearGradient);
+ const QLinearGradient *grad =
+ static_cast<const QLinearGradient*>(brush.gradient());
+ values[0] = grad->start().x();
+ values[1] = grad->start().y();
+ values[2] = grad->finalStop().x();
+ values[3] = grad->finalStop().y();
+ if (prevType != VG_PAINT_TYPE_LINEAR_GRADIENT)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
+ vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, values);
+ setupColorRamp(grad, paint);
+ return VG_PAINT_TYPE_LINEAR_GRADIENT;
+ }
+
+ case Qt::RadialGradientPattern: {
+ // The brush is a radial gradient.
+ Q_ASSERT(brush.gradient()->type() == QGradient::RadialGradient);
+ const QRadialGradient *grad =
+ static_cast<const QRadialGradient*>(brush.gradient());
+ values[0] = grad->center().x();
+ values[1] = grad->center().y();
+ values[2] = grad->focalPoint().x();
+ values[3] = grad->focalPoint().y();
+ values[4] = grad->radius();
+ if (prevType != VG_PAINT_TYPE_RADIAL_GRADIENT)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
+ vgSetParameterfv(paint, VG_PAINT_RADIAL_GRADIENT, 5, values);
+ setupColorRamp(grad, paint);
+ return VG_PAINT_TYPE_RADIAL_GRADIENT;
+ }
+
+ case Qt::TexturePattern: {
+ // The brush is a texture specified by a QPixmap/QImage.
+ QPixmapData *pd = brush.texture().pixmapData();
+ if (!pd)
+ break; // null QPixmap
+ VGImage vgImg;
+ bool deref = false;
+ if (pd->pixelType() == QPixmapData::BitmapType) {
+ // Colorize bitmaps using the brush color and opacity.
+ QColor color = brush.color();
+ if (opacity != 1.0)
+ color.setAlphaF(color.alphaF() * opacity);
+ QImage image = colorizeBitmap(*(pd->buffer()), color);
+ vgImg = toVGImage(image);
+ deref = true;
+ } else if (opacity == 1.0) {
+ if (pd->classId() == QPixmapData::OpenVGClass) {
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ vgImg = vgpd->toVGImage();
+
+ // We don't want the pool to reclaim this image
+ // because we cannot predict when the paint object
+ // will stop using it. Replacing the image with
+ // new data will make the paint object invalid.
+ vgpd->detachImageFromPool();
+ } else {
+ vgImg = toVGImage(*(pd->buffer()));
+ deref = true;
+ }
+ } else if (pd->classId() == QPixmapData::OpenVGClass) {
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ vgImg = vgpd->toVGImage(opacity);
+ vgpd->detachImageFromPool();
+ } else {
+ vgImg = toVGImageWithOpacity(*(pd->buffer()), opacity);
+ deref = true;
+ }
+ if (vgImg == VG_INVALID_HANDLE)
+ break;
+ if (prevType != VG_PAINT_TYPE_PATTERN)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+ vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT);
+ vgPaintPattern(paint, vgImg);
+ if (deref)
+ vgDestroyImage(vgImg); // Will be valid until pattern is destroyed.
+ return VG_PAINT_TYPE_PATTERN;
+ }
+
+ case Qt::ConicalGradientPattern: {
+ // Convert conical gradients into the first stop color.
+ qWarning() << "QVGPaintEnginePrivate::setBrush: conical gradients are not supported by OpenVG";
+ Q_ASSERT(brush.gradient()->type() == QGradient::ConicalGradient);
+ const QConicalGradient *grad =
+ static_cast<const QConicalGradient*>(brush.gradient());
+ const QGradientStops stops = grad->stops();
+ QColor color;
+ if (stops.size() > 0)
+ color = stops[0].second;
+ values[0] = color.redF();
+ values[1] = color.greenF();
+ values[2] = color.blueF();
+ values[3] = color.alphaF() * opacity;
+ if (prevType != VG_PAINT_TYPE_COLOR)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values);
+ return VG_PAINT_TYPE_COLOR;
+ }
+
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::CrossPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::DiagCrossPattern: {
+ // The brush is a traditional dotted or cross-hatched pattern brush.
+ QColor color = brush.color();
+ if (opacity != 1.0)
+ color.setAlphaF(color.alphaF() * opacity);
+ QImage image = colorizeBitmap
+ (qt_imageForBrush(brush.style(), true), color);
+ VGImage vgImg = toVGImage(image);
+ if (prevType != VG_PAINT_TYPE_PATTERN)
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+ vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT);
+ vgPaintPattern(paint, vgImg);
+ vgDestroyImage(vgImg); // Will stay valid until pattern is destroyed.
+ return VG_PAINT_TYPE_PATTERN;
+ }
+
+ default: break;
+ }
+ return (VGPaintType)0;
+}
+
+void QVGPaintEnginePrivate::setPenParams(const QPen& pen)
+{
+ // Note: OpenVG does not support zero-width or cosmetic pens,
+ // so we have to simulate cosmetic pens by reversing the scale.
+ VGfloat width = pen.widthF();
+ if (width <= 0.0f)
+ width = 1.0f;
+ if (pen.isCosmetic()) {
+ if (penScale != 1.0 && penScale != 0.0)
+ width /= penScale;
+ }
+ vgSetf(VG_STROKE_LINE_WIDTH, width);
+
+ if (pen.capStyle() == Qt::FlatCap)
+ vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_BUTT);
+ else if (pen.capStyle() == Qt::SquareCap)
+ vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_SQUARE);
+ else
+ vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
+
+ if (pen.joinStyle() == Qt::MiterJoin) {
+ vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER);
+ vgSetf(VG_STROKE_MITER_LIMIT, pen.miterLimit());
+ } else if (pen.joinStyle() == Qt::BevelJoin) {
+ vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL);
+ } else {
+ vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
+ }
+
+ if (pen.style() == Qt::SolidLine) {
+ vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL);
+ } else {
+ const QVector<qreal> dashPattern = pen.dashPattern();
+ QVector<VGfloat> currentDashPattern(dashPattern.count());
+ for (int i = 0; i < dashPattern.count(); ++i)
+ currentDashPattern[i] = dashPattern[i] * width;
+ vgSetfv(VG_STROKE_DASH_PATTERN, currentDashPattern.count(), currentDashPattern.data());
+ vgSetf(VG_STROKE_DASH_PHASE, pen.dashOffset());
+ vgSetf(VG_STROKE_DASH_PHASE_RESET, VG_FALSE);
+ }
+}
+
+void QVGPaintEnginePrivate::setBrushTransform
+ (const QBrush& brush, VGMatrixMode mode)
+{
+ // Compute the new brush transformation matrix.
+ QTransform transform(brush.transform());
+ if (brushOrigin.x() != 0.0f || brushOrigin.y() != 0.0f)
+ transform.translate(brushOrigin.x(), brushOrigin.y());
+
+ // Bail out if the matrix is the same as last time, to avoid
+ // updating the VG context state unless absolutely necessary.
+ // Most applications won't have a brush transformation set,
+ // which will leave the VG setting at its default of identity.
+ // Always change the transform if coming out of raw VG mode.
+ if (mode == VG_MATRIX_FILL_PAINT_TO_USER) {
+ if (!rawVG && transform == brushTransform)
+ return;
+ brushTransform = transform;
+ } else {
+ if (!rawVG && transform == penTransform)
+ return;
+ penTransform = transform;
+ }
+
+ // Set the brush transformation matrix.
+ if (mode != matrixMode) {
+ vgSeti(VG_MATRIX_MODE, mode);
+ matrixMode = mode;
+ }
+ if (transform.isIdentity()) {
+ vgLoadIdentity();
+ } else {
+ VGfloat mat[9];
+ mat[0] = transform.m11();
+ mat[1] = transform.m12();
+ mat[2] = transform.m13();
+ mat[3] = transform.m21();
+ mat[4] = transform.m22();
+ mat[5] = transform.m23();
+ mat[6] = transform.m31();
+ mat[7] = transform.m32();
+ mat[8] = transform.m33();
+ vgLoadMatrix(mat);
+ }
+}
+
+void QVGPaintEnginePrivate::setupColorRamp(const QGradient *grad, VGPaint paint)
+{
+ QGradient::Spread spread = grad->spread();
+ VGColorRampSpreadMode spreadMode;
+ if (spread == QGradient::ReflectSpread)
+ spreadMode = VG_COLOR_RAMP_SPREAD_REFLECT;
+ else if (spread == QGradient::RepeatSpread)
+ spreadMode = VG_COLOR_RAMP_SPREAD_REPEAT;
+ else
+ spreadMode = VG_COLOR_RAMP_SPREAD_PAD;
+
+ const QGradientStops stops = grad->stops();
+ int n = 5*stops.size();
+ QVector<VGfloat> fill_stops(n);
+
+ for (int i = 0; i < stops.size(); ++i ) {
+ QColor col = stops[i].second;
+ fill_stops[i*5] = stops[i].first;
+ fill_stops[i*5 + 1] = col.redF();
+ fill_stops[i*5 + 2] = col.greenF();
+ fill_stops[i*5 + 3] = col.blueF();
+ fill_stops[i*5 + 4] = col.alphaF() * opacity;
+ }
+
+ vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, spreadMode);
+ vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, VG_FALSE);
+ vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, n, fill_stops.data());
+}
+
+QVGPainterState::QVGPainterState(QVGPainterState& other)
+ : QPainterState(other),
+ isNew(true), clipRegion(other.clipRegion),
+ savedDirty(0)
+{
+}
+
+QVGPainterState::QVGPainterState()
+ : isNew(true), savedDirty(0)
+{
+}
+
+QVGPainterState::~QVGPainterState()
+{
+}
+
+QVGPaintEngine::QVGPaintEngine()
+ : QPaintEngineEx(*new QVGPaintEnginePrivate(this))
+{
+}
+
+QVGPaintEngine::QVGPaintEngine(QVGPaintEnginePrivate &data)
+ : QPaintEngineEx(data)
+{
+}
+
+QVGPaintEngine::~QVGPaintEngine()
+{
+}
+
+QPainterState *QVGPaintEngine::createState(QPainterState *orig) const
+{
+ if (!orig) {
+ return new QVGPainterState();
+ } else {
+ Q_D(const QVGPaintEngine);
+ QVGPaintEnginePrivate *d2 = const_cast<QVGPaintEnginePrivate*>(d);
+ QVGPainterState *origState = static_cast<QVGPainterState *>(orig);
+ origState->savedDirty = d2->dirty;
+ d2->dirty = 0;
+ return new QVGPainterState(*origState);
+ }
+}
+
+void QVGPaintEnginePrivate::draw
+ (VGPath path, const QPen& pen, const QBrush& brush, VGint rule)
+{
+ VGbitfield mode = 0;
+ if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) {
+ ensurePen(pen);
+ mode |= VG_STROKE_PATH;
+ }
+ if (brush.style() != Qt::NoBrush) {
+ ensureBrush(brush);
+ setFillRule(rule);
+ mode |= VG_FILL_PATH;
+ }
+ if (mode != 0) {
+ ensurePathTransform();
+ vgDrawPath(path, mode);
+ }
+}
+
+void QVGPaintEnginePrivate::stroke(VGPath path, const QPen& pen)
+{
+ if (pen.style() == Qt::NoPen)
+ return;
+ ensurePen(pen);
+ ensurePathTransform();
+ vgDrawPath(path, VG_STROKE_PATH);
+}
+
+void QVGPaintEnginePrivate::fill(VGPath path, const QBrush& brush, VGint rule)
+{
+ if (brush.style() == Qt::NoBrush)
+ return;
+ ensureBrush(brush);
+ setFillRule(rule);
+ QPen savedPen = currentPen;
+ currentPen = Qt::NoPen;
+ ensurePathTransform();
+ currentPen = savedPen;
+ vgDrawPath(path, VG_FILL_PATH);
+}
+
+bool QVGPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_UNUSED(pdev);
+ Q_D(QVGPaintEngine);
+
+ // Initialize the VG painting objects if we haven't done it yet.
+ if (!d->penPaint)
+ d->initObjects();
+
+ // The initial clip region is the entire device area.
+ QVGPainterState *s = state();
+ s->clipRegion = defaultClipRegion();
+
+ // Initialize the VG state for this paint operation.
+ restoreState(QPaintEngine::AllDirty);
+ d->dirty = 0;
+ d->rawVG = false;
+ return true;
+}
+
+bool QVGPaintEngine::end()
+{
+ return true;
+}
+
+void QVGPaintEngine::draw(const QVectorPath &path)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ VGPath vgpath = d->vectorPathToVGPath(path);
+ if (!path.hasWindingFill())
+ d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD);
+ else
+ d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
+ vgDestroyPath(vgpath);
+}
+
+void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QVGPaintEngine);
+ VGPath vgpath = d->vectorPathToVGPath(path);
+ if (!path.hasWindingFill())
+ d->fill(vgpath, brush, VG_EVEN_ODD);
+ else
+ d->fill(vgpath, brush, VG_NON_ZERO);
+ vgDestroyPath(vgpath);
+}
+
+void QVGPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ Q_D(QVGPaintEngine);
+ VGPath vgpath = d->vectorPathToVGPath(path);
+ d->stroke(vgpath, pen);
+ vgDestroyPath(vgpath);
+}
+
+// Determine if a co-ordinate transform is simple enough to allow
+// rectangle-based clipping with vgMask(). Simple transforms most
+// often result from origin translations.
+static inline bool clipTransformIsSimple(const QTransform& transform)
+{
+ QTransform::TransformationType type = transform.type();
+ if (type == QTransform::TxNone || type == QTransform::TxTranslate)
+ return true;
+ if (type == QTransform::TxRotate) {
+ // Check for 0, 90, 180, and 270 degree rotations.
+ // (0 might happen after 4 rotations of 90 degrees).
+ qreal m11 = transform.m11();
+ qreal m12 = transform.m12();
+ qreal m21 = transform.m21();
+ qreal m22 = transform.m22();
+ if (m11 == 0.0f && m22 == 0.0f) {
+ if (m12 == 1.0f && m21 == -1.0f)
+ return true; // 90 degrees.
+ else if (m12 == -1.0f && m21 == 1.0f)
+ return true; // 270 degrees.
+ } else if (m12 == 0.0f && m21 == 0.0f) {
+ if (m11 == -1.0f && m22 == -1.0f)
+ return true; // 180 degrees.
+ else if (m11 == 1.0f && m22 == 1.0f)
+ return true; // 0 degrees.
+ }
+ }
+ return false;
+}
+
+#if defined(QVG_SCISSOR_CLIP)
+
+void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ if (op == Qt::NoClip) {
+ s->clipRegion = defaultClipRegion();
+ updateScissor();
+ return;
+ }
+
+ // We aren't using masking, so handle simple QRectF's only.
+ if (path.shape() == QVectorPath::RectangleHint &&
+ path.elementCount() == 4 && clipTransformIsSimple(d->transform)) {
+ // Clipping region that resulted from QPainter::setClipRect(QRectF).
+ // Convert it into a QRect and apply.
+ const qreal *points = path.points();
+ QRectF rect(points[0], points[1], points[2] - points[0],
+ points[5] - points[1]);
+ clip(rect.toRect(), op);
+ return;
+ }
+
+ // Try converting the path into a QRegion that tightly follows
+ // the outline of the path we want to clip with.
+ QRegion region;
+ if (!path.isEmpty())
+ region = QRegion(path.convertToPainterPath().toFillPolygon(QTransform()).toPolygon());
+
+ switch (op) {
+ case Qt::NoClip:
+ {
+ region = defaultClipRegion();
+ }
+ break;
+
+ case Qt::ReplaceClip:
+ {
+ region = d->transform.map(region);
+ }
+ break;
+
+ case Qt::IntersectClip:
+ {
+ region = s->clipRegion.intersect(d->transform.map(region));
+ }
+ break;
+
+ case Qt::UniteClip:
+ {
+ region = s->clipRegion.unite(d->transform.map(region));
+ }
+ break;
+ }
+ if (region.numRects() <= d->maxScissorRects) {
+ // We haven't reached the maximum scissor count yet, so we can
+ // still make use of this region.
+ s->clipRegion = region;
+ updateScissor();
+ return;
+ }
+
+ // The best we can do is clip to the bounding rectangle
+ // of all control points.
+ clip(path.controlPointRect().toRect(), op);
+}
+
+void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ switch (op) {
+ case Qt::NoClip:
+ {
+ s->clipRegion = defaultClipRegion();
+ }
+ break;
+
+ case Qt::ReplaceClip:
+ {
+ s->clipRegion = d->transform.map(QRegion(rect));
+ }
+ break;
+
+ case Qt::IntersectClip:
+ {
+ s->clipRegion = s->clipRegion.intersect(d->transform.map(QRegion(rect)));
+ }
+ break;
+
+ case Qt::UniteClip:
+ {
+ s->clipRegion = s->clipRegion.unite(d->transform.map(QRegion(rect)));
+ }
+ break;
+ }
+
+ updateScissor();
+}
+
+void QVGPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ switch (op) {
+ case Qt::NoClip:
+ {
+ s->clipRegion = defaultClipRegion();
+ }
+ break;
+
+ case Qt::ReplaceClip:
+ {
+ s->clipRegion = d->transform.map(region);
+ }
+ break;
+
+ case Qt::IntersectClip:
+ {
+ s->clipRegion = s->clipRegion.intersect(d->transform.map(region));
+ }
+ break;
+
+ case Qt::UniteClip:
+ {
+ s->clipRegion = s->clipRegion.unite(d->transform.map(region));
+ }
+ break;
+ }
+
+ updateScissor();
+}
+
+void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+ QPaintEngineEx::clip(path, op);
+}
+
+#else // !QVG_SCISSOR_CLIP
+
+void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ if (op == Qt::NoClip) {
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ vgSeti(VG_MASKING, VG_FALSE);
+ return;
+ }
+
+ // We don't have vgRenderToMask(), so handle simple QRectF's only.
+ if (path.shape() == QVectorPath::RectangleHint &&
+ path.elementCount() == 4 && clipTransformIsSimple(d->transform)) {
+ // Clipping region that resulted from QPainter::setClipRect(QRectF).
+ // Convert it into a QRect and apply.
+ const qreal *points = path.points();
+ QRectF rect(points[0], points[1], points[2] - points[0],
+ points[5] - points[1]);
+ clip(rect.toRect(), op);
+ return;
+ }
+
+#if !defined(QVG_NO_RENDER_TO_MASK)
+ QPaintDevice *pdev = paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ if (op == Qt::ReplaceClip) {
+ vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
+ d->maskRect = QRect();
+ } else if (!d->maskValid) {
+ d->ensureMask(this, width, height);
+ }
+
+ d->ensurePathTransform();
+ VGPath vgpath = d->vectorPathToVGPath(path);
+ switch (op) {
+ case Qt::ReplaceClip:
+ case Qt::UniteClip:
+ vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
+ break;
+
+ case Qt::IntersectClip:
+ vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
+ break;
+
+ default: break;
+ }
+ vgDestroyPath(vgpath);
+
+ vgSeti(VG_MASKING, VG_TRUE);
+ d->maskValid = true;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+#endif
+}
+
+void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ // If we have a non-simple transform, then use path-based clipping.
+ if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) {
+ QPaintEngineEx::clip(rect, op);
+ return;
+ }
+
+ switch (op) {
+ case Qt::NoClip:
+ {
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ vgSeti(VG_MASKING, VG_FALSE);
+ }
+ break;
+
+ case Qt::ReplaceClip:
+ {
+ QRect r = d->transform.mapRect(rect);
+ if (isDefaultClipRect(r)) {
+ // Replacing the clip with a full-window region is the
+ // same as turning off clipping.
+ if (d->maskValid)
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ } else {
+ // Special case: if the intersection of the system
+ // clip and "r" is a single rectangle, then use the
+ // scissor for clipping. We try to avoid allocating a
+ // QRegion copy on the heap for the test if we can.
+ QRegion clip = d->systemClip; // Reference-counted, no alloc.
+ QRect clipRect;
+ if (clip.rectCount() == 1) {
+ clipRect = clip.boundingRect().intersected(r);
+ } else if (clip.isEmpty()) {
+ clipRect = r;
+ } else {
+ clip = clip.intersect(r);
+ if (clip.rectCount() != 1) {
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ d->modifyMask(this, VG_FILL_MASK, r);
+ break;
+ }
+ clipRect = clip.boundingRect();
+ }
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = true;
+ d->maskRect = clipRect;
+ vgSeti(VG_MASKING, VG_FALSE);
+ updateScissor();
+ }
+ }
+ break;
+
+ case Qt::IntersectClip:
+ {
+ QRect r = d->transform.mapRect(rect);
+ if (!d->maskValid) {
+ // Mask has not been used yet, so intersect with
+ // the previous scissor-based region in maskRect.
+ if (d->scissorMask)
+ r = r.intersect(d->maskRect);
+ if (isDefaultClipRect(r)) {
+ // The clip is the full window, so turn off clipping.
+ d->maskIsSet = true;
+ d->maskRect = QRect();
+ } else {
+ // Activate the scissor on a smaller maskRect.
+ d->maskIsSet = false;
+ d->maskRect = r;
+ }
+ d->scissorMask = true;
+ updateScissor();
+ } else if (d->maskIsSet && isDefaultClipRect(r)) {
+ // Intersecting a full-window clip with a full-window
+ // region is the same as turning off clipping.
+ if (d->maskValid)
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ } else {
+ d->modifyMask(this, VG_INTERSECT_MASK, r);
+ }
+ }
+ break;
+
+ case Qt::UniteClip:
+ {
+ // If we already have a full-window clip, then uniting a
+ // region with it will do nothing. Otherwise union.
+ if (!(d->maskIsSet))
+ d->modifyMask(this, VG_UNION_MASK, d->transform.mapRect(rect));
+ }
+ break;
+ }
+}
+
+void QVGPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QVGPaintEngine);
+
+ // Use the QRect case if the region consists of a single rectangle.
+ if (region.rectCount() == 1) {
+ clip(region.boundingRect(), op);
+ return;
+ }
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ // If we have a non-simple transform, then use path-based clipping.
+ if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) {
+ QPaintEngineEx::clip(region, op);
+ return;
+ }
+
+ switch (op) {
+ case Qt::NoClip:
+ {
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ vgSeti(VG_MASKING, VG_FALSE);
+ }
+ break;
+
+ case Qt::ReplaceClip:
+ {
+ QRegion r = d->transform.map(region);
+ if (isDefaultClipRegion(r)) {
+ // Replacing the clip with a full-window region is the
+ // same as turning off clipping.
+ if (d->maskValid)
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ } else {
+ // Special case: if the intersection of the system
+ // clip and the region is a single rectangle, then
+ // use the scissor for clipping.
+ QRegion clip = d->systemClip;
+ if (clip.isEmpty())
+ clip = r;
+ else
+ clip = clip.intersect(r);
+ if (clip.rectCount() == 1) {
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = true;
+ d->maskRect = clip.boundingRect();
+ vgSeti(VG_MASKING, VG_FALSE);
+ updateScissor();
+ } else {
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ d->modifyMask(this, VG_FILL_MASK, r);
+ }
+ }
+ }
+ break;
+
+ case Qt::IntersectClip:
+ {
+ if (region.rectCount() != 1) {
+ // If there is more than one rectangle, then intersecting
+ // the rectangles one by one in modifyMask() will not give
+ // the desired result. So fall back to path-based clipping.
+ QPaintEngineEx::clip(region, op);
+ return;
+ }
+ QRegion r = d->transform.map(region);
+ if (d->maskIsSet && isDefaultClipRegion(r)) {
+ // Intersecting a full-window clip with a full-window
+ // region is the same as turning off clipping.
+ if (d->maskValid)
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ } else {
+ d->modifyMask(this, VG_INTERSECT_MASK, r);
+ }
+ }
+ break;
+
+ case Qt::UniteClip:
+ {
+ // If we already have a full-window clip, then uniting a
+ // region with it will do nothing. Otherwise union.
+ if (!(d->maskIsSet))
+ d->modifyMask(this, VG_UNION_MASK, d->transform.map(region));
+ }
+ break;
+ }
+}
+
+#if !defined(QVG_NO_RENDER_TO_MASK)
+
+// Copied from qpathclipper.cpp.
+static bool qt_vg_pathToRect(const QPainterPath &path, QRectF *rect)
+{
+ if (path.elementCount() != 5)
+ return false;
+
+ const bool mightBeRect = path.elementAt(0).isMoveTo()
+ && path.elementAt(1).isLineTo()
+ && path.elementAt(2).isLineTo()
+ && path.elementAt(3).isLineTo()
+ && path.elementAt(4).isLineTo();
+
+ if (!mightBeRect)
+ return false;
+
+ const qreal x1 = path.elementAt(0).x;
+ const qreal y1 = path.elementAt(0).y;
+
+ const qreal x2 = path.elementAt(1).x;
+ const qreal y2 = path.elementAt(2).y;
+
+ if (path.elementAt(1).y != y1)
+ return false;
+
+ if (path.elementAt(2).x != x2)
+ return false;
+
+ if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
+ return false;
+
+ if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
+ return false;
+
+ if (rect)
+ *rect = QRectF(QPointF(x1, y1), QPointF(x2, y2));
+
+ return true;
+}
+
+#endif
+
+void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+#if !defined(QVG_NO_RENDER_TO_MASK)
+ Q_D(QVGPaintEngine);
+
+ // If the path is a simple rectangle, then use clip(QRect) instead.
+ QRectF simpleRect;
+ if (qt_vg_pathToRect(path, &simpleRect)) {
+ clip(simpleRect.toRect(), op);
+ return;
+ }
+
+ d->dirty |= QPaintEngine::DirtyClipRegion;
+
+ if (op == Qt::NoClip) {
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ vgSeti(VG_MASKING, VG_FALSE);
+ return;
+ }
+
+ QPaintDevice *pdev = paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ if (op == Qt::ReplaceClip) {
+ vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
+ d->maskRect = QRect();
+ } else if (!d->maskValid) {
+ d->ensureMask(this, width, height);
+ }
+
+ d->ensurePathTransform();
+ VGPath vgpath = d->painterPathToVGPath(path);
+ switch (op) {
+ case Qt::ReplaceClip:
+ case Qt::UniteClip:
+ vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK);
+ break;
+
+ case Qt::IntersectClip:
+ vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK);
+ break;
+
+ default: break;
+ }
+ vgDestroyPath(vgpath);
+
+ vgSeti(VG_MASKING, VG_TRUE);
+ d->maskValid = true;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+#else
+ QPaintEngineEx::clip(path, op);
+#endif
+}
+
+void QVGPaintEnginePrivate::ensureMask
+ (QVGPaintEngine *engine, int width, int height)
+{
+ scissorMask = false;
+ if (maskIsSet) {
+ vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, width, height);
+ maskRect = QRect();
+ } else {
+ vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height);
+ if (maskRect.isValid()) {
+ vgMask(VG_INVALID_HANDLE, VG_FILL_MASK,
+ maskRect.x(), height - maskRect.y() - maskRect.height(),
+ maskRect.width(), maskRect.height());
+ maskRect = QRect();
+ engine->updateScissor();
+ }
+ }
+}
+
+void QVGPaintEnginePrivate::modifyMask
+ (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region)
+{
+ QPaintDevice *pdev = engine->paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ if (!maskValid)
+ ensureMask(engine, width, height);
+
+ QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ vgMask(VG_INVALID_HANDLE, op,
+ rects[i].x(), height - rects[i].y() - rects[i].height(),
+ rects[i].width(), rects[i].height());
+ }
+
+ vgSeti(VG_MASKING, VG_TRUE);
+ maskValid = true;
+ maskIsSet = false;
+ scissorMask = false;
+}
+
+void QVGPaintEnginePrivate::modifyMask
+ (QVGPaintEngine *engine, VGMaskOperation op, const QRect& rect)
+{
+ QPaintDevice *pdev = engine->paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ if (!maskValid)
+ ensureMask(engine, width, height);
+
+ if (rect.isValid()) {
+ vgMask(VG_INVALID_HANDLE, op,
+ rect.x(), height - rect.y() - rect.height(),
+ rect.width(), rect.height());
+ }
+
+ vgSeti(VG_MASKING, VG_TRUE);
+ maskValid = true;
+ maskIsSet = false;
+ scissorMask = false;
+}
+
+#endif // !QVG_SCISSOR_CLIP
+
+void QVGPaintEngine::updateScissor()
+{
+ Q_D(QVGPaintEngine);
+
+ QRegion region = d->systemClip;
+
+#if defined(QVG_SCISSOR_CLIP)
+ // Using the scissor to do clipping, so combine the systemClip
+ // with the current painting clipRegion.
+
+ if (d->maskValid) {
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ }
+
+ QVGPainterState *s = state();
+ if (s->clipEnabled) {
+ if (region.isEmpty())
+ region = s->clipRegion;
+ else
+ region = region.intersect(s->clipRegion);
+ if (isDefaultClipRegion(region)) {
+ // The scissor region is the entire drawing surface,
+ // so there is no point doing any scissoring.
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ d->scissorActive = false;
+ d->scissorDirty = false;
+ return;
+ }
+ } else
+#endif
+ {
+#if !defined(QVG_SCISSOR_CLIP)
+ // Combine the system clip with the simple mask rectangle.
+ if (d->scissorMask) {
+ if (region.isEmpty())
+ region = d->maskRect;
+ else
+ region = region.intersect(d->maskRect);
+ if (isDefaultClipRegion(region)) {
+ // The scissor region is the entire drawing surface,
+ // so there is no point doing any scissoring.
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ d->scissorActive = false;
+ d->scissorDirty = false;
+ return;
+ }
+ } else
+#endif
+
+ // Disable the scissor completely if the system clip is empty.
+ if (region.isEmpty()) {
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ d->scissorActive = false;
+ d->scissorDirty = false;
+ return;
+ }
+ }
+
+ if (d->scissorActive && region == d->scissorRegion && !d->scissorDirty)
+ return;
+
+ QVector<QRect> rects = region.rects();
+ int count = rects.count();
+ if (count > d->maxScissorRects) {
+#if !defined(QVG_SCISSOR_CLIP)
+ count = d->maxScissorRects;
+#else
+ // Use masking
+ int width = paintDevice()->width();
+ int height = paintDevice()->height();
+ vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK,
+ 0, 0, width, height);
+ for (int i = 0; i < rects.size(); ++i) {
+ vgMask(VG_INVALID_HANDLE, VG_FILL_MASK,
+ rects[i].x(), height - rects[i].y() - rects[i].height(),
+ rects[i].width(), rects[i].height());
+ }
+
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ vgSeti(VG_MASKING, VG_TRUE);
+ d->maskValid = true;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+ d->scissorActive = false;
+ d->scissorDirty = false;
+ d->scissorRegion = region;
+ return;
+#endif
+ }
+
+ QVarLengthArray<VGint> params(count * 4);
+ int height = paintDevice()->height();
+ for (int i = 0; i < count; ++i) {
+ params[i * 4 + 0] = rects[i].x();
+ params[i * 4 + 1] = height - rects[i].y() - rects[i].height();
+ params[i * 4 + 2] = rects[i].width();
+ params[i * 4 + 3] = rects[i].height();
+ }
+
+ vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
+ vgSeti(VG_SCISSORING, VG_TRUE);
+ d->scissorDirty = false;
+ d->scissorActive = true;
+ d->scissorRegion = region;
+}
+
+QRegion QVGPaintEngine::defaultClipRegion()
+{
+ // The default clip region for a paint device is the whole drawing area.
+ QPaintDevice *pdev = paintDevice();
+ return QRegion(0, 0, pdev->width(), pdev->height());
+}
+
+bool QVGPaintEngine::isDefaultClipRegion(const QRegion& region)
+{
+ if (region.rectCount() != 1)
+ return false;
+
+ QPaintDevice *pdev = paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ QRect rect = region.boundingRect();
+ return (rect.x() == 0 && rect.y() == 0 &&
+ rect.width() == width && rect.height() == height);
+}
+
+bool QVGPaintEngine::isDefaultClipRect(const QRect& rect)
+{
+ QPaintDevice *pdev = paintDevice();
+ int width = pdev->width();
+ int height = pdev->height();
+
+ return (rect.x() == 0 && rect.y() == 0 &&
+ rect.width() == width && rect.height() == height);
+}
+
+void QVGPaintEngine::clipEnabledChanged()
+{
+#if defined(QVG_SCISSOR_CLIP)
+ vgSeti(VG_MASKING, VG_FALSE); // disable mask fallback
+ updateScissor();
+#else
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ d->dirty |= QPaintEngine::DirtyClipEnabled;
+ if (s->clipEnabled && s->clipOperation != Qt::NoClip) {
+ // Replay the entire clip stack to put the mask into the right state.
+ d->maskValid = false;
+ d->maskIsSet = true;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ s->clipRegion = defaultClipRegion();
+ d->replayClipOperations();
+ d->transform = s->transform();
+ d->updateTransform(paintDevice());
+ } else {
+ vgSeti(VG_MASKING, VG_FALSE);
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ }
+#endif
+}
+
+void QVGPaintEngine::penChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyPen;
+}
+
+void QVGPaintEngine::brushChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyBrush;
+}
+
+void QVGPaintEngine::brushOriginChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyBrushOrigin;
+ d->brushOrigin = state()->brushOrigin;
+ d->forcePenChange = true;
+ d->forceBrushChange = true;
+}
+
+void QVGPaintEngine::opacityChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyOpacity;
+ d->opacity = state()->opacity;
+ d->forcePenChange = true;
+ d->forceBrushChange = true;
+}
+
+void QVGPaintEngine::compositionModeChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyCompositionMode;
+
+ VGint vgMode = VG_BLEND_SRC_OVER;
+
+ switch (state()->composition_mode) {
+ case QPainter::CompositionMode_SourceOver:
+ vgMode = VG_BLEND_SRC_OVER;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ vgMode = VG_BLEND_DST_OVER;
+ break;
+ case QPainter::CompositionMode_Source:
+ vgMode = VG_BLEND_SRC;
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ vgMode = VG_BLEND_SRC_IN;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ vgMode = VG_BLEND_DST_IN;
+ break;
+ case QPainter::CompositionMode_Plus:
+ vgMode = VG_BLEND_ADDITIVE;
+ break;
+ case QPainter::CompositionMode_Multiply:
+ vgMode = VG_BLEND_MULTIPLY;
+ break;
+ case QPainter::CompositionMode_Screen:
+ vgMode = VG_BLEND_SCREEN;
+ break;
+ case QPainter::CompositionMode_Darken:
+ vgMode = VG_BLEND_DARKEN;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ vgMode = VG_BLEND_LIGHTEN;
+ break;
+ default:
+ if (d->hasAdvancedBlending) {
+ switch (state()->composition_mode) {
+ case QPainter::CompositionMode_Overlay:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_OVERLAY_KHR;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORDODGE_KHR;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_COLORBURN_KHR;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_HARDLIGHT_KHR;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SOFTLIGHT_KHR;
+ break;
+ case QPainter::CompositionMode_Difference:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DIFFERENCE_KHR;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_EXCLUSION_KHR;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_OUT_KHR;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_OUT_KHR;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_SRC_ATOP_KHR;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_DST_ATOP_KHR;
+ break;
+ case QPainter::CompositionMode_Xor:
+ vgMode = QVGPaintEnginePrivate::QT_VG_BLEND_XOR_KHR;
+ break;
+ default: break; // Fall back to VG_BLEND_SRC_OVER.
+ }
+ }
+ if (vgMode == VG_BLEND_SRC_OVER)
+ qWarning() << "QVGPaintEngine::compositionModeChanged unsupported mode" << state()->composition_mode;
+ break;
+ }
+
+ d->setBlendMode(VGBlendMode(vgMode));
+}
+
+void QVGPaintEngine::renderHintsChanged()
+{
+ Q_D(QVGPaintEngine);
+ d->dirty |= QPaintEngine::DirtyHints;
+
+ QPainter::RenderHints hints = state()->renderHints;
+
+ VGRenderingQuality rq =
+ (hints & QPainter::Antialiasing)
+ ? VG_RENDERING_QUALITY_BETTER
+ : VG_RENDERING_QUALITY_NONANTIALIASED;
+ VGImageQuality iq =
+ (hints & QPainter::SmoothPixmapTransform)
+ ? VG_IMAGE_QUALITY_BETTER
+ : VG_IMAGE_QUALITY_NONANTIALIASED;
+
+ d->setRenderingQuality(rq);
+ d->setImageQuality(iq);
+}
+
+void QVGPaintEngine::transformChanged()
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ d->dirty |= QPaintEngine::DirtyTransform;
+ d->transform = s->transform();
+ qreal oldPenScale = d->penScale;
+ d->updateTransform(paintDevice());
+ if (d->penScale != oldPenScale)
+ d->forcePenChange = true;
+}
+
+bool QVGPaintEngine::clearRect(const QRectF &rect, const QColor &color)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ if (!s->clipEnabled || s->clipOperation == Qt::NoClip) {
+ QRect r = d->transform.mapRect(rect).toRect();
+ int height = paintDevice()->height();
+ if (d->clearColor != color || d->clearOpacity != s->opacity) {
+ VGfloat values[4];
+ values[0] = color.redF();
+ values[1] = color.greenF();
+ values[2] = color.blueF();
+ values[3] = color.alphaF() * s->opacity;
+ vgSetfv(VG_CLEAR_COLOR, 4, values);
+ d->clearColor = color;
+ d->clearOpacity = s->opacity;
+ }
+ vgClear(r.x(), height - r.y() - r.height(),
+ r.width(), r.height());
+ return true;
+ }
+ return false;
+}
+
+void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
+{
+ Q_D(QVGPaintEngine);
+
+ if (brush.style() == Qt::NoBrush)
+ return;
+
+ // Check to see if we can use vgClear() for faster filling.
+ if (brush.style() == Qt::SolidPattern && brush.isOpaque() &&
+ clipTransformIsSimple(d->transform) && d->opacity == 1.0f &&
+ clearRect(rect, brush.color())) {
+ return;
+ }
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ VGfloat coords[8];
+ if (d->simpleTransform) {
+ coords[0] = rect.x();
+ coords[1] = rect.y();
+ coords[2] = rect.x() + rect.width();
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = rect.y() + rect.height();
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+ } else {
+ QPointF tl = d->transform.map(rect.topLeft());
+ QPointF tr = d->transform.map(rect.topRight());
+ QPointF bl = d->transform.map(rect.bottomLeft());
+ QPointF br = d->transform.map(rect.bottomRight());
+ coords[0] = tl.x();
+ coords[1] = tl.y();
+ coords[2] = tr.x();
+ coords[3] = tr.y();
+ coords[4] = br.x();
+ coords[5] = br.y();
+ coords[6] = bl.x();
+ coords[7] = bl.y();
+ }
+ vgModifyPathCoords(d->rectPath, 0, 4, coords);
+ d->fill(d->rectPath, brush);
+#else
+ QPaintEngineEx::fillRect(rect, brush);
+#endif
+}
+
+void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color)
+{
+ Q_D(QVGPaintEngine);
+
+ // Check to see if we can use vgClear() for faster filling.
+ if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f && color.alpha() == 255 &&
+ clearRect(rect, color)) {
+ return;
+ }
+
+#if !defined(QVG_NO_MODIFY_PATH)
+ VGfloat coords[8];
+ if (d->simpleTransform) {
+ coords[0] = rect.x();
+ coords[1] = rect.y();
+ coords[2] = rect.x() + rect.width();
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = rect.y() + rect.height();
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+ } else {
+ QPointF tl = d->transform.map(rect.topLeft());
+ QPointF tr = d->transform.map(rect.topRight());
+ QPointF bl = d->transform.map(rect.bottomLeft());
+ QPointF br = d->transform.map(rect.bottomRight());
+ coords[0] = tl.x();
+ coords[1] = tl.y();
+ coords[2] = tr.x();
+ coords[3] = tr.y();
+ coords[4] = br.x();
+ coords[5] = br.y();
+ coords[6] = bl.x();
+ coords[7] = bl.y();
+ }
+ vgModifyPathCoords(d->rectPath, 0, 4, coords);
+ d->fill(d->rectPath, QBrush(color));
+#else
+ QPaintEngineEx::fillRect(rect, QBrush(color));
+#endif
+}
+
+void QVGPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode)
+{
+ Q_D(QVGPaintEngine);
+ if (d->simpleTransform) {
+ QVGPainterState *s = state();
+ VGPath vgpath = d->roundedRectPath(rect, xrad, yrad, mode);
+ d->draw(vgpath, s->pen, s->brush);
+#if defined(QVG_NO_MODIFY_PATH)
+ vgDestroyPath(vgpath);
+#endif
+ } else {
+ QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
+ }
+}
+
+void QVGPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ for (int i = 0; i < rectCount; ++i, ++rects) {
+ VGfloat coords[8];
+ if (d->simpleTransform) {
+ coords[0] = rects->x();
+ coords[1] = rects->y();
+ coords[2] = rects->x() + rects->width();
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = rects->y() + rects->height();
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+ } else {
+ QPointF tl = d->transform.map(QPointF(rects->x(), rects->y()));
+ QPointF tr = d->transform.map(QPointF(rects->x() + rects->width(),
+ rects->y()));
+ QPointF bl = d->transform.map(QPointF(rects->x(),
+ rects->y() + rects->height()));
+ QPointF br = d->transform.map(QPointF(rects->x() + rects->width(),
+ rects->y() + rects->height()));
+ coords[0] = tl.x();
+ coords[1] = tl.y();
+ coords[2] = tr.x();
+ coords[3] = tr.y();
+ coords[4] = br.x();
+ coords[5] = br.y();
+ coords[6] = bl.x();
+ coords[7] = bl.y();
+ }
+ vgModifyPathCoords(d->rectPath, 0, 4, coords);
+ d->draw(d->rectPath, s->pen, s->brush);
+ }
+#else
+ QPaintEngineEx::drawRects(rects, rectCount);
+#endif
+}
+
+void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ for (int i = 0; i < rectCount; ++i, ++rects) {
+ VGfloat coords[8];
+ if (d->simpleTransform) {
+ coords[0] = rects->x();
+ coords[1] = rects->y();
+ coords[2] = rects->x() + rects->width();
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = rects->y() + rects->height();
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+ } else {
+ QPointF tl = d->transform.map(rects->topLeft());
+ QPointF tr = d->transform.map(rects->topRight());
+ QPointF bl = d->transform.map(rects->bottomLeft());
+ QPointF br = d->transform.map(rects->bottomRight());
+ coords[0] = tl.x();
+ coords[1] = tl.y();
+ coords[2] = tr.x();
+ coords[3] = tr.y();
+ coords[4] = br.x();
+ coords[5] = br.y();
+ coords[6] = bl.x();
+ coords[7] = bl.y();
+ }
+ vgModifyPathCoords(d->rectPath, 0, 4, coords);
+ d->draw(d->rectPath, s->pen, s->brush);
+ }
+#else
+ QPaintEngineEx::drawRects(rects, rectCount);
+#endif
+}
+
+void QVGPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ for (int i = 0; i < lineCount; ++i, ++lines) {
+ VGfloat coords[4];
+ if (d->simpleTransform) {
+ coords[0] = lines->x1();
+ coords[1] = lines->y1();
+ coords[2] = lines->x2();
+ coords[3] = lines->y2();
+ } else {
+ QPointF p1 = d->transform.map(QPointF(lines->x1(), lines->y1()));
+ QPointF p2 = d->transform.map(QPointF(lines->x2(), lines->y2()));
+ coords[0] = p1.x();
+ coords[1] = p1.y();
+ coords[2] = p2.x();
+ coords[3] = p2.y();
+ }
+ vgModifyPathCoords(d->linePath, 0, 2, coords);
+ d->stroke(d->linePath, s->pen);
+ }
+#else
+ QPaintEngineEx::drawLines(lines, lineCount);
+#endif
+}
+
+void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ for (int i = 0; i < lineCount; ++i, ++lines) {
+ VGfloat coords[4];
+ if (d->simpleTransform) {
+ coords[0] = lines->x1();
+ coords[1] = lines->y1();
+ coords[2] = lines->x2();
+ coords[3] = lines->y2();
+ } else {
+ QPointF p1 = d->transform.map(lines->p1());
+ QPointF p2 = d->transform.map(lines->p2());
+ coords[0] = p1.x();
+ coords[1] = p1.y();
+ coords[2] = p2.x();
+ coords[3] = p2.y();
+ }
+ vgModifyPathCoords(d->linePath, 0, 2, coords);
+ d->stroke(d->linePath, s->pen);
+ }
+#else
+ QPaintEngineEx::drawLines(lines, lineCount);
+#endif
+}
+
+void QVGPaintEngine::drawEllipse(const QRectF &r)
+{
+ // Based on the description of vguEllipse() in the OpenVG specification.
+ // We don't use vguEllipse(), to avoid unnecessary library dependencies.
+ Q_D(QVGPaintEngine);
+ if (d->simpleTransform) {
+ QVGPainterState *s = state();
+ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 4, // segmentCapacityHint
+ 12, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ static VGubyte segments[4] = {
+ VG_MOVE_TO_ABS,
+ VG_SCCWARC_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_CLOSE_PATH
+ };
+ VGfloat coords[12];
+ VGfloat halfwid = r.width() / 2;
+ VGfloat halfht = r.height() / 2;
+ coords[0] = r.x() + r.width();
+ coords[1] = r.y() + halfht;
+ coords[2] = halfwid;
+ coords[3] = halfht;
+ coords[4] = 0.0f;
+ coords[5] = -r.width();
+ coords[6] = 0.0f;
+ coords[7] = halfwid;
+ coords[8] = halfht;
+ coords[9] = 0.0f;
+ coords[10] = r.width();
+ coords[11] = 0.0f;
+ vgAppendPathData(path, 4, segments, coords);
+ d->draw(path, s->pen, s->brush);
+ vgDestroyPath(path);
+ } else {
+ // The projective transform version of an ellipse is difficult.
+ // Generate a QVectorPath containing cubic curves and transform that.
+ QPaintEngineEx::drawEllipse(r);
+ }
+}
+
+void QVGPaintEngine::drawEllipse(const QRect &r)
+{
+ drawEllipse(QRectF(r));
+}
+
+void QVGPaintEngine::drawPath(const QPainterPath &path)
+{
+ // Shortcut past the QPainterPath -> QVectorPath conversion,
+ // converting the QPainterPath directly into a VGPath.
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ VGPath vgpath = d->painterPathToVGPath(path);
+ if (path.fillRule() == Qt::OddEvenFill)
+ d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD);
+ else
+ d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO);
+ vgDestroyPath(vgpath);
+}
+
+void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+
+ // Set up a new pen if necessary.
+ QPen pen = state()->pen;
+ if (pen.style() == Qt::NoPen)
+ return;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ for (int i = 0; i < pointCount; ++i, ++points) {
+ VGfloat coords[4];
+ if (d->simpleTransform) {
+ coords[0] = points->x();
+ coords[1] = points->y();
+ coords[2] = coords[0];
+ coords[3] = coords[1];
+ } else {
+ QPointF p = d->transform.map(*points);
+ coords[0] = p.x();
+ coords[1] = p.y();
+ coords[2] = coords[0];
+ coords[3] = coords[1];
+ }
+ vgModifyPathCoords(d->linePath, 0, 2, coords);
+ d->stroke(d->linePath, pen);
+ }
+#else
+ QPaintEngineEx::drawPoints(points, pointCount);
+#endif
+}
+
+void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+#if !defined(QVG_NO_MODIFY_PATH)
+ Q_D(QVGPaintEngine);
+
+ // Set up a new pen if necessary.
+ QPen pen = state()->pen;
+ if (pen.style() == Qt::NoPen)
+ return;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ for (int i = 0; i < pointCount; ++i, ++points) {
+ VGfloat coords[4];
+ if (d->simpleTransform) {
+ coords[0] = points->x();
+ coords[1] = points->y();
+ coords[2] = coords[0];
+ coords[3] = coords[1];
+ } else {
+ QPointF p = d->transform.map(QPointF(*points));
+ coords[0] = p.x();
+ coords[1] = p.y();
+ coords[2] = coords[0];
+ coords[3] = coords[1];
+ }
+ vgModifyPathCoords(d->linePath, 0, 2, coords);
+ d->stroke(d->linePath, pen);
+ }
+#else
+ QPaintEngineEx::drawPoints(points, pointCount);
+#endif
+}
+
+void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ pointCount + 1, // segmentCapacityHint
+ pointCount * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ QVarLengthArray<VGfloat, 16> coords;
+ QVarLengthArray<VGubyte, 10> segments;
+ for (int i = 0; i < pointCount; ++i, ++points) {
+ if (d->simpleTransform) {
+ coords.append(points->x());
+ coords.append(points->y());
+ } else {
+ QPointF temp = d->transform.map(*points);
+ coords.append(temp.x());
+ coords.append(temp.y());
+ }
+ if (i == 0)
+ segments.append(VG_MOVE_TO_ABS);
+ else
+ segments.append(VG_LINE_TO_ABS);
+ }
+ if (mode != QPaintEngine::PolylineMode)
+ segments.append(VG_CLOSE_PATH);
+ vgAppendPathData(path, segments.count(),
+ segments.constData(), coords.constData());
+ switch (mode) {
+ case QPaintEngine::WindingMode:
+ d->draw(path, s->pen, s->brush, VG_NON_ZERO);
+ break;
+
+ case QPaintEngine::PolylineMode:
+ d->stroke(path, s->pen);
+ break;
+
+ default:
+ d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
+ break;
+ }
+ vgDestroyPath(path);
+}
+
+void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QVGPaintEngine);
+ QVGPainterState *s = state();
+ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ pointCount + 1, // segmentCapacityHint
+ pointCount * 2, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ QVarLengthArray<VGfloat, 16> coords;
+ QVarLengthArray<VGubyte, 10> segments;
+ for (int i = 0; i < pointCount; ++i, ++points) {
+ if (d->simpleTransform) {
+ coords.append(points->x());
+ coords.append(points->y());
+ } else {
+ QPointF temp = d->transform.map(QPointF(*points));
+ coords.append(temp.x());
+ coords.append(temp.y());
+ }
+ if (i == 0)
+ segments.append(VG_MOVE_TO_ABS);
+ else
+ segments.append(VG_LINE_TO_ABS);
+ }
+ if (mode != QPaintEngine::PolylineMode)
+ segments.append(VG_CLOSE_PATH);
+ vgAppendPathData(path, segments.count(),
+ segments.constData(), coords.constData());
+ switch (mode) {
+ case QPaintEngine::WindingMode:
+ d->draw(path, s->pen, s->brush, VG_NON_ZERO);
+ break;
+
+ case QPaintEngine::PolylineMode:
+ d->stroke(path, s->pen);
+ break;
+
+ default:
+ d->draw(path, s->pen, s->brush, VG_EVEN_ODD);
+ break;
+ }
+ vgDestroyPath(path);
+}
+
+void QVGPaintEnginePrivate::setImageOptions()
+{
+ if (opacity != 1.0f && simpleTransform) {
+ if (opacity != paintOpacity) {
+ VGfloat values[4];
+ values[0] = 1.0f;
+ values[1] = 1.0f;
+ values[2] = 1.0f;
+ values[3] = opacity;
+ vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values);
+ paintOpacity = opacity;
+ }
+ if (fillPaint != opacityPaint) {
+ vgSetPaint(opacityPaint, VG_FILL_PATH);
+ fillPaint = opacityPaint;
+ }
+ setImageMode(VG_DRAW_IMAGE_MULTIPLY);
+ } else {
+ setImageMode(VG_DRAW_IMAGE_NORMAL);
+ }
+}
+
+void QVGPaintEnginePrivate::systemStateChanged()
+{
+ q->updateScissor();
+}
+
+static void drawVGImage(QVGPaintEnginePrivate *d,
+ const QRectF& r, VGImage vgImg,
+ const QSize& imageSize, const QRectF& sr)
+{
+ if (vgImg == VG_INVALID_HANDLE)
+ return;
+ VGImage child = VG_INVALID_HANDLE;
+
+ if (sr.topLeft().isNull() && sr.size() == imageSize) {
+ child = vgImg;
+ } else {
+ QRect src = sr.toRect();
+#if !defined(QT_SHIVAVG)
+ child = vgChildImage(vgImg, src.x(), src.y(), src.width(), src.height());
+#else
+ child = vgImg; // XXX: ShivaVG doesn't have vgChildImage().
+#endif
+ }
+
+ QTransform transform(d->imageTransform);
+ VGfloat scaleX = sr.width() == 0.0f ? 0.0f : r.width() / sr.width();
+ VGfloat scaleY = sr.height() == 0.0f ? 0.0f : r.height() / sr.height();
+ transform.translate(r.x(), r.y());
+ transform.scale(scaleX, scaleY);
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ d->setImageOptions();
+ vgDrawImage(child);
+
+ if(child != vgImg)
+ vgDestroyImage(child);
+}
+
+static void drawVGImage(QVGPaintEnginePrivate *d,
+ const QPointF& pos, VGImage vgImg)
+{
+ if (vgImg == VG_INVALID_HANDLE)
+ return;
+
+ QTransform transform(d->imageTransform);
+ transform.translate(pos.x(), pos.y());
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ d->setImageOptions();
+ vgDrawImage(vgImg);
+}
+
+static void drawImageTiled(QVGPaintEnginePrivate *d,
+ const QRectF &r,
+ const QImage &image,
+ const QRectF &sr = QRectF())
+{
+ const int minTileSize = 16;
+ int tileWidth = 512;
+ int tileHeight = tileWidth;
+
+ VGImageFormat tileFormat = qt_vg_image_to_vg_format(image.format());
+ VGImage tile = VG_INVALID_HANDLE;
+ QVGImagePool *pool = QVGImagePool::instance();
+ while (tile == VG_INVALID_HANDLE && tileWidth >= minTileSize) {
+ tile = pool->createPermanentImage(tileFormat, tileWidth, tileHeight,
+ VG_IMAGE_QUALITY_FASTER);
+ if (tile == VG_INVALID_HANDLE) {
+ tileWidth /= 2;
+ tileHeight /= 2;
+ }
+ }
+ if (tile == VG_INVALID_HANDLE) {
+ qWarning("drawImageTiled: Failed to create %dx%d tile, giving up", tileWidth, tileHeight);
+ return;
+ }
+
+ VGfloat opacityMatrix[20] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, d->opacity,
+ 0.0f, 0.0f, 0.0f, 0.0f
+ };
+ VGImage tileWithOpacity = VG_INVALID_HANDLE;
+ if (d->opacity != 1) {
+ tileWithOpacity = pool->createPermanentImage(VG_sARGB_8888_PRE,
+ tileWidth, tileHeight, VG_IMAGE_QUALITY_FASTER);
+ if (tileWithOpacity == VG_INVALID_HANDLE)
+ qWarning("drawImageTiled: Failed to create extra tile, ignoring opacity");
+ }
+
+ QRect sourceRect = sr.toRect();
+ if (sourceRect.isNull())
+ sourceRect = QRect(0, 0, image.width(), image.height());
+
+ VGfloat scaleX = r.width() / sourceRect.width();
+ VGfloat scaleY = r.height() / sourceRect.height();
+
+ d->setImageOptions();
+
+ for (int y = sourceRect.y(); y < sourceRect.height(); y += tileHeight) {
+ int h = qMin(tileHeight, sourceRect.height() - y);
+ if (h < 1)
+ break;
+ for (int x = sourceRect.x(); x < sourceRect.width(); x += tileWidth) {
+ int w = qMin(tileWidth, sourceRect.width() - x);
+ if (w < 1)
+ break;
+
+ int bytesPerPixel = image.depth() / 8;
+ const uchar *sptr = image.constBits() + x * bytesPerPixel + y * image.bytesPerLine();
+ vgImageSubData(tile, sptr, image.bytesPerLine(), tileFormat, 0, 0, w, h);
+
+ QTransform transform(d->imageTransform);
+ transform.translate(r.x() + x, r.y() + y);
+ transform.scale(scaleX, scaleY);
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ VGImage actualTile = tile;
+ if (tileWithOpacity != VG_INVALID_HANDLE) {
+ vgColorMatrix(tileWithOpacity, actualTile, opacityMatrix);
+ if (w < tileWidth || h < tileHeight)
+ actualTile = vgChildImage(tileWithOpacity, 0, 0, w, h);
+ else
+ actualTile = tileWithOpacity;
+ } else if (w < tileWidth || h < tileHeight) {
+ actualTile = vgChildImage(tile, 0, 0, w, h);
+ }
+ vgDrawImage(actualTile);
+
+ if (actualTile != tile && actualTile != tileWithOpacity)
+ vgDestroyImage(actualTile);
+ }
+ }
+
+ vgDestroyImage(tile);
+ if (tileWithOpacity != VG_INVALID_HANDLE)
+ vgDestroyImage(tileWithOpacity);
+}
+
+// Used by qpixmapfilter_vg.cpp to draw filtered VGImage's.
+void qt_vg_drawVGImage(QPainter *painter, const QPointF& pos, VGImage vgImg)
+{
+ QVGPaintEngine *engine =
+ static_cast<QVGPaintEngine *>(painter->paintEngine());
+ drawVGImage(engine->vgPrivate(), pos, vgImg);
+}
+
+// Used by qpixmapfilter_vg.cpp to draw filtered VGImage's as a stencil.
+void qt_vg_drawVGImageStencil
+ (QPainter *painter, const QPointF& pos, VGImage vgImg, const QBrush& brush)
+{
+ QVGPaintEngine *engine =
+ static_cast<QVGPaintEngine *>(painter->paintEngine());
+
+ QVGPaintEnginePrivate *d = engine->vgPrivate();
+
+ QTransform transform(d->imageTransform);
+ transform.translate(pos.x(), pos.y());
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ d->ensureBrush(brush);
+ d->setImageMode(VG_DRAW_IMAGE_STENCIL);
+ vgDrawImage(vgImg);
+}
+
+bool QVGPaintEngine::canVgWritePixels(const QImage &image) const
+{
+ Q_D(const QVGPaintEngine);
+
+ // qt_vg_image_to_vg_format returns VG_sARGB_8888 as
+ // fallback case if no matching VG format is found.
+ // If given image format is not Format_ARGB32 and returned
+ // format is VG_sARGB_8888, it means that no match was
+ // found. In that case vgWritePixels cannot be used.
+ // Also 1-bit formats cannot be used directly either.
+ if ((image.format() != QImage::Format_ARGB32
+ && qt_vg_image_to_vg_format(image.format()) == VG_sARGB_8888)
+ || image.depth() == 1) {
+ return false;
+ }
+
+ // vgWritePixels ignores masking, blending and xforms so we can only use it if
+ // ALL of the following conditions are true:
+ // - It is a simple translate, or a scale of -1 on the y-axis (inverted)
+ // - The opacity is totally opaque
+ // - The composition mode is "source" OR "source over" provided the image is opaque
+ return ( d->imageTransform.type() <= QTransform::TxScale
+ && d->imageTransform.m11() == 1.0 && qAbs(d->imageTransform.m22()) == 1.0)
+ && d->opacity == 1.0f
+ && (d->blendMode == VG_BLEND_SRC || (d->blendMode == VG_BLEND_SRC_OVER &&
+ !image.hasAlphaChannel()));
+}
+
+void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ QPixmapData *pd = pm.pixmapData();
+ if (!pd)
+ return; // null QPixmap
+ if (pd->classId() == QPixmapData::OpenVGClass) {
+ Q_D(QVGPaintEngine);
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ if (!vgpd->isValid())
+ return;
+ if (d->simpleTransform)
+ drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr);
+ else
+ drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr);
+
+ if(!vgpd->failedToAlloc)
+ return;
+
+ // try to reallocate next time if reasonable small pixmap
+ QSize screenSize = QApplication::desktop()->screenGeometry().size();
+ if (pm.size().width() <= screenSize.width()
+ && pm.size().height() <= screenSize.height())
+ vgpd->failedToAlloc = false;
+
+ vgpd->source.beginDataAccess();
+ drawImage(r, vgpd->source.imageRef(), sr, Qt::AutoColor);
+ vgpd->source.endDataAccess(true);
+ } else {
+ drawImage(r, *(pd->buffer()), sr, Qt::AutoColor);
+ }
+}
+
+void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm)
+{
+ QPixmapData *pd = pm.pixmapData();
+ if (!pd)
+ return; // null QPixmap
+ if (pd->classId() == QPixmapData::OpenVGClass) {
+ Q_D(QVGPaintEngine);
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ if (!vgpd->isValid())
+ return;
+ if (d->simpleTransform)
+ drawVGImage(d, pos, vgpd->toVGImage());
+ else
+ drawVGImage(d, pos, vgpd->toVGImage(d->opacity));
+
+ if (!vgpd->failedToAlloc)
+ return;
+
+ // try to reallocate next time if reasonable small pixmap
+ QSize screenSize = QApplication::desktop()->screenGeometry().size();
+ if (pm.size().width() <= screenSize.width()
+ && pm.size().height() <= screenSize.height())
+ vgpd->failedToAlloc = false;
+
+ vgpd->source.beginDataAccess();
+ drawImage(pos, vgpd->source.imageRef());
+ vgpd->source.endDataAccess(true);
+ } else {
+ drawImage(pos, *(pd->buffer()));
+ }
+}
+
+void QVGPaintEngine::drawImage
+ (const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QVGPaintEngine);
+ if (image.isNull())
+ return;
+ VGImage vgImg;
+ if (d->simpleTransform || d->opacity == 1.0f)
+ vgImg = toVGImageSubRect(image, sr.toRect(), flags);
+ else
+ vgImg = toVGImageWithOpacitySubRect(image, d->opacity, sr.toRect());
+ if (vgImg != VG_INVALID_HANDLE) {
+ if (r.size() == sr.size()) {
+ drawVGImage(d, r.topLeft(), vgImg);
+ } else {
+ drawVGImage(d, r, vgImg, sr.size().toSize(),
+ QRectF(QPointF(0, 0), sr.size()));
+ }
+ } else {
+ if (canVgWritePixels(image) && (r.size() == sr.size()) && !flags) {
+ // Optimization for straight blits, no blending
+ int x = sr.x();
+ int y = sr.y();
+ int bpp = image.depth() >> 3; // bytes
+ int offset = 0;
+ int bpl = image.bytesPerLine();
+ if (d->imageTransform.m22() < 0) {
+ // inverted
+ offset = ((y + sr.height()) * bpl) - ((image.width() - x) * bpp);
+ bpl = -bpl;
+ } else {
+ offset = (y * bpl) + (x * bpp);
+ }
+ const uchar *bits = image.constBits() + offset;
+
+ QPointF mapped = d->imageTransform.map(r.topLeft());
+ vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
+ mapped.x(), mapped.y() - sr.height(), r.width(), r.height());
+ return;
+ } else {
+ // Monochrome images need to use the vgChildImage() path.
+ vgImg = toVGImage(image, flags);
+ if (vgImg == VG_INVALID_HANDLE)
+ drawImageTiled(d, r, image, sr);
+ else
+ drawVGImage(d, r, vgImg, image.size(), sr);
+ }
+ }
+ vgDestroyImage(vgImg);
+}
+
+void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image)
+{
+ Q_D(QVGPaintEngine);
+ if (image.isNull())
+ return;
+ VGImage vgImg;
+ if (canVgWritePixels(image)) {
+ // Optimization for straight blits, no blending
+ bool inverted = (d->imageTransform.m22() < 0);
+ const uchar *bits = inverted ? image.constBits() + image.byteCount() : image.constBits();
+ int bpl = inverted ? -image.bytesPerLine() : image.bytesPerLine();
+
+ QPointF mapped = d->imageTransform.map(pos);
+ vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()),
+ mapped.x(), mapped.y() - image.height(), image.width(), image.height());
+ return;
+ } else if (d->simpleTransform || d->opacity == 1.0f) {
+ vgImg = toVGImage(image);
+ } else {
+ vgImg = toVGImageWithOpacity(image, d->opacity);
+ }
+ if (vgImg == VG_INVALID_HANDLE)
+ drawImageTiled(d, QRectF(pos, image.size()), image);
+ else
+ drawVGImage(d, pos, vgImg);
+ vgDestroyImage(vgImg);
+}
+
+void QVGPaintEngine::drawTiledPixmap
+ (const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ QBrush brush(state()->pen.color(), pixmap);
+ QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
+ brush.setTransform(xform);
+ fillRect(r, brush);
+}
+
+// Best performance will be achieved with QDrawPixmaps::OpaqueHint
+// (i.e. no opacity), no rotation or scaling, and drawing the full
+// pixmap rather than parts of the pixmap. Even having just one of
+// these conditions will improve performance.
+void QVGPaintEngine::drawPixmapFragments(const QPainter::PixmapFragment *drawingData, int dataCount,
+ const QPixmap &pixmap, QFlags<QPainter::PixmapFragmentHint> hints)
+{
+#if !defined(QT_SHIVAVG)
+ Q_D(QVGPaintEngine);
+
+ // If the pixmap is not VG, or the transformation is projective,
+ // then fall back to the default implementation.
+ QPixmapData *pd = pixmap.pixmapData();
+ if (!pd)
+ return; // null QPixmap
+ if (pd->classId() != QPixmapData::OpenVGClass || !d->simpleTransform) {
+ QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
+ return;
+ }
+
+ // Bail out if nothing to do.
+ if (dataCount <= 0)
+ return;
+
+ // Bail out if we don't have a usable VGImage for the pixmap.
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ if (!vgpd->isValid())
+ return;
+ VGImage vgImg = vgpd->toVGImage();
+ if (vgImg == VG_INVALID_HANDLE)
+ return;
+
+ // We cache the results of any vgChildImage() calls because the
+ // same child is very likely to be used over and over in particle
+ // systems. However, performance is even better if vgChildImage()
+ // isn't needed at all, so use full source rects where possible.
+ QVarLengthArray<VGImage> cachedImages;
+ QVarLengthArray<QRect> cachedSources;
+
+ // Select the opacity paint object.
+ if ((hints & QPainter::OpaqueHint) != 0 && d->opacity == 1.0f) {
+ d->setImageMode(VG_DRAW_IMAGE_NORMAL);
+ } else {
+ hints = 0;
+ if (d->fillPaint != d->opacityPaint) {
+ vgSetPaint(d->opacityPaint, VG_FILL_PATH);
+ d->fillPaint = d->opacityPaint;
+ }
+ }
+
+ for (int i = 0; i < dataCount; ++i) {
+ QTransform transform(d->imageTransform);
+ transform.translate(drawingData[i].x, drawingData[i].y);
+ transform.rotate(drawingData[i].rotation);
+
+ VGImage child;
+ QSize imageSize = vgpd->size();
+ QRectF sr(drawingData[i].sourceLeft, drawingData[i].sourceTop,
+ drawingData[i].width, drawingData[i].height);
+ if (sr.topLeft().isNull() && sr.size() == imageSize) {
+ child = vgImg;
+ } else {
+ // Look for a previous child with the same source rectangle
+ // to avoid constantly calling vgChildImage()/vgDestroyImage().
+ QRect src = sr.toRect();
+ int j;
+ for (j = 0; j < cachedSources.size(); ++j) {
+ if (cachedSources[j] == src)
+ break;
+ }
+ if (j < cachedSources.size()) {
+ child = cachedImages[j];
+ } else {
+ child = vgChildImage
+ (vgImg, src.x(), src.y(), src.width(), src.height());
+ cachedImages.append(child);
+ cachedSources.append(src);
+ }
+ }
+
+ VGfloat scaleX = drawingData[i].scaleX;
+ VGfloat scaleY = drawingData[i].scaleY;
+ transform.translate(-0.5 * scaleX * sr.width(),
+ -0.5 * scaleY * sr.height());
+ transform.scale(scaleX, scaleY);
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ if ((hints & QPainter::OpaqueHint) == 0) {
+ qreal opacity = d->opacity * drawingData[i].opacity;
+ if (opacity != 1.0f) {
+ if (d->paintOpacity != opacity) {
+ VGfloat values[4];
+ values[0] = 1.0f;
+ values[1] = 1.0f;
+ values[2] = 1.0f;
+ values[3] = opacity;
+ d->paintOpacity = opacity;
+ vgSetParameterfv
+ (d->opacityPaint, VG_PAINT_COLOR, 4, values);
+ }
+ d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
+ } else {
+ d->setImageMode(VG_DRAW_IMAGE_NORMAL);
+ }
+ }
+
+ vgDrawImage(child);
+ }
+
+ // Destroy the cached child sub-images.
+ for (int i = 0; i < cachedImages.size(); ++i)
+ vgDestroyImage(cachedImages[i]);
+#else
+ QPaintEngineEx::drawPixmapFragments(drawingData, dataCount, pixmap, hints);
+#endif
+}
+
+QVGFontEngineCleaner::QVGFontEngineCleaner(QVGPaintEnginePrivate *d)
+ : QObject(), d_ptr(d)
+{
+}
+
+QVGFontEngineCleaner::~QVGFontEngineCleaner()
+{
+}
+
+void QVGFontEngineCleaner::fontEngineDestroyed()
+{
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ QFontEngine *engine = static_cast<QFontEngine *>(sender());
+ QVGFontCache::Iterator it = d_ptr->fontCache.find(engine);
+ if (it != d_ptr->fontCache.end()) {
+ delete it.value();
+ d_ptr->fontCache.erase(it);
+ }
+#endif
+}
+
+#if !defined(QVG_NO_DRAW_GLYPHS)
+
+QVGFontGlyphCache::QVGFontGlyphCache()
+{
+ font = vgCreateFont(0);
+ scaleX = scaleY = 0.0;
+ invertedGlyphs = false;
+ memset(cachedGlyphsMask, 0, sizeof(cachedGlyphsMask));
+}
+
+QVGFontGlyphCache::~QVGFontGlyphCache()
+{
+ if (font != VG_INVALID_HANDLE)
+ vgDestroyFont(font);
+}
+
+void QVGFontGlyphCache::setScaleFromText(const QFont &font, QFontEngine *fontEngine)
+{
+ QFontInfo fi(font);
+ qreal pixelSize = fi.pixelSize();
+ qreal emSquare = fontEngine->properties().emSquare.toReal();
+ scaleX = scaleY = static_cast<VGfloat>(pixelSize / emSquare);
+}
+
+void QVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d,
+ QFontEngine *fontEngine,
+ const glyph_t *g, int count)
+{
+ VGfloat origin[2];
+ VGfloat escapement[2];
+ glyph_metrics_t metrics;
+ // Some Qt font engines don't set yoff in getUnscaledGlyph().
+ // Zero the metric structure so that everything has a default value.
+ memset(&metrics, 0, sizeof(metrics));
+ while (count-- > 0) {
+ // Skip this glyph if we have already cached it before.
+ glyph_t glyph = *g++;
+ if (glyph < 256) {
+ if ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0)
+ continue;
+ cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32));
+ } else if (cachedGlyphs.contains(glyph)) {
+ continue;
+ } else {
+ cachedGlyphs.insert(glyph);
+ }
+#if !defined(QVG_NO_IMAGE_GLYPHS)
+ Q_UNUSED(d);
+ QImage scaledImage = fontEngine->alphaMapForGlyph(glyph);
+ VGImage vgImage = VG_INVALID_HANDLE;
+ metrics = fontEngine->boundingBox(glyph);
+ if (!scaledImage.isNull()) { // Not a space character
+ if (scaledImage.format() == QImage::Format_Indexed8) {
+ vgImage = vgCreateImage(VG_A_8, scaledImage.width(), scaledImage.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData(vgImage, scaledImage.constBits(), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height());
+ } else if (scaledImage.format() == QImage::Format_Mono) {
+ QImage img = scaledImage.convertToFormat(QImage::Format_Indexed8);
+ vgImage = vgCreateImage(VG_A_8, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height());
+ } else {
+ QImage img = scaledImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ vgImage = vgCreateImage(VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
+ vgImageSubData(vgImage, img.constBits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height());
+ }
+ }
+ origin[0] = -metrics.x.toReal();
+ origin[1] = -metrics.y.toReal();
+ escapement[0] = 0;
+ escapement[1] = 0;
+ vgSetGlyphToImage(font, glyph, vgImage, origin, escapement);
+ vgDestroyImage(vgImage); // Reduce reference count.
+#else
+ // Calculate the path for the glyph and cache it.
+ QPainterPath path;
+ fontEngine->getUnscaledGlyph(glyph, &path, &metrics);
+ VGPath vgPath;
+ if (!path.isEmpty()) {
+ vgPath = d->painterPathToVGPath(path);
+ } else {
+ // Probably a "space" character with no visible outline.
+ vgPath = VG_INVALID_HANDLE;
+ }
+ origin[0] = 0;
+ origin[1] = 0;
+ escapement[0] = 0;
+ escapement[1] = 0;
+ vgSetGlyphToPath(font, glyph, vgPath, VG_FALSE, origin, escapement);
+ vgDestroyPath(vgPath); // Reduce reference count.
+#endif // !defined(QVG_NO_IMAGE_GLYPHS)
+ }
+}
+
+#endif // !defined(QVG_NO_DRAW_GLYPHS)
+
+void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ Q_D(QVGPaintEngine);
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ // If we are not using a simple transform, then fall back
+ // to the default Qt path stroking algorithm.
+ if (!d->simpleTransform) {
+ QPaintEngineEx::drawTextItem(p, textItem);
+ return;
+ }
+
+ // Get the glyphs and positions associated with the text item.
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ if (!drawCachedGlyphs(glyphs.size(), glyphs.data(), ti.font(), ti.fontEngine, p, positions.data()))
+ QPaintEngineEx::drawTextItem(p, textItem);
+#else
+ // OpenGL 1.0 does not have support for VGFont and glyphs,
+ // so fall back to the default Qt path stroking algorithm.
+ QPaintEngineEx::drawTextItem(p, textItem);
+#endif
+}
+
+void QVGPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
+{
+ drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->font, textItem->fontEngine(),
+ QPointF(0, 0), textItem->glyphPositions);
+}
+
+ bool QVGPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFont &font,
+ QFontEngine *fontEngine, const QPointF &p,
+ const QFixedPoint *positions)
+ {
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ Q_D(QVGPaintEngine);
+
+ // Find the glyph cache for this font.
+ QVGFontCache::ConstIterator it = d->fontCache.constFind(fontEngine);
+ QVGFontGlyphCache *glyphCache;
+ if (it != d->fontCache.constEnd()) {
+ glyphCache = it.value();
+ } else {
+#ifdef Q_OS_SYMBIAN
+ glyphCache = new QSymbianVGFontGlyphCache();
+#else
+ glyphCache = new QVGFontGlyphCache();
+#endif
+ if (glyphCache->font == VG_INVALID_HANDLE) {
+ qWarning("QVGPaintEngine::drawTextItem: OpenVG fonts are not supported by the OpenVG engine");
+ delete glyphCache;
+ return false;
+ }
+ glyphCache->setScaleFromText(font, fontEngine);
+ d->fontCache.insert(fontEngine, glyphCache);
+ if (!d->fontEngineCleaner)
+ d->fontEngineCleaner = new QVGFontEngineCleaner(d);
+ QObject::connect(fontEngine, SIGNAL(destroyed()),
+ d->fontEngineCleaner, SLOT(fontEngineDestroyed()));
+ }
+
+ // Set the transformation to use for drawing the current glyphs.
+ QTransform glyphTransform(d->pathTransform);
+ if (d->transform.type() <= QTransform::TxTranslate) {
+ // Prevent blurriness of unscaled, unrotated text by forcing integer coordinates.
+ glyphTransform.translate(
+ floor(p.x() + glyphTransform.dx() + aliasedCoordinateDelta) - glyphTransform.dx(),
+ floor(p.y() - glyphTransform.dy() + aliasedCoordinateDelta) + glyphTransform.dy());
+ } else {
+ glyphTransform.translate(p.x(), p.y());
+ }
+#if defined(QVG_NO_IMAGE_GLYPHS)
+ glyphTransform.scale(glyphCache->scaleX, glyphCache->scaleY);
+#endif
+
+ // Some glyph caches can create the VGImage upright
+ if (glyphCache->invertedGlyphs)
+ glyphTransform.scale(1, -1);
+
+ d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, glyphTransform);
+
+ // Add the glyphs from the text item into the glyph cache.
+ glyphCache->cacheGlyphs(d, fontEngine, glyphs, numGlyphs);
+
+ // Create the array of adjustments between glyphs
+ QVarLengthArray<VGfloat> adjustments_x(numGlyphs);
+ QVarLengthArray<VGfloat> adjustments_y(numGlyphs);
+ for (int i = 1; i < numGlyphs; ++i) {
+ adjustments_x[i-1] = (positions[i].x - positions[i-1].x).round().toReal();
+ adjustments_y[i-1] = (positions[i].y - positions[i-1].y).round().toReal();
+ }
+
+ // Set the glyph drawing origin.
+ VGfloat origin[2];
+ origin[0] = positions[0].x.round().toReal();
+ origin[1] = positions[0].y.round().toReal();
+ vgSetfv(VG_GLYPH_ORIGIN, 2, origin);
+
+ // Fast anti-aliasing for paths, better for images.
+#if !defined(QVG_NO_IMAGE_GLYPHS)
+ d->setImageQuality(VG_IMAGE_QUALITY_BETTER);
+ d->setImageMode(VG_DRAW_IMAGE_STENCIL);
+#else
+ d->setRenderingQuality(VG_RENDERING_QUALITY_FASTER);
+#endif
+
+ // Draw the glyphs. We need to fill with the brush associated with
+ // the Qt pen, not the Qt brush.
+ d->ensureBrush(state()->pen.brush());
+ vgDrawGlyphs(glyphCache->font, numGlyphs, (VGuint*)glyphs,
+ adjustments_x.data(), adjustments_y.data(), VG_FILL_PATH, VG_TRUE);
+ return true;
+#else
+ Q_UNUSED(numGlyphs);
+ Q_UNUSED(glyphs);
+ Q_UNUSED(font);
+ Q_UNUSED(fontEngine);
+ Q_UNUSED(p);
+ Q_UNUSED(positions);
+ return false;
+#endif
+}
+
+void QVGPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QVGPaintEngine);
+ QPaintEngineEx::setState(s);
+ QVGPainterState *ps = static_cast<QVGPainterState *>(s);
+ if (ps->isNew) {
+ // Newly created state object. The call to setState()
+ // will either be followed by a call to begin(), or we are
+ // setting the state as part of a save().
+ ps->isNew = false;
+ } else {
+ // This state object was set as part of a restore().
+ restoreState(d->dirty);
+ d->dirty = ps->savedDirty;
+ }
+}
+
+void QVGPaintEngine::beginNativePainting()
+{
+ Q_D(QVGPaintEngine);
+
+ // About to enter raw VG mode: flush pending changes and make
+ // sure that all matrices are set to the current transformation.
+ QVGPainterState *s = this->state();
+ d->ensurePen(s->pen);
+ d->ensureBrush(s->brush);
+ d->ensurePathTransform();
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform);
+#if !defined(QVG_NO_DRAW_GLYPHS)
+ d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform);
+#endif
+ d->rawVG = true;
+}
+
+void QVGPaintEngine::endNativePainting()
+{
+ Q_D(QVGPaintEngine);
+ // Exiting raw VG mode: force all state values to be
+ // explicitly set on the VG engine to undo any changes
+ // that were made by the raw VG function calls.
+ QPaintEngine::DirtyFlags dirty = d->dirty;
+ d->clearModes();
+ d->forcePenChange = true;
+ d->forceBrushChange = true;
+ d->penType = (VGPaintType)0;
+ d->brushType = (VGPaintType)0;
+ d->clearColor = QColor();
+ d->fillPaint = d->brushPaint;
+ d->scissorDirty = true;
+ restoreState(QPaintEngine::AllDirty);
+ d->dirty = dirty;
+ d->rawVG = false;
+ vgSetPaint(d->penPaint, VG_STROKE_PATH);
+ vgSetPaint(d->brushPaint, VG_FILL_PATH);
+}
+
+QPixmapFilter *QVGPaintEngine::pixmapFilter(int type, const QPixmapFilter *prototype)
+{
+#if !defined(QT_SHIVAVG)
+ Q_D(QVGPaintEngine);
+ switch (type) {
+ case QPixmapFilter::ConvolutionFilter:
+ if (!d->convolutionFilter)
+ d->convolutionFilter.reset(new QVGPixmapConvolutionFilter);
+ return d->convolutionFilter.data();
+ case QPixmapFilter::ColorizeFilter:
+ if (!d->colorizeFilter)
+ d->colorizeFilter.reset(new QVGPixmapColorizeFilter);
+ return d->colorizeFilter.data();
+ case QPixmapFilter::DropShadowFilter:
+ if (!d->dropShadowFilter)
+ d->dropShadowFilter.reset(new QVGPixmapDropShadowFilter);
+ return d->dropShadowFilter.data();
+ case QPixmapFilter::BlurFilter:
+ if (!d->blurFilter)
+ d->blurFilter.reset(new QVGPixmapBlurFilter);
+ return d->blurFilter.data();
+ default: break;
+ }
+#endif
+ return QPaintEngineEx::pixmapFilter(type, prototype);
+}
+
+void QVGPaintEngine::restoreState(QPaintEngine::DirtyFlags dirty)
+{
+ Q_D(QVGPaintEngine);
+
+ // Restore the pen, brush, and other settings.
+ if ((dirty & QPaintEngine::DirtyBrushOrigin) != 0)
+ brushOriginChanged();
+ d->fillRule = 0;
+ if ((dirty & QPaintEngine::DirtyOpacity) != 0)
+ opacityChanged();
+ if ((dirty & QPaintEngine::DirtyTransform) != 0)
+ transformChanged();
+ if ((dirty & QPaintEngine::DirtyCompositionMode) != 0)
+ compositionModeChanged();
+ if ((dirty & QPaintEngine::DirtyHints) != 0)
+ renderHintsChanged();
+ if ((dirty & (QPaintEngine::DirtyClipRegion |
+ QPaintEngine::DirtyClipPath |
+ QPaintEngine::DirtyClipEnabled)) != 0) {
+ d->maskValid = false;
+ d->maskIsSet = false;
+ d->scissorMask = false;
+ d->maskRect = QRect();
+ d->scissorDirty = true;
+ clipEnabledChanged();
+ }
+
+#if defined(QVG_SCISSOR_CLIP)
+ if ((dirty & (QPaintEngine::DirtyClipRegion |
+ QPaintEngine::DirtyClipPath |
+ QPaintEngine::DirtyClipEnabled)) == 0) {
+ updateScissor();
+ }
+#else
+ updateScissor();
+#endif
+}
+
+void QVGPaintEngine::fillRegion
+ (const QRegion& region, const QColor& color, const QSize& surfaceSize)
+{
+ Q_D(QVGPaintEngine);
+ if (d->clearColor != color || d->clearOpacity != 1.0f) {
+ VGfloat values[4];
+ values[0] = color.redF();
+ values[1] = color.greenF();
+ values[2] = color.blueF();
+ values[3] = color.alphaF();
+ vgSetfv(VG_CLEAR_COLOR, 4, values);
+ d->clearColor = color;
+ d->clearOpacity = 1.0f;
+ }
+ if (region.rectCount() == 1) {
+ QRect r = region.boundingRect();
+ vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
+ r.width(), r.height());
+ } else {
+ const QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect r = rects.at(i);
+ vgClear(r.x(), surfaceSize.height() - r.y() - r.height(),
+ r.width(), r.height());
+ }
+ }
+}
+
+#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL)
+
+QVGCompositionHelper::QVGCompositionHelper()
+{
+ d = qt_vg_create_paint_engine()->vgPrivate();
+}
+
+QVGCompositionHelper::~QVGCompositionHelper()
+{
+}
+
+void QVGCompositionHelper::startCompositing(const QSize& screenSize)
+{
+ this->screenSize = screenSize;
+ clearScissor();
+ d->setBlendMode(VG_BLEND_SRC_OVER);
+}
+
+void QVGCompositionHelper::endCompositing()
+{
+ clearScissor();
+}
+
+void QVGCompositionHelper::blitWindow
+ (VGImage image, const QSize& imageSize,
+ const QRect& rect, const QPoint& topLeft, int opacity)
+{
+ if (image == VG_INVALID_HANDLE)
+ return;
+
+ // Determine which sub rectangle of the window to draw.
+ QRect sr = rect.translated(-topLeft);
+
+ if (opacity >= 255) {
+ // Fully opaque: use vgSetPixels() to directly copy the sub-region.
+ int y = screenSize.height() - (rect.bottom() + 1);
+ vgSetPixels(rect.x(), y, image, sr.x(),
+ imageSize.height() - (sr.y() + sr.height()),
+ sr.width(), sr.height());
+ } else {
+ // Extract the child image that we want to draw.
+ VGImage child;
+ if (sr.topLeft().isNull() && sr.size() == imageSize)
+ child = image;
+ else {
+ child = vgChildImage
+ (image, sr.x(), imageSize.height() - (sr.y() + sr.height()),
+ sr.width(), sr.height());
+ }
+
+ // Set the image transform.
+ QTransform transform;
+ int y = screenSize.height() - (rect.bottom() + 1);
+ transform.translate(rect.x() - 0.5f, y - 0.5f);
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+
+ // Enable opacity for image drawing if necessary.
+ if (opacity != d->paintOpacity) {
+ VGfloat values[4];
+ values[0] = 1.0f;
+ values[1] = 1.0f;
+ values[2] = 1.0f;
+ values[3] = ((VGfloat)opacity) / 255.0f;
+ vgSetParameterfv(d->opacityPaint, VG_PAINT_COLOR, 4, values);
+ d->paintOpacity = values[3];
+ }
+ if (d->fillPaint != d->opacityPaint) {
+ vgSetPaint(d->opacityPaint, VG_FILL_PATH);
+ d->fillPaint = d->opacityPaint;
+ }
+ d->setImageMode(VG_DRAW_IMAGE_MULTIPLY);
+
+ // Draw the child image.
+ vgDrawImage(child);
+
+ // Destroy the child image.
+ if(child != image)
+ vgDestroyImage(child);
+ }
+}
+
+static void fillBackgroundRect(const QRect& rect, QVGPaintEnginePrivate *d)
+{
+ VGfloat coords[8];
+ coords[0] = rect.x();
+ coords[1] = rect.y();
+ coords[2] = rect.x() + rect.width();
+ coords[3] = coords[1];
+ coords[4] = coords[2];
+ coords[5] = rect.y() + rect.height();
+ coords[6] = coords[0];
+ coords[7] = coords[5];
+#if !defined(QVG_NO_MODIFY_PATH)
+ vgModifyPathCoords(d->rectPath, 0, 4, coords);
+ vgDrawPath(d->rectPath, VG_FILL_PATH);
+#else
+ Q_UNUSED(d);
+ VGPath rectPath = vgCreatePath
+ (VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1.0f, // scale
+ 0.0f, // bias
+ 5, // segmentCapacityHint
+ 8, // coordCapacityHint
+ VG_PATH_CAPABILITY_ALL);
+ static VGubyte const segments[5] = {
+ VG_MOVE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_LINE_TO_ABS,
+ VG_CLOSE_PATH
+ };
+ vgAppendPathData(rectPath, 5, segments, coords);
+ vgDrawPath(rectPath, VG_FILL_PATH);
+ vgDestroyPath(rectPath);
+#endif
+}
+
+void QVGCompositionHelper::fillBackground
+ (const QRegion& region, const QBrush& brush)
+{
+ if (brush.style() == Qt::SolidPattern) {
+ // Use vgClear() to quickly fill the background.
+ QColor color = brush.color();
+ if (d->clearColor != color || d->clearOpacity != 1.0f) {
+ VGfloat values[4];
+ values[0] = color.redF();
+ values[1] = color.greenF();
+ values[2] = color.blueF();
+ values[3] = color.alphaF();
+ vgSetfv(VG_CLEAR_COLOR, 4, values);
+ d->clearColor = color;
+ d->clearOpacity = 1.0f;
+ }
+ if (region.rectCount() == 1) {
+ QRect r = region.boundingRect();
+ vgClear(r.x(), screenSize.height() - r.y() - r.height(),
+ r.width(), r.height());
+ } else {
+ const QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect r = rects.at(i);
+ vgClear(r.x(), screenSize.height() - r.y() - r.height(),
+ r.width(), r.height());
+ }
+ }
+
+ } else {
+ // Set the path transform to the default viewport transformation.
+ VGfloat devh = screenSize.height();
+ QTransform viewport(1.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f,
+ 0.0f, devh, 1.0f);
+ d->setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, viewport);
+
+ // Set the brush to use to fill the background.
+ d->ensureBrush(brush);
+ d->setFillRule(VG_EVEN_ODD);
+
+ if (region.rectCount() == 1) {
+ fillBackgroundRect(region.boundingRect(), d);
+ } else {
+ const QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ fillBackgroundRect(rects.at(i), d);
+ }
+
+ // We will need to reset the path transform during the next paint.
+ d->pathTransformSet = false;
+ }
+}
+
+void QVGCompositionHelper::drawCursorPixmap
+ (const QPixmap& pixmap, const QPoint& offset)
+{
+ VGImage vgImage = VG_INVALID_HANDLE;
+
+ // Fetch the VGImage from the pixmap if possible.
+ QPixmapData *pd = pixmap.pixmapData();
+ if (!pd)
+ return; // null QPixmap
+ if (pd->classId() == QPixmapData::OpenVGClass) {
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ if (vgpd->isValid())
+ vgImage = vgpd->toVGImage();
+ }
+
+ // Set the image transformation and modes.
+ VGfloat devh = screenSize.height();
+ QTransform transform(1.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f,
+ 0.0f, devh, 1.0f);
+ transform.translate(offset.x(), offset.y());
+ d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform);
+ d->setImageMode(VG_DRAW_IMAGE_NORMAL);
+
+ // Draw the VGImage.
+ if (vgImage != VG_INVALID_HANDLE) {
+ vgDrawImage(vgImage);
+ } else {
+ QImage img = pixmap.toImage().convertToFormat
+ (QImage::Format_ARGB32_Premultiplied);
+
+ vgImage = vgCreateImage
+ (VG_sARGB_8888_PRE, img.width(), img.height(),
+ VG_IMAGE_QUALITY_FASTER);
+ if (vgImage == VG_INVALID_HANDLE)
+ return;
+ vgImageSubData
+ (vgImage, img.constBits() + img.bytesPerLine() * (img.height() - 1),
+ -(img.bytesPerLine()), VG_sARGB_8888_PRE, 0, 0,
+ img.width(), img.height());
+
+ vgDrawImage(vgImage);
+ vgDestroyImage(vgImage);
+ }
+}
+
+void QVGCompositionHelper::setScissor(const QRegion& region)
+{
+ QVector<QRect> rects = region.rects();
+ int count = rects.count();
+ if (count > d->maxScissorRects)
+ count = d->maxScissorRects;
+ QVarLengthArray<VGint> params(count * 4);
+ int height = screenSize.height();
+ for (int i = 0; i < count; ++i) {
+ params[i * 4 + 0] = rects[i].x();
+ params[i * 4 + 1] = height - rects[i].y() - rects[i].height();
+ params[i * 4 + 2] = rects[i].width();
+ params[i * 4 + 3] = rects[i].height();
+ }
+
+ vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
+ vgSeti(VG_SCISSORING, VG_TRUE);
+ d->scissorDirty = false;
+ d->scissorActive = true;
+ d->scissorRegion = region;
+}
+
+void QVGCompositionHelper::clearScissor()
+{
+ if (d->scissorActive || d->scissorDirty) {
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ d->scissorActive = false;
+ d->scissorDirty = false;
+ }
+}
+
+#endif // !QVG_NO_SINGLE_CONTEXT && !QT_NO_EGL
+
+VGImageFormat qt_vg_image_to_vg_format(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_MonoLSB:
+ return VG_BW_1;
+ case QImage::Format_Indexed8:
+ return VG_sL_8;
+ case QImage::Format_ARGB32_Premultiplied:
+ return VG_sARGB_8888_PRE;
+ case QImage::Format_RGB32:
+ return VG_sXRGB_8888;
+ case QImage::Format_ARGB32:
+ return VG_sARGB_8888;
+ case QImage::Format_RGB16:
+ return VG_sRGB_565;
+ case QImage::Format_ARGB4444_Premultiplied:
+ return VG_sARGB_4444;
+ default:
+ break;
+ }
+ return VG_sARGB_8888; // XXX
+}
+
+QT_END_NAMESPACE
+
+#include "qpaintengine_vg.moc"
diff --git a/src/openvg/qpaintengine_vg_p.h b/src/openvg/qpaintengine_vg_p.h
new file mode 100644
index 0000000000..1e9103bc81
--- /dev/null
+++ b/src/openvg/qpaintengine_vg_p.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_VG_P_H
+#define QPAINTENGINE_VG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpaintengineex_p.h>
+#include <QtGui/private/qtextureglyphcache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QFixedPoint;
+class QVGPaintEnginePrivate;
+class QPixmapData;
+class QVGEGLWindowSurfacePrivate;
+
+class Q_OPENVG_EXPORT QVGPainterState : public QPainterState
+{
+public:
+ QVGPainterState(QVGPainterState& other);
+ QVGPainterState();
+ ~QVGPainterState();
+
+ bool isNew;
+ QRegion clipRegion;
+ QPaintEngine::DirtyFlags savedDirty;
+};
+
+class Q_OPENVG_EXPORT QVGPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QVGPaintEngine)
+public:
+ QVGPaintEngine();
+ ~QVGPaintEngine();
+
+ Type type() const { return OpenVG; }
+
+ QPainterState *createState(QPainterState *orig) const;
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void draw(const QVectorPath &path);
+ void fill(const QVectorPath &path, const QBrush &brush);
+ void stroke(const QVectorPath &path, const QPen &pen);
+
+ void clip(const QVectorPath &path, Qt::ClipOperation op);
+ void clip(const QRect &rect, Qt::ClipOperation op);
+ void clip(const QRegion &region, Qt::ClipOperation op);
+ void clip(const QPainterPath &path, Qt::ClipOperation op);
+
+ void clipEnabledChanged();
+ void penChanged();
+ void brushChanged();
+ void brushOriginChanged();
+ void opacityChanged();
+ void compositionModeChanged();
+ void renderHintsChanged();
+ void transformChanged();
+
+ void fillRect(const QRectF &rect, const QBrush &brush);
+ void fillRect(const QRectF &rect, const QColor &color);
+
+ void drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawLines(const QLine *lines, int lineCount);
+ void drawLines(const QLineF *lines, int lineCount);
+
+ void drawEllipse(const QRectF &r);
+ void drawEllipse(const QRect &r);
+
+ void drawPath(const QPainterPath &path);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawPoints(const QPoint *points, int pointCount);
+
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawPixmap(const QPointF &pos, const QPixmap &pm);
+
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ void drawImage(const QPointF &pos, const QImage &image);
+
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ void drawPixmapFragments(const QPainter::PixmapFragment *drawingData, int dataCount, const QPixmap &pixmap,
+ QFlags<QPainter::PixmapFragmentHint> hints);
+
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ void drawStaticTextItem(QStaticTextItem *staticTextItem);
+ bool drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFont &font,
+ QFontEngine *fontEngine, const QPointF &p,
+ const QFixedPoint *positions);
+
+ void setState(QPainterState *s);
+ QVGPainterState *state() { return static_cast<QVGPainterState *>(QPaintEngineEx::state()); }
+ const QVGPainterState *state() const { return static_cast<const QVGPainterState *>(QPaintEngineEx::state()); }
+
+ void beginNativePainting();
+ void endNativePainting();
+
+ QPixmapFilter *pixmapFilter(int type, const QPixmapFilter *prototype);
+
+ QVGPaintEnginePrivate *vgPrivate() { Q_D(QVGPaintEngine); return d; }
+
+ void fillRegion(const QRegion& region, const QColor& color, const QSize& surfaceSize);
+
+protected:
+ QVGPaintEngine(QVGPaintEnginePrivate &data);
+
+private:
+ void restoreState(QPaintEngine::DirtyFlags dirty);
+ void updateScissor();
+ QRegion defaultClipRegion();
+ bool isDefaultClipRegion(const QRegion& region);
+ bool isDefaultClipRect(const QRect& rect);
+ bool clearRect(const QRectF &rect, const QColor &color);
+ bool canVgWritePixels(const QImage &image) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp
new file mode 100644
index 0000000000..ea93748685
--- /dev/null
+++ b/src/openvg/qpixmapdata_vg.cpp
@@ -0,0 +1,575 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdata_vg_p.h"
+#include "qpaintengine_vg_p.h"
+#include <QtGui/private/qdrawhelper_p.h>
+#if !defined(QT_NO_EGL)
+#include <QtGui/private/qegl_p.h>
+#endif
+#include "qvg_p.h"
+#include "qvgimagepool_p.h"
+#include <QBuffer>
+#include <QImageReader>
+#include <QtGui/private/qimage_p.h>
+#include <QtGui/private/qnativeimagehandleprovider_p.h>
+#include <QtGui/private/qfont_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static int qt_vg_pixmap_serial = 0;
+
+QVGPixmapData::QVGPixmapData(PixelType type)
+ : QPixmapData(type, OpenVGClass)
+{
+ Q_ASSERT(type == QPixmapData::PixmapType);
+ vgImage = VG_INVALID_HANDLE;
+ vgImageOpacity = VG_INVALID_HANDLE;
+ cachedOpacity = 1.0f;
+ recreate = true;
+ inImagePool = false;
+ inLRU = false;
+ failedToAlloc = false;
+#if defined(Q_OS_SYMBIAN)
+ nativeImageHandleProvider = 0;
+ nativeImageHandle = 0;
+#endif
+#if !defined(QT_NO_EGL)
+ context = 0;
+ qt_vg_register_pixmap(this);
+#endif
+ updateSerial();
+}
+
+QVGPixmapData::~QVGPixmapData()
+{
+ destroyImageAndContext();
+#if !defined(QT_NO_EGL)
+ qt_vg_unregister_pixmap(this);
+#endif
+}
+
+void QVGPixmapData::destroyImages()
+{
+ if (inImagePool) {
+ QVGImagePool *pool = QVGImagePool::instance();
+ if (vgImage != VG_INVALID_HANDLE)
+ pool->releaseImage(this, vgImage);
+ if (vgImageOpacity != VG_INVALID_HANDLE)
+ pool->releaseImage(this, vgImageOpacity);
+ } else {
+ if (vgImage != VG_INVALID_HANDLE)
+ vgDestroyImage(vgImage);
+ if (vgImageOpacity != VG_INVALID_HANDLE)
+ vgDestroyImage(vgImageOpacity);
+ }
+ vgImage = VG_INVALID_HANDLE;
+ vgImageOpacity = VG_INVALID_HANDLE;
+ inImagePool = false;
+
+#if defined(Q_OS_SYMBIAN)
+ releaseNativeImageHandle();
+#endif
+}
+
+void QVGPixmapData::destroyImageAndContext()
+{
+ if (vgImage != VG_INVALID_HANDLE) {
+ // We need to have a context current to destroy the image.
+#if !defined(QT_NO_EGL)
+ if (!context)
+ context = qt_vg_create_context(0, QInternal::Pixmap);
+ if (context->isCurrent()) {
+ destroyImages();
+ } else {
+ // We don't currently have a widget surface active, but we
+ // need a surface to make the context current. So use the
+ // shared pbuffer surface instead.
+ context->makeCurrent(qt_vg_shared_surface());
+ destroyImages();
+ context->lazyDoneCurrent();
+ }
+#else
+ destroyImages();
+#endif
+ } else {
+#if defined(Q_OS_SYMBIAN)
+ releaseNativeImageHandle();
+#endif
+ }
+#if !defined(QT_NO_EGL)
+ if (context) {
+ qt_vg_destroy_context(context, QInternal::Pixmap);
+ context = 0;
+ }
+#endif
+ recreate = true;
+}
+
+QPixmapData *QVGPixmapData::createCompatiblePixmapData() const
+{
+ return new QVGPixmapData(pixelType());
+}
+
+bool QVGPixmapData::isValid() const
+{
+ return (w > 0 && h > 0);
+}
+
+void QVGPixmapData::updateSerial()
+{
+ setSerialNumber(++qt_vg_pixmap_serial);
+}
+
+void QVGPixmapData::resize(int wid, int ht)
+{
+ if (w == wid && h == ht) {
+ updateSerial();
+ return;
+ }
+
+ w = wid;
+ h = ht;
+ d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
+ is_null = (w <= 0 || h <= 0);
+ source = QVolatileImage();
+ recreate = true;
+
+ updateSerial();
+}
+
+void QVGPixmapData::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return;
+
+ QImage img = image;
+ createPixmapForImage(img, flags, false);
+}
+
+void QVGPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
+
+ createPixmapForImage(image, flags, true);
+}
+
+bool QVGPixmapData::fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = QImageReader(filename, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+bool QVGPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+QImage::Format QVGPixmapData::idealFormat(QImage *image, Qt::ImageConversionFlags flags) const
+{
+ QImage::Format format = sourceFormat();
+ int d = image->depth();
+ if (d == 1 || d == 16 || d == 24 || (d == 32 && !image->hasAlphaChannel()))
+ format = QImage::Format_RGB32;
+ else if (!(flags & Qt::NoOpaqueDetection) && image->data_ptr()->checkForAlphaPixels())
+ format = sourceFormat();
+ else
+ format = image->hasAlphaChannel() ? sourceFormat() : QImage::Format_RGB32;
+ return format;
+}
+
+void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
+{
+ resize(image.width(), image.height());
+
+ QImage::Format format = idealFormat(&image, flags);
+
+ if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
+ source = QVolatileImage(image);
+ } else {
+ QImage convertedImage = image.convertToFormat(format);
+ // convertToFormat won't detach the image if format stays the
+ // same. Detaching is needed to prevent issues with painting
+ // onto this QPixmap later on.
+ convertedImage.detach();
+ source = QVolatileImage(convertedImage);
+ }
+
+ recreate = true;
+}
+
+void QVGPixmapData::fill(const QColor &color)
+{
+ if (!isValid())
+ return;
+ forceToImage();
+ if (source.depth() == 1) {
+ // Pick the best approximate color in the image's colortable.
+ int gray = qGray(color.rgba());
+ if (qAbs(qGray(source.imageRef().color(0)) - gray)
+ < qAbs(qGray(source.imageRef().color(1)) - gray))
+ source.fill(0);
+ else
+ source.fill(1);
+ } else {
+ source.fill(PREMUL(color.rgba()));
+ }
+}
+
+bool QVGPixmapData::hasAlphaChannel() const
+{
+ ensureReadback(true);
+ if (!source.isNull())
+ return source.hasAlphaChannel();
+ else
+ return isValid();
+}
+
+void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ if (!isValid())
+ return;
+ forceToImage();
+ source.setAlphaChannel(alphaChannel);
+}
+
+QImage QVGPixmapData::toImage() const
+{
+ if (!isValid())
+ return QImage();
+ ensureReadback(true);
+ if (source.isNull()) {
+ source = QVolatileImage(w, h, sourceFormat());
+ recreate = true;
+ }
+ return source.toImage();
+}
+
+void QVGPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ // toImage() is potentially expensive with QVolatileImage so provide a
+ // more efficient implementation of copy() that does not rely on it.
+ if (!data) {
+ return;
+ }
+ if (data->classId() != OpenVGClass) {
+ fromImage(data->toImage(rect), Qt::NoOpaqueDetection);
+ return;
+ }
+ const QVGPixmapData *pd = static_cast<const QVGPixmapData *>(data);
+ QRect r = rect;
+ if (r.isNull() || r.contains(QRect(0, 0, pd->w, pd->h))) {
+ r = QRect(0, 0, pd->w, pd->h);
+ }
+ resize(r.width(), r.height());
+ recreate = true;
+ if (!pd->source.isNull()) {
+ source = QVolatileImage(r.width(), r.height(), pd->source.format());
+ source.copyFrom(&pd->source, r);
+ }
+}
+
+QImage *QVGPixmapData::buffer()
+{
+ // Cannot be safely implemented and QVGPixmapData is not (must not be) RasterClass anyway.
+ return 0;
+}
+
+QPaintEngine* QVGPixmapData::paintEngine() const
+{
+ // If the application wants to paint into the QPixmap, we first
+ // force it to QImage format and then paint into that.
+ // This is simpler than juggling multiple VG contexts.
+ const_cast<QVGPixmapData *>(this)->forceToImage();
+ return source.paintEngine();
+}
+
+VGImage QVGPixmapData::toVGImage()
+{
+ if (!isValid() || failedToAlloc)
+ return VG_INVALID_HANDLE;
+
+#if !defined(QT_NO_EGL)
+ // Increase the reference count on the shared context.
+ if (!context)
+ context = qt_vg_create_context(0, QInternal::Pixmap);
+#endif
+
+ if (recreate && prevSize != QSize(w, h))
+ destroyImages();
+ else if (recreate)
+ cachedOpacity = -1.0f; // Force opacity image to be refreshed later.
+
+#if defined(Q_OS_SYMBIAN)
+ if (recreate && nativeImageHandleProvider && !nativeImageHandle) {
+ createFromNativeImageHandleProvider();
+ }
+#endif
+
+ if (vgImage == VG_INVALID_HANDLE) {
+ vgImage = QVGImagePool::instance()->createImageForPixmap
+ (qt_vg_image_to_vg_format(source.format()), w, h, VG_IMAGE_QUALITY_FASTER, this);
+
+ // Bail out if we run out of GPU memory - try again next time.
+ if (vgImage == VG_INVALID_HANDLE) {
+ failedToAlloc = true;
+ return VG_INVALID_HANDLE;
+ }
+
+ inImagePool = true;
+ } else if (inImagePool) {
+ QVGImagePool::instance()->useImage(this);
+ }
+
+ if (!source.isNull() && recreate) {
+ source.beginDataAccess();
+ vgImageSubData
+ (vgImage,
+ source.constBits(), source.bytesPerLine(),
+ qt_vg_image_to_vg_format(source.format()), 0, 0, w, h);
+ source.endDataAccess(true);
+ }
+
+ recreate = false;
+ prevSize = QSize(w, h);
+
+ return vgImage;
+}
+
+VGImage QVGPixmapData::toVGImage(qreal opacity)
+{
+#if !defined(QT_SHIVAVG)
+ // Force the primary VG image to be recreated if necessary.
+ if (toVGImage() == VG_INVALID_HANDLE)
+ return VG_INVALID_HANDLE;
+
+ if (opacity == 1.0f)
+ return vgImage;
+
+ // Create an alternative image for the selected opacity.
+ if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) {
+ if (vgImageOpacity == VG_INVALID_HANDLE) {
+ if (inImagePool) {
+ vgImageOpacity = QVGImagePool::instance()->createImageForPixmap
+ (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
+ } else {
+ vgImageOpacity = vgCreateImage
+ (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER);
+ }
+
+ // Bail out if we run out of GPU memory - try again next time.
+ if (vgImageOpacity == VG_INVALID_HANDLE)
+ return VG_INVALID_HANDLE;
+ }
+ VGfloat matrix[20] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, opacity,
+ 0.0f, 0.0f, 0.0f, 0.0f
+ };
+ vgColorMatrix(vgImageOpacity, vgImage, matrix);
+ cachedOpacity = opacity;
+ }
+
+ return vgImageOpacity;
+#else
+ // vgColorMatrix() doesn't work with ShivaVG, so ignore the opacity.
+ Q_UNUSED(opacity);
+ return toVGImage();
+#endif
+}
+
+void QVGPixmapData::detachImageFromPool()
+{
+ if (inImagePool) {
+ QVGImagePool::instance()->detachImage(this);
+ inImagePool = false;
+ }
+}
+
+void QVGPixmapData::hibernate()
+{
+ // If the image was imported (e.g, from an SgImage under Symbian), then
+ // skip the hibernation, there is no sense in copying it back to main
+ // memory because the data is most likely shared between several processes.
+ bool skipHibernate = (vgImage != VG_INVALID_HANDLE && source.isNull());
+#if defined(Q_OS_SYMBIAN)
+ // However we have to proceed normally if the image was retrieved via
+ // a handle provider.
+ skipHibernate &= !nativeImageHandleProvider;
+#endif
+ if (skipHibernate)
+ return;
+
+ forceToImage(false); // no readback allowed here
+ destroyImageAndContext();
+}
+
+void QVGPixmapData::reclaimImages()
+{
+ if (!inImagePool)
+ return;
+ forceToImage();
+ destroyImages();
+}
+
+int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 0;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(w * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(h * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QVGPixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+// Ensures that the pixmap is backed by some valid data and forces the data to
+// be re-uploaded to the VGImage when toVGImage() is called next time.
+void QVGPixmapData::forceToImage(bool allowReadback)
+{
+ if (!isValid())
+ return;
+
+ if (allowReadback)
+ ensureReadback(false);
+
+ if (source.isNull())
+ source = QVolatileImage(w, h, sourceFormat());
+
+ recreate = true;
+}
+
+void QVGPixmapData::ensureReadback(bool readOnly) const
+{
+ if (vgImage != VG_INVALID_HANDLE && source.isNull()) {
+ source = QVolatileImage(w, h, sourceFormat());
+ source.beginDataAccess();
+ vgGetImageSubData(vgImage, source.bits(), source.bytesPerLine(),
+ qt_vg_image_to_vg_format(source.format()),
+ 0, 0, w, h);
+ source.endDataAccess();
+ if (readOnly) {
+ recreate = false;
+ } else {
+ // Once we did a readback, the original VGImage must be destroyed
+ // because it may be shared (e.g. created via SgImage) and a subsequent
+ // upload of the image data may produce unexpected results.
+ const_cast<QVGPixmapData *>(this)->destroyImages();
+#if defined(Q_OS_SYMBIAN)
+ // There is now an own copy of the data so drop the handle provider,
+ // otherwise toVGImage() would request the handle again, which is wrong.
+ nativeImageHandleProvider = 0;
+#endif
+ recreate = true;
+ }
+ }
+}
+
+QImage::Format QVGPixmapData::sourceFormat() const
+{
+ return QImage::Format_ARGB32_Premultiplied;
+}
+
+/*
+ \internal
+
+ Returns the VGImage that is storing the contents of \a pixmap.
+ Returns VG_INVALID_HANDLE if \a pixmap is not owned by the OpenVG
+ graphics system or \a pixmap is invalid.
+
+ This function is typically used to access the backing store
+ for a pixmap when executing raw OpenVG calls. It must only
+ be used when a QPainter is active and the OpenVG paint engine
+ is in use by the QPainter.
+
+ \sa {QtOpenVG Module}
+*/
+Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap)
+{
+ QPixmapData *pd = pixmap.pixmapData();
+ if (!pd)
+ return VG_INVALID_HANDLE; // null QPixmap
+ if (pd->classId() == QPixmapData::OpenVGClass) {
+ QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
+ if (vgpd->isValid())
+ return vgpd->toVGImage();
+ }
+ return VG_INVALID_HANDLE;
+}
+
+QT_END_NAMESPACE
diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h
new file mode 100644
index 0000000000..c28459674d
--- /dev/null
+++ b/src/openvg/qpixmapdata_vg_p.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_VG_P_H
+#define QPIXMAPDATA_VG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmap_raster_p.h>
+#include <QtGui/private/qvolatileimage_p.h>
+#include "qvg_p.h"
+
+#if defined(Q_OS_SYMBIAN)
+class RSGImage;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QEglContext;
+class QVGImagePool;
+class QImageReader;
+
+#if !defined(QT_NO_EGL)
+class QVGPixmapData;
+class QVGSharedContext;
+
+void qt_vg_register_pixmap(QVGPixmapData *pd);
+void qt_vg_unregister_pixmap(QVGPixmapData *pd);
+void qt_vg_hibernate_pixmaps(QVGSharedContext *context);
+#endif
+
+class QNativeImageHandleProvider;
+
+class Q_OPENVG_EXPORT QVGPixmapData : public QPixmapData
+{
+public:
+ QVGPixmapData(PixelType type);
+ ~QVGPixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ // Is this pixmap valid (i.e. non-zero in size)?
+ bool isValid() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags);
+ bool fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags);
+ bool fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags);
+
+ void fill(const QColor &color);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QImage toImage() const;
+ void copy(const QPixmapData *data, const QRect &rect);
+ QImage *buffer();
+ QPaintEngine* paintEngine() const;
+
+ // Return the VGImage form of this pixmap, creating it if necessary.
+ // This assumes that there is a VG context current.
+ virtual VGImage toVGImage();
+
+ // Return the VGImage form for a specific opacity setting.
+ virtual VGImage toVGImage(qreal opacity);
+
+ // Detach this image from the image pool.
+ virtual void detachImageFromPool();
+
+ // Release the VG resources associated with this pixmap and copy
+ // the pixmap's contents out of the GPU back into main memory.
+ // The VG resource will be automatically recreated the next time
+ // toVGImage() is called. Does nothing if the pixmap cannot be
+ // hibernated for some reason (e.g. VGImage is shared with another
+ // process via a SgImage).
+ virtual void hibernate();
+
+ // Called when the QVGImagePool wants to reclaim this pixmap's
+ // VGImage objects to reuse storage.
+ virtual void reclaimImages();
+
+ // If vgImage is valid but source is null, copies pixel data from GPU back
+ // into main memory and destroys vgImage. For a normal pixmap this function
+ // does nothing, however if the pixmap was created directly from a VGImage
+ // (e.g. via SgImage on Symbian) then by doing the readback this ensures
+ // that QImage-based functions can operate too.
+ virtual void ensureReadback(bool readOnly) const;
+
+ QSize size() const { return QSize(w, h); }
+
+#if defined(Q_OS_SYMBIAN)
+ void* toNativeType(NativeType type);
+ void fromNativeType(void* pixmap, NativeType type);
+ bool initFromNativeImageHandle(void *handle, const QString &type);
+ void createFromNativeImageHandleProvider();
+ void releaseNativeImageHandle();
+#endif
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace);
+
+#if defined(Q_OS_SYMBIAN)
+ void cleanup();
+#endif
+
+private:
+ QVGPixmapData *nextLRU;
+ QVGPixmapData *prevLRU;
+ bool inLRU;
+ bool failedToAlloc;
+ friend class QVGImagePool;
+ friend class QVGPaintEngine;
+
+#if !defined(QT_NO_EGL)
+ QVGPixmapData *next;
+ QVGPixmapData *prev;
+
+ friend void qt_vg_register_pixmap(QVGPixmapData *pd);
+ friend void qt_vg_unregister_pixmap(QVGPixmapData *pd);
+ friend void qt_vg_hibernate_pixmaps(QVGSharedContext *context);
+#endif
+
+protected:
+ QSize prevSize;
+ VGImage vgImage;
+ VGImage vgImageOpacity;
+ qreal cachedOpacity;
+ mutable QVolatileImage source;
+ mutable bool recreate;
+ bool inImagePool;
+#if !defined(QT_NO_EGL)
+ mutable QEglContext *context;
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ mutable QNativeImageHandleProvider *nativeImageHandleProvider;
+ void *nativeImageHandle;
+ QString nativeImageType;
+#endif
+
+ void forceToImage(bool allowReadback = true);
+ QImage::Format sourceFormat() const;
+ QImage::Format idealFormat(QImage *image, Qt::ImageConversionFlags flags) const;
+ void updateSerial();
+
+ void destroyImageAndContext();
+ void destroyImages();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qpixmapfilter_vg.cpp b/src/openvg/qpixmapfilter_vg.cpp
new file mode 100644
index 0000000000..7d323f2a86
--- /dev/null
+++ b/src/openvg/qpixmapfilter_vg.cpp
@@ -0,0 +1,356 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapfilter_vg_p.h"
+#include "qvgimagepool_p.h"
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_SHIVAVG)
+
+QVGPixmapConvolutionFilter::QVGPixmapConvolutionFilter()
+ : QPixmapConvolutionFilter()
+{
+}
+
+QVGPixmapConvolutionFilter::~QVGPixmapConvolutionFilter()
+{
+}
+
+extern void qt_vg_drawVGImage
+ (QPainter *painter, const QPointF& pos, VGImage vgImg);
+extern void qt_vg_drawVGImageStencil
+ (QPainter *painter, const QPointF& pos, VGImage vgImg, const QBrush& brush);
+
+void QVGPixmapConvolutionFilter::draw
+ (QPainter *painter, const QPointF &dest,
+ const QPixmap &src, const QRectF &srcRect) const
+{
+ if (src.isNull())
+ return;
+
+ if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) {
+ // The pixmap data is not an instance of QVGPixmapData, so fall
+ // back to the default convolution filter implementation.
+ QPixmapConvolutionFilter::draw(painter, dest, src, srcRect);
+ return;
+ }
+
+ QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData());
+
+ VGImage srcImage = pd->toVGImage();
+ if (srcImage == VG_INVALID_HANDLE)
+ return;
+
+ QSize size = pd->size();
+ VGImage dstImage = QVGImagePool::instance()->createTemporaryImage
+ (VG_sARGB_8888_PRE, size.width(), size.height(),
+ VG_IMAGE_QUALITY_FASTER, pd);
+ if (dstImage == VG_INVALID_HANDLE)
+ return;
+
+ int kernelWidth = rows();
+ int kernelHeight = columns();
+ const qreal *kern = convolutionKernel();
+ QVarLengthArray<VGshort> kernel;
+ for (int i = 0; i < kernelWidth; ++i) {
+ for (int j = 0; j < kernelHeight; ++j) {
+ kernel.append((VGshort)(kern[j * kernelWidth + i] * 1024.0f));
+ }
+ }
+
+ VGfloat values[4];
+ values[0] = 0.0f;
+ values[1] = 0.0f;
+ values[2] = 0.0f;
+ values[3] = 0.0f;
+ vgSetfv(VG_TILE_FILL_COLOR, 4, values);
+
+ vgConvolve(dstImage, srcImage,
+ kernelWidth, kernelHeight, 0, 0,
+ kernel.constData(), 1.0f / 1024.0f, 0.0f,
+ VG_TILE_FILL);
+
+ VGImage child = VG_INVALID_HANDLE;
+
+ if (srcRect.isNull() ||
+ (srcRect.topLeft().isNull() && srcRect.size() == size)) {
+ child = dstImage;
+ } else {
+ QRect src = srcRect.toRect();
+ child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height());
+ }
+
+ qt_vg_drawVGImage(painter, dest, child);
+
+ if(child != dstImage)
+ vgDestroyImage(child);
+ QVGImagePool::instance()->releaseImage(0, dstImage);
+}
+
+QVGPixmapColorizeFilter::QVGPixmapColorizeFilter()
+ : QPixmapColorizeFilter()
+{
+}
+
+QVGPixmapColorizeFilter::~QVGPixmapColorizeFilter()
+{
+}
+
+void QVGPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
+{
+ if (src.isNull())
+ return;
+
+ if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) {
+ // The pixmap data is not an instance of QVGPixmapData, so fall
+ // back to the default colorize filter implementation.
+ QPixmapColorizeFilter::draw(painter, dest, src, srcRect);
+ return;
+ }
+
+ QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData());
+
+ VGImage srcImage = pd->toVGImage();
+ if (srcImage == VG_INVALID_HANDLE)
+ return;
+
+ QSize size = pd->size();
+ VGImage dstImage = QVGImagePool::instance()->createTemporaryImage
+ (VG_sARGB_8888_PRE, size.width(), size.height(),
+ VG_IMAGE_QUALITY_FASTER, pd);
+ if (dstImage == VG_INVALID_HANDLE)
+ return;
+
+ // Determine the weights for the matrix from the color and strength.
+ QColor c = color();
+ VGfloat strength = this->strength();
+ VGfloat weights[3];
+ VGfloat invweights[3];
+ VGfloat alpha = c.alphaF();
+ weights[0] = c.redF() * alpha;
+ weights[1] = c.greenF() * alpha;
+ weights[2] = c.blueF() * alpha;
+ invweights[0] = (1.0f - weights[0]) * strength;
+ invweights[1] = (1.0f - weights[1]) * strength;
+ invweights[2] = (1.0f - weights[2]) * strength;
+
+ // Grayscale weights.
+ static const VGfloat redGray = 11.0f / 32.0f;
+ static const VGfloat greenGray = 16.0f / 32.0f;
+ static const VGfloat blueGray = 1.0f - (redGray + greenGray);
+
+ VGfloat matrix[5][4];
+ matrix[0][0] = redGray * invweights[0] + (1.0f - strength);
+ matrix[0][1] = redGray * invweights[1];
+ matrix[0][2] = redGray * invweights[2];
+ matrix[0][3] = 0.0f;
+ matrix[1][0] = greenGray * invweights[0];
+ matrix[1][1] = greenGray * invweights[1] + (1.0f - strength);
+ matrix[1][2] = greenGray * invweights[2];
+ matrix[1][3] = 0.0f;
+ matrix[2][0] = blueGray * invweights[0];
+ matrix[2][1] = blueGray * invweights[1];
+ matrix[2][2] = blueGray * invweights[2] + (1.0f - strength);
+ matrix[2][3] = 0.0f;
+ matrix[3][0] = 0.0f;
+ matrix[3][1] = 0.0f;
+ matrix[3][2] = 0.0f;
+ matrix[3][3] = 1.0f;
+ matrix[4][0] = weights[0] * strength;
+ matrix[4][1] = weights[1] * strength;
+ matrix[4][2] = weights[2] * strength;
+ matrix[4][3] = 0.0f;
+
+ vgColorMatrix(dstImage, srcImage, matrix[0]);
+
+ VGImage child = VG_INVALID_HANDLE;
+
+ if (srcRect.isNull() ||
+ (srcRect.topLeft().isNull() && srcRect.size() == size)) {
+ child = dstImage;
+ } else {
+ QRect src = srcRect.toRect();
+ child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height());
+ }
+
+ qt_vg_drawVGImage(painter, dest, child);
+
+ if(child != dstImage)
+ vgDestroyImage(child);
+ QVGImagePool::instance()->releaseImage(0, dstImage);
+}
+
+QVGPixmapDropShadowFilter::QVGPixmapDropShadowFilter()
+ : QPixmapDropShadowFilter()
+{
+}
+
+QVGPixmapDropShadowFilter::~QVGPixmapDropShadowFilter()
+{
+}
+
+void QVGPixmapDropShadowFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
+{
+ if (src.isNull())
+ return;
+
+ if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) {
+ // The pixmap data is not an instance of QVGPixmapData, so fall
+ // back to the default drop shadow filter implementation.
+ QPixmapDropShadowFilter::draw(painter, dest, src, srcRect);
+ return;
+ }
+
+ QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData());
+
+ VGImage srcImage = pd->toVGImage();
+ if (srcImage == VG_INVALID_HANDLE)
+ return;
+
+ QSize size = pd->size();
+ VGImage dstImage = QVGImagePool::instance()->createTemporaryImage
+ (VG_A_8, size.width(), size.height(),
+ VG_IMAGE_QUALITY_FASTER, pd);
+ if (dstImage == VG_INVALID_HANDLE)
+ return;
+
+ // Clamp the radius range. We divide by 2 because the OpenVG blur
+ // is "too blurry" compared to the default raster implementation.
+ VGfloat maxRadius = VGfloat(vgGeti(VG_MAX_GAUSSIAN_STD_DEVIATION));
+ VGfloat radiusF = VGfloat(blurRadius()) / 2.0f;
+ if (radiusF < 0.001f)
+ radiusF = 0.001f;
+ else if (radiusF > maxRadius)
+ radiusF = maxRadius;
+
+ // Blur the blackened source image.
+ vgGaussianBlur(dstImage, srcImage, radiusF, radiusF, VG_TILE_PAD);
+
+ VGImage child = VG_INVALID_HANDLE;
+
+ QRect srect;
+ if (srcRect.isNull() ||
+ (srcRect.topLeft().isNull() && srcRect.size() == size)) {
+ child = dstImage;
+ srect = QRect(0, 0, size.width(), size.height());
+ } else {
+ srect = srcRect.toRect();
+ child = vgChildImage(dstImage, srect.x(), srect.y(), srect.width(), srect.height());
+ }
+
+ qt_vg_drawVGImageStencil(painter, dest + offset(), child, color());
+
+ if(child != dstImage)
+ vgDestroyImage(child);
+ QVGImagePool::instance()->releaseImage(0, dstImage);
+
+ // Now draw the actual pixmap over the top.
+ painter->drawPixmap(dest, src, srect);
+}
+
+QVGPixmapBlurFilter::QVGPixmapBlurFilter(QObject *parent)
+ : QPixmapBlurFilter(parent)
+{
+}
+
+QVGPixmapBlurFilter::~QVGPixmapBlurFilter()
+{
+}
+
+void QVGPixmapBlurFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
+{
+ if (src.isNull())
+ return;
+
+ if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) {
+ // The pixmap data is not an instance of QVGPixmapData, so fall
+ // back to the default blur filter implementation.
+ QPixmapBlurFilter::draw(painter, dest, src, srcRect);
+ return;
+ }
+
+ QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData());
+
+ VGImage srcImage = pd->toVGImage();
+ if (srcImage == VG_INVALID_HANDLE)
+ return;
+
+ QSize size = pd->size();
+ VGImage dstImage = QVGImagePool::instance()->createTemporaryImage
+ (VG_sARGB_8888_PRE, size.width(), size.height(),
+ VG_IMAGE_QUALITY_FASTER, pd);
+ if (dstImage == VG_INVALID_HANDLE)
+ return;
+
+ // Clamp the radius range. We divide by 2 because the OpenVG blur
+ // is "too blurry" compared to the default raster implementation.
+ VGfloat maxRadius = VGfloat(vgGeti(VG_MAX_GAUSSIAN_STD_DEVIATION));
+ VGfloat radiusF = VGfloat(radius()) / 2.0f;
+ if (radiusF < 0.001f)
+ radiusF = 0.001f;
+ else if (radiusF > maxRadius)
+ radiusF = maxRadius;
+
+ vgGaussianBlur(dstImage, srcImage, radiusF, radiusF, VG_TILE_PAD);
+
+ VGImage child = VG_INVALID_HANDLE;
+
+ if (srcRect.isNull() ||
+ (srcRect.topLeft().isNull() && srcRect.size() == size)) {
+ child = dstImage;
+ } else {
+ QRect src = srcRect.toRect();
+ child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height());
+ }
+
+ qt_vg_drawVGImage(painter, dest, child);
+
+ if(child != dstImage)
+ vgDestroyImage(child);
+ QVGImagePool::instance()->releaseImage(0, dstImage);
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/openvg/qpixmapfilter_vg_p.h b/src/openvg/qpixmapfilter_vg_p.h
new file mode 100644
index 0000000000..867f9a5c54
--- /dev/null
+++ b/src/openvg/qpixmapfilter_vg_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPFILTER_VG_P_H
+#define QPIXMAPFILTER_VG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qpixmapdata_vg_p.h"
+#include <QtGui/private/qpixmapfilter_p.h>
+#include <QtCore/qvarlengtharray.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_SHIVAVG)
+
+class QVGPixmapConvolutionFilter : public QPixmapConvolutionFilter
+{
+ Q_OBJECT
+public:
+ QVGPixmapConvolutionFilter();
+ ~QVGPixmapConvolutionFilter();
+
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const;
+};
+
+class QVGPixmapColorizeFilter : public QPixmapColorizeFilter
+{
+ Q_OBJECT
+public:
+ QVGPixmapColorizeFilter();
+ ~QVGPixmapColorizeFilter();
+
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const;
+};
+
+class QVGPixmapDropShadowFilter : public QPixmapDropShadowFilter
+{
+ Q_OBJECT
+public:
+ QVGPixmapDropShadowFilter();
+ ~QVGPixmapDropShadowFilter();
+
+ void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src) const;
+};
+
+class QVGPixmapBlurFilter : public QPixmapBlurFilter
+{
+ Q_OBJECT
+public:
+ QVGPixmapBlurFilter(QObject *parent = 0);
+ ~QVGPixmapBlurFilter();
+
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qvg.h b/src/openvg/qvg.h
new file mode 100644
index 0000000000..da2eb9674a
--- /dev/null
+++ b/src/openvg/qvg.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVG_H
+#define QVG_H
+
+#include <QtCore/qglobal.h>
+
+// Include the OpenVG headers for use in applications that
+// issue raw OpenVG function calls.
+#if defined(QT_LOWER_CASE_VG_INCLUDES)
+#include <vg/openvg.h>
+#else
+#include <VG/openvg.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenVG)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/openvg/qvg_p.h b/src/openvg/qvg_p.h
new file mode 100644
index 0000000000..cb03166011
--- /dev/null
+++ b/src/openvg/qvg_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVG_P_H
+#define QVG_P_H
+
+#include "qvg.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// vgDrawGlyphs() only exists in OpenVG 1.1 and higher.
+#if !defined(OPENVG_VERSION_1_1) && !defined(QVG_NO_DRAW_GLYPHS)
+#define QVG_NO_DRAW_GLYPHS 1
+#endif
+
+#include <QtGui/qimage.h>
+
+#if !defined(QT_NO_EGL)
+#include <QtGui/private/qeglcontext_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QVGPaintEngine;
+
+#if !defined(QT_NO_EGL)
+
+class QEglContext;
+
+// Create an EGL context, but don't bind it to a surface. If single-context
+// mode is enabled, this will return the previously-created context.
+// "devType" indicates the type of device using the context, usually
+// QInternal::Widget or QInternal::Pixmap.
+Q_OPENVG_EXPORT QEglContext *qt_vg_create_context
+ (QPaintDevice *device, int devType);
+
+// Destroy an EGL context that was created by qt_vg_create_context().
+// If single-context mode is enabled, this will decrease the reference count.
+// "devType" indicates the type of device destroying the context, usually
+// QInternal::Widget or QInternal::Pixmap.
+Q_OPENVG_EXPORT void qt_vg_destroy_context
+ (QEglContext *context, int devType);
+
+// Return the shared pbuffer surface that can be made current to
+// destroy VGImage objects when there is no other surface available.
+Q_OPENVG_EXPORT EGLSurface qt_vg_shared_surface(void);
+
+// Convert the configuration format in a context to a VG or QImage format.
+Q_OPENVG_EXPORT VGImageFormat qt_vg_config_to_vg_format(QEglContext *context);
+Q_OPENVG_EXPORT QImage::Format qt_vg_config_to_image_format(QEglContext *context);
+
+#endif
+
+// Create a paint engine. Returns the common engine in single-context mode.
+Q_OPENVG_EXPORT QVGPaintEngine *qt_vg_create_paint_engine(void);
+
+// Destroy a paint engine. Does nothing in single-context mode.
+Q_OPENVG_EXPORT void qt_vg_destroy_paint_engine(QVGPaintEngine *engine);
+
+// Convert between QImage and VGImage format values.
+Q_OPENVG_EXPORT VGImageFormat qt_vg_image_to_vg_format(QImage::Format format);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qvg_symbian.cpp b/src/openvg/qvg_symbian.cpp
new file mode 100644
index 0000000000..0d2ed9e6d2
--- /dev/null
+++ b/src/openvg/qvg_symbian.cpp
@@ -0,0 +1,366 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdata_vg_p.h"
+#include "qvgfontglyphcache_p.h"
+#include <QtGui/private/qnativeimagehandleprovider_p.h>
+#include <private/qt_s60_p.h>
+
+#include <fbs.h>
+
+#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE
+# include <sgresource/sgimage.h>
+# ifdef SYMBIAN_FBSERV_GLYPHDATA // defined in fbs.h
+# define QT_SYMBIAN_HARDWARE_GLYPH_CACHE
+# include <graphics/fbsglyphdataiterator.h>
+# include <private/qfontengine_s60_p.h>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+typedef VGImage (*_vgCreateEGLImageTargetKHR)(VGeglImageKHR);
+static _vgCreateEGLImageTargetKHR qt_vgCreateEGLImageTargetKHR = 0;
+
+namespace QVG
+{
+ VGImage vgCreateEGLImageTargetKHR(VGeglImageKHR eglImage);
+}
+
+VGImage QVG::vgCreateEGLImageTargetKHR(VGeglImageKHR eglImage)
+{
+ if (!qt_vgCreateEGLImageTargetKHR && QEgl::hasExtension("EGL_KHR_image"))
+ qt_vgCreateEGLImageTargetKHR = (_vgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR");
+
+ return qt_vgCreateEGLImageTargetKHR ? qt_vgCreateEGLImageTargetKHR(eglImage) : 0;
+}
+
+extern int qt_vg_pixmap_serial;
+
+#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE
+static VGImage sgImageToVGImage(QEglContext *context, const RSgImage &sgImage)
+{
+ // when "0" used as argument then
+ // default display, context are used
+ if (!context)
+ context = qt_vg_create_context(0, QInternal::Pixmap);
+
+ VGImage vgImage = VG_INVALID_HANDLE;
+
+ if (sgImage.IsNull())
+ return vgImage;
+
+ const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
+ EGLImageKHR eglImage = QEgl::eglCreateImageKHR(QEgl::display(),
+ EGL_NO_CONTEXT,
+ EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)&sgImage,
+ (EGLint*)KEglImageAttribs);
+
+ if (!eglImage)
+ return vgImage;
+
+ vgImage = QVG::vgCreateEGLImageTargetKHR(eglImage);
+
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ return vgImage;
+}
+#endif
+
+void QVGPixmapData::cleanup()
+{
+ is_null = w = h = 0;
+ recreate = false;
+ source = QVolatileImage();
+}
+
+bool QVGPixmapData::initFromNativeImageHandle(void *handle, const QString &type)
+{
+ if (type == QLatin1String("RSgImage")) {
+ fromNativeType(handle, QPixmapData::SgImage);
+ return true;
+ } else if (type == QLatin1String("CFbsBitmap")) {
+ fromNativeType(handle, QPixmapData::FbsBitmap);
+ return true;
+ }
+ return false;
+}
+
+void QVGPixmapData::createFromNativeImageHandleProvider()
+{
+ void *handle = 0;
+ QString type;
+ nativeImageHandleProvider->get(&handle, &type);
+ if (handle) {
+ if (initFromNativeImageHandle(handle, type)) {
+ nativeImageHandle = handle;
+ nativeImageType = type;
+ } else {
+ qWarning("QVGPixmapData: Unknown native image type '%s'", qPrintable(type));
+ }
+ } else {
+ qWarning("QVGPixmapData: Native handle is null");
+ }
+}
+
+void QVGPixmapData::releaseNativeImageHandle()
+{
+ if (nativeImageHandleProvider && nativeImageHandle) {
+ nativeImageHandleProvider->release(nativeImageHandle, nativeImageType);
+ nativeImageHandle = 0;
+ nativeImageType = QString();
+ }
+}
+
+static inline bool conversionLessFormat(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_RGB16: // EColor64K
+ case QImage::Format_RGB32: // EColor16MU
+ case QImage::Format_ARGB32: // EColor16MA
+ case QImage::Format_ARGB32_Premultiplied: // EColor16MAP
+ case QImage::Format_Indexed8: // EGray256, EColor256
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QVGPixmapData::fromNativeType(void* pixmap, NativeType type)
+{
+ if (type == QPixmapData::SgImage && pixmap) {
+#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
+ RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmap);
+ destroyImages();
+ prevSize = QSize();
+
+ vgImage = sgImageToVGImage(context, *sgImage);
+ if (vgImage != VG_INVALID_HANDLE) {
+ w = vgGetParameteri(vgImage, VG_IMAGE_WIDTH);
+ h = vgGetParameteri(vgImage, VG_IMAGE_HEIGHT);
+ d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
+ }
+
+ is_null = (w <= 0 || h <= 0);
+ source = QVolatileImage(); // readback will be done later, only when needed
+ recreate = false;
+ prevSize = QSize(w, h);
+ updateSerial();
+#endif
+ } else if (type == QPixmapData::FbsBitmap && pixmap) {
+ CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap *>(pixmap);
+ QSize size(bitmap->SizeInPixels().iWidth, bitmap->SizeInPixels().iHeight);
+ resize(size.width(), size.height());
+ source = QVolatileImage(bitmap); // duplicates only, if possible
+ if (source.isNull())
+ return;
+ if (!conversionLessFormat(source.format())) {
+ // Here we may need to copy if the formats do not match.
+ // (e.g. for display modes other than EColor16MAP and EColor16MU)
+ source.beginDataAccess();
+ QImage::Format format = idealFormat(&source.imageRef(), Qt::AutoColor);
+ source.endDataAccess(true);
+ source.ensureFormat(format);
+ }
+ recreate = true;
+ } else if (type == QPixmapData::VolatileImage && pixmap) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(pixmap);
+ resize(img->width(), img->height());
+ source = *img;
+ recreate = true;
+ } else if (type == QPixmapData::NativeImageHandleProvider && pixmap) {
+ destroyImages();
+ nativeImageHandleProvider = static_cast<QNativeImageHandleProvider *>(pixmap);
+ // Cannot defer the retrieval, we need at least the size right away.
+ createFromNativeImageHandleProvider();
+ }
+}
+
+void* QVGPixmapData::toNativeType(NativeType type)
+{
+ if (type == QPixmapData::SgImage) {
+#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
+ toVGImage();
+
+ if (!isValid() || vgImage == VG_INVALID_HANDLE)
+ return 0;
+
+ TInt err = 0;
+
+ RSgDriver driver;
+ err = driver.Open();
+ if (err != KErrNone)
+ return 0;
+
+ TSgImageInfo sgInfo;
+ sgInfo.iPixelFormat = EUidPixelFormatARGB_8888_PRE;
+ sgInfo.iSizeInPixels.SetSize(w, h);
+ sgInfo.iUsage = ESgUsageBitOpenVgImage | ESgUsageBitOpenVgSurface;
+
+ QScopedPointer<RSgImage> sgImage(new RSgImage());
+ err = sgImage->Create(sgInfo, NULL, NULL);
+ if (err != KErrNone) {
+ driver.Close();
+ return 0;
+ }
+
+ const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
+ EGLImageKHR eglImage = QEgl::eglCreateImageKHR(QEgl::display(),
+ EGL_NO_CONTEXT,
+ EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)sgImage.data(),
+ (EGLint*)KEglImageAttribs);
+ if (!eglImage || eglGetError() != EGL_SUCCESS) {
+ sgImage->Close();
+ driver.Close();
+ return 0;
+ }
+
+ VGImage dstVgImage = QVG::vgCreateEGLImageTargetKHR(eglImage);
+ if (!dstVgImage || vgGetError() != VG_NO_ERROR) {
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ sgImage->Close();
+ driver.Close();
+ return 0;
+ }
+
+ vgCopyImage(dstVgImage, 0, 0,
+ vgImage, 0, 0,
+ w, h, VG_FALSE);
+
+ if (vgGetError() != VG_NO_ERROR) {
+ sgImage->Close();
+ sgImage.reset();
+ }
+
+ // release stuff
+ vgDestroyImage(dstVgImage);
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ driver.Close();
+ return reinterpret_cast<void*>(sgImage.take());
+#endif
+ } else if (type == QPixmapData::FbsBitmap && isValid()) {
+ ensureReadback(true);
+ if (source.isNull()) {
+ source = QVolatileImage(w, h, sourceFormat());
+ }
+ // Just duplicate the bitmap handle, no data copying happens.
+ return source.duplicateNativeImage();
+ }
+ return 0;
+}
+
+QSymbianVGFontGlyphCache::QSymbianVGFontGlyphCache() : QVGFontGlyphCache()
+{
+#ifdef QT_SYMBIAN_HARDWARE_GLYPH_CACHE
+ invertedGlyphs = true;
+#endif
+}
+
+void QSymbianVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d,
+ QFontEngine *fontEngine,
+ const glyph_t *g, int count)
+{
+#ifdef QT_SYMBIAN_HARDWARE_GLYPH_CACHE
+ QFontEngineS60 *s60fontEngine = static_cast<QFontEngineS60*>(fontEngine);
+ if (s60fontEngine->m_activeFont->TypeUid() != KCFbsFontUid)
+ return QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count);
+
+ QVector<glyph_t> uncachedGlyphs;
+ while (count-- > 0) {
+ // Skip this glyph if we have already cached it before.
+ glyph_t glyph = *g++;
+ if (((glyph < 256) && ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0))
+ || cachedGlyphs.contains(glyph))
+ continue;
+ if (!uncachedGlyphs.contains(glyph))
+ uncachedGlyphs.append(glyph);
+ }
+
+ if (!uncachedGlyphs.isEmpty()) {
+ CFbsFont *cfbsFont = static_cast<CFbsFont *>(s60fontEngine->m_activeFont);
+ RFbsGlyphDataIterator iter;
+
+ int err = iter.Open(*cfbsFont, (const unsigned int*)uncachedGlyphs.constData(), uncachedGlyphs.count());
+
+ if (err == KErrNotSupported || err == KErrInUse) { // Fallback in possibly supported error cases
+ iter.Close();
+ qWarning("Falling back to default QVGFontGlyphCache");
+ return QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count);
+ }
+
+ for (; err == KErrNone; err = iter.Next()) {
+ const unsigned int glyph = iter.GlyphCode();
+
+ const RSgImage& image = iter.Image();
+ const TOpenFontCharMetrics& metrics = iter.Metrics();
+
+ TRect glyphBounds;
+ metrics.GetHorizBounds(glyphBounds);
+ VGImage vgImage = sgImageToVGImage(0, image);
+ VGfloat origin[2];
+ VGfloat escapement[2];
+ origin[0] = -glyphBounds.iTl.iX;
+ origin[1] = glyphBounds.iBr.iY;
+ escapement[0] = 0;
+ escapement[1] = 0;
+ vgSetGlyphToImage(font, glyph, vgImage, origin, escapement);
+ vgDestroyImage(vgImage);
+
+ // Add to cache
+ if (glyph < 256)
+ cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32));
+ else
+ cachedGlyphs.insert(glyph);
+ }
+ iter.Close();
+
+ if (err == KErrNoMemory || err == KErrNoGraphicsMemory)
+ qWarning("Not enough memory to cache glyph");
+ else if (err != KErrNotFound)
+ qWarning("Received error %d from glyph cache", err);
+ }
+#else
+ QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/openvg/qvgcompositionhelper_p.h b/src/openvg/qvgcompositionhelper_p.h
new file mode 100644
index 0000000000..c3a8bed6a5
--- /dev/null
+++ b/src/openvg/qvgcompositionhelper_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVGCOMPOSITIONHELPER_H
+#define QVGCOMPOSITIONHELPER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsurface_vgegl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL)
+
+class QVGPaintEnginePrivate;
+class QVGEGLWindowSurfacePrivate;
+
+class Q_OPENVG_EXPORT QVGCompositionHelper
+{
+public:
+ QVGCompositionHelper();
+ virtual ~QVGCompositionHelper();
+
+ void startCompositing(const QSize& screenSize);
+ void endCompositing();
+
+ void blitWindow(VGImage image, const QSize& imageSize,
+ const QRect& rect, const QPoint& topLeft, int opacity);
+ void fillBackground(const QRegion& region, const QBrush& brush);
+ void drawCursorPixmap(const QPixmap& pixmap, const QPoint& offset);
+ void setScissor(const QRegion& region);
+ void clearScissor();
+
+private:
+ QVGPaintEnginePrivate *d;
+ QSize screenSize;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qvgfontglyphcache_p.h b/src/openvg/qvgfontglyphcache_p.h
new file mode 100644
index 0000000000..ce12301645
--- /dev/null
+++ b/src/openvg/qvgfontglyphcache_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVGFONTGLYPHCACHE_H
+#define QVGFONTGLYPHCACHE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/private/qfontengine_p.h>
+
+#include "qvg_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QVGPaintEnginePrivate;
+
+#ifndef QVG_NO_DRAW_GLYPHS
+
+class QVGFontGlyphCache
+{
+public:
+ QVGFontGlyphCache();
+ virtual ~QVGFontGlyphCache();
+
+ virtual void cacheGlyphs(QVGPaintEnginePrivate *d,
+ QFontEngine *fontEngine,
+ const glyph_t *g, int count);
+ void setScaleFromText(const QFont &font, QFontEngine *fontEngine);
+
+ VGFont font;
+ VGfloat scaleX;
+ VGfloat scaleY;
+ bool invertedGlyphs;
+ uint cachedGlyphsMask[256 / 32];
+ QSet<glyph_t> cachedGlyphs;
+};
+
+#if defined(Q_OS_SYMBIAN)
+class QSymbianVGFontGlyphCache : public QVGFontGlyphCache
+{
+public:
+ QSymbianVGFontGlyphCache();
+ void cacheGlyphs(QVGPaintEnginePrivate *d,
+ QFontEngine *fontEngine,
+ const glyph_t *g, int count);
+};
+#endif
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QVGFONTGLYPHCACHE_H
diff --git a/src/openvg/qvgimagepool.cpp b/src/openvg/qvgimagepool.cpp
new file mode 100644
index 0000000000..7a7ec78fe3
--- /dev/null
+++ b/src/openvg/qvgimagepool.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvgimagepool_p.h"
+#include "qpixmapdata_vg_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static QVGImagePool *qt_vg_image_pool = 0;
+
+class QVGImagePoolPrivate
+{
+public:
+ QVGImagePoolPrivate() : lruFirst(0), lruLast(0) {}
+
+ QVGPixmapData *lruFirst;
+ QVGPixmapData *lruLast;
+};
+
+QVGImagePool::QVGImagePool()
+ : d_ptr(new QVGImagePoolPrivate())
+{
+}
+
+QVGImagePool::~QVGImagePool()
+{
+}
+
+QVGImagePool *QVGImagePool::instance()
+{
+ if (!qt_vg_image_pool)
+ qt_vg_image_pool = new QVGImagePool();
+ return qt_vg_image_pool;
+}
+
+void QVGImagePool::setImagePool(QVGImagePool *pool)
+{
+ if (qt_vg_image_pool != pool)
+ delete qt_vg_image_pool;
+ qt_vg_image_pool = pool;
+}
+
+VGImage QVGImagePool::createTemporaryImage(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality,
+ QVGPixmapData *keepData)
+{
+ VGImage image;
+ do {
+ image = vgCreateImage(format, width, height, allowedQuality);
+ if (image != VG_INVALID_HANDLE)
+ return image;
+ } while (reclaimSpace(format, width, height, keepData));
+ qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d temporary image",
+ width, height);
+ return VG_INVALID_HANDLE;
+}
+
+VGImage QVGImagePool::createImageForPixmap(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality,
+ QVGPixmapData *data)
+{
+ VGImage image;
+ do {
+ image = vgCreateImage(format, width, height, allowedQuality);
+ if (image != VG_INVALID_HANDLE) {
+ if (data)
+ moveToHeadOfLRU(data);
+ return image;
+ }
+ } while (reclaimSpace(format, width, height, data));
+ qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d pixmap",
+ width, height);
+ return VG_INVALID_HANDLE;
+}
+
+VGImage QVGImagePool::createPermanentImage(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality)
+{
+ VGImage image;
+ do {
+ image = vgCreateImage(format, width, height, allowedQuality);
+ if (image != VG_INVALID_HANDLE)
+ return image;
+ } while (reclaimSpace(format, width, height, 0));
+ qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d image",
+ width, height);
+ return VG_INVALID_HANDLE;
+}
+
+void QVGImagePool::releaseImage(QVGPixmapData *data, VGImage image)
+{
+ // Very simple strategy at the moment: just destroy the image.
+ if (data)
+ removeFromLRU(data);
+ vgDestroyImage(image);
+}
+
+void QVGImagePool::useImage(QVGPixmapData *data)
+{
+ moveToHeadOfLRU(data);
+}
+
+void QVGImagePool::detachImage(QVGPixmapData *data)
+{
+ removeFromLRU(data);
+}
+
+bool QVGImagePool::reclaimSpace(VGImageFormat format,
+ VGint width, VGint height,
+ QVGPixmapData *data)
+{
+ Q_UNUSED(format); // For future use in picking the best image to eject.
+ Q_UNUSED(width);
+ Q_UNUSED(height);
+
+ bool succeeded = false;
+ bool wasInLRU = false;
+ if (data) {
+ wasInLRU = data->inLRU;
+ moveToHeadOfLRU(data);
+ }
+
+ QVGPixmapData *lrudata = pixmapLRU();
+ if (lrudata && lrudata != data) {
+ lrudata->reclaimImages();
+ succeeded = true;
+ }
+
+ if (data && !wasInLRU)
+ removeFromLRU(data);
+
+ return succeeded;
+}
+
+void QVGImagePool::hibernate()
+{
+ Q_D(QVGImagePool);
+ QVGPixmapData *pd = d->lruLast;
+ while (pd) {
+ QVGPixmapData *prevLRU = pd->prevLRU;
+ pd->inImagePool = false;
+ pd->inLRU = false;
+ pd->nextLRU = 0;
+ pd->prevLRU = 0;
+ pd->hibernate();
+ pd = prevLRU;
+ }
+ d->lruFirst = 0;
+ d->lruLast = 0;
+}
+
+void QVGImagePool::moveToHeadOfLRU(QVGPixmapData *data)
+{
+ Q_D(QVGImagePool);
+ if (data->inLRU) {
+ if (!data->prevLRU)
+ return; // Already at the head of the list.
+ removeFromLRU(data);
+ }
+ data->inLRU = true;
+ data->nextLRU = d->lruFirst;
+ data->prevLRU = 0;
+ if (d->lruFirst)
+ d->lruFirst->prevLRU = data;
+ else
+ d->lruLast = data;
+ d->lruFirst = data;
+}
+
+void QVGImagePool::removeFromLRU(QVGPixmapData *data)
+{
+ Q_D(QVGImagePool);
+ if (!data->inLRU)
+ return;
+ if (data->nextLRU)
+ data->nextLRU->prevLRU = data->prevLRU;
+ else
+ d->lruLast = data->prevLRU;
+ if (data->prevLRU)
+ data->prevLRU->nextLRU = data->nextLRU;
+ else
+ d->lruFirst = data->nextLRU;
+ data->inLRU = false;
+}
+
+QVGPixmapData *QVGImagePool::pixmapLRU()
+{
+ Q_D(QVGImagePool);
+ return d->lruLast;
+}
+
+QT_END_NAMESPACE
diff --git a/src/openvg/qvgimagepool_p.h b/src/openvg/qvgimagepool_p.h
new file mode 100644
index 0000000000..3510cfab26
--- /dev/null
+++ b/src/openvg/qvgimagepool_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVGIMAGEPOOL_P_H
+#define QVGIMAGEPOOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qvg.h"
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVGPixmapData;
+class QVGImagePoolPrivate;
+
+class Q_OPENVG_EXPORT QVGImagePool
+{
+public:
+ QVGImagePool();
+ virtual ~QVGImagePool();
+
+ static QVGImagePool *instance();
+
+ // This function can be used from system-specific graphics system
+ // plugins to alter the image allocation strategy.
+ static void setImagePool(QVGImagePool *pool);
+
+ // Create a new VGImage from the pool with the specified parameters
+ // that is not associated with a pixmap. The VGImage is returned to
+ // the pool when releaseImage() is called.
+ //
+ // This function will call reclaimSpace() when vgCreateImage() fails.
+ //
+ // This function is typically called when allocating temporary
+ // VGImage's for pixmap filters. The "keepData" object will not
+ // be reclaimed if reclaimSpace() needs to be called.
+ virtual VGImage createTemporaryImage(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality,
+ QVGPixmapData *keepData = 0);
+
+ // Create a new VGImage with the specified parameters and associate
+ // it with "data". The QVGPixmapData will be notified when the
+ // VGImage needs to be reclaimed by the pool.
+ //
+ // This function will call reclaimSpace() when vgCreateImage() fails.
+ virtual VGImage createImageForPixmap(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality,
+ QVGPixmapData *data);
+
+ // Create a permanent VGImage with the specified parameters.
+ // If there is insufficient space for the vgCreateImage call,
+ // then this function will call reclaimSpace() and try again.
+ //
+ // The caller is responsible for calling vgDestroyImage()
+ // when it no longer needs the VGImage, as the image is not
+ // recorded in the image pool.
+ //
+ // This function is typically used for pattern brushes where
+ // the OpenVG engine is responsible for managing the lifetime
+ // of the VGImage, destroying it automatically when the brush
+ // is no longer in use.
+ virtual VGImage createPermanentImage(VGImageFormat format,
+ VGint width, VGint height,
+ VGbitfield allowedQuality);
+
+ // Release a VGImage that is no longer required.
+ virtual void releaseImage(QVGPixmapData *data, VGImage image);
+
+ // Notify the pool that a QVGPixmapData object is using
+ // an image again. This allows the pool to move the image
+ // within a least-recently-used list of QVGPixmapData objects.
+ virtual void useImage(QVGPixmapData *data);
+
+ // Notify the pool that the VGImage's associated with a
+ // QVGPixmapData are being detached from the pool. The caller
+ // will become responsible for calling vgDestroyImage().
+ virtual void detachImage(QVGPixmapData *data);
+
+ // Reclaim space for an image allocation with the specified parameters.
+ // Returns true if space was reclaimed, or false if there is no
+ // further space that can be reclaimed. The "data" parameter
+ // indicates the pixmap that is trying to obtain space which should
+ // not itself be reclaimed.
+ virtual bool reclaimSpace(VGImageFormat format,
+ VGint width, VGint height,
+ QVGPixmapData *data);
+
+ // Hibernate the image pool because the context is about to be
+ // destroyed. All VGImage's left in the pool should be released.
+ virtual void hibernate();
+
+protected:
+ // Helper functions for managing the LRU list of QVGPixmapData objects.
+ void moveToHeadOfLRU(QVGPixmapData *data);
+ void removeFromLRU(QVGPixmapData *data);
+ QVGPixmapData *pixmapLRU();
+
+private:
+ QScopedPointer<QVGImagePoolPrivate> d_ptr;
+
+ Q_DECLARE_PRIVATE(QVGImagePool)
+ Q_DISABLE_COPY(QVGImagePool)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qwindowsurface_vg.cpp b/src/openvg/qwindowsurface_vg.cpp
new file mode 100644
index 0000000000..eedfea51fd
--- /dev/null
+++ b/src/openvg/qwindowsurface_vg.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_vg_p.h"
+#include "qwindowsurface_vgegl_p.h"
+#include "qpaintengine_vg_p.h"
+#include "qpixmapdata_vg_p.h"
+#include "qvg_p.h"
+
+#if !defined(QT_NO_EGL)
+
+#include <QtGui/private/qeglcontext_p.h>
+#include <QtGui/private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QVGWindowSurface::QVGWindowSurface(QWidget *window)
+ : QWindowSurface(window)
+{
+ // Create the default type of EGL window surface for windows.
+ d_ptr = new QVGEGLWindowSurfaceDirect(this);
+}
+
+QVGWindowSurface::QVGWindowSurface
+ (QWidget *window, QVGEGLWindowSurfacePrivate *d)
+ : QWindowSurface(window), d_ptr(d)
+{
+}
+
+QVGWindowSurface::~QVGWindowSurface()
+{
+ delete d_ptr;
+}
+
+QPaintDevice *QVGWindowSurface::paintDevice()
+{
+ return this;
+}
+
+void QVGWindowSurface::flush(QWidget *widget, const QRegion &region, const QPoint &offset)
+{
+ Q_UNUSED(offset);
+ QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
+ d_ptr->endPaint(parent, region);
+}
+
+void QVGWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+}
+
+bool QVGWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ if (!d_ptr->scroll(window(), area, dx, dy))
+ return QWindowSurface::scroll(area, dx, dy);
+ return true;
+}
+
+void QVGWindowSurface::beginPaint(const QRegion &region)
+{
+ d_ptr->beginPaint(window());
+
+ // If the window is not opaque, then fill the region we are about
+ // to paint with the transparent color.
+ if (!qt_widget_private(window())->isOpaque &&
+ window()->testAttribute(Qt::WA_TranslucentBackground)) {
+ QVGPaintEngine *engine = static_cast<QVGPaintEngine *>
+ (d_ptr->paintEngine());
+ engine->fillRegion(region, Qt::transparent, d_ptr->surfaceSize());
+ }
+}
+
+void QVGWindowSurface::endPaint(const QRegion &region)
+{
+ // Nothing to do here.
+ Q_UNUSED(region);
+}
+
+QPaintEngine *QVGWindowSurface::paintEngine() const
+{
+ return d_ptr->paintEngine();
+}
+
+QWindowSurface::WindowSurfaceFeatures QVGWindowSurface::features() const
+{
+ WindowSurfaceFeatures features = PartialUpdates | PreservedContents;
+ if (d_ptr->supportsStaticContents())
+ features |= StaticContents;
+ return features;
+}
+
+int QVGWindowSurface::metric(PaintDeviceMetric met) const
+{
+ return qt_paint_device_metric(window(), met);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qwindowsurface_vg_p.h b/src/openvg/qwindowsurface_vg_p.h
new file mode 100644
index 0000000000..d63d077237
--- /dev/null
+++ b/src/openvg/qwindowsurface_vg_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_VG_P_H
+#define QWINDOWSURFACE_VG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qwindowsurface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_EGL)
+
+class QVGEGLWindowSurfacePrivate;
+
+class Q_OPENVG_EXPORT QVGWindowSurface : public QWindowSurface, public QPaintDevice
+{
+public:
+ QVGWindowSurface(QWidget *window);
+ QVGWindowSurface(QWidget *window, QVGEGLWindowSurfacePrivate *d);
+ ~QVGWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ void beginPaint(const QRegion &region);
+ void endPaint(const QRegion &region);
+
+ QPaintEngine *paintEngine() const;
+
+ WindowSurfaceFeatures features() const;
+
+protected:
+ int metric(PaintDeviceMetric metric) const;
+
+private:
+ QVGEGLWindowSurfacePrivate *d_ptr;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_VG_P_H
diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp
new file mode 100644
index 0000000000..866453ff60
--- /dev/null
+++ b/src/openvg/qwindowsurface_vgegl.cpp
@@ -0,0 +1,782 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_vgegl_p.h"
+#include "qpaintengine_vg_p.h"
+#include "qpixmapdata_vg_p.h"
+#include "qvgimagepool_p.h"
+#include "qvg_p.h"
+
+#if !defined(QT_NO_EGL)
+
+QT_BEGIN_NAMESPACE
+
+// Turn off "direct to window" rendering if EGL cannot support it.
+#if !defined(EGL_RENDER_BUFFER) || !defined(EGL_SINGLE_BUFFER)
+#if defined(QVG_DIRECT_TO_WINDOW)
+#undef QVG_DIRECT_TO_WINDOW
+#endif
+#endif
+
+// Determine if preserved window contents should be used.
+#if !defined(EGL_SWAP_BEHAVIOR) || !defined(EGL_BUFFER_PRESERVED)
+#if !defined(QVG_NO_PRESERVED_SWAP)
+#define QVG_NO_PRESERVED_SWAP 1
+#endif
+#endif
+
+VGImageFormat qt_vg_config_to_vg_format(QEglContext *context)
+{
+ return qt_vg_image_to_vg_format
+ (qt_vg_config_to_image_format(context));
+}
+
+QImage::Format qt_vg_config_to_image_format(QEglContext *context)
+{
+ EGLint red = context->configAttrib(EGL_RED_SIZE);
+ EGLint green = context->configAttrib(EGL_GREEN_SIZE);
+ EGLint blue = context->configAttrib(EGL_BLUE_SIZE);
+ EGLint alpha = context->configAttrib(EGL_ALPHA_SIZE);
+ QImage::Format argbFormat;
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+ EGLint type = context->configAttrib(EGL_SURFACE_TYPE);
+ if ((type & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0)
+ argbFormat = QImage::Format_ARGB32_Premultiplied;
+ else
+ argbFormat = QImage::Format_ARGB32;
+#else
+ argbFormat = QImage::Format_ARGB32;
+#endif
+ if (red == 8 && green == 8 && blue == 8 && alpha == 8)
+ return argbFormat;
+ else if (red == 8 && green == 8 && blue == 8 && alpha == 0)
+ return QImage::Format_RGB32;
+ else if (red == 5 && green == 6 && blue == 5 && alpha == 0)
+ return QImage::Format_RGB16;
+ else if (red == 4 && green == 4 && blue == 4 && alpha == 4)
+ return QImage::Format_ARGB4444_Premultiplied;
+ else
+ return argbFormat; // XXX
+}
+
+#if !defined(QVG_NO_SINGLE_CONTEXT)
+
+class QVGSharedContext
+{
+public:
+ QVGSharedContext();
+ ~QVGSharedContext();
+
+ QEglContext *context;
+ int refCount;
+ int widgetRefCount;
+ QVGPaintEngine *engine;
+ EGLSurface surface;
+ QVGPixmapData *firstPixmap;
+};
+
+QVGSharedContext::QVGSharedContext()
+ : context(0)
+ , refCount(0)
+ , widgetRefCount(0)
+ , engine(0)
+ , surface(EGL_NO_SURFACE)
+ , firstPixmap(0)
+{
+}
+
+QVGSharedContext::~QVGSharedContext()
+{
+ // Don't accidentally destroy the QEglContext if the reference
+ // count falls to zero while deleting the paint engine.
+ ++refCount;
+
+ if (context)
+ context->makeCurrent(qt_vg_shared_surface());
+ delete engine;
+ if (context)
+ context->doneCurrent();
+ if (context && surface != EGL_NO_SURFACE)
+ context->destroySurface(surface);
+ delete context;
+}
+
+Q_GLOBAL_STATIC(QVGSharedContext, sharedContext);
+
+QVGPaintEngine *qt_vg_create_paint_engine(void)
+{
+ QVGSharedContext *shared = sharedContext();
+ if (!shared->engine)
+ shared->engine = new QVGPaintEngine();
+ return shared->engine;
+}
+
+void qt_vg_destroy_paint_engine(QVGPaintEngine *engine)
+{
+ Q_UNUSED(engine);
+}
+
+void qt_vg_register_pixmap(QVGPixmapData *pd)
+{
+ QVGSharedContext *shared = sharedContext();
+ pd->next = shared->firstPixmap;
+ pd->prev = 0;
+ if (shared->firstPixmap)
+ shared->firstPixmap->prev = pd;
+ shared->firstPixmap = pd;
+}
+
+void qt_vg_unregister_pixmap(QVGPixmapData *pd)
+{
+ if (pd->next)
+ pd->next->prev = pd->prev;
+ if (pd->prev) {
+ pd->prev->next = pd->next;
+ } else {
+ QVGSharedContext *shared = sharedContext();
+ if (shared)
+ shared->firstPixmap = pd->next;
+ }
+}
+
+#else
+
+QVGPaintEngine *qt_vg_create_paint_engine(void)
+{
+ return new QVGPaintEngine();
+}
+
+void qt_vg_destroy_paint_engine(QVGPaintEngine *engine)
+{
+ delete engine;
+}
+
+void qt_vg_register_pixmap(QVGPixmapData *pd)
+{
+ Q_UNUSED(pd);
+}
+
+void qt_vg_unregister_pixmap(QVGPixmapData *pd)
+{
+ Q_UNUSED(pd);
+}
+
+#endif
+
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+
+static bool isPremultipliedContext(const QEglContext *context)
+{
+ return context->configAttrib(EGL_SURFACE_TYPE) & EGL_VG_ALPHA_FORMAT_PRE_BIT;
+}
+
+#endif
+
+static QEglContext *createContext(QPaintDevice *device)
+{
+ QEglContext *context;
+
+ // Create the context object and open the display.
+ context = new QEglContext();
+ context->setApi(QEgl::OpenVG);
+
+ // Set the swap interval for the display.
+ QByteArray interval = qgetenv("QT_VG_SWAP_INTERVAL");
+ if (!interval.isEmpty())
+ eglSwapInterval(QEgl::display(), interval.toInt());
+ else
+ eglSwapInterval(QEgl::display(), 1);
+
+#ifdef EGL_RENDERABLE_TYPE
+ // Has the user specified an explicit EGL configuration to use?
+ QByteArray configId = qgetenv("QT_VG_EGL_CONFIG");
+ if (!configId.isEmpty()) {
+ EGLint cfgId = configId.toInt();
+ EGLint properties[] = {
+ EGL_CONFIG_ID, cfgId,
+ EGL_NONE
+ };
+ EGLint matching = 0;
+ EGLConfig cfg;
+ if (eglChooseConfig
+ (QEgl::display(), properties, &cfg, 1, &matching) &&
+ matching > 0) {
+ // Check that the selected configuration actually supports OpenVG
+ // and then create the context with it.
+ EGLint id = 0;
+ EGLint type = 0;
+ eglGetConfigAttrib
+ (QEgl::display(), cfg, EGL_CONFIG_ID, &id);
+ eglGetConfigAttrib
+ (QEgl::display(), cfg, EGL_RENDERABLE_TYPE, &type);
+ if (cfgId == id && (type & EGL_OPENVG_BIT) != 0) {
+ context->setConfig(cfg);
+ if (!context->createContext()) {
+ delete context;
+ return 0;
+ }
+ return context;
+ } else {
+ qWarning("QT_VG_EGL_CONFIG: %d is not a valid OpenVG configuration", int(cfgId));
+ }
+ }
+ }
+#endif
+
+ // Choose an appropriate configuration for rendering into the device.
+ QEglProperties configProps;
+ configProps.setPaintDeviceFormat(device);
+ int redSize = configProps.value(EGL_RED_SIZE);
+ if (redSize == EGL_DONT_CARE || redSize == 0)
+ configProps.setPixelFormat(QImage::Format_ARGB32); // XXX
+ configProps.setValue(EGL_ALPHA_MASK_SIZE, 1);
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+ configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
+ EGL_VG_ALPHA_FORMAT_PRE_BIT);
+ configProps.setRenderableType(QEgl::OpenVG);
+ if (!context->chooseConfig(configProps)) {
+ // Try again without the "pre" bit.
+ configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+ if (!context->chooseConfig(configProps)) {
+ delete context;
+ return 0;
+ }
+ }
+#else
+ configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+ configProps.setRenderableType(QEgl::OpenVG);
+ if (!context->chooseConfig(configProps)) {
+ delete context;
+ return 0;
+ }
+#endif
+
+ // Construct a new EGL context for the selected configuration.
+ if (!context->createContext()) {
+ delete context;
+ return 0;
+ }
+
+ return context;
+}
+
+#if !defined(QVG_NO_SINGLE_CONTEXT)
+
+QEglContext *qt_vg_create_context(QPaintDevice *device, int devType)
+{
+ QVGSharedContext *shared = sharedContext();
+ if (devType == QInternal::Widget)
+ ++(shared->widgetRefCount);
+ if (shared->context) {
+ ++(shared->refCount);
+ return shared->context;
+ } else {
+ shared->context = createContext(device);
+ shared->refCount = 1;
+ return shared->context;
+ }
+}
+
+static void qt_vg_destroy_shared_context(QVGSharedContext *shared)
+{
+ shared->context->makeCurrent(qt_vg_shared_surface());
+ delete shared->engine;
+ shared->engine = 0;
+ shared->context->doneCurrent();
+ if (shared->surface != EGL_NO_SURFACE) {
+ eglDestroySurface(QEgl::display(), shared->surface);
+ shared->surface = EGL_NO_SURFACE;
+ }
+ delete shared->context;
+ shared->context = 0;
+}
+
+void qt_vg_hibernate_pixmaps(QVGSharedContext *shared)
+{
+ // Artificially increase the reference count to prevent the
+ // context from being destroyed until after we have finished
+ // the hibernation process.
+ ++(shared->refCount);
+
+ // We need a context current to hibernate the VGImage objects.
+ shared->context->makeCurrent(qt_vg_shared_surface());
+
+ // Scan all QVGPixmapData objects in the system and hibernate them.
+ QVGPixmapData *pd = shared->firstPixmap;
+ while (pd != 0) {
+ pd->hibernate();
+ pd = pd->next;
+ }
+
+ // Hibernate any remaining VGImage's in the image pool.
+ QVGImagePool::instance()->hibernate();
+
+ // Don't need the current context any more.
+ shared->context->lazyDoneCurrent();
+
+ // Decrease the reference count and destroy the context if necessary.
+ if (--(shared->refCount) <= 0)
+ qt_vg_destroy_shared_context(shared);
+}
+
+void qt_vg_destroy_context(QEglContext *context, int devType)
+{
+ QVGSharedContext *shared = sharedContext();
+ if (shared->context != context) {
+ // This is not the shared context. Shouldn't happen!
+ delete context;
+ return;
+ }
+ if (devType == QInternal::Widget)
+ --(shared->widgetRefCount);
+ if (--(shared->refCount) <= 0) {
+ qt_vg_destroy_shared_context(shared);
+ } else if (shared->widgetRefCount <= 0 && devType == QInternal::Widget) {
+ // All of the widget window surfaces have been destroyed
+ // but we still have VG pixmaps active. Ask them to hibernate
+ // to free up GPU resources until a widget is shown again.
+ // This may eventually cause the EGLContext to be destroyed
+ // because nothing in the system needs a context, which will
+ // free up even more GPU resources.
+ qt_vg_hibernate_pixmaps(shared);
+ }
+}
+
+EGLSurface qt_vg_shared_surface(void)
+{
+ QVGSharedContext *shared = sharedContext();
+ if (shared->surface == EGL_NO_SURFACE) {
+ EGLint attribs[7];
+ attribs[0] = EGL_WIDTH;
+ attribs[1] = 16;
+ attribs[2] = EGL_HEIGHT;
+ attribs[3] = 16;
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+ if (isPremultipliedContext(shared->context)) {
+ attribs[4] = EGL_VG_ALPHA_FORMAT;
+ attribs[5] = EGL_VG_ALPHA_FORMAT_PRE;
+ attribs[6] = EGL_NONE;
+ } else
+#endif
+ {
+ attribs[4] = EGL_NONE;
+ }
+ shared->surface = eglCreatePbufferSurface
+ (QEgl::display(), shared->context->config(), attribs);
+ }
+ return shared->surface;
+}
+
+#else
+
+QEglContext *qt_vg_create_context(QPaintDevice *device, int devType)
+{
+ Q_UNUSED(devType);
+ return createContext(device);
+}
+
+void qt_vg_destroy_context(QEglContext *context, int devType)
+{
+ Q_UNUSED(devType);
+ delete context;
+}
+
+EGLSurface qt_vg_shared_surface(void)
+{
+ return EGL_NO_SURFACE;
+}
+
+#endif
+
+QVGEGLWindowSurfacePrivate::QVGEGLWindowSurfacePrivate(QWindowSurface *win)
+{
+ winSurface = win;
+ engine = 0;
+}
+
+QVGEGLWindowSurfacePrivate::~QVGEGLWindowSurfacePrivate()
+{
+ // Destroy the paint engine if it hasn't been destroyed already.
+ destroyPaintEngine();
+}
+
+QVGPaintEngine *QVGEGLWindowSurfacePrivate::paintEngine()
+{
+ if (!engine)
+ engine = qt_vg_create_paint_engine();
+ return engine;
+}
+
+VGImage QVGEGLWindowSurfacePrivate::surfaceImage() const
+{
+ return VG_INVALID_HANDLE;
+}
+
+void QVGEGLWindowSurfacePrivate::destroyPaintEngine()
+{
+ if (engine) {
+ qt_vg_destroy_paint_engine(engine);
+ engine = 0;
+ }
+}
+
+QSize QVGEGLWindowSurfacePrivate::windowSurfaceSize(QWidget *widget) const
+{
+ Q_UNUSED(widget);
+
+ QRect rect = winSurface->geometry();
+ QSize newSize = rect.size();
+
+#if defined(Q_WS_QWS)
+ // Account for the widget mask, if any.
+ if (widget && !widget->mask().isEmpty()) {
+ const QRegion region = widget->mask()
+ & rect.translated(-widget->geometry().topLeft());
+ newSize = region.boundingRect().size();
+ }
+#endif
+
+ return newSize;
+}
+
+#if defined(QVG_VGIMAGE_BACKBUFFERS)
+
+QVGEGLWindowSurfaceVGImage::QVGEGLWindowSurfaceVGImage(QWindowSurface *win)
+ : QVGEGLWindowSurfacePrivate(win)
+ , context(0)
+ , backBuffer(VG_INVALID_HANDLE)
+ , backBufferSurface(EGL_NO_SURFACE)
+ , recreateBackBuffer(false)
+ , isPaintingActive(false)
+ , windowSurface(EGL_NO_SURFACE)
+{
+}
+
+QVGEGLWindowSurfaceVGImage::~QVGEGLWindowSurfaceVGImage()
+{
+ destroyPaintEngine();
+ if (context) {
+ if (backBufferSurface != EGL_NO_SURFACE) {
+ // We need a current context to be able to destroy the image.
+ // We use the shared surface because the native window handle
+ // associated with "windowSurface" may have been destroyed already.
+ context->makeCurrent(qt_vg_shared_surface());
+ context->destroySurface(backBufferSurface);
+ vgDestroyImage(backBuffer);
+ context->doneCurrent();
+ }
+ if (windowSurface != EGL_NO_SURFACE)
+ context->destroySurface(windowSurface);
+ qt_vg_destroy_context(context, QInternal::Widget);
+ }
+}
+
+QEglContext *QVGEGLWindowSurfaceVGImage::ensureContext(QWidget *widget)
+{
+ QSize newSize = windowSurfaceSize(widget);
+ if (context && size != newSize) {
+ // The surface size has changed, so we need to recreate
+ // the back buffer. Keep the same context and paint engine.
+ size = newSize;
+ if (isPaintingActive)
+ context->doneCurrent();
+ isPaintingActive = false;
+ recreateBackBuffer = true;
+ }
+ if (!context) {
+ // Create a new EGL context. We create the surface in beginPaint().
+ size = newSize;
+ context = qt_vg_create_context(widget, QInternal::Widget);
+ if (!context)
+ return 0;
+ isPaintingActive = false;
+ }
+ return context;
+}
+
+void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget)
+{
+ QEglContext *context = ensureContext(widget);
+ if (context) {
+ if (recreateBackBuffer || backBufferSurface == EGL_NO_SURFACE) {
+ // Create a VGImage object to act as the back buffer
+ // for this window. We have to create the VGImage with a
+ // current context, so activate the main surface for the window.
+ context->makeCurrent(mainSurface());
+ recreateBackBuffer = false;
+ if (backBufferSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(QEgl::display(), backBufferSurface);
+ backBufferSurface = EGL_NO_SURFACE;
+ }
+ if (backBuffer != VG_INVALID_HANDLE) {
+ vgDestroyImage(backBuffer);
+ }
+ VGImageFormat format = qt_vg_config_to_vg_format(context);
+ backBuffer = vgCreateImage
+ (format, size.width(), size.height(),
+ VG_IMAGE_QUALITY_FASTER);
+ if (backBuffer != VG_INVALID_HANDLE) {
+ // Create an EGL surface for rendering into the VGImage.
+ backBufferSurface = eglCreatePbufferFromClientBuffer
+ (QEgl::display(), EGL_OPENVG_IMAGE,
+ (EGLClientBuffer)(backBuffer),
+ context->config(), NULL);
+ if (backBufferSurface == EGL_NO_SURFACE) {
+ vgDestroyImage(backBuffer);
+ backBuffer = VG_INVALID_HANDLE;
+ }
+ }
+ }
+ if (backBufferSurface != EGL_NO_SURFACE)
+ context->makeCurrent(backBufferSurface);
+ else
+ context->makeCurrent(mainSurface());
+ isPaintingActive = true;
+ }
+}
+
+void QVGEGLWindowSurfaceVGImage::endPaint
+ (QWidget *widget, const QRegion& region, QImage *image)
+{
+ Q_UNUSED(region);
+ Q_UNUSED(image);
+ QEglContext *context = ensureContext(widget);
+ if (context) {
+ if (backBufferSurface != EGL_NO_SURFACE) {
+ if (isPaintingActive)
+ vgFlush();
+ context->lazyDoneCurrent();
+ }
+ isPaintingActive = false;
+ }
+}
+
+VGImage QVGEGLWindowSurfaceVGImage::surfaceImage() const
+{
+ return backBuffer;
+}
+
+EGLSurface QVGEGLWindowSurfaceVGImage::mainSurface() const
+{
+ if (windowSurface != EGL_NO_SURFACE)
+ return windowSurface;
+ else
+ return qt_vg_shared_surface();
+}
+
+#endif // QVG_VGIMAGE_BACKBUFFERS
+
+QVGEGLWindowSurfaceDirect::QVGEGLWindowSurfaceDirect(QWindowSurface *win)
+ : QVGEGLWindowSurfacePrivate(win)
+ , context(0)
+ , isPaintingActive(false)
+ , needToSwap(false)
+ , windowSurface(EGL_NO_SURFACE)
+{
+}
+
+QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect()
+{
+ destroyPaintEngine();
+ if (context) {
+ if (windowSurface != EGL_NO_SURFACE)
+ context->destroySurface(windowSurface);
+ qt_vg_destroy_context(context, QInternal::Widget);
+ }
+}
+
+QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget)
+{
+ QSize newSize = windowSurfaceSize(widget);
+ QEglProperties surfaceProps;
+
+#if defined(QVG_RECREATE_ON_SIZE_CHANGE)
+#if !defined(QVG_NO_SINGLE_CONTEXT)
+ if (context && size != newSize) {
+ // The surface size has changed, so we need to recreate it.
+ // We can keep the same context and paint engine.
+ size = newSize;
+ if (isPaintingActive)
+ context->doneCurrent();
+ context->destroySurface(windowSurface);
+#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT)
+ if (isPremultipliedContext(context)) {
+ surfaceProps.setValue
+ (EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE);
+ } else {
+ surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT);
+ }
+#endif
+ windowSurface = context->createSurface(widget, &surfaceProps);
+ isPaintingActive = false;
+ needToSwap = true;
+ }
+#else
+ if (context && size != newSize) {
+ // The surface size has changed, so we need to recreate
+ // the EGL context for the widget. We also need to recreate
+ // the surface's paint engine if context sharing is not
+ // enabled because we cannot reuse the existing paint objects
+ // in the new context.
+ qt_vg_destroy_paint_engine(engine);
+ engine = 0;
+ context->destroySurface(windowSurface);
+ qt_vg_destroy_context(context, QInternal::Widget);
+ context = 0;
+ windowSurface = EGL_NO_SURFACE;
+ }
+#endif
+#endif
+ if (!context) {
+ // Create a new EGL context and bind it to the widget surface.
+ size = newSize;
+ context = qt_vg_create_context(widget, QInternal::Widget);
+ if (!context)
+ return 0;
+ // We want a direct to window rendering surface if possible.
+#if defined(QVG_DIRECT_TO_WINDOW)
+ surfaceProps.setValue(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
+#endif
+#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT)
+ if (isPremultipliedContext(context)) {
+ surfaceProps.setValue
+ (EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE);
+ } else {
+ surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT);
+ }
+#endif
+ EGLSurface surface = context->createSurface(widget, &surfaceProps);
+ if (surface == EGL_NO_SURFACE) {
+ qt_vg_destroy_context(context, QInternal::Widget);
+ context = 0;
+ return 0;
+ }
+ needToSwap = true;
+#if defined(QVG_DIRECT_TO_WINDOW)
+ // Did we get a direct to window rendering surface?
+ EGLint buffer = 0;
+ if (eglQueryContext(QEgl::display(), context->context(),
+ EGL_RENDER_BUFFER, &buffer) &&
+ buffer == EGL_SINGLE_BUFFER) {
+ needToSwap = false;
+ }
+#endif
+ windowSurface = surface;
+ isPaintingActive = false;
+ }
+
+#if !defined(QVG_NO_PRESERVED_SWAP)
+ // Try to force the surface back buffer to preserve its contents.
+ if (needToSwap) {
+ bool succeeded = eglSurfaceAttrib(QEgl::display(), windowSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+ if (!succeeded && eglGetError() != EGL_SUCCESS) {
+ qWarning("QVG: could not enable preserved swap");
+ }
+ }
+#endif
+ return context;
+}
+
+void QVGEGLWindowSurfaceDirect::beginPaint(QWidget *widget)
+{
+ QEglContext *context = ensureContext(widget);
+ if (context) {
+ context->makeCurrent(windowSurface);
+ isPaintingActive = true;
+ }
+}
+
+void QVGEGLWindowSurfaceDirect::endPaint
+ (QWidget *widget, const QRegion& region, QImage *image)
+{
+ Q_UNUSED(region);
+ Q_UNUSED(image);
+ QEglContext *context = ensureContext(widget);
+ if (context) {
+ if (needToSwap) {
+ if (!isPaintingActive)
+ context->makeCurrent(windowSurface);
+ context->swapBuffers(windowSurface);
+ context->lazyDoneCurrent();
+ } else if (isPaintingActive) {
+ vgFlush();
+ context->lazyDoneCurrent();
+ }
+ isPaintingActive = false;
+ }
+}
+
+bool QVGEGLWindowSurfaceDirect::supportsStaticContents() const
+{
+#if defined(QVG_BUFFER_SCROLLING) && !defined(QVG_NO_PRESERVED_SWAP)
+ return true;
+#else
+ return QVGEGLWindowSurfacePrivate::supportsStaticContents();
+#endif
+}
+
+bool QVGEGLWindowSurfaceDirect::scroll(QWidget *widget, const QRegion& area, int dx, int dy)
+{
+#ifdef QVG_BUFFER_SCROLLING
+ QEglContext *context = ensureContext(widget);
+ if (context) {
+ context->makeCurrent(windowSurface);
+ QRect scrollRect = area.boundingRect();
+ int sx = scrollRect.x();
+ int sy = size.height() - scrollRect.y() - scrollRect.height();
+ vgSeti(VG_SCISSORING, VG_FALSE);
+ vgCopyPixels(sx + dx, sy - dy, sx, sy, scrollRect.width(), scrollRect.height());
+ context->lazyDoneCurrent();
+ return true;
+ }
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(area);
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+#endif
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/openvg/qwindowsurface_vgegl_p.h b/src/openvg/qwindowsurface_vgegl_p.h
new file mode 100644
index 0000000000..f3f3af49bd
--- /dev/null
+++ b/src/openvg/qwindowsurface_vgegl_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_VGEGL_P_H
+#define QWINDOWSURFACE_VGEGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qwindowsurface_p.h>
+#include "qvg_p.h"
+
+#if !defined(QT_NO_EGL)
+
+#include <QtGui/private/qeglcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowSurface;
+
+class Q_OPENVG_EXPORT QVGEGLWindowSurfacePrivate
+{
+public:
+ QVGEGLWindowSurfacePrivate(QWindowSurface *win);
+ virtual ~QVGEGLWindowSurfacePrivate();
+
+ QVGPaintEngine *paintEngine();
+ virtual QEglContext *ensureContext(QWidget *widget) = 0;
+ virtual void beginPaint(QWidget *widget) = 0;
+ virtual void endPaint
+ (QWidget *widget, const QRegion& region, QImage *image = 0) = 0;
+ virtual VGImage surfaceImage() const;
+ virtual QSize surfaceSize() const = 0;
+ virtual bool supportsStaticContents() const { return false; }
+ virtual bool scroll(QWidget *, const QRegion&, int, int) { return false; }
+
+private:
+ QVGPaintEngine *engine;
+
+protected:
+ QWindowSurface *winSurface;
+
+ void destroyPaintEngine();
+ QSize windowSurfaceSize(QWidget *widget) const;
+};
+
+#if defined(EGL_OPENVG_IMAGE) && !defined(QVG_NO_SINGLE_CONTEXT)
+
+#define QVG_VGIMAGE_BACKBUFFERS 1
+
+class Q_OPENVG_EXPORT QVGEGLWindowSurfaceVGImage : public QVGEGLWindowSurfacePrivate
+{
+public:
+ QVGEGLWindowSurfaceVGImage(QWindowSurface *win);
+ virtual ~QVGEGLWindowSurfaceVGImage();
+
+ QEglContext *ensureContext(QWidget *widget);
+ void beginPaint(QWidget *widget);
+ void endPaint(QWidget *widget, const QRegion& region, QImage *image);
+ VGImage surfaceImage() const;
+ QSize surfaceSize() const { return size; }
+
+protected:
+ QEglContext *context;
+ VGImage backBuffer;
+ EGLSurface backBufferSurface;
+ bool recreateBackBuffer;
+ bool isPaintingActive;
+ QSize size;
+ EGLSurface windowSurface;
+
+ EGLSurface mainSurface() const;
+};
+
+#endif // EGL_OPENVG_IMAGE
+
+class Q_OPENVG_EXPORT QVGEGLWindowSurfaceDirect : public QVGEGLWindowSurfacePrivate
+{
+public:
+ QVGEGLWindowSurfaceDirect(QWindowSurface *win);
+ virtual ~QVGEGLWindowSurfaceDirect();
+
+ QEglContext *ensureContext(QWidget *widget);
+ void beginPaint(QWidget *widget);
+ void endPaint(QWidget *widget, const QRegion& region, QImage *image);
+ QSize surfaceSize() const { return size; }
+ bool supportsStaticContents() const;
+ bool scroll(QWidget *widget, const QRegion& area, int dx, int dy);
+
+protected:
+ QEglContext *context;
+ QSize size;
+ bool isPaintingActive;
+ bool needToSwap;
+ EGLSurface windowSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // !QT_NO_EGL
+
+#endif // QWINDOWSURFACE_VGEGL_P_H