/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtOpenVG module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** 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 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** $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.

    bool hasExtendedRadialGradientPen; // Current pen's brush is extended radial gradient.
    bool hasExtendedRadialGradientBrush; // Current brush is extended radial gradient.

    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;
        }
    }

    inline bool needsEmulation(const QBrush &brush) const
    {
        Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush);
        return qt_isExtendedRadialGradient(brush);
    }

    inline bool needsEmulation() const
    {
        return hasExtendedRadialGradientPen || hasExtendedRadialGradientBrush;
    }

    inline bool needsPenEmulation() const
    {
        return hasExtendedRadialGradientPen;
    }

    inline bool needsBrushEmulation() const
    {
        return hasExtendedRadialGradientBrush;
    }

    // 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;

    hasExtendedRadialGradientPen = false;
    hasExtendedRadialGradientBrush = false;

    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()
{
    vgSeti(VG_SCISSORING, VG_FALSE);
    vgSeti(VG_MASKING, VG_FALSE);
    return true;
}

void QVGPaintEngine::draw(const QVectorPath &path)
{
    Q_D(QVGPaintEngine);
    if (d->needsEmulation()) {
        QPaintEngineEx::draw(path);
        return;
    }
    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);
}

Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path);

void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
{
    Q_D(QVGPaintEngine);
    if (d->needsEmulation(brush)) {
        QPainter *p = painter();
        QBrush oldBrush = p->brush();
        p->setBrush(brush);
        qt_draw_helper(p->d_ptr.data(), qt_painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
        p->setBrush(oldBrush);
        return;
    }
    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);
    if (d->needsEmulation(pen.brush())) {
        QPaintEngineEx::stroke(path, pen);
        return;
    }
    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;

    d->hasExtendedRadialGradientPen =
        state()->pen.style() != Qt::NoPen && d->needsEmulation(state()->pen.brush());
}

void QVGPaintEngine::brushChanged()
{
    Q_D(QVGPaintEngine);
    d->dirty |= QPaintEngine::DirtyBrush;

    d->hasExtendedRadialGradientPen = d->needsEmulation(state()->brush);
}

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 (d->needsEmulation(brush)) {
        QPaintEngineEx::fillRect(rect, brush);
        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->needsEmulation()) {
        QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
        return;
    }
    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);
    if (d->needsEmulation()) {
        QPaintEngineEx::drawRects(rects, rectCount);
        return;
    }
    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);
    if (d->needsEmulation()) {
        QPaintEngineEx::drawRects(rects, rectCount);
        return;
    }
    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);
    if (d->needsEmulation()) {
        QPaintEngineEx::drawLines(lines, lineCount);
        return;
    }
    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);
    if (d->needsEmulation()) {
        QPaintEngineEx::drawLines(lines, lineCount);
        return;
    }
    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->needsEmulation()) {
        QPaintEngineEx::drawEllipse(r);
        return;
    }
    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);
    if (d->needsEmulation()) {
        QPaintEngineEx::drawPath(path);
        return;
    }
    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);

    if (d->needsPenEmulation()) {
        QPaintEngineEx::drawPoints(points, pointCount);
        return;
    }

    // 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);

    if (d->needsEmulation()) {
        QPaintEngineEx::drawPoints(points, pointCount);
        return;
    }

    // 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);

    if (d->needsEmulation()) {
        QPaintEngineEx::drawPolygon(points, pointCount, mode);
        return;
    }

    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);

    if (d->needsEmulation()) {
        QPaintEngineEx::drawPolygon(points, pointCount, mode);
        return;
    }

    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_NONANTIALIASED);
        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();
    VGImageQuality oldImageQuality = d->imageQuality;
    VGRenderingQuality oldRenderingQuality = d->renderingQuality;
    d->setImageQuality(VG_IMAGE_QUALITY_NONANTIALIASED);
    d->setRenderingQuality(VG_RENDERING_QUALITY_NONANTIALIASED);

    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);

    d->setImageQuality(oldImageQuality);
    d->setRenderingQuality(oldRenderingQuality);
}

// 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->vgImage != VG_INVALID_HANDLE)
            return;

        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->vgImage != VG_INVALID_HANDLE)
            return;

        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.bytesPerLine() : 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;
    }

    if (d->needsPenEmulation()) {
        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
    vgSeti(VG_SCISSORING, VG_FALSE);
    vgSeti(VG_MASKING, VG_FALSE);
    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;
    d->clearColor = QColor();
    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"
