/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qdebug.h>
#include "qpaintengine_d3d_p.h"

#include "private/qdrawhelper_p.h"
#include "private/qfont_p.h"
#include "private/qfontengine_p.h"
#include "private/qpaintengine_p.h"
#include "private/qtessellator_p.h"
#include <private/qbezier_p.h>
#include <private/qpainter_p.h>
#include <private/qpixmap_raster_p.h>
#include <private/qpolygonclipper_p.h>
#include <qbuffer.h>
#include <qcache.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qlibrary.h>
#include <qlibraryinfo.h>
#include <qmath.h>
#include <qpaintdevice.h>
#include <qpixmapcache.h>

#include <qwidget.h>
#include <d3d9.h>
#include <d3dx9.h>

#include <mmintrin.h>
#include <xmmintrin.h>

QT_BEGIN_NAMESPACE

#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

#define QD3D_MASK_MARGIN 1
#define QD3D_BATCH_SIZE 256

// for the ClearType detection stuff..
#ifndef SPI_GETFONTSMOOTHINGTYPE
#define SPI_GETFONTSMOOTHINGTYPE 0x200A
#endif

#ifndef FE_FONTSMOOTHINGCLEARTYPE
#define FE_FONTSMOOTHINGCLEARTYPE 0x0002
#endif

//#include <performance.h>
#define PM_INIT
#define PM_MEASURE(A)
#define PM_DISPLAY

//debugging
//#define QT_DEBUG_VERTEXBUFFER_ACCESS
//#define QT_DEBUG_D3D
//#define QT_DEBUG_D3D_CALLS

#define QD3D_SET_MARK(output) \
    D3DPERF_SetMarker(0, QString(output).utf16());

#define QT_VERTEX_RESET_LIMIT   24576
#define QT_VERTEX_BUF_SIZE      32768
#define QD3DFVF_CSVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE4(0) |  D3DFVF_TEXCOORDSIZE4(1))

// this is a different usage of the effect framework than intended,
// but it's convenient for us to use (See effect file)
#define PASS_STENCIL_ODDEVEN                0
#define PASS_STENCIL_WINDING                1
#define PASS_STENCIL_DRAW                   2
#define PASS_STENCIL_DRAW_DIRECT            3
#define PASS_STENCIL_CLIP                   4
#define PASS_STENCIL_NOSTENCILCHECK         5
#define PASS_STENCIL_NOSTENCILCHECK_DIRECT  6
#define PASS_TEXT                           7
#define PASS_CLEARTYPE_TEXT                 8
#define PASS_ALIASED_LINES                  9
#define PASS_ALIASED_LINES_DIRECT          10

#define PASS_AA_CREATEMASK                  0
#define PASS_AA_DRAW                        1
#define PASS_AA_DRAW_DIRECT                 2

#define D3D_STAGE_COUNT             2
#define D3D_RENDER_STATES           210
#define D3D_TEXTURE_STATES          33
#define D3D_SAMPLE_STATES           14


typedef HRESULT (APIENTRY *PFND3DXCREATEBUFFER)(DWORD, LPD3DXBUFFER *);
typedef HRESULT (APIENTRY *PFND3DXCREATEEFFECT)(LPDIRECT3DDEVICE9, LPCVOID, UINT, CONST D3DXMACRO *,
                                                LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL,
                                                LPD3DXEFFECT *, LPD3DXBUFFER *);
typedef D3DXMATRIX *(APIENTRY *PFND3DXMATRIXORTHOOFFCENTERLH)(D3DMATRIX *, FLOAT, FLOAT,
                                                              FLOAT, FLOAT, FLOAT, FLOAT);
typedef IDirect3D9 *(APIENTRY *PFNDIRECT3DCREATE9)(uint);

static PFNDIRECT3DCREATE9 pDirect3DCreate9 = 0;
static PFND3DXCREATEBUFFER pD3DXCreateBuffer = 0;
static PFND3DXCREATEEFFECT pD3DXCreateEffect = 0;
static PFND3DXMATRIXORTHOOFFCENTERLH pD3DXMatrixOrthoOffCenterLH = 0;


class QD3DSurfaceManager : public QObject {
    Q_OBJECT

public:
    enum QD3DSurfaceManagerStatus {
        NoStatus = 0,
        NeedsResetting = 0x01,
        MaxSizeChanged = 0x02
    };

    QD3DSurfaceManager();
    ~QD3DSurfaceManager();

    void init(LPDIRECT3D9 object);

    void setPaintDevice(QPaintDevice *pd);

    int status() const;
    void reset();

    LPDIRECT3DSURFACE9 renderTarget();

    LPDIRECT3DSURFACE9 surface(QPaintDevice *pd);
    LPDIRECT3DSWAPCHAIN9 swapChain(QPaintDevice *pd);
    void releasePaintDevice(QPaintDevice *pd);

    LPDIRECT3DDEVICE9 device();
    void cleanup();

    QSize maxSize() const;

private:
    struct D3DSwapChain {
        QSize size;
        LPDIRECT3DSWAPCHAIN9 swapchain;
        LPDIRECT3DSURFACE9 surface;
    };

    void updateMaxSize();
    void initPresentParameters(D3DPRESENT_PARAMETERS *params);
    D3DSwapChain *createSwapChain(QWidget *w);

    QSize m_max_size;
    int m_status;
    QMap<QPaintDevice *, D3DSwapChain *> m_swapchains;

    LPDIRECT3DDEVICE9 m_device;
    QPaintDevice *m_pd;
    HWND m_dummy;
    D3DSwapChain *m_current;

private Q_SLOTS:
    void cleanupPaintDevice(QObject *);
};

struct vertex {
    D3DVECTOR pos;
    DWORD color;
    FLOAT s0, t0, r0, q0;
    FLOAT s1, t1, r1, q1;
};

struct QD3DMaskPosition {
    int x, y, channel;
};


struct QD3DBatchItem {
    enum QD3DBatchInfo {
        BI_WINDING          = 0x0001,
        BI_AA               = 0x0002,
        BI_BRECT            = 0x0004,
        BI_MASKFULL         = 0x0008,
        BI_TEXT             = 0x0010,
        BI_MASK             = 0x0020,
        BI_CLIP             = 0x0040,
        BI_SCISSOR          = 0x0080,

        BI_PIXMAP           = 0x0100,
        BI_IMAGE            = 0x0200,
        BI_COMPLEXBRUSH     = 0x0400,

        BI_CLEARCLIP        = 0x0800, // clip nothing (filling the clip mask with 0)
        BI_TRANSFORM        = 0x1000,
        BI_MASKSCISSOR      = 0x2000,
        BI_FASTLINE         = 0x4000,
        BI_COSMETICPEN      = 0x8000
    };

    int m_info;

    int m_count;
    int m_offset;

    QD3DMaskPosition m_maskpos;
    qreal m_xoffset;
    qreal m_yoffset;
    qreal m_opacity;

    QPixmap m_pixmap;
    QRectF m_brect;
    QBrush m_brush;

    IDirect3DTexture9 *m_texture;

    qreal m_width;
    qreal m_distance;

    QTransform m_matrix;
    QPainter::CompositionMode m_cmode;

    QVector<int> m_pointstops;
};

struct QD3DBatch {
    int m_item_index;
    QD3DBatchItem items[QD3D_BATCH_SIZE];
};

class QD3DStateManager;
class QD3DFontCache;
class QD3DDrawHelper;
class QD3DGradientCache;

class QDirect3DPaintEnginePrivate : public QPaintEnginePrivate
{
    Q_DECLARE_PUBLIC(QDirect3DPaintEngine)

public:
    enum RenderTechnique {
        RT_NoTechnique,
        RT_Antialiased,
        RT_Aliased,
    };

    QDirect3DPaintEnginePrivate()
        : m_d3d_object(0)
        , m_d3d_device(0)
        , m_txop(QTransform::TxNone)
        , m_effect(0)
        , m_flush_on_end(0)
    { init(); }

    ~QDirect3DPaintEnginePrivate();

    bool init();
    void initDevice();

    inline QD3DBatchItem *nextBatchItem();

    QPolygonF brushCoordinates(const QRectF &r, bool stroke, qreal *fp) const;
    void fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform);
    void fillAntialiasedPath(const QPainterPath &path, const QRectF &brect,
        const QTransform &txform, bool stroke);
    void fillPath(const QPainterPath &path, QRectF brect);

    void strokePath(const QPainterPath &path, QRectF brect, bool simple = false);
    QPainterPath strokePathFastPen(const QPainterPath &path);
    void strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform);

    void flushBatch();
    int flushAntialiased(int offset);
    void flushAliased(QD3DBatchItem *item, int offset);
    void flushText(QD3DBatchItem *item, int offset);
    void flushLines(QD3DBatchItem *item, int offset);

    void updateTransform(const QTransform &matrix);
    void updatePen(const QPen &pen);
    void updateBrush(const QBrush &pen);
    void updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op = Qt::ReplaceClip);
    void updateClipPath(const QPainterPath &clipregion, Qt::ClipOperation op = Qt::ReplaceClip);
    void updateFont(const QFont &font);

    void setRenderTechnique(RenderTechnique technique);

    QPointF transformPoint(const QPointF &p, qreal *w) const;

    bool prepareBatch(QD3DBatchItem *item, int offset);
    void prepareItem(QD3DBatchItem *item);
    void cleanupItem(QD3DBatchItem *item);
    void setCompositionMode(QPainter::CompositionMode mode);

    void verifyTexture(const QPixmap &pixmap);

    bool isFastRect(const QRectF &rect);

    void releaseDC();

    void cleanup();
    bool testCaps();

    QPixmap getPattern(Qt::BrushStyle style) const;

    // clipping
    QPainterPath m_sysclip_path;
    QPainterPath m_clip_path;
    QRegion m_sysclip_region;
    QRegion m_clip_region;

    qreal m_opacity;
    D3DCOLOR m_opacity_color;

    int m_current_state;

    ID3DXEffect* m_effect;

    RenderTechnique m_current_technique;

    QTransform m_matrix;
    qreal m_inv_scale;

    QPen m_pen;
    Qt::BrushStyle m_pen_brush_style;
    QTransform m_inv_pen_matrix;
    D3DCOLOR m_pen_color;
    qreal m_pen_width;

    QBrush m_brush;
    Qt::BrushStyle m_brush_style;
    QTransform m_inv_brush_matrix;
    D3DCOLOR m_brush_color;
    QTransform m_brush_origin;

    uint m_clipping_enabled : 1;
    uint m_has_complex_clipping : 1;
    uint m_cleartype_text: 1;
    uint m_has_pen : 1;
    uint m_has_cosmetic_pen : 1;
    uint m_has_brush : 1;
    uint m_has_fast_pen : 1;
    uint m_has_aa_fast_pen : 1;
    uint m_flush_on_end : 1;
    uint m_supports_d3d : 1;

    QTransform::TransformationType m_txop;

    QPainter::CompositionMode m_cmode;

    QD3DSurfaceManager m_surface_manager;
    QSize m_surface_size;

    LPDIRECT3D9 m_d3d_object;
    LPDIRECT3DDEVICE9 m_d3d_device;
    IDirect3DSurface9 *m_current_surface;
    bool m_in_scene;

    QD3DGradientCache *m_gradient_cache;
    QD3DDrawHelper *m_draw_helper;
    QD3DBatch m_batch;
    QD3DStateManager *m_statemanager;

    HDC m_dc;
    IDirect3DSurface9 *m_dcsurface;

    QMap<Qt::BrushStyle, QPixmap> m_patterns;
};


class QD3DStateManager : public ID3DXEffectStateManager {
public:
    QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect);
    void reset();

    inline void startStateBlock();
    inline void endStateBlock();

    inline void setCosmeticPen(bool enabled);
    inline void setBrushMode(int mode);
    inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture);
    inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread);
    inline void setTransformation(const QTransform *matrix = 0);
    inline void setProjection(const D3DXMATRIX *pMatrix);
    inline void setMaskChannel(int channel);
    inline void setMaskOffset(qreal x, qreal y);
    inline void setFocalDistance(const qreal &fd);

    inline void beginPass(int pass);
    inline void endPass();

    STDMETHOD(QueryInterface)(REFIID iid, LPVOID *ppv);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    STDMETHOD(SetTransform)(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix);
    STDMETHOD(SetMaterial)(CONST D3DMATERIAL9 *pMaterial);
    STDMETHOD(SetLight)(DWORD Index, CONST D3DLIGHT9 *pLight);
    STDMETHOD(LightEnable)(DWORD Index, BOOL Enable);
    STDMETHOD(SetRenderState)(D3DRENDERSTATETYPE State, DWORD Value);
    STDMETHOD(SetTexture)(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture);
    STDMETHOD(SetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value);
    STDMETHOD(SetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value);
    STDMETHOD(SetNPatchMode)(FLOAT NumSegments);
    STDMETHOD(SetFVF)(DWORD FVF);
    STDMETHOD(SetVertexShader)(LPDIRECT3DVERTEXSHADER9 pShader);
    STDMETHOD(SetVertexShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount);
    STDMETHOD(SetVertexShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount);
    STDMETHOD(SetVertexShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount);
    STDMETHOD(SetPixelShader)(LPDIRECT3DPIXELSHADER9 pShader);
    STDMETHOD(SetPixelShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount);
    STDMETHOD(SetPixelShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount);
    STDMETHOD(SetPixelShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount);
private:
    LPDIRECT3DVERTEXSHADER9 m_vertexshader;
    LPDIRECT3DPIXELSHADER9 m_pixelshader;

    LPDIRECT3DBASETEXTURE9 m_textures[D3D_STAGE_COUNT];
    DWORD m_texturestates[D3D_STAGE_COUNT][D3D_TEXTURE_STATES];
    DWORD m_samplerstates[D3D_STAGE_COUNT][D3D_SAMPLE_STATES];
    DWORD m_renderstate[D3D_RENDER_STATES];

    qreal m_radgradfd;

    bool m_cosmetic_pen;
    int m_pass;
    int m_maskchannel;
    int m_brushmode;
    LPDIRECT3DBASETEXTURE9 m_texture;
    D3DXMATRIX m_projection;

    D3DXMATRIX m_d3dIdentityMatrix;
    bool m_isIdentity;
    QTransform m_transformation;

    LPDIRECT3DDEVICE9 m_pDevice;
    ID3DXEffect *m_effect;

    LONG m_refs;
    bool m_changed;
    qreal m_xoffset, m_yoffset;
    static int m_mask_channels[4][4];
};

//
// font cache stuff
//

struct QD3DGlyphCoord {
    // stores the offset and size of a glyph texture
    qreal x;
    qreal y;
    qreal width;
    qreal height;
    qreal log_width;
    qreal log_height;
    QFixed x_offset;
    QFixed y_offset;
};

struct QD3DFontTexture {
    int x_offset; // current glyph offset within the texture
    int y_offset;
    int width;
    int height;
    IDirect3DTexture9 *texture;
};

typedef QHash<glyph_t, QD3DGlyphCoord*>  QD3DGlyphHash;
typedef QHash<QFontEngine*, QD3DGlyphHash*> QD3DFontGlyphHash;
typedef QHash<quint64, QD3DFontTexture*> QD3DFontTexHash;

class QD3DGlyphCache : public QObject
{
    Q_OBJECT
public:
    QD3DGlyphCache()
        : QObject(0)
        , current_cache(0) {}
    ~QD3DGlyphCache();
    QD3DGlyphCoord *lookup(QFontEngine *, glyph_t);
    void cacheGlyphs(QDirect3DPaintEngine *, const QTextItemInt &, const QVarLengthArray<glyph_t> &,
                     bool);
    void cleanCache();
    inline QD3DFontTexture *fontTexture(QFontEngine *engine) {
        return font_textures.constFind(reinterpret_cast<quint64>(engine)).value();
    }

public slots:
    void fontEngineDestroyed(QObject *);

private:
    QImage clearTypeGlyph(QFontEngine *, glyph_t glyph);
    QD3DGlyphHash *current_cache;
    QD3DFontTexHash font_textures;
    QD3DFontGlyphHash font_cache;
};

QD3DGlyphCache::~QD3DGlyphCache()
{
}

QD3DGlyphCoord *QD3DGlyphCache::lookup(QFontEngine *, glyph_t g)
{
    Q_ASSERT(current_cache != 0);
    QD3DGlyphHash::const_iterator it = current_cache->constFind(g);
    if (it == current_cache->constEnd())
        return 0;
    return it.value();
}

void QD3DGlyphCache::cleanCache()
{
    QList<quint64> keys = font_textures.keys();
    for (int i=0; i<keys.size(); ++i)
        font_textures.value(keys.at(i))->texture->Release();

    qDeleteAll(font_textures);
    qDeleteAll(font_cache);
    font_textures.clear();
    font_cache.clear();
    current_cache = 0;
}

void QD3DGlyphCache::fontEngineDestroyed(QObject *object)
{
//     qDebug() << "=> font engine destroyed: " << object;
    QFontEngine *engine = static_cast<QFontEngine *>(object);

    QD3DFontGlyphHash::iterator cache_it = font_cache.find(engine);
    if (cache_it != font_cache.end()) {
        QD3DGlyphHash *cache = font_cache.take(engine);
        delete cache;
    }

    quint64 font_key = reinterpret_cast<quint64>(engine);
    QD3DFontTexture *tex = font_textures.take(font_key);
    if (tex) {
        tex->texture->Release();
        delete tex;
    }
}

