summaryrefslogtreecommitdiffstats
path: root/src/openvg
diff options
context:
space:
mode:
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