QImage QD3DGlyphCache::clearTypeGlyph(QFontEngine *engine, glyph_t glyph)
{
    glyph_metrics_t gm = engine->boundingBox(glyph);
    int glyph_x = qFloor(gm.x.toReal());
    int glyph_y = qFloor(gm.y.toReal());
    int glyph_width = qCeil((gm.x + gm.width).toReal()) -  glyph_x + 2;
    int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y + 2;

    if (glyph_width + glyph_x <= 0 || glyph_height <= 0)
        return QImage();
    QImage im(glyph_width + glyph_x, glyph_height, QImage::Format_ARGB32_Premultiplied);
    im.fill(0xff000000); // solid black
    QPainter p(&im);

    p.setPen(Qt::white);
    p.setBrush(Qt::NoBrush);

    QTextItemInt ti;
    ti.ascent = engine->ascent();
    ti.descent = engine->descent();
    ti.width = glyph_width;
    ti.fontEngine = engine;

    QGlyphLayoutArray<1> glyphLayout;
    ti.glyphs = glyphLayout;
    ti.glyphs.glyphs[0] = glyph;
    ti.glyphs.advances_x[0] = glyph_width;
    p.drawTextItem(QPointF(-glyph_x, -glyph_y), ti);
    p.end();
    return im;
}

#if 0
static void dump_font_texture(QD3DFontTexture *tex)
{
    QColor color(Qt::red);
    D3DLOCKED_RECT rect;
    if (FAILED(tex->texture->LockRect(0, &rect, 0, 0))) {
        qDebug() << "debug: unable to lock texture rect.";
        return;
    }

// cleartype version
//     uint *tex_data = (uint *) rect.pBits;
//     QImage im(tex->width, tex->height, QImage::Format_ARGB32);
//     for (int y=0; y<tex->height; ++y) {
//         for (int x=0; x<tex->width; ++x) {
//             im.setPixel(x, y, ((*(tex_data+x+y*tex->width))));
//         }
//     }
    uchar *tex_data = (uchar *) rect.pBits;
    QImage im(rect.Pitch, tex->height, QImage::Format_ARGB32);
    for (int y=0; y<tex->height; ++y) {
        for (int x=0; x<rect.Pitch; ++x) {
            uchar val = ((*(tex_data+x+y*rect.Pitch)));
            im.setPixel(x, y, 0xff000000 | (val << 16) | (val << 8) | val);
        }
    }
    tex->texture->UnlockRect(0);
    static  int i= 0;
    im.save(QString("tx%1.png").arg(i++));
}
#endif

void QD3DGlyphCache::cacheGlyphs(QDirect3DPaintEngine *engine, const QTextItemInt &ti,
                                 const QVarLengthArray<glyph_t> &glyphs, bool clearType)
{
    IDirect3DDevice9 *device = engine->d_func()->m_d3d_device;
    QD3DFontGlyphHash::const_iterator cache_it = font_cache.constFind(ti.fontEngine);
    QD3DGlyphHash *cache = 0;
    if (cache_it == font_cache.constEnd()) {
        cache = new QD3DGlyphHash;
        font_cache.insert(ti.fontEngine, cache);
        connect(ti.fontEngine, SIGNAL(destroyed(QObject *)), SLOT(fontEngineDestroyed(QObject *)));
    } else {
        cache = cache_it.value();
    }

    current_cache = cache;

    D3DFORMAT tex_format = clearType ? D3DFMT_A8R8G8B8 : D3DFMT_A8;
    quint64 font_key = reinterpret_cast<quint64>(ti.fontEngine);
    QD3DFontTexHash::const_iterator it = font_textures.constFind(font_key);
    QD3DFontTexture *font_tex = 0;
    if (it == font_textures.constEnd()) {
        // alloc a new texture, put it into the cache
        int tex_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5;
        int tex_width = tex_height * 30; // ###
        IDirect3DTexture9 *tex;
        if (FAILED(device->CreateTexture(tex_width, tex_height, 1, 0,
                                         tex_format, D3DPOOL_MANAGED, &tex, NULL)))
        {
            qWarning("QD3DGlyphCache::cacheGlyphs(): can't allocate font texture (%dx%d).",
                     tex_width, tex_height);
            return;
        } else {
//             qDebug() << "=> new font texture: " << QSize(tex_width,tex_height);
            font_tex = new QD3DFontTexture;
            font_tex->texture = tex;
            font_tex->x_offset = 0;
            font_tex->y_offset = 0;
            font_tex->width = tex_width;
            font_tex->height = tex_height;
            font_textures.insert(font_key, font_tex);
        }
    } else {
        font_tex = it.value();
        // make it current render target..
    }

    // cache each glyph
    for (int i=0; i<glyphs.size(); ++i) {
        QD3DGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
        if (it == cache->constEnd()) {
            glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
            int glyph_width = qCeil(metrics.width.toReal()) + 5;
            int glyph_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5;
            if (font_tex->x_offset + glyph_width > font_tex->width) {
                // no room on the current line, start new glyph strip
                int strip_height = glyph_height;
                font_tex->x_offset = 0;
                font_tex->y_offset += strip_height;
                if (font_tex->y_offset >= font_tex->height) {
                    // if no room in the current texture - realloc a larger texture
                    int old_tex_height = font_tex->height;
                    font_tex->height += strip_height;

                    IDirect3DTexture9 *new_tex;
                    if (FAILED(device->CreateTexture(font_tex->width, font_tex->height, 1, 0,
                                                     tex_format, D3DPOOL_MANAGED, &new_tex, NULL)))
                    {
                        qWarning("QD3DGlyphCache(): can't re-allocate font texture.");
                        return;
                    } else {
//                         qDebug() << " -> new glyph strip added:" << QSize(font_tex->width,font_tex->height);

                        D3DLOCKED_RECT new_rect, old_rect;
                        if (FAILED(font_tex->texture->LockRect(0, &old_rect, 0, D3DLOCK_READONLY))) {
                            qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
                            return;
                        }
                        if (FAILED(new_tex->LockRect(0, &new_rect, 0, 0))) {
                            qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
                            return;
                        }
                        memcpy(new_rect.pBits, old_rect.pBits, new_rect.Pitch * old_tex_height);
                        font_tex->texture->UnlockRect(0);
                        new_tex->UnlockRect(0);
                        engine->d_func()->flushBatch();
                        font_tex->texture->Release();
                        font_tex->texture = new_tex;
                    }

                    // update the texture coords and the y offset for the existing glyphs in
                    // the cache, because of the texture size change
                    QD3DGlyphHash::iterator it = cache->begin();
                    while (it != cache->end()) {
                        it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
                        it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
                        ++it;
                    }
                }
            }
            QD3DGlyphCoord *d3d_glyph = new QD3DGlyphCoord;
            d3d_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
            d3d_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
            d3d_glyph->width = qreal(glyph_width) / font_tex->width;
            d3d_glyph->height = qreal(glyph_height) / font_tex->height;
            d3d_glyph->log_width = d3d_glyph->width * font_tex->width;
            d3d_glyph->log_height = d3d_glyph->height * font_tex->height;
            d3d_glyph->x_offset = -metrics.x;
            d3d_glyph->y_offset = metrics.y;

            QImage glyph_im;
            if (clearType)
                glyph_im = clearTypeGlyph(ti.fontEngine, glyphs[i]);
            else
                glyph_im = ti.fontEngine->alphaMapForGlyph(glyphs[i]).convertToFormat(QImage::Format_Indexed8);

            // write glyph to texture
            D3DLOCKED_RECT rect;
            RECT glyph_rect = { font_tex->x_offset, font_tex->y_offset,
                                font_tex->x_offset + glyph_im.width(),
                                font_tex->y_offset + glyph_im.height() };

//             qDebug() << "  > new glyph char added:" << QSize(glyph_im.width(), glyph_im.height());
            if (FAILED(font_tex->texture->LockRect(0, &rect, &glyph_rect, 0))) {
                qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
                return;
            }

            // ### unify these loops
            if (clearType) {
                int ppl = rect.Pitch / 4;
                uint *tex_data = (uint *) rect.pBits;
                for (int y=0; y<glyph_im.height(); ++y) {
                    uint *s = (uint *) glyph_im.scanLine(y);
                    for (int x=0; x<glyph_im.width(); ++x) {
                        tex_data[ppl*y + x] = *s;
                        ++s;
                    }
                }
            } else {
                int ppl = rect.Pitch;
                uchar *tex_data = (uchar *) rect.pBits;
                for (int y=0; y<glyph_im.height(); ++y) {
                    uchar *s = (uchar *) glyph_im.scanLine(y);
                    for (int x=0; x<glyph_im.width(); ++x) {
                        tex_data[ppl*y + x] = *s;
                        ++s;
                    }
                }
            }
            font_tex->texture->UnlockRect(0);

            // debug
//             dump_font_texture(font_tex);

            if (font_tex->x_offset + glyph_width > font_tex->width) {
                font_tex->x_offset = 0;
                font_tex->y_offset += glyph_height;
            } else {
                font_tex->x_offset += glyph_width;
            }

            cache->insert(glyphs[i], d3d_glyph);
        }
    }
}

Q_GLOBAL_STATIC(QD3DGlyphCache, qd3d_glyph_cache)

//
// end font caching stuff
//


//
//  D3D image cache stuff
//

// ### keep the GL stuff in mind..
typedef void (*_qt_image_cleanup_hook_64)(qint64);
extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;

static void qd3d_image_cleanup(qint64 key);

class QD3DImage
{
public:
    QD3DImage(IDirect3DDevice9 *device, const QImage &image);
    ~QD3DImage();

    IDirect3DTexture9 *texture;
};

static QList<IDirect3DTexture9 *> qd3d_release_list;

QD3DImage::QD3DImage(IDirect3DDevice9 *device, const QImage &image)
{
    texture = 0;
    Q_ASSERT(device);
    QImage im = image.convertToFormat(QImage::Format_ARGB32);
    if (FAILED(device->CreateTexture(im.width(), im.height(), 1, 0,
                                     D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture, 0))) {
        qWarning("QD3DImage(): unable to create Direct3D texture.");
        return;
    }
//     qDebug(" -> created image texture: %p - 0x%08x%08x",texture,uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff));
    D3DLOCKED_RECT rect;
    if (FAILED(texture->LockRect(0, &rect, 0, 0))) {
        qDebug() << "QD3DImage: unable to lock texture rect.";
        return;
    }
    DWORD *dst = (DWORD *) rect.pBits;
    DWORD *src = (DWORD *) im.scanLine(0);

    Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4));
    memcpy(dst, src, rect.Pitch*im.height());
    texture->UnlockRect(0);
}

QD3DImage::~QD3DImage()
{
    if (texture)
        qd3d_release_list.append(texture);
}

static int qd3d_cache_limit = 64*1024; // cache ~64 MB worth of textures
typedef QCache<quint64, QD3DImage> QD3DImageCache;

class QD3DImageManager
{
public:
    QD3DImageManager() {
        // ### GL does the same!
        qt_image_cleanup_hook_64 = qd3d_image_cleanup;
        cache.setMaxCost(qd3d_cache_limit);
    }
    ~QD3DImageManager() {
//         qDebug() << "unhooking d3d image cache";
        qt_image_cleanup_hook_64 = 0;
        cache.clear();
    }

    IDirect3DTexture9 *lookup(IDirect3DDevice9 *device, const QImage &image);
    void remove(quint64 key);

private:
    QD3DImageCache cache;
};

IDirect3DTexture9 *QD3DImageManager::lookup(IDirect3DDevice9 *device, const QImage &image)
{
    QD3DImage *tex_image = 0;

    tex_image = cache.object(image.cacheKey());
    if (!tex_image) {
        // to avoid cache thrashing we remove images from the cache
        // that have the same serial no as the cached image, since
        // that image is most likely destoyed already, and we got a
        // stale cache entry
        uint serial = (uint) (image.cacheKey() >> 32);
        QList<quint64> keys = cache.keys();
        for (int i=0; i<keys.size(); ++i) {
            if ((uint)(keys.at(i) >> 32) == serial) {
                cache.remove(keys.at(i));
                break;
            }
        }
//         qDebug(" => cached: %d, adding cache image: 0x%08x%08x",cache.size(), uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff));
        // add cache entry
        int cost = image.width()*image.height()*4/1024;
        if (cache.totalCost() + cost > cache.maxCost()) {
            // no room for new entries? kick out half the cached images
            int old_max_cost = cache.maxCost();
            cache.setMaxCost(old_max_cost/2);
            cache.setMaxCost(old_max_cost);
        }
        tex_image = new QD3DImage(device, image);
        cache.insert(image.cacheKey(), tex_image, cost);
//         qDebug() << "==> total cache cost: " << cache.totalCost() << cost;
    }

    return tex_image->texture;
}

void QD3DImageManager::remove(quint64 key)
{
//     QList<quint64> keys = cache.keys();
//     if (keys.contains(key))
//         qDebug() << "entery removed from cache";
    cache.remove(key);
}

Q_GLOBAL_STATIC(QD3DImageManager, qd3d_image_cache)

static void qd3d_image_cleanup(qint64 key)
{
//     qDebug() << "qd3d_image_cleanup:";
//     qDebug("  => key: 0x%08x%08x", (uint) (key >> 32), (uint)(key & 0xffffffff));
    qd3d_image_cache()->remove(key);
}

//
// end D3D image cache stuff
//

class QD3DDrawHelper : public QTessellator
{
public:
    QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe);
    ~QD3DDrawHelper();

    bool needsFlushing() const;
    QD3DMaskPosition allocateMaskPosition(const QRectF &brect, bool *breakbatch);

    void setClipPath(const QPainterPath &path, QD3DBatchItem **item);

    void queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect);
    QRectF queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color);

    void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect);
    void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color);

    void queueTextGlyph(const QRectF &rect, const qreal *tex_coords, QD3DBatchItem *item,
                        D3DCOLOR color);

    void queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect);
    void queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item);

    int drawAntialiasedMask(int offset, int maxoffset);
    void drawAliasedMask(int offset);
    void drawAntialiasedBoundingRect(QD3DBatchItem *item);
    void drawAliasedBoundingRect(QD3DBatchItem *item);
    void drawTextItem(QD3DBatchItem *item);
    void drawAliasedLines(QD3DBatchItem *item);

    void setMaskSize(QSize size);

    void beforeReset();
    void afterReset();

    IDirect3DSurface9 *freeMaskSurface();

    inline void lockVertexBuffer();
    inline void unlockVertexBuffer();

    inline int index() { return m_index; }

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    enum VertexBufferAccess {
        CLEAR   = 0x00,
        READ    = 0x01,
        WRITE   = 0x02
    };
    int accesscontrol[QT_VERTEX_BUF_SIZE];
#endif

private:
    void addTrap(const Trapezoid &trap);
    void tessellate(const QPolygonF &poly);
    inline void lineToStencil(qreal x, qreal y);
    inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
    QRectF pathToVertexArrays(const QPainterPath &path);
    void resetMask();

    QDirect3DPaintEnginePrivate *m_pe;

    qreal m_xoffset, m_yoffset;
    int m_startindex;
    int m_index;
    int m_height, m_width;
    LPDIRECT3DVERTEXBUFFER9 m_d3dvbuff;
    vertex *m_vbuff;
    QD3DBatchItem *m_item;
    QRectF m_boundingRect;

    qreal max_x;
    qreal max_y;
    qreal min_x;
    qreal min_y;
    qreal firstx;
    qreal firsty;

    QPointF tess_lastpoint;
    int tess_index;

    bool m_locked;
    IDirect3DTexture9 *m_mask;
    IDirect3DSurface9 *m_maskSurface;
    IDirect3DSurface9 *m_depthStencilSurface;

    D3DCOLOR m_color;
    bool m_clearmask;
    bool m_isLine;
    bool m_firstPoint;

    QD3DMaskPosition m_mask_position;
    int m_mask_offsetX2;
    int m_mask_offsetY2;
};

QD3DStateManager::QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect)
    : m_pDevice(pDevice), m_effect(effect), m_refs(0)
{
    if (FAILED(D3DXMatrixIdentity(&m_d3dIdentityMatrix))) {
        qWarning("QDirect3DPaintEngine: D3DXMatrixIdentity failed");
    }
    reset();
}

void QD3DStateManager::reset()
{
    m_radgradfd = -1;

    m_cosmetic_pen = false;
    m_pass = -1;
    m_maskchannel = -1;
    m_brushmode = -1;
    m_texture = 0;
    m_xoffset = INT_MAX;
    m_yoffset = INT_MAX;

    m_vertexshader = 0;
    m_pixelshader = 0;

    m_isIdentity = true;
    m_transformation = QTransform();
    m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix);

    ZeroMemory(&m_projection, sizeof(D3DMATRIX));
    ZeroMemory(m_textures, sizeof(LPDIRECT3DBASETEXTURE9) * D3D_STAGE_COUNT);
    FillMemory(m_samplerstates, sizeof(DWORD) * D3D_SAMPLE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE);
    FillMemory(m_texturestates, sizeof(DWORD) * D3D_TEXTURE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE);
    FillMemory(m_renderstate, sizeof(DWORD) * D3D_RENDER_STATES, 0xFFFFFFFE);
}

inline void QD3DStateManager::beginPass(int pass)
{
    if (pass != m_pass) {
        if (m_pass != -1)
            m_effect->EndPass();
        m_effect->BeginPass(pass);
        m_pass = pass;
    }
}

inline void QD3DStateManager::endPass()
{
    if (m_pass != -1) {
        m_pass = -1;
        m_effect->EndPass();
    }
}

inline void QD3DStateManager::startStateBlock() {
    m_changed = false;
}

inline void QD3DStateManager::setCosmeticPen(bool enabled)
{
    if (enabled != m_cosmetic_pen) {
        m_effect->SetBool("g_mCosmeticPen", enabled);
        m_cosmetic_pen = enabled;
        m_changed = true;
    }
}

inline void QD3DStateManager::setBrushMode(int mode)
{
    if (mode != m_brushmode) {
        m_effect->SetInt("g_mBrushMode", mode);
        m_brushmode = mode;
        m_changed = true;
    }
}

inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture)
{
    SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
    SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);

    if (pTexture != m_texture) {
        m_texture = pTexture;
        m_effect->SetTexture("g_mTexture", pTexture);
        m_changed = true;
    }
}

inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread)
{
    switch(spread) {
        case QGradient::RepeatSpread:
            SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
            SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
            break;
        case QGradient::ReflectSpread:
            SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
            SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
            break;
        default:
            SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
            SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
            break;
    };

    if (pTexture != m_texture) {
        m_texture = pTexture;
        m_effect->SetTexture("g_mTexture", pTexture);
        m_changed = true;
    }
}

inline void QD3DStateManager::setTransformation(const QTransform *matrix)
{
    if (matrix) {
        if (*matrix != m_transformation) {
            D3DXMATRIX dxmatrix(matrix->m11(), matrix->m12(), 0, matrix->m13(),
                    matrix->m21(), matrix->m22(), 0, matrix->m23(),
                    0, 0, 1, 0,
                    matrix->dx(), matrix->dy(), 0, 1);
            m_effect->SetMatrix("g_mTransformation", &dxmatrix);
            m_transformation = *matrix;
            m_changed = true;
            m_isIdentity = false;
        }
    } else if (!m_isIdentity) {
        m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix);
        m_transformation = QTransform();
        m_changed = true;
        m_isIdentity = true;
    }
}

inline void QD3DStateManager::setProjection(const D3DXMATRIX *pMatrix)
{
    if (*pMatrix != m_projection) {
        m_effect->SetMatrix("g_mViewProjection", pMatrix);
        m_projection = *pMatrix;
        m_changed = true;
    }
}

inline void QD3DStateManager::setFocalDistance(const qreal &fd)
{
    if (fd != m_radgradfd) {
        m_effect->SetFloat("g_mFocalDist", fd);
        m_changed = true;
        m_radgradfd = fd;
    }
}

inline void QD3DStateManager::setMaskOffset(qreal x, qreal y)
{
    if (x != m_xoffset || y != m_yoffset) {
        float offset[2] = {x, y};
        m_effect->SetFloatArray("g_mMaskOffset", offset, 2);
        m_xoffset = x;
        m_yoffset = y;
        m_changed = true;
    }
}

inline void QD3DStateManager::setMaskChannel(int channel)
{
    if (m_maskchannel != channel) {
        m_effect->SetIntArray("g_mChannel", m_mask_channels[channel], 4);
        m_maskchannel = channel;
        m_changed = true;
    }
}

inline void QD3DStateManager::endStateBlock()
{
    if (m_changed) {
        m_effect->CommitChanges();
        m_changed = false;
    }
}

STDMETHODIMP QD3DStateManager::QueryInterface(REFIID iid, LPVOID *ppv)
{
    if(iid == IID_IUnknown || iid == IID_ID3DXEffectStateManager)
    {
        *ppv = this;
        ++m_refs;
        return NOERROR;
    }
    *ppv = NULL;
    return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) QD3DStateManager::AddRef(void)
{
    return (ULONG)InterlockedIncrement( &m_refs );
}


STDMETHODIMP_(ULONG) QD3DStateManager::Release(void)
{
    if( 0L == InterlockedDecrement( &m_refs ) ) {
        delete this;
        return 0L;
    }

    return m_refs;
}
STDMETHODIMP QD3DStateManager::SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix)
{
    return m_pDevice->SetTransform(State, pMatrix);
}

STDMETHODIMP QD3DStateManager::SetMaterial(CONST D3DMATERIAL9 *pMaterial)
{
    return m_pDevice->SetMaterial(pMaterial);
}

STDMETHODIMP QD3DStateManager::SetLight(DWORD Index, CONST D3DLIGHT9 *pLight)
{
    return m_pDevice->SetLight(Index, pLight);
}

STDMETHODIMP QD3DStateManager::LightEnable(DWORD Index, BOOL Enable)
{
    return m_pDevice->LightEnable(Index, Enable);
}

STDMETHODIMP QD3DStateManager::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value)
{
    if (State < D3D_RENDER_STATES) {
        if (m_renderstate[State] == Value)
            return S_OK;
        m_renderstate[State] = Value;
    }
    return m_pDevice->SetRenderState(State, Value);
}

STDMETHODIMP QD3DStateManager::SetTexture(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture)
{
    if (Stage < D3D_STAGE_COUNT) {
        if (m_textures[Stage] == pTexture)
            return S_OK;
        m_textures[Stage] = pTexture;
    }
    return m_pDevice->SetTexture(Stage, pTexture);
}

STDMETHODIMP QD3DStateManager::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value)
{
    if (Stage < D3D_STAGE_COUNT && Type < D3D_TEXTURE_STATES) {
        if (m_texturestates[Stage][Type] == Value)
            return S_OK;
        m_texturestates[Stage][Type] = Value;
    }
    return m_pDevice->SetTextureStageState(Stage, Type, Value);
}

STDMETHODIMP QD3DStateManager::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value)
{
    if (Sampler < D3D_STAGE_COUNT && Type < D3D_SAMPLE_STATES) {
        if (m_samplerstates[Sampler][Type] == Value)
            return S_OK;
        m_samplerstates[Sampler][Type] = Value;
    }
    return m_pDevice->SetSamplerState(Sampler, Type, Value);
}

STDMETHODIMP QD3DStateManager::SetNPatchMode(FLOAT NumSegments)
{
    return m_pDevice->SetNPatchMode(NumSegments);
}

STDMETHODIMP QD3DStateManager::SetFVF(DWORD FVF)
{
    return m_pDevice->SetFVF(FVF);
}

STDMETHODIMP QD3DStateManager::SetVertexShader(LPDIRECT3DVERTEXSHADER9 pShader)
{
    if (m_vertexshader == pShader)
        return S_OK;
    m_vertexshader = pShader;
    return m_pDevice->SetVertexShader(pShader);
}

STDMETHODIMP QD3DStateManager::SetVertexShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetVertexShaderConstantF(RegisterIndex, pConstantData, RegisterCount);
}

STDMETHODIMP QD3DStateManager::SetVertexShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetVertexShaderConstantI(RegisterIndex, pConstantData, RegisterCount);
}

STDMETHODIMP QD3DStateManager::SetVertexShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetVertexShaderConstantB(RegisterIndex, pConstantData, RegisterCount);
}

STDMETHODIMP QD3DStateManager::SetPixelShader(LPDIRECT3DPIXELSHADER9 pShader)
{
    if (m_pixelshader == pShader)
        return S_OK;
    m_pixelshader = pShader;
    return m_pDevice->SetPixelShader(pShader);
}

STDMETHODIMP QD3DStateManager::SetPixelShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetPixelShaderConstantF(RegisterIndex, pConstantData, RegisterCount);
}

STDMETHODIMP QD3DStateManager::SetPixelShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetPixelShaderConstantI(RegisterIndex, pConstantData, RegisterCount);
}

STDMETHODIMP QD3DStateManager::SetPixelShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount)
{
    return m_pDevice->SetPixelShaderConstantB(RegisterIndex, pConstantData, RegisterCount);
}

#define QD3D_GRADIENT_CACHE_SIZE    60
#define QD3D_GRADIENT_PALETTE_SIZE  1024

class QD3DGradientCache
{
    struct CacheInfo
    {
        inline CacheInfo(QGradientStops s, qreal op) :
            stops(s), opacity(op) {}

        IDirect3DTexture9 *texture;
        QGradientStops stops;
        qreal opacity;
    };

    typedef QMultiHash<quint64, CacheInfo> QD3DGradientColorTableHash;

public:
    QD3DGradientCache(LPDIRECT3DDEVICE9 device);
    ~QD3DGradientCache();

    inline IDirect3DTexture9 *getBuffer(const QGradientStops &stops, qreal opacity);

protected:
    inline void generateGradientColorTable(const QGradientStops& s,
                                           uint *colorTable,
                                           int size, qreal opacity) const;
    IDirect3DTexture9 *addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity);
    void cleanCache();

    QD3DGradientColorTableHash cache;
    LPDIRECT3DDEVICE9 m_device;
};

QD3DGradientCache::QD3DGradientCache(LPDIRECT3DDEVICE9 device)
    : m_device(device)
{

}

QD3DGradientCache::~QD3DGradientCache()
{
    cleanCache();
}

inline IDirect3DTexture9 *QD3DGradientCache::getBuffer(const QGradientStops &stops, qreal opacity)
{
    quint64 hash_val = 0;

    for (int i = 0; i < stops.size() && i <= 2; i++)
        hash_val += stops[i].second.rgba();

    QD3DGradientColorTableHash::const_iterator it = cache.constFind(hash_val);

    if (it == cache.constEnd())
        return addCacheElement(hash_val, stops, opacity);
    else {
        do {
            const CacheInfo &cache_info = it.value();
            if (cache_info.stops == stops && cache_info.opacity == opacity) {
                return cache_info.texture;
            }
            ++it;
        } while (it != cache.constEnd() && it.key() == hash_val);
        // an exact match for these stops and opacity was not found, create new cache
        return addCacheElement(hash_val, stops, opacity);
    }
}

void QD3DGradientCache::generateGradientColorTable(const QGradientStops& s, uint *colorTable, int size, qreal opacity) const
{
    int pos = 0;
    qreal fpos = 0.0;
    qreal incr = 1.0 / qreal(size);
    QVector<uint> colors(s.size());

    for (int i = 0; i < s.size(); ++i)
        colors[i] = s[i].second.rgba();

    uint alpha = qRound(opacity * 255);
    while (fpos < s.first().first) {
        colorTable[pos] = ARGB_COMBINE_ALPHA(colors[0], alpha);
        pos++;
        fpos += incr;
    }

    for (int i = 0; i < s.size() - 1; ++i) {
        qreal delta = 1/(s[i+1].first - s[i].first);
        while (fpos < s[i+1].first && pos < size) {
            int dist = int(256 * ((fpos - s[i].first) * delta));
            int idist = 256 - dist;
            uint current_color = ARGB_COMBINE_ALPHA(colors[i], alpha);
            uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
            colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
#else
            uint c = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
            colorTable[pos] = ( (c << 24) & 0xff000000)
                              | ((c >> 24) & 0x000000ff)
                              | ((c << 8) & 0x00ff0000)
                              | ((c >> 8) & 0x0000ff00);
#endif // Q_BYTE_ORDER
            ++pos;
            fpos += incr;
        }
    }
    for (;pos < size; ++pos)
        colorTable[pos] = colors[s.size() - 1];
}

IDirect3DTexture9 *QD3DGradientCache::addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity)
{
    if (cache.size() == QD3D_GRADIENT_CACHE_SIZE) {
        int elem_to_remove = qrand() % QD3D_GRADIENT_CACHE_SIZE;
        uint key = cache.keys()[elem_to_remove];

        // need to call release on each removed cache entry:
        QD3DGradientColorTableHash::const_iterator it = cache.constFind(key);
        do {
            it.value().texture->Release();
        } while (++it != cache.constEnd() && it.key() == key);

        cache.remove(key); // may remove more than 1, but OK
    }

    CacheInfo cache_entry(stops, opacity);
    uint buffer[QD3D_GRADIENT_PALETTE_SIZE];
    generateGradientColorTable(stops, buffer, QD3D_GRADIENT_PALETTE_SIZE, opacity);

    if (FAILED(m_device->CreateTexture(QD3D_GRADIENT_PALETTE_SIZE, 1, 1, 0,
                    D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &cache_entry.texture, 0))) {
        qWarning("QD3DGradientCache::addCacheElement(): unable to create Direct3D texture.");
        return 0;
    }

    D3DLOCKED_RECT rect;
    if (FAILED(cache_entry.texture->LockRect(0, &rect, 0, 0))) {
        qDebug() << "QD3DGradientCache::addCacheElement(): unable to lock texture rect.";
        return 0;
    }
    memcpy(rect.pBits, buffer, rect.Pitch);
    cache_entry.texture->UnlockRect(0);

    return cache.insert(hash_val, cache_entry).value().texture;
}

void QD3DGradientCache::cleanCache()
{
    QD3DGradientColorTableHash::const_iterator it = cache.constBegin();
    for (; it != cache.constEnd(); ++it) {
        const CacheInfo &cache_info = it.value();
        cache_info.texture->Release();
    }
    cache.clear();
}

QD3DSurfaceManager::QD3DSurfaceManager() :
    m_status(NoStatus), m_dummy(0), m_device(0), m_pd(0), m_current(0)
{

}

QD3DSurfaceManager::~QD3DSurfaceManager()
{
}

void QD3DSurfaceManager::setPaintDevice(QPaintDevice *pd)
{
    m_status = NoStatus;
    m_pd = pd;
    m_current = 0;

    if (m_device->TestCooperativeLevel() != D3D_OK) {
        m_status = NeedsResetting;
        return;
    }

    m_current = m_swapchains.value(pd, 0);
    QWidget *w = static_cast<QWidget*>(pd);

    if (m_current) {
        if (m_current->size != w->size()) {
            m_swapchains.remove(pd);
            m_current->surface->Release();
            m_current->swapchain->Release();
            delete m_current;
            m_current = 0;
        }
    }

    if (!m_current) {
        m_current = createSwapChain(w);
        updateMaxSize();
    }
}

int QD3DSurfaceManager::status() const
{
    return m_status;
}

void QD3DSurfaceManager::reset()
{
    QList<QPaintDevice *> pds = m_swapchains.keys();

    QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
    while (i != m_swapchains.constEnd()) {
        i.value()->surface->Release();
        i.value()->swapchain->Release();
        ++i;
    }
    qDeleteAll(m_swapchains.values());
    m_swapchains.clear();

    D3DPRESENT_PARAMETERS params;
    initPresentParameters(&params);
    params.hDeviceWindow = m_dummy;

    HRESULT res = m_device->Reset(&params);
    if (FAILED(res)) {
        switch (res) {
            case D3DERR_DEVICELOST:
                qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DEVICELOST)");
                break;
            case D3DERR_DRIVERINTERNALERROR:
                qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DRIVERINTERNALERROR)");
                break;
            case D3DERR_OUTOFVIDEOMEMORY:
                qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_OUTOFVIDEOMEMORY)");
                break;
            default:
                qWarning("QDirect3DPaintEngine: Reset failed");
        };
    }

    for (int i=0; i<pds.count(); ++i) {
        QWidget *w = static_cast<QWidget*>(pds.at(i));
        createSwapChain(w);
    }

    // reset the mask as well
    m_status = MaxSizeChanged;

    setPaintDevice(m_pd);
    updateMaxSize();
}

LPDIRECT3DSURFACE9 QD3DSurfaceManager::renderTarget()
{
    return m_current ? m_current->surface : 0;
}

LPDIRECT3DSURFACE9 QD3DSurfaceManager::surface(QPaintDevice *pd)
{
    D3DSwapChain *swapchain = m_swapchains.value(pd, 0);
    return swapchain ? swapchain->surface : 0;
}

LPDIRECT3DSWAPCHAIN9 QD3DSurfaceManager::swapChain(QPaintDevice *pd)
{
    D3DSwapChain *swapchain = m_swapchains.value(pd, 0);
    return swapchain ? swapchain->swapchain : 0;
}

void QD3DSurfaceManager::releasePaintDevice(QPaintDevice *pd)
{
    D3DSwapChain *swapchain = m_swapchains.take(pd);

    if (swapchain) {
        swapchain->surface->Release();
        swapchain->swapchain->Release();
        delete swapchain;
        if (swapchain == m_current)
            m_current = 0;
    }
}

LPDIRECT3DDEVICE9 QD3DSurfaceManager::device()
{
    return m_device;
}

void QD3DSurfaceManager::cleanup()
{
    QPixmapCache::clear();
    qd3d_glyph_cache()->cleanCache();

    // release doomed textures
    for (int k=0; k<qd3d_release_list.size(); ++k)
        qd3d_release_list.at(k)->Release();
    qd3d_release_list.clear();

    QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
    while (i != m_swapchains.constEnd()) {
        i.value()->surface->Release();
        i.value()->swapchain->Release();
        ++i;
    }
    qDeleteAll(m_swapchains.values());

    if (m_device)
        m_device->Release();

    DestroyWindow(m_dummy);
    QString cname(QLatin1String("qt_d3d_dummy"));
    QT_WA({
        UnregisterClass((TCHAR*)cname.utf16(), (HINSTANCE)qWinAppInst());
    } , {
        UnregisterClassA(cname.toLatin1(), (HINSTANCE)qWinAppInst());
    });
}

QSize QD3DSurfaceManager::maxSize() const
{
    return m_max_size;
}

extern "C" {
    LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
};

void QD3DSurfaceManager::init(LPDIRECT3D9 object)
{
    QString cname(QLatin1String("qt_d3d_dummy"));
    uint style = CS_DBLCLKS | CS_SAVEBITS;
    ATOM atom;
    QT_WA({
        WNDCLASS wc;
        wc.style         = style;
        wc.lpfnWndProc   = (WNDPROC)QtWndProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = (HINSTANCE)qWinAppInst();
        wc.hIcon         = 0;
        wc.hCursor       = 0;
        wc.hbrBackground = 0;
        wc.lpszMenuName  = 0;
        wc.lpszClassName = (TCHAR*)cname.utf16();
        atom = RegisterClass(&wc);
    } , {
        WNDCLASSA wc;
        wc.style         = style;
        wc.lpfnWndProc   = (WNDPROC)QtWndProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = (HINSTANCE)qWinAppInst();
        wc.hIcon         = 0;
        wc.hCursor       = 0;
        wc.hbrBackground = 0;
        wc.lpszMenuName  = 0;
        QByteArray tempArray = cname.toLatin1();
        wc.lpszClassName = tempArray;
        atom = RegisterClassA(&wc);
    });

    QT_WA({
        const TCHAR *className = (TCHAR*)cname.utf16();
        m_dummy = CreateWindow(className, className, 0,
                               0, 0, 1, 1,
                               0, 0, qWinAppInst(), 0);
    } , {
        m_dummy = CreateWindowA(cname.toLatin1(), cname.toLatin1(), 0,
                                0, 0, 1, 1,
                                0, 0, qWinAppInst(), 0);
    });

    D3DPRESENT_PARAMETERS params;
    initPresentParameters(&params);
    params.hDeviceWindow = m_dummy;

    HRESULT res = object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 0,
        D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES|D3DCREATE_FPU_PRESERVE,
        &params, &m_device);

    if (FAILED(res) || m_device == 0)
        qWarning("QDirect3DPaintEngine: failed to create Direct3D device (error=0x%x).", res);
}

void QD3DSurfaceManager::updateMaxSize()
{
    int w = 0, h = 0;
    QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
    while (i != m_swapchains.constEnd()) {

        int nw = i.key()->width();
        if (nw > w)
            w = nw;

        int nh = i.key()->height();
        if (nh > h)
            h = nh;

        ++i;
    }

    QSize newsize = QSize(w, h);
    if (newsize != m_max_size) {
        m_status |= MaxSizeChanged;
        m_max_size = newsize;
    }
}

void QD3DSurfaceManager::initPresentParameters(D3DPRESENT_PARAMETERS *params)
{
    ZeroMemory(params, sizeof(D3DPRESENT_PARAMETERS));
    params->Windowed = true;
    params->SwapEffect = D3DSWAPEFFECT_COPY;
    params->BackBufferFormat = D3DFMT_UNKNOWN;
    params->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
    params->Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
}

QD3DSurfaceManager::D3DSwapChain *QD3DSurfaceManager::createSwapChain(QWidget *w)
{
    D3DPRESENT_PARAMETERS params;
    initPresentParameters(&params);
    params.hDeviceWindow = w->winId();
    D3DSwapChain *swapchain = new D3DSwapChain();
    swapchain->size = w->size();
    if (FAILED(m_device->CreateAdditionalSwapChain(&params, &swapchain->swapchain)))
        qWarning("QDirect3DPaintEngine: CreateAdditionalSwapChain failed");
    if (FAILED(swapchain->swapchain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapchain->surface)))
        qWarning("QDirect3DPaintEngine: GetBackBuffer failed");
    m_swapchains.insert(w, swapchain);
    connect(w, SIGNAL(destroyed(QObject *)), SLOT(cleanupPaintDevice(QObject *)));

    // init with background color
    QColor bg = w->palette().color(QPalette::Background);
    m_device->ColorFill(swapchain->surface, 0, D3DCOLOR_ARGB(bg.alpha(), bg.red(),bg.green(),bg.blue()));

    return swapchain;
}

void QD3DSurfaceManager::cleanupPaintDevice(QObject *object)
{
    QWidget *w = static_cast<QWidget *>(object);
    releasePaintDevice(w);
}

int QD3DStateManager::m_mask_channels[4][4] =
    {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};

QD3DDrawHelper::QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe)
    : m_pe(pe), m_d3dvbuff(0), m_maskSurface(0), m_depthStencilSurface(0),
      m_locked(false), m_mask(0), m_startindex(0), m_index(0), m_vbuff(0), m_clearmask(true),
      m_isLine(false), m_firstPoint(true)
{
    resetMask();
#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess));
#endif

    // create vertex buffer
    afterReset();
}

QD3DDrawHelper::~QD3DDrawHelper()
{
    if (m_maskSurface)
        m_maskSurface->Release();

    if (m_mask)
        m_mask->Release();

    if (m_depthStencilSurface)
        m_depthStencilSurface->Release();

    if (m_d3dvbuff)
        m_d3dvbuff->Release();
}

inline void QD3DDrawHelper::lockVertexBuffer()
{
    if (!m_locked) {
        DWORD lockflags = D3DLOCK_NOOVERWRITE;
        if (m_startindex >= QT_VERTEX_RESET_LIMIT) {
            m_startindex = 0;
            m_index = 0;

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
            for (int i=0; i<QT_VERTEX_BUF_SIZE; ++i) {
                if (accesscontrol[i] != (WRITE|READ) && accesscontrol[i] != CLEAR)
                    qDebug() << "Vertex Buffer: Access Error";
            }
            memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess));
#endif

            lockflags = D3DLOCK_DISCARD;
        }

        if (FAILED(m_d3dvbuff->Lock(0, 0, (void**)&m_vbuff, lockflags))) {
            qWarning() << "QDirect3DPaintEngine: unable to lock vertex buffer.";
        }
        m_locked = true;
    }
}

inline void QD3DDrawHelper::unlockVertexBuffer()
{
    if (m_locked) {
        if (FAILED(m_d3dvbuff->Unlock())) {
            qWarning() << "QDirect3DPaintEngine: unable to unlock vertex buffer.";
        }
        m_locked = false;
    }
}

void QD3DDrawHelper::setClipPath(const QPainterPath &path, QD3DBatchItem **item)
{
    lockVertexBuffer();

    m_item = *item;
    m_item->m_maskpos.x = m_item->m_maskpos.y = 0;
    m_item->m_maskpos.channel = 3;
    m_item->m_info |= QD3DBatchItem::BI_CLIP;

    bool winding = (path.fillRule() == Qt::WindingFill);
    if (winding)
        m_item->m_info |= QD3DBatchItem::BI_WINDING;

    if (!path.isEmpty()) {
        m_item->m_info |= QD3DBatchItem::BI_MASK;
        m_item->m_info &= ~QD3DBatchItem::BI_AA;
        m_color = 0;
        QRectF brect = pathToVertexArrays(path);
        queueRect(brect, m_item, 0);
    }

    *item = m_item;
}



void QD3DDrawHelper::queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect)
{
    lockVertexBuffer();

    m_item = *item;
    m_item->m_info |= QD3DBatchItem::BI_MASK;
    setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING);

    int xoffset = m_item->m_maskpos.x;
    int yoffset = m_item->m_maskpos.y;

    int x = brect.left();
    int y = brect.top();

    m_item->m_xoffset = (xoffset - x) + 1;
    m_item->m_yoffset = (yoffset - y) + 1;

    m_boundingRect = brect;
    tessellate(poly);

    *item = m_item;
}

QRectF QD3DDrawHelper::queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color)
{
    lockVertexBuffer();

    m_color = color;
    m_item = *item;
    m_item->m_info |= QD3DBatchItem::BI_MASK;

    bool winding = (path.fillRule() == Qt::WindingFill);
    if (winding)
        m_item->m_info |= QD3DBatchItem::BI_WINDING;

    QRectF result = pathToVertexArrays(path);
    *item = m_item;
    return result;
}

// used for drawing aliased transformed rects directly
// don't use for antialiased or masked drawing
void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect)
{
    lockVertexBuffer();

    qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f;
    item->m_info |= QD3DBatchItem::BI_BRECT;

    // if the item does not have a mask, the offset is different
    if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
        item->m_offset = m_index;
        item->m_count = (item->m_info & QD3DBatchItem::BI_AA) ? 0 : -2;
    }

    qreal x1 = rect.left();
    qreal y1 = rect.top();
    qreal x2 = rect.right();
    qreal y2 = rect.bottom();

    QPointF tc = trect.at(0);
    vertex v1 = { {x1, y1, zval} , color,
          tc.x(), tc.y(), 0.f, 0.f,
          0.f   , 0.f   , 0.f, 0.f };

    tc = trect.at(1);
    vertex v2 = { {x2, y1, zval} , color,
          tc.x(), tc.y(), 0.f, 0.f,
          0.f   , 0.f   , 0.f, 0.f};

    tc = trect.at(2);
    vertex v3 = { {x2, y2, zval} , color,
          tc.x(), tc.y(), 0.f, 0.f,
          0.f   , 0.f   , 0.f, 0.f};;

    tc = trect.at(3);
    vertex v4 = { {x1, y2, zval} , color,
          tc.x(), tc.y(), 0.f, 0.f,
          0.f   , 0.f   , 0.f, 0.f};

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    for (int i=m_index; i<(m_index + 4); ++i) {
        if ((m_index + 4) > QT_VERTEX_BUF_SIZE)
            qDebug() << "Vertex Buffer: Buffer overflow";
        if (accesscontrol[i] != CLEAR)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[i] |= WRITE;
    }
#endif

    m_vbuff[m_index++] = v1;
    m_vbuff[m_index++] = v2;
    m_vbuff[m_index++] = v3;
    m_vbuff[m_index++] = v4;

    m_startindex = m_index;
}


QD3DMaskPosition QD3DDrawHelper::allocateMaskPosition(const QRectF &brect, bool *breakbatch)
{
    int w = brect.width();
    int h = brect.height();

    w += 3;
    h += 3;

    if (w > m_width)
        w = m_width;
    if (h > m_height)
        h = m_height;

    *breakbatch = false;

    if ((m_height - m_mask_offsetY2) >= h && (m_width - m_mask_position.x) >= w) {
        m_mask_position.y = m_mask_offsetY2;
    } else if ((m_width - m_mask_offsetX2) >= w) {
        m_mask_position.y = QD3D_MASK_MARGIN;
        m_mask_position.x = m_mask_offsetX2;
    } else if (m_mask_position.channel < 3) {
        ++m_mask_position.channel;
        m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN;
        m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN;
    } else {
        resetMask();
        *breakbatch = true;
    }

    int newoffset = m_mask_position.x + w;
    if (m_mask_offsetX2 < newoffset)
        m_mask_offsetX2 = newoffset;
    m_mask_offsetY2 = (m_mask_position.y + h);

    return m_mask_position;

}

void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color)
{
    lockVertexBuffer();

    QRectF brect;
    item->m_info |= QD3DBatchItem::BI_BRECT;
    qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f;

    if (item->m_info & QD3DBatchItem::BI_AA) {
        int xoffset = item->m_maskpos.x;
        int yoffset = item->m_maskpos.y;

        int x = rect.left();
        int y = rect.top();

        brect = QRectF(x, y, rect.width() + 1, rect.height() + 1);

        item->m_xoffset = (xoffset - x) + 1;
        item->m_yoffset = (yoffset - y) + 1;

        // if the item does not have a mask, the offset is different
        if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
            item->m_offset = m_index;
            item->m_count = 0;
        }
    } else {
        brect = rect;

        if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
            item->m_offset = m_index;
            item->m_count = -2;
        }
    }

    qreal left = brect.left();
    qreal right = brect.right();
    qreal top = brect.top();
    qreal bottom = brect.bottom();

    vertex v1 = { {left, bottom, zval}, color,
        0.f, 0.f, 0.f, 0.f,
        0.f, 0.f, 0.f, 0.f};
    vertex v2 = { {left, top, zval}, color,
        0.f, 0.f, 0.f, 0.f,
        0.f, 0.f, 0.f, 0.f};
    vertex v3 = { {right, top, zval}, color,
        0.f, 0.f, 0.f, 0.f,
        0.f, 0.f, 0.f, 0.f};
    vertex v4 = { {right, bottom, zval}, color,
        0.f, 0.f, 0.f, 0.f,
        0.f, 0.f, 0.f, 0.f};

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    for (int i=m_index; i<(m_index + 4); ++i) {
        if ((m_index + 4) > QT_VERTEX_BUF_SIZE)
            qDebug() << "Vertex Buffer: Buffer overflow";
        if (accesscontrol[i] != CLEAR)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[i] |= WRITE;
    }
#endif

    m_vbuff[m_index++] = v1;
    m_vbuff[m_index++] = v2;
    m_vbuff[m_index++] = v3;
    m_vbuff[m_index++] = v4;

    m_startindex = m_index;
}

void QD3DDrawHelper::queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect)
{
    lockVertexBuffer();

    m_item = *item;
    m_item->m_info |= QD3DBatchItem::BI_MASK;
    setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING);

    int xoffset = m_item->m_maskpos.x;
    int yoffset = m_item->m_maskpos.y;
    int x = brect.left();
    int y = brect.top();

    m_item->m_xoffset = (xoffset - x) + 1;
    m_item->m_yoffset = (yoffset - y) + 1;

    m_boundingRect = brect;

    m_xoffset = (x - xoffset) + 0.5f;
    m_yoffset = (y - yoffset) + 0.5f;

    QPointF last;
    for (int i = 0; i < path.elementCount(); ++i) {
        QPainterPath::Element element = path.elementAt(i);

        //Q_ASSERT(!element.isCurveTo());

        if (element.isLineTo())
            QTessellator::tessellateRect(last, element, m_item->m_width);

        last = element;
    }

    m_item->m_offset = m_startindex;
    m_item->m_count = ( m_index - m_startindex ) / 3;
    m_startindex = m_index;

    *item = m_item;
}

void QD3DDrawHelper::queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item)
{
    lockVertexBuffer();

    m_item = *item;
    m_item->m_info |= QD3DBatchItem::BI_FASTLINE;

    for (int i=0; i<lineCount; ++i) {
        const QLineF line = lines[i];
        qreal p1x = line.p1().x();
        qreal p1y = line.p1().y();
        qreal p2x = line.p2().x();
        qreal p2y = line.p2().y();

        vertex v1 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
                    -1.f, -1.f, p2x, p2y,
                    0.f, 0.f, 0.f, 0.f };
        vertex v2 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
                    1.f, -1.f, p2x, p2y,
                    0.f, 0.f, 0.f, 0.f };
        vertex v3 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
                    1.f, 1.f, p2x, p2y,
                    0.f, 0.f, 0.f, 0.f };
        vertex v4 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
                    -1.f, 1.f, p2x, p2y,
                    0.f, 0.f, 0.f, 0.f };

        m_vbuff[m_index++] = v1;
        m_vbuff[m_index++] = v2;
        m_vbuff[m_index++] = v4;
        m_vbuff[m_index++] = v4;
        m_vbuff[m_index++] = v2;
        m_vbuff[m_index++] = v3;

        if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
            m_item->m_offset = m_startindex;
            m_item->m_count = ( m_index - m_startindex ) / 2;
            m_startindex = m_index;

            QD3DBatchItem itemcopy = *m_item;
            m_item = m_pe->nextBatchItem();
            *m_item = itemcopy;

            lockVertexBuffer();
        }
    }

    m_item->m_offset = m_startindex;
    m_item->m_count = ( m_index - m_startindex ) - 2;
    m_startindex = m_index;

    *item = m_item;
}

void QD3DDrawHelper::queueTextGlyph(const QRectF &rect, const qreal *tex_coords,
                                      QD3DBatchItem *item, D3DCOLOR color)
{
    lockVertexBuffer();

    qreal x1 = rect.left();
    qreal y1 = rect.top();
    qreal x2 = rect.right();
    qreal y2 = rect.bottom();

    vertex v1 = { {x1, y1, 0.5f}, color,
                 tex_coords[0], tex_coords[1], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};
    vertex v2 = { {x2, y1, 0.5f}, color,
                 tex_coords[2], tex_coords[1], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};
    vertex v3 = { {x2, y2, 0.5f}, color,
                 tex_coords[2], tex_coords[3], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};
    vertex v4 = { {x1, y1, 0.5f}, color,
                 tex_coords[0], tex_coords[1], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};
    vertex v5 = { {x2, y2, 0.5f}, color,
                 tex_coords[2], tex_coords[3], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};
    vertex v6 = { {x1, y2, 0.5f}, color,
                 tex_coords[0], tex_coords[3], 0.f, 0.f,
                 0.f   , 0.f   , 0.f, 0.f};

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    for (int i=m_index; i<(m_index + 6); ++i) {
        if ((m_index + 6) > QT_VERTEX_BUF_SIZE)
            qDebug() << "Vertex Buffer: Buffer overflow";
        if (accesscontrol[i] != CLEAR)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[i] |= WRITE;
    }
#endif

    m_vbuff[m_index++] = v1;
    m_vbuff[m_index++] = v2;
    m_vbuff[m_index++] = v3;
    m_vbuff[m_index++] = v4;
    m_vbuff[m_index++] = v5;
    m_vbuff[m_index++] = v6;

    m_startindex = m_index;
    ++item->m_count;
}

bool QD3DDrawHelper::needsFlushing() const
{
    return (m_pe->m_batch.m_item_index >= QD3D_BATCH_SIZE || m_startindex >= QT_VERTEX_RESET_LIMIT);
}

void QD3DDrawHelper::setMaskSize(QSize size)
{
    m_width = size.width();
    m_height = size.height();

    if (m_maskSurface)
        m_maskSurface->Release();

    if (m_mask)
        m_mask->Release();

    if (FAILED(m_pe->m_d3d_device->CreateTexture(m_width, m_height, 1, D3DUSAGE_RENDERTARGET,
        D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_mask, NULL))) {
            qWarning() << "QDirect3DPaintEngine: CreateTexture() failed.";
    }

    if (m_depthStencilSurface)
        m_depthStencilSurface->Release();

    if (FAILED(m_pe->m_d3d_device->CreateDepthStencilSurface(m_width, m_height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0,
                                                   TRUE, &m_depthStencilSurface, NULL))) {
            qWarning() << "QDirect3DPaintEngine: CreateDepthStencilSurface() failed.";
    }

    m_pe->m_d3d_device->SetDepthStencilSurface(m_depthStencilSurface);
    m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0, 0.0f, 0);

    if (FAILED(m_mask->GetSurfaceLevel(0, &m_maskSurface))) {
        qWarning() << "QDirect3DPaintEngine: GetSurfaceLevel() failed.";
    }

    m_pe->m_d3d_device->ColorFill(m_maskSurface, 0, D3DCOLOR_ARGB(0,0,0,0));
    D3DXMATRIX projMatrix;
    pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, m_width, m_height, 0, 0, 1);
    m_pe->m_effect->SetMatrix("g_mMaskProjection", &projMatrix);
    m_pe->m_effect->SetTexture("g_mAAMask", m_mask);
}

void QD3DDrawHelper::beforeReset()
{
    resetMask();
    m_clearmask = true;

    if (m_maskSurface) {
        m_maskSurface->Release();
        m_maskSurface = 0;
    }

    if (m_mask) {
        m_mask->Release();
        m_mask = 0;
    }

    if (m_depthStencilSurface) {
        m_depthStencilSurface->Release();
        m_depthStencilSurface = 0;
    }

    if (m_d3dvbuff)
        m_d3dvbuff->Release();
}

void QD3DDrawHelper::afterReset()
{
    if (FAILED(m_pe->m_d3d_device->CreateVertexBuffer(QT_VERTEX_BUF_SIZE*sizeof(vertex), D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
        QD3DFVF_CSVERTEX,
        D3DPOOL_DEFAULT, &m_d3dvbuff, NULL))) {
            qWarning() << "QDirect3DPaintEngine: failed to create vertex buffer.";
    }

    m_pe->m_d3d_device->SetStreamSource(0, m_d3dvbuff, 0, sizeof(vertex));
    m_pe->m_d3d_device->SetFVF(QD3DFVF_CSVERTEX);

    m_startindex = 0;
    m_index = 0;
}

IDirect3DSurface9 *QD3DDrawHelper::freeMaskSurface()
{
     // we need to make sure the mask is cleared when it's used for something else
    resetMask();
    m_clearmask = true;

    return m_maskSurface;
}

int QD3DDrawHelper::drawAntialiasedMask(int offset, int maxoffset)
{
    int newoffset = offset;
    QD3DBatchItem *item = &(m_pe->m_batch.items[offset]);

    // set mask as render target
    if (FAILED(m_pe->m_d3d_device->SetRenderTarget(0, m_maskSurface)))
        qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";

    if (m_clearmask) {
        m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_TARGET,D3DCOLOR_ARGB(0,0,0,0), 0, 0);
        m_clearmask = false;
    }

    // fill the mask
    m_pe->m_statemanager->beginPass(PASS_AA_CREATEMASK);
    for (; newoffset<maxoffset; ++newoffset) {
        item = &(m_pe->m_batch.items[newoffset]);
        if (!(item->m_info & QD3DBatchItem::BI_AA) || !(item->m_info & QD3DBatchItem::BI_MASK)) {
            break;
        } else if (item->m_info & QD3DBatchItem::BI_MASKFULL) {
            item->m_info &= ~QD3DBatchItem::BI_MASKFULL;
            m_clearmask = true;
            break;
        }

        m_pe->m_statemanager->startStateBlock();
        if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) {
            RECT rect;
            QRectF srect = item->m_brect.adjusted(item->m_xoffset, item->m_yoffset,
                item->m_xoffset, item->m_yoffset);
            rect.left = qMax(qRound(srect.left()), 0);
            rect.top = qMax(qRound(srect.top()), 0);
            rect.bottom = qMin(m_height, qRound(srect.bottom()));
            rect.right = qMin(m_width, qRound(srect.right()));
            m_pe->m_d3d_device->SetScissorRect(&rect);
            m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
        }
        m_pe->m_statemanager->setMaskChannel(item->m_maskpos.channel);
        m_pe->m_statemanager->endStateBlock();

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
        int vbstart = item->m_offset;
        for (int i=vbstart; i<(vbstart + (item->m_count * 3)); ++i) {
            if (accesscontrol[i] != WRITE)
                qDebug() << "Vertex Buffer: Access Error";
            accesscontrol[i] |= READ;
        }
#endif

        m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count);

        if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) {
            m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
        }
    }
    m_pe->m_statemanager->endPass();

    return newoffset;
}

void QD3DDrawHelper::drawAliasedMask(int offset)
{
    QD3DBatchItem *item = &(m_pe->m_batch.items[offset]);
    if (item->m_info & QD3DBatchItem::BI_MASK) {
        m_pe->m_statemanager->beginPass( (item->m_info & QD3DBatchItem::BI_WINDING) ? PASS_STENCIL_WINDING : PASS_STENCIL_ODDEVEN );
        int prev_stop = 0;
        for (int i=0; i<item->m_pointstops.count(); ++i) {
            int stop = item->m_pointstops.at(i);

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
            int vbstart = (item->m_offset + prev_stop);
            for (int j=vbstart; j<(vbstart+(stop - prev_stop)); ++j) {
                if (accesscontrol[j] != WRITE)
                    qDebug() << "Vertex Buffer: Access Error";
                accesscontrol[j] |= READ;
            }
#endif
            m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + prev_stop, (stop - prev_stop) - 2);
            prev_stop = stop;
        }
        m_pe->m_statemanager->endPass();
    }
}

void QD3DDrawHelper::drawTextItem(QD3DBatchItem *item)
{
#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    int vbstart = item->m_offset;
    for (int j=vbstart; j<(vbstart + ((item->m_count * 2) * 3)); ++j) {
        if (accesscontrol[j] != WRITE)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[j] |= READ;
    }
#endif
    m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count*2);
}

void QD3DDrawHelper::drawAliasedLines(QD3DBatchItem *item)
{
    m_pe->m_statemanager->setCosmeticPen(item->m_info & QD3DBatchItem::BI_COSMETICPEN);
    if (item->m_info & QD3DBatchItem::BI_TRANSFORM) {
        m_pe->m_statemanager->setTransformation(&item->m_matrix);
    } else {
        m_pe->m_statemanager->setTransformation();
    }
    int pass = (item->m_info & QD3DBatchItem::BI_MASK)
               ? PASS_ALIASED_LINES
               : PASS_ALIASED_LINES_DIRECT;
    m_pe->m_statemanager->beginPass(pass);
    m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, (item->m_count + 2) / 3);
    m_pe->m_statemanager->endPass();
}

void QD3DDrawHelper::drawAntialiasedBoundingRect(QD3DBatchItem *item)
{
    if (item->m_info & QD3DBatchItem::BI_SCISSOR) {
        RECT rect;
        rect.left = qMax(qRound(item->m_brect.left()), 0);
        rect.top = qMax(qRound(item->m_brect.top()), 0);
        rect.bottom = qMin(m_height, qRound(item->m_brect.bottom()));
        rect.right = qMin(m_width, qRound(item->m_brect.right()));
        m_pe->m_d3d_device->SetScissorRect(&rect);
        m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
    }

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    int vbstart = item->m_offset + (item->m_count * 3);
    for (int j=vbstart; j<(vbstart + 4); ++j) {
        if (accesscontrol[j] != WRITE)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[j] |= READ;
    }
#endif

    m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + (item->m_count * 3), 2);

    if (item->m_info & QD3DBatchItem::BI_SCISSOR) {
        m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
    }
}

void QD3DDrawHelper::drawAliasedBoundingRect(QD3DBatchItem *item)
{
#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    int vbstart = (item->m_offset + item->m_count + 2);
    for (int j=vbstart; j<(vbstart + 4); ++j) {
        if (accesscontrol[j] != WRITE)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[j] |= READ;
    }
#endif

    m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + item->m_count + 2, 2);
}

void QD3DDrawHelper::addTrap(const Trapezoid &trap)
{
    qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y) - m_yoffset;
    qreal topLeftX = Q27Dot5ToDouble(trap.topLeft->x) - m_xoffset;
    qreal topRightY = Q27Dot5ToDouble(trap.topRight->y) - m_yoffset;
    qreal topRightX = Q27Dot5ToDouble(trap.topRight->x) - m_xoffset;
    qreal top = Q27Dot5ToDouble(trap.top) - m_yoffset;
    qreal bottom = Q27Dot5ToDouble(trap.bottom) - m_yoffset;

    Q27Dot5 _h = trap.topLeft->y - trap.bottomLeft->y;
    Q27Dot5 _w = trap.topLeft->x - trap.bottomLeft->x;
    qreal _leftA = (qreal)_w/_h;
    qreal _leftB = topLeftX - _leftA * topLeftY;

    _h = trap.topRight->y - trap.bottomRight->y;
    _w = trap.topRight->x - trap.bottomRight->x;
    qreal _rightA = (qreal)_w/_h;
    qreal _rightB = topRightX - _rightA * topRightY;

    qreal invLeftA = qFuzzyCompare(_leftA + 1, 1) ? 0.0 : 1.0 / _leftA;
    qreal invRightA = qFuzzyCompare(_rightA + 1, 1) ? 0.0 : 1.0 / _rightA;

    vertex v1 = { {1.f, top - 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};
    vertex v2 = { {0.f, top - 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};
    vertex v3 = { {0.f, bottom + 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};

    vertex v4 = { {1.f, top - 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};
    vertex v5 = { {0.f, bottom + 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};
    vertex v6 = { {1.f, bottom + 1.f, 0.5f}, 0.f,
        top, bottom, invLeftA, -invRightA,
        _leftA, _leftB, _rightA, _rightB};

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    for (int i=m_index; i<(m_index + 6); ++i) {
        if ((m_index + 6) > QT_VERTEX_BUF_SIZE)
            qDebug() << "Vertex Buffer: Buffer overflow";
        if (accesscontrol[i] != CLEAR)
            qDebug() << "Vertex Buffer: Access Error";
        accesscontrol[i] |= WRITE;
    }
#endif

    m_vbuff[m_index++] = v1;
    m_vbuff[m_index++] = v2;
    m_vbuff[m_index++] = v3;
    m_vbuff[m_index++] = v4;
    m_vbuff[m_index++] = v5;
    m_vbuff[m_index++] = v6;

    // check if buffer is full
    if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
        m_item->m_offset = m_startindex;
        m_item->m_count = ( m_index - m_startindex ) / 3;
        m_startindex = m_index;

        QD3DBatchItem itemcopy = *m_item;
        m_item = m_pe->nextBatchItem();
        *m_item = itemcopy;
        m_item->m_info &= ~QD3DBatchItem::BI_MASKFULL;

        lockVertexBuffer();
    }
}

void QD3DDrawHelper::tessellate(const QPolygonF &poly) {
    int xoffset = m_item->m_maskpos.x;
    int yoffset = m_item->m_maskpos.y;

    int x = m_boundingRect.left();
    int y = m_boundingRect.top();
    m_xoffset = (x - xoffset) + 0.5f;
    m_yoffset = (y - yoffset) + 0.5f;

    QTessellator::tessellate(poly.data(), poly.count());

    m_item->m_offset = m_startindex;
    m_item->m_count = ( m_index - m_startindex ) / 3;
    m_startindex = m_index;
}

inline void QD3DDrawHelper::lineToStencil(qreal x, qreal y)
{
    QPointF lastPt = tess_lastpoint;
    tess_lastpoint = QPointF(x, y);

    if (m_isLine && m_firstPoint)
        return;


#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
    if (m_index > QT_VERTEX_BUF_SIZE)
        qDebug() << "Vertex Buffer: Buffer overflow";
    if (accesscontrol[m_index] != CLEAR)
        qDebug() << "Vertex Buffer: Access Error";
    accesscontrol[m_index] |= WRITE;
#endif

    vertex v;
    if (m_isLine) {
        vertex v1 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
                     -1.f, -1.f, x, y,
                     0.f, 0.f, 0.f, 0.f};
        vertex v2 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
                     1.f, -1.f, x, y,
                     0.f, 0.f, 0.f, 0.f};
        vertex v3 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
                     1.f, 1.f, x, y,
                     0.f, 0.f, 0.f, 0.f};
        vertex v4 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
                     -1.f, 1.f, x, y,
                     0.f, 0.f, 0.f, 0.f};
        m_vbuff[m_index++] = v1;
        m_vbuff[m_index++] = v2;
        m_vbuff[m_index++] = v4;
        m_vbuff[m_index++] = v4;
        m_vbuff[m_index++] = v2;
        m_vbuff[m_index++] = v3;
    } else {
        vertex v1 = { {x, y, 0.5f}, m_color,
                     0.f, 0.f, 0.f, 0.f,
                     0.f, 0.f, 0.f, 0.f};
        m_vbuff[m_index++] = v1;
        v = v1;
    }
    ++tess_index;

    // check if buffer is full
    if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
        int firstindex = m_startindex;
        if (!m_item->m_pointstops.isEmpty())
            firstindex = m_item->m_pointstops.last();

        vertex first = m_vbuff[firstindex];

        // finish current polygon
        m_item->m_pointstops.append(tess_index);
        m_item->m_offset = m_startindex;
        m_startindex = m_index;

        // copy item
        QD3DBatchItem itemcopy = *m_item;
        m_item = m_pe->nextBatchItem();
        *m_item = itemcopy;

        // start new polygon
        lockVertexBuffer();
        m_item->m_pointstops.clear();
        if (!m_isLine) {
            tess_index = 2;

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
            if (accesscontrol[m_index] != CLEAR)
                qDebug() << "Vertex Buffer: Access Error";
            accesscontrol[m_index] |= WRITE;
#endif

            m_vbuff[m_index++] = first;

#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
            if (accesscontrol[m_index] != CLEAR)
                qDebug() << "Vertex Buffer: Access Error";
            accesscontrol[m_index] |= WRITE;
#endif

            m_vbuff[m_index++] = v;
        } else {
            tess_index = 0;
        }
    }

    if (x > max_x)
        max_x = x;
    else if (x < min_x)
        min_x = x;
    if (y > max_y)
        max_y = y;
    else if (y < min_y)
        min_y = y;
}

inline void QD3DDrawHelper::curveToStencil(const QPointF &cp1, const QPointF &cp2,
                                             const QPointF &ep)
{
    qreal inverseScale = 0.5f;
    qreal inverseScaleHalf = inverseScale / 2;

    QBezier beziers[32];
    beziers[0] = QBezier::fromPoints(tess_lastpoint, cp1, cp2, ep);
    QBezier *b = beziers;
    while (b >= beziers) {
        // check if we can pop the top bezier curve from the stack
        qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
        qreal d;
        if (l > inverseScale) {
            d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
                + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
            d /= l;
        } else {
            d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
                qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
        }
        if (d < inverseScaleHalf || b == beziers + 31) {
            // good enough, we pop it off and add the endpoint
            lineToStencil(b->x4, b->y4);
            --b;
        } else {
            // split, second half of the polygon goes lower into the stack
            b->split(b+1, b);
           ++b;
        }
    }
}

QRectF QD3DDrawHelper::pathToVertexArrays(const QPainterPath &path)
{
    m_isLine = (m_item->m_info & QD3DBatchItem::BI_FASTLINE);
    const QPainterPath::Element &first = path.elementAt(0);
    firstx = first.x;
    firsty = first.y;
    min_x = max_x = firstx;
    min_y = max_y = firsty;

    m_firstPoint = true;
    tess_index = 0;
    m_item->m_pointstops.clear();
    lineToStencil(firstx, firsty);
    m_firstPoint = false;

    for (int i=1; i<path.elementCount(); ++i) {
        const QPainterPath::Element &e = path.elementAt(i);
        switch (e.type) {
        case QPainterPath::MoveToElement:
            m_item->m_pointstops.append(tess_index);
            m_firstPoint = true;
            lineToStencil(e.x, e.y);
            m_firstPoint = false;
            break;
        case QPainterPath::LineToElement:
            lineToStencil(e.x, e.y);
            break;
        case QPainterPath::CurveToElement:
            curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
            i+=2;
            break;
        default:
            break;
        }
    }

    if (!m_isLine)
        lineToStencil(firstx, firsty);

    m_item->m_pointstops.append(tess_index);

    m_item->m_offset = m_startindex;
    m_item->m_count = ( m_index - m_startindex ) - 2;
    m_startindex = m_index;

    QRectF result;
    result.setLeft(min_x);
    result.setRight(max_x);
    result.setTop(min_y);
    result.setBottom(max_y);

    if (m_isLine)
        result.adjust(0,0,1,1);

    return result;
}

void QD3DDrawHelper::resetMask()
{
    m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN;
    m_mask_position.channel = 0;
    m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN;
}


static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
    QPainterPathStroker stroker;
    if (cpen.style() == Qt::CustomDashLine)
        stroker.setDashPattern(cpen.dashPattern());
    else
        stroker.setDashPattern(cpen.style());

    stroker.setCapStyle(cpen.capStyle());
    stroker.setJoinStyle(cpen.joinStyle());
    stroker.setMiterLimit(cpen.miterLimit());
    stroker.setWidth(cpen.widthF());

    QPainterPath stroke = stroker.createStroke(path);
    stroke.setFillRule(Qt::WindingFill);
    return stroke;
}


QDirect3DPaintEnginePrivate::~QDirect3DPaintEnginePrivate()
{

}

void QDirect3DPaintEnginePrivate::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
{
    //#### remove me
    QRegion r(path.toFillPolygon().toPolygon(), path.fillRule());
    updateClipRegion(r, op);

/*    if (m_draw_helper->needsFlushing())
        flushBatch();

    if (op == Qt::IntersectClip && !has_clipping)
        op = Qt::ReplaceClip;

    // switch to paths
    if (!m_has_complex_clipping) {
        m_clip_path = QPainterPath();
        m_clip_path.addRegion(m_clip_region);
        m_clip_region = QRegion();
        m_sysclip_path = QPainterPath();
        m_sysclip_path.addRegion(m_sysclip_region);
        m_sysclip_region = QRegion();
        m_has_complex_clipping = true;
    }

    QPainterPath cpath = m_matrix.map(path);

    QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++];
    item->m_info = QD3DBatchItem::BI_COMPLEXCLIP;

    switch (op) {
        case Qt::UniteClip:
            has_clipping = true;
            m_clip_path = m_clip_path.united(cpath);
            break;
        case Qt::ReplaceClip:
            has_clipping = true;
            m_clip_path = cpath;
            break;
        case Qt::NoClip:
            m_has_complex_clipping = false;
            has_clipping = false;
            item->m_info |= QD3DBatchItem::BI_CLEARCLIP;
            break;
        default: // intersect clip
            has_clipping = true;
            m_clip_path = m_clip_path.intersected(cpath);
            break;
    }

    if (!m_sysclip_path.isEmpty()) {
        item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP;
        if (has_clipping)
            m_clip_path = m_clip_path.intersected(m_sysclip_path);
        else
            m_clip_path = m_sysclip_path;
    }

    // update the aliased clipping mask
    m_draw_helper->setClipPath(m_clip_path, item);

    // update the antialiased clipping mask
    if (m_draw_helper->needsFlushing())
        flushBatch();

    QD3DBatchItem *aaitem = &m_batch.items[m_batch.m_item_index++];
    aaitem->m_info = item->m_info|QD3DBatchItem::BI_AA;
    m_draw_helper->setClipPath(m_clip_path, aaitem); */
}

extern QPainterPath qt_regionToPath(const QRegion &region);

void QDirect3DPaintEnginePrivate::updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op)
{
    if (m_draw_helper->needsFlushing())
        flushBatch();
    if (m_has_complex_clipping) {
        QPainterPath path = qt_regionToPath(clipregion);
        updateClipPath(path, op);
        return;
    }

    if (op == Qt::IntersectClip && m_clip_region.isEmpty())
        op = Qt::ReplaceClip;

    QRegion cregion = m_matrix.map(clipregion);

    QD3DBatchItem *item = nextBatchItem();
    item->m_info &= ~QD3DBatchItem::BI_AA;

    switch (op) {
        case Qt::UniteClip:
            m_clip_region = m_clip_region.united(cregion);
            break;
        case Qt::ReplaceClip:
            m_clip_region = cregion;
            break;
        case Qt::NoClip:
            m_clip_region = QRegion();
            item->m_info |= QD3DBatchItem::BI_CLEARCLIP;
            break;
        default: // intersect clip
            m_clip_region = m_clip_region.intersected(cregion);
            break;
    }

    QRegion crgn = m_clip_region;
    if (!m_sysclip_region.isEmpty()) {
        item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP;
        if (!crgn.isEmpty())
            crgn = crgn.intersected(m_sysclip_region);
        else
            crgn = m_sysclip_region;
    }

    QPainterPath path = qt_regionToPath(crgn);
    m_draw_helper->setClipPath(path, &item);
}

void QDirect3DPaintEnginePrivate::updateFont(const QFont &)
{
}

void QDirect3DPaintEnginePrivate::setRenderTechnique(RenderTechnique technique)
{
    if (m_current_technique != technique) {
        if (m_current_technique != RT_NoTechnique)
            m_effect->End();

        if (technique == RT_Aliased) {
            m_effect->SetTechnique("Aliased");
            m_effect->Begin(0,D3DXFX_DONOTSAVESTATE);
        } else if (technique == RT_Antialiased) {
            m_effect->SetTechnique("Antialiased");
            m_effect->Begin(0,D3DXFX_DONOTSAVESTATE);
        }
    }

    m_current_technique = technique;
}

/*QPolygonF QDirect3DPaintEnginePrivate::transformedRect(const QRectF &brect) const
{
    QPolygonF poly(brect);
    return m_matrix.map(poly);
}

QPolygonF QDirect3DPaintEnginePrivate::calcTextureCoords(const QPolygonF &trect) const
{
    QPolygonF result(4);
    QRectF brect = trect.boundingRect();
    qreal angle = atan(trect.at(0).x() -
}

QPolygonF QDirect3DPaintEnginePrivate::offsetTextureCoords(const QRectF &brect, const QPolygonF &trect) const
{

}*/

inline QD3DBatchItem *QDirect3DPaintEnginePrivate::nextBatchItem()
{
    if (m_draw_helper->needsFlushing())
        flushBatch();

    QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++];
    item->m_info = m_current_state;
    item->m_cmode = m_cmode;
    return item;
}

qreal calculateAngle(qreal dx, qreal dy)
{
    qreal angle;

    if (qFuzzyCompare(dx + 1, 1)) {
        angle = (dy < 0) ? -M_PI/2 : M_PI/2;
    } else {
        angle = atanf(dy/dx);
        if (dx < 0)
            angle += M_PI;
    }

    return angle;
}

QPolygonF QDirect3DPaintEnginePrivate::brushCoordinates(const QRectF &r, bool stroke, qreal *fd) const
{
    QBrush brush;
    QTransform matrix;
    Qt::BrushStyle style;

    if (stroke) {
        brush = m_pen.brush();
        matrix = m_inv_pen_matrix;
        style = m_pen_brush_style;
    } else {
        brush = m_brush;
        matrix = m_inv_brush_matrix;
        style = m_brush_style;
    }

    QPolygonF bpoly;
    switch(style) {
        case Qt::TexturePattern: {
            QTransform totxcoords;
            QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
            totxcoords.scale(1.0f/brush.texture().width(),
                1.0f/brush.texture().height());
            bpoly = matrix.map(QPolygonF(adj_brect));
            bpoly = totxcoords.map(bpoly);
            break; }
        case Qt::LinearGradientPattern: {
            const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
            QPointF start = g->start();
            QPointF stop = g->finalStop();
            qreal dx = stop.x() - start.x();
            qreal dy = stop.y() - start.y();
            qreal length = sqrt(dx * dx + dy * dy);
            qreal angle = calculateAngle(dx, dy);
            QTransform totxcoords;
            QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
            totxcoords.scale(1.0f/length, 1.0f/length);
            totxcoords.rotateRadians(-angle);
            totxcoords.translate(-start.x(), -start.y());
            bpoly = matrix.map(QPolygonF(adj_brect));
            bpoly = totxcoords.map(bpoly);
            break; }
        case Qt::ConicalGradientPattern: {
            const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
            QPointF center = g->center();
            qreal angle = g->angle();
            QTransform totxcoords;
            totxcoords.rotate(angle);
            totxcoords.translate(-center.x(), -center.y());
            bpoly = matrix.map(QPolygonF(r));
            bpoly = totxcoords.map(bpoly);
            break; }
        case Qt::RadialGradientPattern: {
            const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
            QPointF center = g->center();
            QPointF focalpoint = g->focalPoint();
            qreal dx = focalpoint.x() - center.x();
            qreal dy = focalpoint.y() - center.y();
            qreal radius = g->radius();
            *fd = sqrt(dx * dx + dy * dy) / radius;
            qreal angle = calculateAngle(dx, dy);
            QTransform totxcoords;
            totxcoords.scale(1.0f/radius, 1.0f/radius);
            totxcoords.rotateRadians(-angle);
            totxcoords.translate(-center.x(), -center.y());
            bpoly = matrix.map(QPolygonF(r));
            bpoly = totxcoords.map(bpoly);
            break; }
        default: {
            QTransform totxcoords;
            QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
            QPixmap pat = getPattern(style);
            totxcoords.scale(1.0f/pat.width(),
                1.0f/pat.height());
            bpoly = matrix.map(QPolygonF(adj_brect));
            bpoly = totxcoords.map(bpoly); }
    };

    return bpoly;
}

void QDirect3DPaintEnginePrivate::strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform)
{
    D3DCOLOR solid_color;
    QD3DBatchItem *item = nextBatchItem();

    if (!txform.isIdentity())
        path = txform.map(path);

    QRectF trect;
    QPolygonF txcoord;

    solid_color = m_pen_color;
    bool has_complex_brush = false;
    if (m_pen_brush_style != Qt::SolidPattern) {
        has_complex_brush = true;
        item->m_brush = m_pen.brush();
        item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH;
        item->m_opacity = m_opacity;
    }

    if (m_has_fast_pen) {
        item->m_info |= QD3DBatchItem::BI_FASTLINE;
        if (m_pen_brush_style == Qt::SolidPattern) {
            m_draw_helper->queueAliasedMask(path, &item, solid_color);
            item->m_info &= ~QD3DBatchItem::BI_MASK; // bypass stencil buffer
            return;
        }
    }

    QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0);

    if (has_complex_brush) {
        trect = brect;
        txcoord = brushCoordinates(brect, true, &item->m_distance);
        item->m_info |= QD3DBatchItem::BI_TRANSFORM;
        item->m_matrix = m_matrix;
    } else {
        trect = txrect;
        static const QPolygonF empty_poly(4);
        txcoord = empty_poly;
    }

    m_draw_helper->queueRect(trect, item, solid_color, txcoord);
}

void QDirect3DPaintEnginePrivate::fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform)
{
    D3DCOLOR solid_color;
    QD3DBatchItem *item = nextBatchItem();

    if (!txform.isIdentity())
        path = txform.map(path);

    QRectF trect;
    QPolygonF txcoord;

    solid_color = m_brush_color;
    bool has_complex_brush = false;
    if (m_brush_style != Qt::SolidPattern) {
        has_complex_brush = true;
        item->m_brush = m_brush;
        item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH;
        item->m_opacity = m_opacity;
    }

    QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0);

    if (has_complex_brush) {
        trect = brect;
        txcoord = brushCoordinates(brect, false, &item->m_distance);
        item->m_info |= QD3DBatchItem::BI_TRANSFORM;
        item->m_matrix = m_matrix;
    } else {
        trect = txrect;
        static const QPolygonF empty_poly(4);
        txcoord = empty_poly;
    }

    m_draw_helper->queueRect(trect, item, solid_color, txcoord);
}

void QDirect3DPaintEnginePrivate::fillAntialiasedPath(const QPainterPath &path, const QRectF &brect,
                                                      const QTransform &txform, bool stroke)
{
    D3DCOLOR solid_color;
    bool winding = (path.fillRule() == Qt::WindingFill);
    QPolygonF poly;
    QRectF txrect;
    QPainterPath tpath;

    if (m_has_aa_fast_pen && stroke) {
        tpath = txform.map(path);
        txrect = tpath.controlPointRect();
        txrect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width);
    } else {
        poly = path.toFillPolygon(txform);
        txrect = poly.boundingRect();
    }

    // brect = approx. bounding rect before transformation
    // txrect = exact bounding rect after transformation
    // trect = the rectangle to be drawn
    // txcoord = the texture coordinates
    // adj_txrect = adjusted rect to include aliased outline

    bool use_scissor = false;
    if (txrect.left() < 0) {
        txrect.adjust(-txrect.left(),0,0,0);
        use_scissor = true;
    }
    if (txrect.top() < 0) {
        txrect.adjust(0,-txrect.top(),0,0);
        use_scissor = true;
    }

    if (!txrect.isValid())
        return;

    QD3DBatchItem *item = nextBatchItem();

    QRectF adj_txrect = txrect.adjusted(-1,-1,1,1);
    QRectF trect;
    QPolygonF txcoord;

    bool has_complex_brush = false;
    if (stroke) {
        solid_color = m_pen_color;
        if (m_pen_brush_style != Qt::SolidPattern) {
            has_complex_brush = true;
            item->m_brush = m_pen.brush();
        }
        item->m_width = m_pen_width;
    } else {
        solid_color = m_brush_color;
        if (m_brush_style != Qt::SolidPattern) {
            has_complex_brush = true;
            item->m_brush = m_brush;
        }
    }

    qreal focaldist = 0;
    if (has_complex_brush) {
        trect = brect;
        txcoord = brushCoordinates(brect, stroke, &focaldist);
    } else {
        trect = adj_txrect;
        static const QPolygonF empty_poly(4);
        txcoord = empty_poly;
    }

    bool maskfull;
    item->m_maskpos = m_draw_helper->allocateMaskPosition(txrect, &maskfull);
    if (maskfull)
        item->m_info |= QD3DBatchItem::BI_MASKFULL;
    item->m_distance = focaldist;

    if (winding)
        item->m_info |= QD3DBatchItem::BI_WINDING;

    if (has_complex_brush) {
        item->m_info |= QD3DBatchItem::BI_SCISSOR|QD3DBatchItem::BI_COMPLEXBRUSH|
            QD3DBatchItem::BI_TRANSFORM;
        item->m_brect = adj_txrect;
        item->m_matrix = m_matrix;
        item->m_opacity = m_opacity;
    }
    if (use_scissor) {
        item->m_info |= QD3DBatchItem::BI_MASKSCISSOR;
        item->m_brect = adj_txrect;
    }

    if (m_has_aa_fast_pen && stroke) {
        m_draw_helper->queueAntialiasedLines(tpath, &item, txrect);
    } else {
        m_draw_helper->queueAntialiasedMask(poly, &item, txrect);
    }

    m_draw_helper->queueRect(trect, item, solid_color, txcoord);
}

QPainterPath QDirect3DPaintEnginePrivate::strokePathFastPen(const QPainterPath &path)
{
    QPainterPath result;
    QBezier beziers[32];
    for (int i=0; i<path.elementCount(); ++i) {
        const QPainterPath::Element &e = path.elementAt(i);
        switch (e.type) {
        case QPainterPath::MoveToElement:
            result.moveTo(e.x, e.y);
            break;
        case QPainterPath::LineToElement:
            result.lineTo(e.x, e.y);
            break;

        case QPainterPath::CurveToElement:
        {
            QPointF sp = path.elementAt(i-1);
            QPointF cp2 = path.elementAt(i+1);
            QPointF ep = path.elementAt(i+2);
            i+=2;

            qreal inverseScaleHalf = m_inv_scale / 2;
            beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
            QBezier *b = beziers;
            while (b >= beziers) {
                // check if we can pop the top bezier curve from the stack
                qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
                qreal d;
                if (l > m_inv_scale) {
                    d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
                              - (b->y4 - b->y1)*(b->x1 - b->x2) )
                        + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
                                - (b->y4 - b->y1)*(b->x1 - b->x3) );
                    d /= l;
                } else {
                    d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
                        qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
                }
                if (d < inverseScaleHalf || b == beziers + 31) {
                    // good enough, we pop it off and add the endpoint
                    result.lineTo(b->x4, b->y4);
                    --b;
                } else {
                    // split, second half of the polygon goes lower into the stack
                    b->split(b+1, b);
                    ++b;
                }
            }
        } // case CurveToElement
        default:
            break;
        } // end of switch
    }
    return result;
}

void QDirect3DPaintEnginePrivate::strokePath(const QPainterPath &path, QRectF brect, bool simple)
{
    QTransform txform;
    QPainterPath tpath;

    if (m_has_fast_pen || m_has_aa_fast_pen) {
        if (!simple)
            tpath = strokePathFastPen(path);
        else
            tpath = path;  //already only lines
    } else {
        tpath = strokeForPath(path, m_pen);
    }

    if (tpath.isEmpty())
        return;

    //brect is null if the path is not transformed
    if (brect.isNull())
        txform = m_matrix;

    if (!brect.isNull()) {
        // brect is set when the path is transformed already,
        // this is the case when we have a cosmetic pen.
        brect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width);
    }

    if (brect.isNull())
        brect = tpath.controlPointRect();
    brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing

    if (m_current_state & QD3DBatchItem::BI_AA) {
        fillAntialiasedPath(tpath, brect, txform, true);
    } else {
        strokeAliasedPath(tpath, brect, txform);
    }
}

void QDirect3DPaintEnginePrivate::fillPath(const QPainterPath &path, QRectF brect)
{
    QTransform txform;

    //brect is null if the path is not transformed
    if (brect.isNull())
        txform = m_matrix;

    if (brect.isNull())
        brect = path.controlPointRect();
    brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing

    if (m_current_state & QD3DBatchItem::BI_AA) {
        fillAntialiasedPath(path, brect, txform, false);
    } else {
        fillAliasedPath(path, brect, txform);
    }
}


bool QDirect3DPaintEnginePrivate::init()
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEnginePrivate::init()";
#endif

    m_draw_helper = 0;
    m_gradient_cache = 0;
    m_dc = 0;
    m_dcsurface = 0;

    m_supports_d3d = false;
    m_current_state = 0;
    m_in_scene = false;
    m_has_fast_pen = false;
    m_has_aa_fast_pen = false;
    m_has_pen = false;
    m_has_brush = false;
    m_pen_color = 0;
    m_brush_color = 0;
    m_current_surface = 0;
    m_batch.m_item_index = 0;
    m_current_technique = RT_NoTechnique;

    if (!pDirect3DCreate9) {
        QLibrary d3d_lib(QLatin1String("d3d9.dll"));
        pDirect3DCreate9 = (PFNDIRECT3DCREATE9) d3d_lib.resolve("Direct3DCreate9");
        if (!pDirect3DCreate9) {
            qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3d9.dll.\n"
                     "Make sure you have the DirectX run-time installed.");
            return false;
        }
    }

    if (!pD3DXCreateBuffer || !pD3DXCreateEffect || !pD3DXMatrixOrthoOffCenterLH) {
        QLibrary d3dx_lib(QLatin1String("d3dx9_32.dll"));
        pD3DXCreateBuffer = (PFND3DXCREATEBUFFER) d3dx_lib.resolve("D3DXCreateBuffer");
        pD3DXCreateEffect = (PFND3DXCREATEEFFECT) d3dx_lib.resolve("D3DXCreateEffect");
        pD3DXMatrixOrthoOffCenterLH = (PFND3DXMATRIXORTHOOFFCENTERLH)
                                        d3dx_lib.resolve("D3DXMatrixOrthoOffCenterLH");
        if (!(pD3DXCreateBuffer && pD3DXCreateEffect && pD3DXMatrixOrthoOffCenterLH)) {
            qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3dx9_32.dll.\n"
                     "Make sure you have the DirectX run-time installed.");
            return false;
        }
    }

    if (!m_d3d_object) {
        m_d3d_object = pDirect3DCreate9(D3D_SDK_VERSION);
        if (!m_d3d_object) {
            qWarning("QDirect3DPaintEngine: failed to create Direct3D object.\n"
                     "Direct3D support in Qt will be disabled.");
            return false;
        }
    }

    m_supports_d3d = testCaps();
    if (!m_supports_d3d)
        return false;

    m_surface_manager.init(m_d3d_object);
    m_d3d_device = m_surface_manager.device();

    if (!m_d3d_device)
        return false;

    /* load shaders */
    QFile file(QLatin1String(":/qpaintengine_d3d.fx"));
    QByteArray fxFile;
    if (file.open(QFile::ReadOnly))
        fxFile = file.readAll();

    if (fxFile.size() > 0) {
        LPD3DXBUFFER compout;
        pD3DXCreateBuffer(4096, &compout);
        DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE|D3DXFX_DONOTSAVESTATE|D3DXSHADER_OPTIMIZATION_LEVEL3;
        if(FAILED(pD3DXCreateEffect(m_d3d_device, fxFile.constData(), fxFile.size(),
                                    NULL, NULL, dwShaderFlags, NULL, &m_effect, &compout))) {
            qWarning("QDirect3DPaintEngine: failed to compile effect file");
            if (compout)
                qWarning((char *)compout->GetBufferPointer());
            m_supports_d3d = false;
            return false;
        }
        if (m_effect) {
            m_statemanager = new QD3DStateManager(m_d3d_device, m_effect);
            m_effect->SetStateManager(m_statemanager);
            m_draw_helper = new QD3DDrawHelper(this);
            initDevice();
            m_gradient_cache = new QD3DGradientCache(m_d3d_device);
        }
    } else {
        return false;
    }

    return true;
}

QPixmap QDirect3DPaintEnginePrivate::getPattern(Qt::BrushStyle style) const
{
    if (!m_patterns.contains(style)) {
        QImage img(16,16,QImage::Format_ARGB32);
        img.fill(0);
        QPainter p(&img);
        p.setBrush(QBrush(Qt::white, style));
        p.setPen(Qt::NoPen);
        p.drawRect(0,0,16,16);
        p.end();
        QPixmap pattern(QPixmap::fromImage(img));
        QDirect3DPaintEnginePrivate *ct = const_cast<QDirect3DPaintEnginePrivate *>(this);
        ct->verifyTexture(pattern);
        ct->m_patterns.insert(style, pattern);
    }

    return m_patterns.value(style);
}

bool QDirect3DPaintEnginePrivate::testCaps()
{
    D3DCAPS9 caps;
    if (FAILED(m_d3d_object->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps)))
        return false;

    if ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE)
        && (caps.DevCaps & D3DDEVCAPS_PUREDEVICE)
        && (caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST)
        && (caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED))
        return true;
#if 0
    qDebug() << "Direct3D caps:";
    qDebug() << "D3DPRESENT_INTERVAL_IMMEDIATE:" << ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) != 0);
    qDebug() << "D3DDEVCAPS_PUREDEVICE:" << ((caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0);
    qDebug() << "D3DPRASTERCAPS_SCISSORTEST:" << ((caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST) != 0);
    qDebug() << "D3DSTENCILCAPS_TWOSIDED:" << ((caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0);
#endif
    return false;
}

void QDirect3DPaintEnginePrivate::initDevice()
{
    m_statemanager->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    m_statemanager->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    m_statemanager->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
    m_statemanager->SetRenderState(D3DRS_LIGHTING, FALSE);
    m_statemanager->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTALPHA);
}

void QDirect3DPaintEnginePrivate::updatePen(const QPen &pen)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::updatePen";
#endif
    m_pen = pen;
    m_has_cosmetic_pen = false;
    m_has_pen = (m_pen.style() != Qt::NoPen);
    if (m_has_pen) {
        m_pen_brush_style = m_pen.brush().style();

        if (m_pen_brush_style >= Qt::SolidPattern && m_pen_brush_style <= Qt::DiagCrossPattern) {
            int a, r, g, b;
            m_pen.color().getRgb(&r, &g, &b, &a);
            m_pen_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b);
        } else {
            m_pen_color = m_opacity_color;
        }

        m_has_cosmetic_pen = m_pen.isCosmetic();

        if (m_pen_brush_style != Qt::NoBrush &&
            m_pen_brush_style != Qt::SolidPattern) {
            bool ok;
            m_inv_pen_matrix = m_pen.brush().transform().inverted(&ok);
            if (!ok)
                qWarning() << "QDirect3DPaintEngine: No inverse matix for pen brush matrix.";
        }

        m_pen_width = m_pen.widthF();
        if (m_pen_width == 0.0f)
            m_pen_width = 1.0f;
    }
}

void QDirect3DPaintEnginePrivate::updateBrush(const QBrush &brush)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::updateBrush";
#endif
    m_brush = brush;
    m_brush_style = m_brush.style();
    m_has_brush = (m_brush_style != Qt::NoBrush);
    if (m_has_brush) {
        if (m_brush_style >= Qt::SolidPattern && m_brush_style <= Qt::DiagCrossPattern) {
            int a, r, g, b;
            m_brush.color().getRgb(&r, &g, &b, &a);
            m_brush_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b);
        } else {
            m_brush_color = m_opacity_color;
        }

        if (m_brush_style != Qt::SolidPattern) {
            bool ok;
            m_inv_brush_matrix = (m_brush.transform() * m_brush_origin).inverted(&ok);
            if (!ok)
                qWarning() << "QDirect3DPaintEngine: No inverse matix for brush matrix.";

            // make sure the texture is loaded as a texture
            if (m_brush_style == Qt::TexturePattern)
                verifyTexture(m_brush.texture());


        }
    }
}

void QDirect3DPaintEnginePrivate::updateTransform(const QTransform &matrix)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::updateTransform";
#endif
    m_matrix = matrix;
    m_inv_scale = qMax(1 / qMax( qMax(qAbs(m_matrix.m11()), qAbs(m_matrix.m22())),
        qMax(qAbs(m_matrix.m12()), qAbs(m_matrix.m21())) ), 0.0001);
    m_txop = matrix.type();
}

int QDirect3DPaintEnginePrivate::flushAntialiased(int offset)
{
    // fills the mask (returns number of items added to the mask)
    int newoffset = m_draw_helper->drawAntialiasedMask(offset, m_batch.m_item_index);

    // set the render target to the current output surface
    if (FAILED(m_d3d_device->SetRenderTarget(0, m_current_surface)))
        qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";

    // draw the bounding boxes (using the mask generated by drawAntialiasedMask)
    for (int i=offset; i<newoffset; ++i) {
        QD3DBatchItem *item = &(m_batch.items[i]);
        int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_AA_DRAW : PASS_AA_DRAW_DIRECT;
        m_statemanager->beginPass(pass);
        prepareItem(item);
        if (item->m_info & QD3DBatchItem::BI_BRECT)
            m_draw_helper->drawAntialiasedBoundingRect(item);
        cleanupItem(item);
    }

    m_statemanager->endPass();

    return newoffset;
}

bool QDirect3DPaintEnginePrivate::prepareBatch(QD3DBatchItem *item, int offset)
{
    if (item->m_info & QD3DBatchItem::BI_CLIP) {
        setRenderTechnique(RT_Aliased);
        if (item->m_info & QD3DBatchItem::BI_CLEARCLIP) {
            m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 0.0f, 0);
            return true;
        }

        m_draw_helper->drawAliasedMask(offset);
        m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
        if (item->m_info & QD3DBatchItem::BI_BRECT) {
            m_statemanager->beginPass(PASS_STENCIL_CLIP);
            m_draw_helper->drawAliasedBoundingRect(item);
            m_statemanager->endPass();
        }

        return true;
    }

    if (item->m_info & QD3DBatchItem::BI_AA) {
        setRenderTechnique(RT_Antialiased);
    } else {
        setRenderTechnique(RT_Aliased);
    }

    return false;
}

void QDirect3DPaintEnginePrivate::prepareItem(QD3DBatchItem *item) {
    // pixmap
    int brushmode = 0;
    m_statemanager->startStateBlock();
    if ((item->m_info & QD3DBatchItem::BI_PIXMAP) || (item->m_info & QD3DBatchItem::BI_IMAGE)) {
        QRasterPixmapData *data = static_cast<QRasterPixmapData*>(item->m_pixmap.data);
        IDirect3DTexture9 *tex = (item->m_info & QD3DBatchItem::BI_PIXMAP) ?
                                 data->texture : item->m_texture;
        m_statemanager->setTexture(tex);
        brushmode = 5;
    }

    if (item->m_info & QD3DBatchItem::BI_AA) {
        m_statemanager->setMaskChannel(item->m_maskpos.channel);
        m_statemanager->setMaskOffset(item->m_xoffset, item->m_yoffset);
    }

    if (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) {
        const QBrush brush = item->m_brush;
        switch (brush.style()) {
    	    case Qt::TexturePattern: {
                QRasterPixmapData *data = static_cast<QRasterPixmapData*>(brush.texture().data);
                m_statemanager->setTexture(data->texture, QGradient::RepeatSpread);
                brushmode = 1;
                break;
	    }
            case Qt::LinearGradientPattern:
                m_statemanager->setTexture(m_gradient_cache->
                    getBuffer(brush.gradient()->stops(), item->m_opacity),
                    brush.gradient()->spread());
                brushmode = 2;
                break;
            case Qt::ConicalGradientPattern:
                m_statemanager->setTexture(m_gradient_cache->
                    getBuffer(brush.gradient()->stops(), item->m_opacity),
                    brush.gradient()->spread());
                brushmode = 3;
                break;
            case Qt::RadialGradientPattern:
                m_statemanager->setTexture(m_gradient_cache->
                    getBuffer(brush.gradient()->stops(), item->m_opacity),
                    brush.gradient()->spread());
                m_statemanager->setFocalDistance(item->m_distance);
                brushmode = 4;
                break;
    	    default: {
                QRasterPixmapData *data = static_cast<QRasterPixmapData*>(getPattern(brush.style()).data);
                m_statemanager->setTexture(data->texture, QGradient::RepeatSpread);
                brushmode = 5;
	    }
        };
    }

    if (item->m_info & QD3DBatchItem::BI_TRANSFORM) {
        m_statemanager->setTransformation(&item->m_matrix);
    } else {
        m_statemanager->setTransformation();
    }

    m_statemanager->setBrushMode(brushmode);
    setCompositionMode(item->m_cmode);
    m_statemanager->endStateBlock();
}


void QDirect3DPaintEnginePrivate::releaseDC()
{
    if (m_dc) {
        m_dcsurface->ReleaseDC(m_dc);
        m_dcsurface = 0;
        m_dc = 0;
    }
}


void QDirect3DPaintEnginePrivate::cleanupItem(QD3DBatchItem *item)
{
    if (item->m_info & QD3DBatchItem::BI_PIXMAP)
        item->m_pixmap = QPixmap();
    item->m_brush = QBrush();
}

void QDirect3DPaintEnginePrivate::verifyTexture(const QPixmap &pm)
{
    QRasterPixmapData *pmData = static_cast<QRasterPixmapData*>(pm.data);
    if (!pmData->texture) {
        QImage im = pmData->image;
        // bitmaps are drawn with the current pen color
        if (im.depth() == 1) {
            QVector<QRgb> colors(2);
            colors[0] = 0;
            colors[1] = m_pen.color().rgba();
            im.setColorTable(colors);
        }
        im = im.convertToFormat(QImage::Format_ARGB32);
        if (FAILED(m_d3d_device->CreateTexture(im.width(), im.height(), 1, 0,
                                                 D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pmData->texture, 0)))
        {
            qWarning("QDirect3DPaintEngine: unable to create Direct3D texture from pixmap.");
            return;
        }
        D3DLOCKED_RECT rect;
        if (FAILED(pmData->texture->LockRect(0, &rect, 0, 0))) {
            qDebug() << "QDirect3DPaintEngine: unable to lock texture rect.";
            return;
        }
        DWORD *dst = (DWORD *) rect.pBits;
        DWORD *src = (DWORD *) im.scanLine(0);

        Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4));
        memcpy(dst, src, rect.Pitch*im.height());
        pmData->texture->UnlockRect(0);
    }
}

bool QDirect3DPaintEnginePrivate::isFastRect(const QRectF &rect)
{
    if (m_matrix.type() < QTransform::TxRotate) {
        QRectF r = m_matrix.mapRect(rect);
        return r.topLeft().toPoint() == r.topLeft()
            && r.bottomRight().toPoint() == r.bottomRight();
    }

    return false;
}

void QDirect3DPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode)
{
    switch(mode) {
        case QPainter::CompositionMode_SourceOver:
        default:
            m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
            m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    };
}

void QDirect3DPaintEnginePrivate::cleanup()
{
    // clean batch
    for(int i=0; i<QD3D_BATCH_SIZE; ++i) {
        m_batch.items[i].m_brush = QBrush();
        m_batch.items[i].m_pixmap = QPixmap();
    }

    m_surface_manager.cleanup();
    m_patterns.clear();

    delete m_gradient_cache;
    delete m_draw_helper;

    if (m_effect)
        m_effect->Release();

    if (m_d3d_object)
        m_d3d_object->Release();

    m_effect = 0;
    m_d3d_object = 0;
    m_gradient_cache = 0;
    m_draw_helper = 0;
}

void QDirect3DPaintEnginePrivate::flushAliased(QD3DBatchItem *item, int offset)
{
    m_draw_helper->drawAliasedMask(offset);

    if (item->m_info & QD3DBatchItem::BI_BRECT) {
        int pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW_DIRECT : PASS_STENCIL_NOSTENCILCHECK_DIRECT;
        if (item->m_info & (QD3DBatchItem::BI_COMPLEXBRUSH|QD3DBatchItem::BI_IMAGE|QD3DBatchItem::BI_PIXMAP) )
            pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW : PASS_STENCIL_NOSTENCILCHECK;
        m_statemanager->beginPass(pass);
        prepareItem(item);
        m_draw_helper->drawAliasedBoundingRect(item);
        cleanupItem(item);
        m_statemanager->endPass();
    }
}

void QDirect3DPaintEnginePrivate::flushText(QD3DBatchItem *item, int)
{
    prepareItem(item);
    m_statemanager->setTexture(item->m_texture);
    m_statemanager->setBrushMode(1);
//     m_statemanager->SetRenderState(D3DRS_BLENDFACTOR, item->m_brush.color().rgba());
    m_statemanager->beginPass(m_cleartype_text ? PASS_CLEARTYPE_TEXT : PASS_TEXT);
    m_draw_helper->drawTextItem(item);
    m_statemanager->endPass();
    cleanupItem(item);
}

void QDirect3DPaintEnginePrivate::flushLines(QD3DBatchItem *item, int)
{
    m_draw_helper->drawAliasedLines(item);

    if (item->m_info & QD3DBatchItem::BI_BRECT) {
        int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_STENCIL_DRAW : PASS_STENCIL_DRAW_DIRECT;
        m_statemanager->beginPass(pass);
        prepareItem(item);
        m_draw_helper->drawAliasedBoundingRect(item);
        cleanupItem(item);
        m_statemanager->endPass();
    }
}

void QDirect3DPaintEnginePrivate::flushBatch()
{
//     static int dbgcounter = 0;
//     ++dbgcounter;
//     qDebug() << " -> flush" << dbgcounter;

    int offset = 0;
    m_draw_helper->unlockVertexBuffer();
    releaseDC();

    // iterate over all items in the batch
    while (offset != m_batch.m_item_index) {
        QD3DBatchItem *item = &(m_batch.items[offset]);

        if (prepareBatch(item, offset)) {
            ++offset;
            continue;
        }

        if (item->m_info & QD3DBatchItem::BI_FASTLINE) {
            flushLines(item, offset++);
        } else if (item->m_info & QD3DBatchItem::BI_AA) {
            offset = flushAntialiased(offset);
        } else if (item->m_info & QD3DBatchItem::BI_TEXT) {
            flushText(item, offset++);
        } else {
            flushAliased(item, offset++);
        }
    }

    // reset batch
    m_batch.m_item_index = 0;

    // release doomed textures
    for (int i=0; i<qd3d_release_list.size(); ++i)
        qd3d_release_list.at(i)->Release();
    qd3d_release_list.clear();
}

QDirect3DPaintEngine::QDirect3DPaintEngine()
    : QPaintEngine(*(new QDirect3DPaintEnginePrivate),
                   PaintEngineFeatures(AllFeatures & ~ObjectBoundingModeGradients))
{ }

QDirect3DPaintEngine::~QDirect3DPaintEngine()
{
}

bool QDirect3DPaintEngine::begin(QPaintDevice *device)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::begin";
#endif
    Q_D(QDirect3DPaintEngine);
    setActive(true);

    QSize old_size = d->m_surface_size;
    d->m_surface_size = QRect(0, 0, device->width(), device->height()).size();

    d->m_current_state = 0;
    d->m_inv_scale = 1;
    d->m_opacity = 1.0f;
    d->m_opacity_color = D3DCOLOR_ARGB(255,255,255,255);
    d->m_matrix = QTransform();
    d->m_brush_origin = QTransform();
    d->m_txop = QTransform::TxNone;
    d->m_cmode = QPainter::CompositionMode_SourceOver;

    Q_ASSERT(device && device->devType() == QInternal::Widget);
    if (d->m_d3d_device == 0) {
        qWarning() << "QDirect3DPaintEngine: No Device!";
        return false;
    }

    d->m_cleartype_text = false;
//     QT_WA({
//         UINT result;
//         BOOL ok;
//         ok = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
//         if (ok)
//             d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE);
//     }, {
//         UINT result;
//         BOOL ok;
//         ok = SystemParametersInfoA(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
//         if (ok)
//             d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE);
//     });

    d->m_surface_manager.setPaintDevice(device);
    int status = d->m_surface_manager.status();
    if (status & QD3DSurfaceManager::NeedsResetting) {
        d->m_effect->OnLostDevice();
        d->m_draw_helper->beforeReset();
        d->m_statemanager->reset();
        d->m_surface_manager.reset();
        d->m_draw_helper->afterReset();
        d->m_effect->OnResetDevice();
        d->initDevice();
    }

    LPDIRECT3DSURFACE9 newsurface = d->m_surface_manager.renderTarget();
    if (d->m_current_surface != newsurface) {
        d->m_current_surface = newsurface;
        if (FAILED(d->m_d3d_device->SetRenderTarget(0, newsurface)))
            qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";
    }

    status = d->m_surface_manager.status();
    if (status & QD3DSurfaceManager::MaxSizeChanged) {
        QSize maxsize = d->m_surface_manager.maxSize();
        d->m_draw_helper->setMaskSize(maxsize);
        int masksize[2] = {maxsize.width(), maxsize.height()};
        d->m_effect->SetIntArray("g_mMaskSize", masksize, 2);
    }

    if (old_size != d->m_surface_size) {
        D3DXMATRIX projMatrix;
        pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, d->m_surface_size.width(), d->m_surface_size.height(), 0, 0.0f, 1.0f);
        d->m_statemanager->setProjection(&projMatrix);
    }

    if (!d->m_in_scene) {
        if (FAILED(d->m_d3d_device->BeginScene())) {
            qWarning() << "QDirect3DPaintEngine: BeginScene() failed.";
            return false;
        }
        QWidget *widget = static_cast<QWidget *>(device);
        if (widget->autoFillBackground() == true) {
            QColor color = widget->palette().brush(widget->backgroundRole()).color();
            RECT rect = {0, 0, widget->width(), widget->height()};
            d->m_d3d_device->ColorFill(d->m_current_surface, &rect,
                D3DCOLOR_ARGB(color.alpha(), color.red(), color.green(), color.blue()));
        }
        d->m_in_scene = true;
    }

    // set system clip
    d->m_clipping_enabled = false;
    d->m_has_complex_clipping = false;

    d->m_sysclip_region = systemClip();
    QVector<QRect> rects = d->m_sysclip_region.rects();
    if (rects.count() == 1 && rects.at(0).size() == d->m_surface_size)
        d->m_sysclip_region = QRegion();

    d->updateClipRegion(QRegion(), Qt::NoClip);

    return true;
}

void QDirect3DPaintEngine::drawEllipse(const QRectF &rect)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawEllipse (float)";
#endif
    QPaintEngine::drawEllipse(rect);
}

void QDirect3DPaintEngine::drawEllipse(const QRect &rect)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawEllipse";
#endif
    QPaintEngine::drawEllipse(rect);
}

void QDirect3DPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
                                     Qt::ImageConversionFlags)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawImage";
#endif

    Q_D(QDirect3DPaintEngine);
    int width = image.width();
    int height = image.height();

    // transform rectangle
    QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height,
        sr.width() / width, sr.height() / height));

    QD3DBatchItem *item = d->nextBatchItem();
    item->m_info = QD3DBatchItem::BI_IMAGE | QD3DBatchItem::BI_TRANSFORM;
    item->m_texture = qd3d_image_cache()->lookup(d->m_d3d_device, image);
    item->m_matrix = d->m_matrix;
    d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect);
}

void QDirect3DPaintEngine::drawLines(const QLineF *lines, int lineCount)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawLines (float)";
#endif
    Q_D(QDirect3DPaintEngine);

    if (!d->m_has_pen)
        return;

    if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) {
        QD3DBatchItem *item = d->nextBatchItem();
        if (d->m_pen.isCosmetic())
            item->m_info |= QD3DBatchItem::BI_COSMETICPEN;
        item->m_info |= QD3DBatchItem::BI_TRANSFORM;
        item->m_matrix = d->m_matrix;
        d->m_draw_helper->queueAliasedLines(lines, lineCount, &item);
    } else {
        QRectF brect;
        QPainterPath path;

        // creates a path with the lines
        path.moveTo(lines[0].x1(), lines[0].y1());
        qreal lastx = lines[0].x2();
        qreal lasty = lines[0].y2();
        path.lineTo(lastx, lasty);

        for (int i=1; i<lineCount; ++i) {
            qreal x = lines[i].x1();
            qreal y = lines[i].y1();
            if (lastx != x || lasty != y) {
                path.moveTo(x, y);
            }
            path.lineTo(lines[i].x2(), lines[i].y2());
        }

        if (d->m_has_cosmetic_pen) {
            brect = path.controlPointRect();
            path = d->m_matrix.map(path);
        }

        d->strokePath(path, brect, true);
    }
}

void QDirect3DPaintEngine::drawLines(const QLine *lines, int lineCount)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawLines";
#endif
    QPaintEngine::drawLines(lines, lineCount);
}

void QDirect3DPaintEngine::drawPath(const QPainterPath &path)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPath";
#endif
    Q_D(QDirect3DPaintEngine);

    if (path.isEmpty())
        return;

    QRectF brect;
    QPainterPath tpath;

    if (d->m_has_cosmetic_pen) {
        brect = path.controlPointRect();
        tpath = d->m_matrix.map(path);
    } else {
        tpath = path;
    }

    if (d->m_has_brush)
        d->fillPath(tpath, brect);

    if (d->m_has_pen)
        d->strokePath(tpath, brect);
}


QPointF QDirect3DPaintEnginePrivate::transformPoint(const QPointF &p, qreal *w) const
{
    (*w) = 1.0f;
    qreal fx = p.x();
    qreal fy = p.y();
    qreal nx = m_matrix.m11()*fx + m_matrix.m21()*fy + m_matrix.m31();
    qreal ny = m_matrix.m12()*fx + m_matrix.m22()*fy + m_matrix.m32();
    if (!m_matrix.isAffine()) {
        *w = m_matrix.m13()*fx + m_matrix.m23()*fy + m_matrix.m33();
        //*w = 1/(*w);
        nx = nx/(*w);
        ny = ny/(*w);
    }
    return QPointF(nx, ny);
}

void QDirect3DPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPixmap";
#endif
    Q_D(QDirect3DPaintEngine);

    if (d->m_draw_helper->needsFlushing())
        d->flushBatch();

    int width = pm.width();
    int height = pm.height();

    // transform rectangle
    QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height,
        sr.width() / width, sr.height() / height));

    QD3DBatchItem *item = d->nextBatchItem();
    item->m_info = QD3DBatchItem::BI_PIXMAP|QD3DBatchItem::BI_TRANSFORM;

    item->m_pixmap = pm;
    d->verifyTexture(item->m_pixmap);

    item->m_matrix = d->m_matrix;
    d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect);
}

void QDirect3DPaintEngine::drawPoints(const QPointF *points, int pointCount)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPoints (float)";
#endif
    QPaintEngine::drawPoints(points, pointCount);
}

void QDirect3DPaintEngine::drawPoints(const QPoint *points, int pointCount)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPoints";
#endif
    QPaintEngine::drawPoints(points, pointCount);
}

void QDirect3DPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPolygon";
#endif
    Q_D(QDirect3DPaintEngine);

    if (d->m_has_brush && mode != PolylineMode) {
        QPainterPath path;
        path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
        path.moveTo(points[0]);
        for (int i=1; i<pointCount; ++i)
            path.lineTo(points[i]);
        if (path.isEmpty())
            return;
        d->fillPath(path, QRectF());
    }

    if (d->m_has_pen) {
        QPainterPath path(points[0]);
        for (int i = 1; i < pointCount; ++i)
            path.lineTo(points[i]);
        if (mode != PolylineMode)
            path.lineTo(points[0]);

        if (path.isEmpty())
            return;
        QRectF brect;
        if (d->m_has_cosmetic_pen) {
            brect = path.controlPointRect();
            path = d->m_matrix.map(path);
        }

        d->strokePath(path, brect);
    }
}

void QDirect3DPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawPolygon";
#endif
    QPaintEngine::drawPolygon(points, pointCount, mode);
}

void QDirect3DPaintEngine::drawRects(const QRectF *rects, int rectCount)
{
    Q_D(QDirect3DPaintEngine);
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawRects (float)";
#endif
    for (int i=0; i<rectCount; ++i) {
        if ((d->m_brush_style == Qt::SolidPattern) &&
            (!(d->m_current_state & QD3DBatchItem::BI_AA) || d->isFastRect(rects[i]))) {
            QD3DBatchItem *item = d->nextBatchItem();
            item->m_info |= QD3DBatchItem::BI_TRANSFORM;
            item->m_info &= ~QD3DBatchItem::BI_AA;
            item->m_matrix = d->m_matrix;
            const QRectF rect = rects[i];
            d->m_draw_helper->queueRect(rect, item, d->m_brush_color);

            if (d->m_has_pen) {
                if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) {
                    QLineF lines[4];
                    qreal x1 = rect.x();
                    qreal y1 = rect.y();
                    qreal x2 = rect.width() + x1;
                    qreal y2 = rect.height() + y1;
                    lines[0] = QLineF(x1, y1, x2, y1);
                    lines[1] = QLineF(x2, y1, x2, y2);
                    lines[2] = QLineF(x2, y2, x1, y2);
                    lines[3] = QLineF(x1, y2, x1, y1);
                    QD3DBatchItem *item = d->nextBatchItem();
                    if (d->m_pen.isCosmetic())
                        item->m_info |= QD3DBatchItem::BI_COSMETICPEN;
                    item->m_info |= QD3DBatchItem::BI_TRANSFORM;
                    item->m_matrix = d->m_matrix;
                    d->m_draw_helper->queueAliasedLines(lines, 4, &item);
                } else {
                    QPainterPath path;
                    QRectF brect;

                    path.addRect(rects[i]);
                    if (d->m_has_cosmetic_pen) {
                        brect = path.controlPointRect();
                        path = d->m_matrix.map(path);
                    }

                    d->strokePath(path, brect, true);
                }
            }
        } else {
            QPainterPath path;
            QRectF brect;

            path.addRect(rects[i]);
            if (d->m_has_cosmetic_pen) {
                brect = path.controlPointRect();
                path = d->m_matrix.map(path);
            }

            if (d->m_has_brush)
                d->fillPath(path, brect);

            if (d->m_has_pen)
                d->strokePath(path, brect, true);
        }
    }
}

void QDirect3DPaintEngine::drawRects(const QRect *rects, int rectCount)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawRects";
#endif
    QPaintEngine::drawRects(rects, rectCount);
}


void QDirect3DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
{
    Q_D(QDirect3DPaintEngine);

#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawTextItem";
#endif
//     if (d->m_matrix.isScaling() || (d->m_pen_brush_style >= Qt::LinearGradientPattern
//                                     && d->m_pen_brush_style <= Qt::ConicalGradientPattern)) {
//         QPaintEngine::drawTextItem(p, textItem);
//         return;
//     }

    const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
    QVarLengthArray<QFixedPoint> positions;
    QVarLengthArray<glyph_t> glyphs;
    QTransform matrix;
    matrix.translate(p.x(), p.y());
    ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);

    qd3d_glyph_cache()->cacheGlyphs(this, ti, glyphs, d->m_cleartype_text);
    QD3DFontTexture *font_tex = qd3d_glyph_cache()->fontTexture(ti.fontEngine);

    QD3DBatchItem *item = d->nextBatchItem();
    d->m_draw_helper->lockVertexBuffer();

    item->m_info = QD3DBatchItem::BI_TEXT
        | (d->m_current_state & ~QD3DBatchItem::BI_AA) | QD3DBatchItem::BI_TRANSFORM;
    item->m_texture = font_tex->texture;
    item->m_offset = d->m_draw_helper->index();
    item->m_matrix = d->m_matrix;
    item->m_count = 0;
    item->m_brush = d->m_pen.brush();

    for (int i=0; i< glyphs.size(); ++i) {
        QD3DGlyphCoord *g = qd3d_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);

        // we don't cache glyphs with no width/height
        if (!g)
            continue;

        // texture coords
        qreal tex_coords[] = { g->x, g->y, g->x + g->width, g->y + g->height };
        QPointF logical_pos(qRound((positions[i].x - g->x_offset).toReal()) - 0.5f,
                            qRound((positions[i].y + g->y_offset).toReal()) - 0.5f);

        QRectF glyph_rect(logical_pos, QSizeF(g->log_width, g->log_height));
        d->m_draw_helper->queueTextGlyph(glyph_rect, tex_coords, item, d->m_pen_color);
    }
}

void QDirect3DPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::drawTiledPixmap";
#endif
    QPaintEngine::drawTiledPixmap(rect, pixmap, p);
}

bool QDirect3DPaintEngine::end()
{
    Q_D(QDirect3DPaintEngine);

    d->flushBatch();

    if (d->m_flush_on_end) {
        QPaintDevice *pdev = paintDevice();
        LPDIRECT3DSWAPCHAIN9 swapchain = swapChain(pdev);


        QWidget *w = 0;
        if (pdev->devType() == QInternal::Widget) {
            w = static_cast<QWidget *>(pdev);
        }

        if (w && swapchain) {
            QRect br = w->rect();
            QRect wbr = br;//.translated(-w->pos());

            RECT destrect;
            destrect.left   = wbr.x();
            destrect.top    = wbr.y();
            destrect.right  = destrect.left + wbr.width();
            destrect.bottom = destrect.top  + wbr.height();

            RECT srcrect;
            srcrect.left    = br.x();// + w->x();
            srcrect.top     = br.y();// + w->y();
            srcrect.right   = wbr.width() + srcrect.left;
            srcrect.bottom  = wbr.height() + srcrect.top;
            int devwidth = w->width();
            int devheight = w->height();

            if (devwidth <= srcrect.right) {
                int diff = srcrect.right - devwidth;
                srcrect.right -= diff;
                destrect.right -= diff;
                if (srcrect.right <= srcrect.left)
                    return false;
            }
            if (devheight <= srcrect.bottom) {
                int diff = srcrect.bottom - devheight;
                srcrect.bottom -= diff;
                destrect.bottom -= diff;
                if (srcrect.bottom <= srcrect.top)
                    return false;
            }

            if (FAILED(swapchain->Present(&srcrect, &destrect, w->winId(), 0, 0)))
                qWarning("QDirect3DPaintEngine: failed to present back buffer.");
        }
    }


    return true;
}

void QDirect3DPaintEngine::updateState(const QPaintEngineState &state)
{
#ifdef QT_DEBUG_D3D_CALLS
    qDebug() << "QDirect3DPaintEngine::updateState";
#endif
    Q_D(QDirect3DPaintEngine);

    bool update_fast_pen = false;
    DirtyFlags flags = state.state();

    if (flags & DirtyOpacity) {
        d->m_opacity = state.opacity();
        if (d->m_opacity > 1.0f)
            d->m_opacity = 1.0f;
        if (d->m_opacity < 0.f)
            d->m_opacity = 0.f;
        uint c = (d->m_opacity * 255);
        d->m_opacity_color = D3DCOLOR_ARGB(c,c,c,c);
        flags |= (DirtyPen | DirtyBrush);
    }

    if (flags & DirtyCompositionMode) {
        d->m_cmode = state.compositionMode();
    }

    if (flags & DirtyTransform) {
        d->updateTransform(state.transform());
        update_fast_pen = true;
    }

    if (flags & DirtyHints) {
        if (state.renderHints() & QPainter::Antialiasing)
            d->m_current_state |= QD3DBatchItem::BI_AA;
        else
            d->m_current_state &= ~QD3DBatchItem::BI_AA;
        update_fast_pen = true;
    }

    if (flags & DirtyFont) {
        d->updateFont(state.font());
    }

    if (state.state() & DirtyClipEnabled) {
        if (state.isClipEnabled() && !d->m_clipping_enabled) {
            d->m_clipping_enabled = true;
            if (d->m_has_complex_clipping)
                d->updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
            else
                d->updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
        } else if (!state.isClipEnabled() && d->m_clipping_enabled) {
            d->m_clipping_enabled = false;
            if (d->m_has_complex_clipping)
                d->updateClipPath(QPainterPath(), Qt::NoClip);
            else
                d->updateClipRegion(QRegion(), Qt::NoClip);
        }
    }

    if (flags & DirtyClipRegion) {
        d->updateClipRegion(state.clipRegion(), state.clipOperation());
    }

    if (flags & DirtyClipPath) {
        d->updateClipPath(state.clipPath(), state.clipOperation());
    }

    if (flags & DirtyBrushOrigin) {
        d->m_brush_origin = QTransform();
        d->m_brush_origin.translate(-state.brushOrigin().x(),
            -state.brushOrigin().y());
        flags |= DirtyBrush;
    }

    if (flags & DirtyPen) {
        d->updatePen(state.pen());
        update_fast_pen = true;
    }

    if (flags & DirtyBrush)
        d->updateBrush(state.brush());

    if (update_fast_pen && d->m_has_pen) {
        if (d->m_current_state & QD3DBatchItem::BI_AA) {
            d->m_has_fast_pen = false;
            d->m_has_aa_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen)
                && (d->m_pen_width <= 1.0f)
                && (d->m_pen.style() == Qt::SolidLine);
        } else {
            d->m_has_aa_fast_pen = false;
            d->m_has_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen)
                              && (d->m_pen.style() == Qt::SolidLine)
                              && (d->m_pen.capStyle() == Qt::SquareCap);
        }
    }
}

void QDirect3DPaintEngine::cleanup()
{
    Q_D(QDirect3DPaintEngine);
    d->cleanup();
}

void QDirect3DPaintEngine::scroll(QPaintDevice *pd, const RECT &srcrect, const RECT &destrect)
{
    Q_D(QDirect3DPaintEngine);
    LPDIRECT3DSURFACE9 srcsurf = d->m_surface_manager.surface(pd);
    LPDIRECT3DSURFACE9 masksurf = d->m_draw_helper->freeMaskSurface();
    if (FAILED(d->m_d3d_device->StretchRect(srcsurf, &srcrect, masksurf, &srcrect, D3DTEXF_NONE)))
        qWarning("QDirect3DPaintEngine: StretchRect failed.");
    if (FAILED(d->m_d3d_device->StretchRect(masksurf, &srcrect, srcsurf, &destrect, D3DTEXF_NONE)))
        qWarning("QDirect3DPaintEngine: StretchRect failed.");
}

LPDIRECT3DSWAPCHAIN9 QDirect3DPaintEngine::swapChain(QPaintDevice *pd)
{
    Q_D(QDirect3DPaintEngine);

    if (d->m_in_scene) {
        if (d->m_d3d_device == 0) {
            qWarning("QDirect3DPaintEngine: No device!");
            return false;
        }

        d->setRenderTechnique(QDirect3DPaintEnginePrivate::RT_NoTechnique);
        if (FAILED(d->m_d3d_device->EndScene()))
            qWarning("QDirect3DPaintEngine: failed to end scene.");

        d->m_in_scene = false;
    }

    return d->m_surface_manager.swapChain(pd);
}

void QDirect3DPaintEngine::releaseSwapChain(QPaintDevice *pd)
{
    Q_D(QDirect3DPaintEngine);
    d->m_surface_manager.releasePaintDevice(pd);
}

HDC QDirect3DPaintEngine::getDC() const
{
    QDirect3DPaintEnginePrivate *d = const_cast<QDirect3DPaintEnginePrivate *>(d_func());

    if (!d->m_dc && d->m_current_surface) {
        d->m_dcsurface = d->m_current_surface;
        if (FAILED(d->m_current_surface->GetDC(&d->m_dc)))
            qWarning() << "QDirect3DPaintEngine::getDC() failed!";
    }

    return d->m_dc;
}

void QDirect3DPaintEngine::setFlushOnEnd(bool flushOnEnd)
{
    Q_D(QDirect3DPaintEngine);

    d->m_flush_on_end = flushOnEnd;
}

bool QDirect3DPaintEngine::hasDirect3DSupport()
{
    Q_D(QDirect3DPaintEngine);
    return d->m_supports_d3d;
}

QT_END_NAMESPACE

#include "qpaintengine_d3d.moc"
