// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <qtest.h>
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
#include <QImage>
#include <QPaintEngine>
#include <QTileRules>
#include <qmath.h>

#include <private/qpixmap_raster_p.h>

using namespace Qt::StringLiterals;

Q_DECLARE_METATYPE(QPainterPath)
Q_DECLARE_METATYPE(QPainter::RenderHint)
Q_DECLARE_METATYPE(QPainter::CompositionMode)
Q_DECLARE_METATYPE(QImage::Format)

enum PrimitiveType {
    Primitive_Int_DiagLine,
    Primitive_Int_VerLine,
    Primitive_Int_HorLine,
    Primitive_Int_Rect,
    Primitive_Int_Ellipse,
    Primitive_Int_Pie,
    Primitive_Int_Arc,
    Primitive_Int_Chord,
    Primitive_Int_TriPoly,
    Primitive_Int_RectPoly,
    Primitive_Int_2RectPoly,

    Primitive_Float_DiagLine,
    Primitive_Float_VerLine,
    Primitive_Float_HorLine,
    Primitive_Float_Rect,
    Primitive_Float_Ellipse,
    Primitive_Float_Pie,
    Primitive_Float_Arc,
    Primitive_Float_Chord,
    Primitive_Float_TriPoly,
    Primitive_Float_RectPoly,
    Primitive_Float_2RectPoly,

    Primitive_Float_TriPath,
    Primitive_Float_RectPath,
    Primitive_Float_2RectPath,
    Primitive_Float_EllipsePath,
    Primitive_Last_Primitive

};


enum StateChanges {
    ChangePen         = 0x0001,
    ChangeBrush       = 0x0002,
    ChangeClip        = 0x0004,
    ChangeTransform   = 0x0008
};


struct PrimitiveSet {
    QRect i_rect;
    QLine i_line_diag;
    QLine i_line_ver;
    QLine i_line_hor;
    QPolygon i_poly_tri;
    QPolygon i_poly_2rects;
    QPolygon i_poly_rect;

    QRectF f_rect;
    QLineF f_line_diag;
    QLineF f_line_ver;
    QLineF f_line_hor;
    QPolygonF f_poly_tri;
    QPolygonF f_poly_2rects;
    QPolygonF f_poly_rect;

    QPainterPath f_path_tri;
    QPainterPath f_path_2rects;
    QPainterPath f_path_rect;
    QPainterPath f_path_ellipse;
};


QPixmap rasterPixmap(int width, int height)
{
    QPlatformPixmap *data =
        new QRasterPlatformPixmap(QPlatformPixmap::PixmapType);

    data->resize(width, height);

    return QPixmap(data);
}

QPixmap rasterPixmap(const QSize &size)
{
    return rasterPixmap(size.width(), size.height());
}

QPixmap rasterPixmap(const QImage &image)
{
    QPlatformPixmap *data =
        new QRasterPlatformPixmap(QPlatformPixmap::PixmapType);

    data->fromImage(image, Qt::AutoColor | Qt::NoFormatConversion);

    return QPixmap(data);
}


class tst_QPainter : public QObject
{
    Q_OBJECT

    public:
    tst_QPainter()
    {
        setupBrushes();
        createPrimitives();
        m_surface = surface();
    }

private slots:
    void beginAndEnd();

    void saveRestore_data();
    void saveRestore();

    void drawLine_data();
    void drawLine();
    void drawLine_clipped_data();
    void drawLine_clipped();
    void drawLine_antialiased_clipped_data();
    void drawLine_antialiased_clipped();

    void drawPixmap_data();
    void drawPixmap();

    void drawImage_data();
    void drawImage();

    void drawTiledPixmap_data();
    void drawTiledPixmap();

    void compositionModes_data();
    void compositionModes();

    void fillPrimitives_10_data() { drawPrimitives_data_helper(false); }
    void fillPrimitives_100_data() { drawPrimitives_data_helper(false); }
    void fillPrimitives_1000_data() { drawPrimitives_data_helper(false); }
    void fillPrimitives_10();
    void fillPrimitives_100();
    void fillPrimitives_1000();

    void strokePrimitives_10_data() { drawPrimitives_data_helper(true); }
    void strokePrimitives_100_data() { drawPrimitives_data_helper(true); }
    void strokePrimitives_1000_data() { drawPrimitives_data_helper(true); }
    void strokePrimitives_10();
    void strokePrimitives_100();
    void strokePrimitives_1000();

    void drawText_data();
    void drawText();

    void clipAndFill_data();
    void clipAndFill();

    void drawRoundedRect();
    void drawScaledRoundedRect();
    void drawTransformedRoundedRect();

    void drawScaledAntialiasedRoundedRect_data();
    void drawTransformedAntialiasedRoundedRect_data();
    void drawAntialiasedRoundedRect();
    void drawScaledAntialiasedRoundedRect();
    void drawTransformedAntialiasedRoundedRect();

    void drawScaledImageRoundedRect_data();
    void drawTransformedImageRoundedRect_data();
    void drawImageRoundedRect();
    void drawScaledImageRoundedRect();
    void drawTransformedImageRoundedRect();

    void drawScaledBorderPixmapRoundedRect_data();
    void drawTransformedBorderPixmapRoundedRect_data();
    void drawBorderPixmapRoundedRect();
    void drawScaledBorderPixmapRoundedRect();
    void drawTransformedBorderPixmapRoundedRect();

    void drawTransformedTransparentImage_data();
    void drawTransformedSemiTransparentImage_data();
    void drawTransformedFilledImage_data();
    void drawTransformedTransparentImage();
    void drawTransformedSemiTransparentImage();
    void drawTransformedFilledImage();

    void drawPathExceedingDevice_data();
    void drawPathExceedingDevice();

    void setPen();
    void setBrush();

private:
    void setupBrushes();
    void createPrimitives();

    void drawPrimitives_data_helper(bool fancypens);
    void drawPixmapImage_data_helper(bool);
    void fillPrimitives_helper(QPainter *painter, PrimitiveType type, PrimitiveSet *s);

    QTransform transformForAngle(qreal angle);

    QPaintDevice *surface()
    {
        m_pixmap = rasterPixmap(1024, 1024);
        return &m_pixmap;
    }


    QMap<QString, QPen> m_pens;
    QMap<QString, QBrush> m_brushes;

    PrimitiveSet m_primitives_10;
    PrimitiveSet m_primitives_100;
    PrimitiveSet m_primitives_1000;

    QPixmap m_pixmap;
    QPaintDevice *m_surface;
    QPainter m_painter;

};

void tst_QPainter::createPrimitives()
{
    for (int i=0; i<3; ++i) {
        PrimitiveSet *ps;
        int size;
        switch (i) {
        case 0:
            ps = &m_primitives_10;
            size = 10;
            break;
        case 1:
            ps = &m_primitives_100;
            size = 100;
            break;
        case 2:
            ps = &m_primitives_1000;
            size = 1000;
            break;
        }

        ps->f_rect = QRectF(0, 0, size, size);
        ps->f_line_diag = QLineF(0, 0, size, size);
        ps->f_line_ver = QLineF(10, 0, 10, size);
        ps->f_line_hor = QLineF(0, 10, size, 10);
        ps->f_poly_rect = QPolygonF() << QPointF(0, 0)
                                      << QPointF(size, 0)
                                      << QPointF(size, size)
                                      << QPointF(0, size);
        ps->f_poly_2rects = QPolygonF() << QPointF(0, 0)
                                        << QPointF(size * 0.75, 0)
                                        << QPointF(size * 0.75, size * 0.75)
                                        << QPointF(size * 0.25, size * 0.75)
                                        << QPointF(size * 0.25, size * 0.25)
                                        << QPointF(size, size * 0.25)
                                        << QPointF(size, size)
                                        << QPointF(0, size);
        ps->f_poly_tri = QPolygonF() << QPointF(size / 2.0, 0)
                                     << QPointF(0, size)
                                     << QPointF(size, size);

        ps->f_path_tri.addPolygon(ps->f_poly_tri);
        ps->f_path_rect.addRect(ps->f_rect);
        ps->f_path_2rects.addPolygon(ps->f_poly_2rects);
        ps->f_path_ellipse.addEllipse(ps->f_rect);

        ps->i_rect = ps->f_rect.toRect();
        ps->i_line_diag = ps->f_line_diag.toLine();
        ps->i_line_hor = ps->f_line_hor.toLine();
        ps->i_line_ver = ps->f_line_ver.toLine();
        ps->i_poly_tri = ps->f_poly_tri.toPolygon();
        ps->i_poly_rect = ps->f_poly_rect.toPolygon();
        ps->i_poly_2rects = ps->f_poly_2rects.toPolygon();
    }
}

void tst_QPainter::drawLine_data()
{
    QTest::addColumn<QLine>("line");
    QTest::addColumn<QPen>("pen");

    const QPen pens[] = {
        QPen(Qt::black),
        QPen(Qt::black, 0, Qt::DashDotLine),
        QPen(Qt::black, 4),
        QPen(Qt::black, 4, Qt::DashDotLine),
        QPen(QColor(255, 0, 0, 200)),
        QPen(QColor(255, 0, 0, 200), 0, Qt::DashDotLine),
        QPen(QColor(255, 0, 0, 200), 4),
        QPen(QColor(255, 0, 0, 200), 4, Qt::DashDotLine)
    };

    const char *penNames[] = {
        "black-0",
        "black-0-dashdot",
        "black-4",
        "black-4-dashdot",
        "alpha-0",
        "alpha-0-dashdot",
        "alpha-4",
        "alpha-4-dashdot",
    };

    QCOMPARE(std::size(pens), std::size(penNames));

    for (size_t i = 0; i < std::size(pens); ++i) {
        const QPen &pen = pens[i];
        const QString s = QString("%1:%2"_L1).arg(QLatin1StringView(penNames[i]));
        QTest::newRow(qPrintable(s.arg("horizontal")))
            << QLine(0, 20, 100, 20) << pen;
        QTest::newRow(qPrintable(s.arg("vertical:")))
            << QLine(20, 0, 20, 100) << pen;
        QTest::newRow(qPrintable(s.arg("0-45:")))
            << QLine(0, 20, 100, 0) << pen;
        QTest::newRow(qPrintable(s.arg("45-90:")))
            << QLine(0, 100, 20, 0) << pen;
        QTest::newRow(qPrintable(s.arg("90-135:")))
            << QLine(20, 100, 0, 0) << pen;
        QTest::newRow(qPrintable(s.arg("135-180:")))
            << QLine(100, 20, 0, 0) << pen;
        QTest::newRow(qPrintable(s.arg("180-225:")))
            << QLine(100, 0, 0, 20) << pen;
        QTest::newRow(qPrintable(s.arg("225-270:")))
            << QLine(20, 0, 0, 100) << pen;
        QTest::newRow(qPrintable(s.arg("270-315:")))
            << QLine(0, 0, 20, 100) << pen;
        QTest::newRow(qPrintable(s.arg("315-360:")))
            << QLine(0, 0, 100, 20) << pen;
    }
}

void tst_QPainter::setupBrushes()
{
    // Solid brushes...
    m_brushes["black-brush"] = QBrush(Qt::black);
    m_brushes["white-brush"] = QBrush(Qt::white);
    m_brushes["transparent-brush"] = QBrush(QColor(255, 255, 255, 0));
    m_brushes["alpha1-brush"] = QBrush(QColor(255, 255, 255, 100));
    m_brushes["alpha2-brush"] = QBrush(QColor(255, 255, 255, 200));


    // Patterns
    m_brushes["dense1-brush"] = QBrush(Qt::Dense1Pattern);
    m_brushes["dense2-brush"] = QBrush(Qt::Dense2Pattern);
    m_brushes["dense3-brush"] = QBrush(Qt::Dense3Pattern);
    m_brushes["dense4-brush"] = QBrush(Qt::Dense4Pattern);
    m_brushes["dense5-brush"] = QBrush(Qt::Dense5Pattern);
    m_brushes["dense6-brush"] = QBrush(Qt::Dense6Pattern);
    m_brushes["dense7-brush"] = QBrush(Qt::Dense7Pattern);
    m_brushes["hor-brush"] = QBrush(Qt::HorPattern);
    m_brushes["ver-brush"] = QBrush(Qt::VerPattern);
    m_brushes["cross-brush"] = QBrush(Qt::CrossPattern);
    m_brushes["bdiag-brush"] = QBrush(Qt::BDiagPattern);
    m_brushes["fdiag-brush"] = QBrush(Qt::FDiagPattern);
    m_brushes["diagcross-brush"] = QBrush(Qt::DiagCrossPattern);

    // Gradients
    QGradientStops gradient_white_black;
    gradient_white_black << QPair<qreal, QColor>(0, QColor(Qt::white));
    gradient_white_black << QPair<qreal, QColor>(1, QColor(Qt::black));

    QGradientStops gradient_white_black10;
    for (int i=0; i<10; ++i) {
        gradient_white_black10 << QPair<qreal, QColor>(i/10.0, QColor(Qt::white));
        gradient_white_black10 << QPair<qreal, QColor>(i/10.0+0.05, QColor(Qt::black));
    }

    QGradientStops gradient_white_alpha;
    gradient_white_alpha << QPair<qreal, QColor>(0, QColor(Qt::white));
    gradient_white_alpha << QPair<qreal, QColor>(0, QColor(Qt::transparent));

    QGradientStops gradient_white_alpha10;
    for (int i=0; i<10; ++i) {
        gradient_white_alpha10 << QPair<qreal, QColor>(i/10.0, QColor(Qt::white));
        gradient_white_alpha10 << QPair<qreal, QColor>(i/10.0+0.05, QColor(Qt::black));
    }


    for (int j=0; j<4; ++j) {
        QLinearGradient lg;
        lg.setStart(0, 0);

        QRadialGradient rg;
        QConicalGradient cg;

        QGradientStops stops;
        if (j == 0) stops = gradient_white_black;
        else if (j == 1) stops = gradient_white_black10;
        else if (j == 2) stops = gradient_white_alpha;
        else if (j == 3) stops = gradient_white_alpha10;
        lg.setStops(stops);
        rg.setStops(stops);
        cg.setStops(stops);

        for (int i=0; i<6; ++i) {
            lg.setSpread((QGradient::Spread) (i % 3));
            lg.setCoordinateMode((QGradient::CoordinateMode) (j / 3));

            QString name = QString::fromLatin1("-%1%2")
                           .arg(lg.spread())
                           .arg(lg.coordinateMode());

            lg.setFinalStop(100, 0);
            m_brushes["hor-lingrad-w/b-brush" + name] = QBrush(lg);

            lg.setFinalStop(0, 100);
            m_brushes["ver-lingrad-w/b-brush" + name] = QBrush(lg);

            lg.setFinalStop(100, 100);
            m_brushes["diag-lingrad-w/b-brush" + name] = QBrush(lg);

            rg.setRadius(100);
            rg.setCenter(0, 0);
            rg.setFocalPoint(50, 50);
            m_brushes["radgrad-brush" + name] = QBrush(rg);

            cg.setCenter(0, 0);
            cg.setAngle(40);
            m_brushes["congrad-brush" + name] = QBrush(cg);
        }
    }

    // Set up pens...


//     m_pens["black-pen"] = QPen(Qt::black);
//     m_pens["white-pen"] = QPen(Qt::white);
//     m_pens["transparent-pen"] = QPen(QColor(255, 255, 255, 0));
//     m_pens["translucent1-pen"] = QPen(QColor(255, 255, 255, 100));
//     m_pens["translucent2-pen"] = QPen(QColor(255, 255, 255, 200));



}


// void QPainter_Primitives::fillRect_data() {

//     QTest::addColumn<QBrush>("brush");
//     QTest::addColumn<QSize>("size");

//     for (QMap<QString, QBrush>::const_iterator it = m_brushes.constBegin();
//          it != m_brushes.constEnd(); ++it) {
//         for (int w=2; w<1025; w*=2) {
//             for (int h=2; h<1025; h*=2) {
//                 QTest::newRow(QString("brush=%1; size=[%2,%3]").arg(it.key()).arg(w).arg(h).toLatin1().data())
//                     << *it << QSize(w, h);
//             }
//         }
//     }
// }





// void QPainter_Primitives::fillRect()
// {
//     QFETCH(QBrush, brush);
//     QFETCH(QSize, size);

//     QImage img(1024, 1024, QImage::Format_ARGB32_Premultiplied);
//     QPainter p(&img);
//     p.setPen(Qt::NoPen);
//     p.setBrush(brush);
//     QRect rect(QPoint(0, 0), size);
//     QBENCHMARK {
//         p.drawRect(rect);
//     }
// }




void tst_QPainter::beginAndEnd()
{
    QPixmap pixmap = rasterPixmap(100, 100);

    QBENCHMARK {
        QPainter p;
        p.begin(&pixmap);
        p.end();
    }
}

void tst_QPainter::drawLine()
{
    QFETCH(QLine, line);
    QFETCH(QPen, pen);

    const int offset = 5;
    QPixmap pixmapUnclipped =
        rasterPixmap(qMin(line.x1(), line.x2())
                     + 2*offset + qAbs(line.dx()),
                     qMin(line.y1(), line.y2())
                     + 2*offset + qAbs(line.dy()));
    pixmapUnclipped.fill(Qt::white);

    QPainter p(&pixmapUnclipped);
    p.translate(offset, offset);
    p.setPen(pen);
    p.paintEngine()->syncState();

    QBENCHMARK {
        p.drawLine(line);
    }

    p.end();

}

void tst_QPainter::drawLine_clipped_data()
{
    drawLine_data();
}

void tst_QPainter::drawLine_clipped()
{
    QFETCH(QLine, line);
    QFETCH(QPen, pen);

    const int offset = 5;
    QPixmap pixmapClipped
        = rasterPixmap(qMin(line.x1(), line.x2())
                       + 2*offset + qAbs(line.dx()),
                       qMin(line.y1(), line.y2())
                       + 2*offset + qAbs(line.dy()));

    const QRect clip = QRect::span(line.p1(), line.p2());

    pixmapClipped.fill(Qt::white);
    QPainter p(&pixmapClipped);
    p.translate(offset, offset);
    p.setClipRect(clip);
    p.setPen(pen);
    p.paintEngine()->syncState();

    QBENCHMARK {
        p.drawLine(line);
    }

    p.end();
}


void tst_QPainter::drawLine_antialiased_clipped_data()
{
    drawLine_data();
}


void tst_QPainter::drawLine_antialiased_clipped()
{
    QFETCH(QLine, line);
    QFETCH(QPen, pen);

    const int offset = 5;
    QPixmap pixmapClipped
        = rasterPixmap(qMin(line.x1(), line.x2())
                       + 2*offset + qAbs(line.dx()),
                       qMin(line.y1(), line.y2())
                       + 2*offset + qAbs(line.dy()));

    const QRect clip = QRect::span(line.p1(), line.p2());

    pixmapClipped.fill(Qt::white);
    QPainter p(&pixmapClipped);
    p.setRenderHint(QPainter::Antialiasing);
    p.translate(offset, offset);
    p.setClipRect(clip);
    p.setPen(pen);
    p.paintEngine()->syncState();

    QBENCHMARK {
        p.drawLine(line);
    }

    p.end();
}

void tst_QPainter::drawPixmapImage_data_helper(bool pixmaps)
{
    QTest::addColumn<QImage::Format>("sourceFormat");
    QTest::addColumn<QImage::Format>("targetFormat");
    QTest::addColumn<QSize>("size");
    QTest::addColumn<int>("type"); // 0 = circle, 1 = diag line, 2 = solid rect, 3 = alpharect

    QList<QSize> sizes;
    sizes << QSize(10, 10)
          << QSize(1000, 1000);

    const char *typeNames[] = {
        "circle",
        "line",
        "solidrect",
        "alpharect"
    };

    const char *formatNames[] = {
        "Invalid",
        "Mono",
        "MonoLSB",
        "Indexed8",
        "RGB32",
        "ARGB32",
        "ARGB32_pm",
        "RGB16",
        "ARGB8565_pm",
        "RGB666",
        "ARGB6666_pm",
        "RGB555",
        "ARGB8555_pm",
        "RGB888",
        "RGB444",
        "ARGB4444_pm",
        "RGBx8888",
        "RGBA8888",
        "RGBA8888_pm",
        "BGR30",
        "A2BGR30_pm",
        "RGB30",
        "A2RGB30_pm",
        "Alpha8",
        "Grayscale8",
        "RGBx64",
        "RGBA64",
        "RGBA64_pm",
    };

    const QImage::Format pixmapFormats[] = {
        QImage::Format_RGB32,
        QImage::Format_ARGB32_Premultiplied,
        QImage::Format_RGB16,
        QImage::Format_BGR30,
        QImage::Format_Invalid
    };

    const QImage::Format targetImageFormats[] = {
        QImage::Format_RGB32,
        QImage::Format_ARGB32,
        QImage::Format_ARGB32_Premultiplied,
        QImage::Format_RGB16,
        QImage::Format_ARGB8565_Premultiplied,
        QImage::Format_RGBX8888,
        QImage::Format_RGBA8888_Premultiplied,
        QImage::Format_BGR30,
        QImage::Format_A2RGB30_Premultiplied,
        QImage::Format_Grayscale8,
        QImage::Format_Invalid
    };

    const QImage::Format sourceImageFormats[] = {
        QImage::Format_Indexed8,
        QImage::Format_RGB32,
        QImage::Format_ARGB32,
        QImage::Format_ARGB32_Premultiplied,
        QImage::Format_RGB16,
        QImage::Format_RGB888,
        QImage::Format_RGBX8888,
        QImage::Format_RGBA8888,
        QImage::Format_RGB30,
        QImage::Format_Grayscale8,
        QImage::Format_Invalid
    };

    const QImage::Format *targetFormats = pixmaps ? pixmapFormats : targetImageFormats;
    for (; *targetFormats != QImage::Format_Invalid; ++targetFormats) {
        const QImage::Format *sourceFormats = pixmaps ? pixmapFormats : sourceImageFormats;
        for (; *sourceFormats != QImage::Format_Invalid; ++sourceFormats) {
            for (const QSize &s : std::as_const(sizes)) {
                for (int type=0; type<=3; ++type) {
                    QString name = QString::fromLatin1("%1 on %2, (%3x%4), %5")
                                   .arg(formatNames[*sourceFormats])
                                   .arg(formatNames[*targetFormats])
                                   .arg(s.width()).arg(s.height())
                                   .arg(typeNames[type]);
                    QTest::newRow(name.toLatin1()) << *sourceFormats
                                                   << *targetFormats
                                                   << s
                                                   << type;
                }
            }
        }
    }
}

static QImage createImage(int type, const QSize &size) {
    QImage base(size, QImage::Format_ARGB32_Premultiplied);
    base.fill(0);
    QPainter p(&base);
    p.setRenderHint(QPainter::Antialiasing);
    switch (type) {
    case 0: // ellipse
        p.setBrush(Qt::red);
        p.drawEllipse(0, 0, size.width(), size.height());
        break;
    case 1: // line
        p.drawLine(0, 0, size.width(), size.height());
        break;
    case 2:
        p.fillRect(0, 0, size.width(), size.height(), Qt::red);
        break;
    case 3:
        p.fillRect(0, 0, size.width(), size.height(), QColor(0, 255, 0, 127));
        break;
    }
    p.end();
    return base;
}


void tst_QPainter::drawPixmap_data()
{
    drawPixmapImage_data_helper(true);
}

void tst_QPainter::drawPixmap()
{
    QFETCH(QImage::Format, sourceFormat);
    QFETCH(QImage::Format, targetFormat);
    QFETCH(QSize, size);
    QFETCH(int, type);

    QImage sourceImage = createImage(type, size).convertToFormat(sourceFormat);
    QImage targetImage(size, targetFormat);

    QPixmap sourcePixmap = rasterPixmap(sourceImage);
    QPixmap targetPixmap = rasterPixmap(targetImage);

    QPainter p(&targetPixmap);

    QBENCHMARK {
        p.drawPixmap(0, 0, sourcePixmap);
    }
}

void tst_QPainter::drawImage_data()
{
    drawPixmapImage_data_helper(false);
}


void tst_QPainter::drawImage()
{
    QFETCH(QImage::Format, sourceFormat);
    QFETCH(QImage::Format, targetFormat);
    QFETCH(QSize, size);
    QFETCH(int, type);

    QImage sourceImage = createImage(type, size).convertToFormat(sourceFormat);
    QImage targetImage(size, targetFormat);

    QPainter p(&targetImage);
    QBENCHMARK {
        p.drawImage(0, 0, sourceImage);
    }
}


void tst_QPainter::compositionModes_data()
{
    QTest::addColumn<QPainter::CompositionMode>("mode");
    QTest::addColumn<QSize>("size");
    QTest::addColumn<QColor>("color");

    const int n = QPainter::RasterOp_SourceAndNotDestination;
    for (int i = 0; i <= n; ++i) {
        QString title("%1:%2");
        QTest::newRow(qPrintable(title.arg(i).arg("10x10:opaque")))
            << (QPainter::CompositionMode)(i)
            << QSize(10, 10) << QColor(255, 0, 0);
        QTest::newRow(qPrintable(title.arg(i).arg("10x10:!opaque")))
            << (QPainter::CompositionMode)(i)
            << QSize(10, 10) << QColor(127, 127, 127, 127);
        QTest::newRow(qPrintable(title.arg(i).arg("300x300:opaque")))
            << (QPainter::CompositionMode)(i)
            << QSize(300, 300) << QColor(255, 0, 0);
        QTest::newRow(qPrintable(title.arg(i).arg("300x300:!opaque")))
            << (QPainter::CompositionMode)(i)
            << QSize(300, 300) << QColor(127, 127, 127, 127);
    }
}

void tst_QPainter::compositionModes()
{
    QFETCH(QPainter::CompositionMode, mode);
    QFETCH(QSize, size);
    QFETCH(QColor, color);

    QPixmap src = rasterPixmap(size);
    src.fill(color);

    QPixmap dest = rasterPixmap(size);
    if (mode < QPainter::RasterOp_SourceOrDestination)
        color.setAlpha(127); // porter-duff needs an alpha channel
    dest.fill(color);

    QPainter p(&dest);
    p.setCompositionMode(mode);

    QBENCHMARK {
        p.drawPixmap(0, 0, src);
    }
}

void tst_QPainter::drawTiledPixmap_data()
{
    QTest::addColumn<QSize>("srcSize");
    QTest::addColumn<QSize>("dstSize");
    QTest::addColumn<QTransform>("transform");
    QTest::addColumn<QColor>("color");
    QTest::addColumn<QPainter::RenderHint>("renderHint");

    QTest::newRow("10x10=>20x20")
        << QSize(10, 10) << QSize(20, 20) << (QTransform())
        << QColor(Qt::black) << QPainter::RenderHint(0);
    QTest::newRow("10x10=>20x20, smooth")
        << QSize(10, 10) << QSize(20, 20) << (QTransform())
        << QColor(Qt::black) << QPainter::SmoothPixmapTransform;
    QTest::newRow("10x10=>20x20, !opaque")
        << QSize(10, 10) << QSize(20, 20) << (QTransform())
        << QColor(127, 127, 127, 127) << QPainter::RenderHint(0);
    QTest::newRow("10x10=>20x20, !opaque, smooth")
        << QSize(10, 10) << QSize(20, 20) << (QTransform())
        << QColor(127, 127, 127, 127) << QPainter::SmoothPixmapTransform;

    QTest::newRow("10x10=>20x20, rotate(30)")
        << QSize(10, 10) << QSize(20, 20) << (QTransform().rotate(30))
        << QColor(Qt::black) << QPainter::RenderHint(0);
    QTest::newRow("10x10=>20x20, rotate(30), smooth")
        << QSize(10, 10) << QSize(20, 20) << (QTransform().rotate(30))
        << QColor(Qt::black) << QPainter::SmoothPixmapTransform;
    QTest::newRow("10x10=>20x20, rotate(30), !opaque")
        << QSize(10, 10) << QSize(20, 20) << (QTransform().rotate(30))
        << QColor(127, 127, 127, 127) << QPainter::RenderHint(0);
    QTest::newRow("10x10=>20x20, rotate(30), !opaque, smooth")
        << QSize(10, 10) << QSize(20, 20) << (QTransform().rotate(30))
        << QColor(127, 127, 127, 127) << QPainter::SmoothPixmapTransform;

    QTest::newRow("100x100=>200x200")
        << QSize(100, 100) << QSize(200, 200) << (QTransform())
        << QColor(Qt::black) << QPainter::RenderHint(0);
    QTest::newRow("100x100=>200x200, smooth")
        << QSize(100, 100) << QSize(200, 200) << (QTransform())
        << QColor(Qt::black) << QPainter::SmoothPixmapTransform;
    QTest::newRow("100x100=>200x200, !opaque")
        << QSize(100, 100) << QSize(200, 200) << (QTransform())
        << QColor(127, 127, 127, 127) << QPainter::RenderHint(0);
    QTest::newRow("100x100=>200x200, !opaque, smooth")
        << QSize(100, 100) << QSize(200, 200) << (QTransform())
        << QColor(127, 127, 127, 127) << QPainter::SmoothPixmapTransform;

    QTest::newRow("100x100=>200x200, rotate(30)")
        << QSize(100, 100) << QSize(200, 200) << (QTransform().rotate(30))
        << QColor(Qt::black) << QPainter::RenderHint(0);
    QTest::newRow("100x100=>200x200, rotate(30), smooth")
        << QSize(100, 100) << QSize(200, 200) << (QTransform().rotate(30))
        << QColor(Qt::black) << QPainter::SmoothPixmapTransform;
    QTest::newRow("100x100=>200x200, rotate(30), !opaque")
        << QSize(100, 100) << QSize(200, 200) << (QTransform().rotate(30))
        << QColor(127, 127, 127, 127) << QPainter::RenderHint(0);
    QTest::newRow("100x100=>200x200, rotate(30), !opaque, smooth")
        << QSize(100, 100) << QSize(200, 200) << (QTransform().rotate(30))
        << QColor(127, 127, 127, 127) << QPainter::SmoothPixmapTransform;
}

void tst_QPainter::drawTiledPixmap()
{
    QFETCH(QSize, srcSize);
    QFETCH(QSize, dstSize);
    QFETCH(QTransform, transform);
    QFETCH(QColor, color);
    QFETCH(QPainter::RenderHint, renderHint);

    QPixmap src = rasterPixmap(srcSize);
    src.fill(color);

    const QRect dstRect = transform.mapRect(QRect(QPoint(), dstSize));
    QPixmap dst = rasterPixmap(dstRect.right() + 5, dstRect.bottom() + 5);
    QPainter p(&dst);
    p.setTransform(transform);
    p.setRenderHint(renderHint);

    QBENCHMARK {
        p.drawTiledPixmap(QRect(QPoint(), dstSize), src);
    }
}

void tst_QPainter::fillPrimitives_helper(QPainter *p, PrimitiveType type, PrimitiveSet *s)
{
    p->paintEngine()->syncState();

    switch (type) {
    case Primitive_Int_DiagLine:        QBENCHMARK { p->drawLine(s->i_line_diag); } break;
    case Primitive_Int_VerLine:         QBENCHMARK { p->drawLine(s->i_line_ver); } break;
    case Primitive_Int_HorLine:         QBENCHMARK { p->drawLine(s->i_line_hor); } break;
    case Primitive_Int_Rect:            QBENCHMARK { p->drawRect(s->i_rect); } break;
    case Primitive_Int_Ellipse:         QBENCHMARK { p->drawEllipse(s->i_rect); } break;
    case Primitive_Int_Pie:             QBENCHMARK { p->drawPie(s->i_rect, 45*16, 270*16); } break;
    case Primitive_Int_Arc:             QBENCHMARK { p->drawArc(s->i_rect, 45*16, 270*16); } break;
    case Primitive_Int_Chord:           QBENCHMARK { p->drawChord(s->i_rect, 45*16, 270*16); } break;
    case Primitive_Int_TriPoly:         QBENCHMARK { p->drawPolygon(s->i_poly_tri); } break;
    case Primitive_Int_RectPoly:        QBENCHMARK { p->drawPolygon(s->i_poly_rect); } break;
    case Primitive_Int_2RectPoly:       QBENCHMARK { p->drawPolygon(s->i_poly_2rects); } break;

    case Primitive_Float_DiagLine:      QBENCHMARK { p->drawLine(s->f_line_diag); } break;
    case Primitive_Float_VerLine:       QBENCHMARK { p->drawLine(s->f_line_ver); } break;
    case Primitive_Float_HorLine:       QBENCHMARK { p->drawLine(s->f_line_hor); } break;
    case Primitive_Float_Rect:          QBENCHMARK { p->drawRect(s->f_rect); } break;
    case Primitive_Float_Ellipse:       QBENCHMARK { p->drawEllipse(s->f_rect); } break;
    case Primitive_Float_Pie:           QBENCHMARK { p->drawPie(s->f_rect, 45*16, 270*16); } break;
    case Primitive_Float_Arc:           QBENCHMARK { p->drawArc(s->f_rect, 45*16, 270*16); } break;
    case Primitive_Float_Chord:         QBENCHMARK { p->drawChord(s->f_rect, 45*16, 270*16); } break;
    case Primitive_Float_TriPoly:       QBENCHMARK { p->drawPolygon(s->f_poly_tri); } break;
    case Primitive_Float_RectPoly:      QBENCHMARK { p->drawPolygon(s->f_poly_rect); } break;
    case Primitive_Float_2RectPoly:     QBENCHMARK { p->drawPolygon(s->f_poly_2rects); } break;

    case Primitive_Float_TriPath:       QBENCHMARK { p->drawPath(s->f_path_tri); } break;
    case Primitive_Float_RectPath:      QBENCHMARK { p->drawPath(s->f_path_rect); } break;
    case Primitive_Float_2RectPath:     QBENCHMARK { p->drawPath(s->f_path_2rects); } break;
    case Primitive_Float_EllipsePath:   QBENCHMARK { p->drawPath(s->f_path_ellipse); } break;
    case Primitive_Last_Primitive: break;
    }
}

void tst_QPainter::drawPrimitives_data_helper(bool fancypens)
{
    QTest::addColumn<int>("type");
    QTest::addColumn<bool>("aa");
    QTest::addColumn<bool>("dash");
    QTest::addColumn<int>("width");

    const char * const names[] = {
        "IDLine",
        "IVLine",
        "IHLine",
        "IRect",
        "IElli",
        "IPie",
        "IArc",
        "IChord",
        "ITriPol",
        "IRectPol",
        "I2RectPol",
        "FDLine",
        "FVLine",
        "FHLine",
        "FRect",
        "FElli",
        "FPie",
        "FArc",
        "FChord",
        "FTriPol",
        "FRectPol",
        "F2RectPol",
        "FTriPa",
        "FRectPa",
        "F2RectPa",
        "FElliPa"
    };

    if (fancypens) {
        for (int dash=0; dash<2; ++dash) {
            for (int width=0; width<=4; width+=4) {
                for (int aa=0; aa<2; ++aa) {
                    for (int type=0; type<Primitive_Last_Primitive; ++type) {
                        QString name = QString::fromLatin1(names[type]);

                        if (aa) name += " aa";
                        if (dash) name += " dotted";
                        if (width) name += QString::fromLatin1(" width=%1").arg(width);

                        QTest::newRow(name.toLatin1()) << type << (bool) aa << (bool) dash << width;
                    }
                }
            }
        }
    } else {
        for (int aa=0; aa<2; ++aa) {
            for (int type=0; type<Primitive_Last_Primitive; ++type) {
                QString name = QString::fromLatin1(names[type]);
                if (aa) name += " aa";
                QTest::newRow(name.toLatin1()) << type << (bool) aa;
            }
        }
    }
}


void tst_QPainter::fillPrimitives_10()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QPainter p(m_surface);
    p.setPen(Qt::NoPen);
    p.setBrush(Qt::red);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_10);
}


void tst_QPainter::fillPrimitives_100()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QPainter p(m_surface);
    p.setPen(Qt::NoPen);
    p.setBrush(Qt::red);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_100);
}


void tst_QPainter::fillPrimitives_1000()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QPainter p(m_surface);
    p.setPen(Qt::NoPen);
    p.setBrush(Qt::red);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_1000);
}

void tst_QPainter::strokePrimitives_10()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QFETCH(bool, dash);
    QFETCH(int, width);
    QPainter p(m_surface);
    p.setPen(QPen(Qt::red, width, dash ? Qt::DashLine : Qt::SolidLine));
    p.setBrush(Qt::NoBrush);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_10);
}

void tst_QPainter::strokePrimitives_100()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QFETCH(bool, dash);
    QFETCH(int, width);
    QPainter p(m_surface);
    p.setPen(QPen(Qt::red, width, dash ? Qt::DashLine : Qt::SolidLine));
    p.setBrush(Qt::NoBrush);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_100);
}

void tst_QPainter::strokePrimitives_1000()
{
    QFETCH(int, type);
    QFETCH(bool, aa);
    QFETCH(bool, dash);
    QFETCH(int, width);
    QPainter p(m_surface);
    p.setPen(QPen(Qt::red, width, dash ? Qt::DashLine : Qt::SolidLine));
    p.setBrush(Qt::NoBrush);
    p.setRenderHint(QPainter::Antialiasing, aa);
    fillPrimitives_helper(&p, (PrimitiveType) type, &m_primitives_1000);
}

void tst_QPainter::drawText_data()
{
    QTest::addColumn<QString>("text");

    QTest::newRow("a") << QString::fromLatin1("a");
    QTest::newRow("ab") << QString::fromLatin1("ab");
    QTest::newRow("abc") << QString::fromLatin1("abc");
    QTest::newRow("abcd") << QString::fromLatin1("abcd");
    QTest::newRow("abcde") << QString::fromLatin1("abcde");
    QTest::newRow("abcdef") << QString::fromLatin1("abcdef");
    QTest::newRow("abcdefg") << QString::fromLatin1("abcdefg");
}

void tst_QPainter::drawText()
{
    QFETCH(QString, text);

    QPainter p(m_surface);

    QBENCHMARK {
        p.drawText(QPointF(5, 5), text);
    }
}

void tst_QPainter::saveRestore_data()
{
    QTest::addColumn<int>("change");

    for (int i=0; i<16; ++i) {
        QString change = "change=";
        if (i == 0) change += " none";
        if (i & ChangePen) change += " pen";
        if (i & ChangeBrush) change += " brush";
        if (i & ChangeClip) change += " clip";
        if (i & ChangeTransform) change += " xform";

        QTest::newRow(change.toLatin1()) << i;
    }
}

void tst_QPainter::saveRestore()
{
    QFETCH(int, change);

    QPen pen(Qt::blue);
    QBrush brush(Qt::green);
    QRect r(100, 100, 100, 20);

    QPainter p(m_surface);

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

    QBENCHMARK {
        p.save();
        if (change & ChangePen) { p.setPen(pen); p.setPen(Qt::NoPen); }
        if (change & ChangeBrush) { p.setBrush(brush); p.setBrush(Qt::NoBrush); }
        if (change & ChangeClip) p.setClipRect(r);
        if (change & ChangeTransform) { p.scale(3, 5); p.scale(1/3.0, 1/5.0); }
        p.drawRect(0, 0, 1, 1);
        p.restore();
    };
}

enum ClipType {
    RectClipType,
    RectPathClipType,
    RectRegionClipType,
    RegionClipType,
    PathClipType
};

void tst_QPainter::clipAndFill_data()
{
    QTest::addColumn<int>("type");

    QTest::newRow("rect") << (int) RectClipType;
    QTest::newRow("rectpath") << (int) RectPathClipType;
    QTest::newRow("rectregion") << (int) RectRegionClipType;
    QTest::newRow("ellipseRegion") << (int) RegionClipType;
    QTest::newRow("ellipsePath") << (int) PathClipType;
}


void tst_QPainter::clipAndFill()
{
    QFETCH(int, type);

    QRegion region;
    QPainterPath path;
    QRectF rect;

    switch (type) {
    case RectClipType:
        rect = QRectF(100, 100, 100, 100);
        break;
    case RectPathClipType:
        path.addRect(100, 100, 100, 100);
        break;
    case RectRegionClipType:
        region = QRegion(100, 100, 100, 100);
        break;
    case RegionClipType:
        region = QRegion(100, 100, 100, 100, QRegion::Ellipse);
        break;
    case PathClipType:
        path.addEllipse(100, 100, 100, 100);
        break;
    }

    QPainter p(m_surface);

    p.setPen(Qt::NoPen);
    p.setBrush(Qt::red);

    QBENCHMARK {
        if (type == RectClipType)
            p.setClipRect(rect);
        else if (type == RectPathClipType || type == PathClipType)
            p.setClipPath(path);
        else
            p.setClipRegion(region);
        p.drawRect(110, 110, 10, 10);
    }
}

QTransform tst_QPainter::transformForAngle(qreal angle)
{
    const qreal inv_dist_to_plane = 1. / 1024.;

    QTransform transform;

    QTransform rotTrans;
    rotTrans.translate(-40, 0);
    QTransform rotTrans2;
    rotTrans2.translate(40, 0);

    qreal rad = qDegreesToRadians(angle);
    qreal c = ::cos(rad);
    qreal s = ::sin(rad);

    qreal x = 0;
    qreal y = 80;
    qreal z = 0;

    qreal len = x * x + y * y + z * z;
    if (len != 1.) {
        len = ::sqrt(len);
        x /= len;
        y /= len;
        z /= len;
    }

    QTransform rot(x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s*inv_dist_to_plane,
                   y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*inv_dist_to_plane,
                   0, 0, 1);

    transform *= rotTrans;
    transform *= rot;
    transform *= rotTrans2;

    return transform;
}

void tst_QPainter::drawRoundedRect()
{
    QImage surface(100, 100, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);

    QBENCHMARK {
        p.drawRoundedRect(QRectF(.5, .5, 80, 80), 10, 10);
    }
}

void tst_QPainter::drawScaledRoundedRect()
{
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);
    p.scale(3, 3);

    QBENCHMARK {
        p.drawRoundedRect(10, 10, 80, 80, 10, 10);
    }
}

void tst_QPainter::drawTransformedRoundedRect()
{
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);

    QBENCHMARK {
        p.setWorldTransform(QTransform(0.956957, 0, 0.000704124, 0, 1, 0, 16.141, 0, 0.735953));
        p.drawRoundedRect(100, 100, 80, 80, 10, 10);
    }
}

void tst_QPainter::drawAntialiasedRoundedRect()
{
    QImage surface(100, 100, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setRenderHint(QPainter::Antialiasing, true);
    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);

    QBENCHMARK {
        p.drawRoundedRect(QRectF(.5, .5, 80, 80), 10, 10);
    }
}

void tst_QPainter::drawScaledAntialiasedRoundedRect_data()
{
    QTest::addColumn<float>("scale");

    for (float i = 0; i < 3; i += .1f)
        QTest::newRow(QString(QLatin1String("scale=%1")).arg(i).toLatin1()) << i;
}

void tst_QPainter::drawScaledAntialiasedRoundedRect()
{
    QFETCH(float, scale);

    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setRenderHint(QPainter::Antialiasing, true);
    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);
    p.scale(scale, scale);

    QBENCHMARK {
        p.drawRoundedRect(10, 10, 80, 80, 10, 10);
    }
}

void tst_QPainter::drawTransformedAntialiasedRoundedRect_data()
{
    QTest::addColumn<QTransform>("transform");

    for (float angle = 0; angle < 360; angle += 10)
        QTest::newRow(QString(QLatin1String("angle=%1")).arg(angle).toLatin1()) << transformForAngle(angle);
}

void tst_QPainter::drawTransformedAntialiasedRoundedRect()
{
    QFETCH(QTransform, transform);

    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    p.setRenderHint(QPainter::Antialiasing, true);
    p.setPen(QPen(Qt::black, 1));
    p.setBrush(Qt::red);

    QBENCHMARK {
        p.setWorldTransform(transform);
        p.drawRoundedRect(100, 100, 80, 80, 10, 10);
    }
}

void tst_QPainter::drawImageRoundedRect()
{
    //setup image
    const int radius = 10;
    QImage rectImage(81, 81, QImage::Format_ARGB32_Premultiplied);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    rp.drawRoundedRect(QRectF(.5, .5, 80, 80), radius, radius);

    //setup surface
    QImage surface(100, 100, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.drawImage(0,0, rectImage);
    }
}

void tst_QPainter::drawScaledImageRoundedRect_data()
{
    QTest::addColumn<int>("imageType");

    QTest::newRow("imagetype=ARGB32_Pre") << (int)QImage::Format_ARGB32_Premultiplied;
    QTest::newRow("imagetype=ARGB8565_Pre") << (int)QImage::Format_ARGB8565_Premultiplied;
}

void tst_QPainter::drawScaledImageRoundedRect()
{
    QFETCH(int, imageType);

    //setup image
    const int radius = 10;
    QImage rectImage(81, 81, (QImage::Format)imageType);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    rp.drawRoundedRect(QRectF(.5, .5, 80, 80), radius, radius);

    //setup surface
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);
    p.scale(3, 3);

    QBENCHMARK {
        p.drawImage(0,0, rectImage);
    }
}

void tst_QPainter::drawTransformedImageRoundedRect_data()
{
    QTest::addColumn<int>("imageType");

    QTest::newRow("imagetype=ARGB32_Pre") << (int)QImage::Format_ARGB32_Premultiplied;
    QTest::newRow("imagetype=ARGB8565_Pre") << (int)QImage::Format_ARGB8565_Premultiplied;
}

void tst_QPainter::drawTransformedImageRoundedRect()
{
    QFETCH(int, imageType);

    //setup image
    const int radius = 10;
    QImage rectImage(81, 81, (QImage::Format)imageType);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    rp.drawRoundedRect(QRectF(.5, .5, 80, 80), radius, radius);

    //setup surface
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.setWorldTransform(QTransform(0.956957, 0, 0.000704124, 0, 1, 0, 16.141, 0, 0.735953));
        p.drawImage(100,100, rectImage);
    }
}

//code from QDeclarativeRectangle for drawing rounded rects
void tst_QPainter::drawBorderPixmapRoundedRect()
{
    //setup image
    const int pw = 1;
    const int radius = 10;
    QImage rectImage(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2, QImage::Format_ARGB32_Premultiplied);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    if (pw%2)
        rp.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, rectImage.width()-(pw+1), rectImage.height()-(pw+1)), radius, radius);
    else
        rp.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, rectImage.width()-pw, rectImage.height()-pw), radius, radius);
    QPixmap rectPixmap = rasterPixmap(rectImage);

    //setup surface
    QImage surface(100, 100, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        const int pw = 2;
        int width = 80;
        int height = 80;

        int xOffset = (rectPixmap.width()-1)/2;
        int yOffset = (rectPixmap.height()-1)/2;
        Q_ASSERT(rectPixmap.width() == 2*xOffset + 1);
        Q_ASSERT(rectPixmap.height() == 2*yOffset + 1);

        QMargins margins(xOffset, yOffset, xOffset, yOffset);
        QTileRules rules(Qt::StretchTile, Qt::StretchTile);
        //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects
        qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width+pw, height+pw), margins, rectPixmap, rectPixmap.rect(), margins, rules);
    }
}

void tst_QPainter::drawScaledBorderPixmapRoundedRect_data()
{
    QTest::addColumn<float>("scale");
    QTest::addColumn<int>("imageType");

    for (float i = 0; i < 3; i += .1f)
        QTest::newRow(QString(QLatin1String("scale=%1; imagetype=ARGB32_Pre")).arg(i).toLatin1()) << i << (int)QImage::Format_ARGB32_Premultiplied;
    //for (float i = 0; i < 3; i += .1)
    //    QTest::newRow(QString(QLatin1String("scale=%1; imagetype=ARGB8565_Pre")).arg(i).toLatin1()) << i << (int)QImage::Format_ARGB8565_Premultiplied;
}

//code from QDeclarativeRectangle for drawing rounded rects
void tst_QPainter::drawScaledBorderPixmapRoundedRect()
{
    QFETCH(float, scale);
    QFETCH(int, imageType);

    //setup image
    const int pw = 1;
    const int radius = 10;
    QImage rectImage(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2, (QImage::Format)imageType);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    if (pw%2)
        rp.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, rectImage.width()-(pw+1), rectImage.height()-(pw+1)), radius, radius);
    else
        rp.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, rectImage.width()-pw, rectImage.height()-pw), radius, radius);

    QPixmap rectPixmap = rasterPixmap(rectImage);

    //setup surface
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);
    p.scale(scale, scale);

    QBENCHMARK {
        const int pw = 2;
        int width = 80;
        int height = 80;

        int xOffset = (rectPixmap.width()-1)/2;
        int yOffset = (rectPixmap.height()-1)/2;
        Q_ASSERT(rectPixmap.width() == 2*xOffset + 1);
        Q_ASSERT(rectPixmap.height() == 2*yOffset + 1);

        QMargins margins(xOffset, yOffset, xOffset, yOffset);
        QTileRules rules(Qt::StretchTile, Qt::StretchTile);
        qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width+pw, height+pw), margins, rectPixmap, rectPixmap.rect(), margins, rules);
    }
}

void tst_QPainter::drawTransformedBorderPixmapRoundedRect_data()
{
    QTest::addColumn<QTransform>("transform");
    QTest::addColumn<int>("imageType");

    for (float angle = 0; angle < 360; angle += 10)
        QTest::newRow(QString(QLatin1String("angle=%1; imagetype=ARGB32_Pre")).arg(angle).toLatin1()) << transformForAngle(angle) << (int)QImage::Format_ARGB32_Premultiplied;
    //for (float angle = 0; angle < 360; angle += 10)
    //    QTest::newRow(QString(QLatin1String("angle=%1; imagetype=ARGB8565_Pre")).arg(angle).toLatin1()) << transformForAngle(angle) << (int)QImage::Format_ARGB8565_Premultiplied;

}

//code from QDeclarativeRectangle for drawing rounded rects
void tst_QPainter::drawTransformedBorderPixmapRoundedRect()
{
    QFETCH(QTransform, transform);
    QFETCH(int, imageType);

    //setup image
    const int pw = 1;
    const int radius = 10;
    QImage rectImage(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2, (QImage::Format)imageType);
    rectImage.fill(0);
    QPainter rp(&rectImage);
    rp.setRenderHint(QPainter::Antialiasing);
    rp.setPen(Qt::black);
    rp.setBrush(Qt::red);
    if (pw%2)
        rp.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, rectImage.width()-(pw+1), rectImage.height()-(pw+1)), radius, radius);
    else
        rp.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, rectImage.width()-pw, rectImage.height()-pw), radius, radius);

    QPixmap rectPixmap = rasterPixmap(rectImage);

    //setup surface
    QImage surface(400, 400, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.setWorldTransform(transform);
        const int pw = 2;
        int width = 80;
        int height = 80;

        int xOffset = (rectPixmap.width()-1)/2;
        int yOffset = (rectPixmap.height()-1)/2;
        Q_ASSERT(rectPixmap.width() == 2*xOffset + 1);
        Q_ASSERT(rectPixmap.height() == 2*yOffset + 1);

        QMargins margins(xOffset, yOffset, xOffset, yOffset);
        QTileRules rules(Qt::StretchTile, Qt::StretchTile);
        qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width+pw, height+pw), margins, rectPixmap, rectPixmap.rect(), margins, rules);
    }
}

void tst_QPainter::drawTransformedTransparentImage_data()
{
    QTest::addColumn<int>("imageType");

    QTest::newRow("imagetype=ARGB32_Pre") << (int)QImage::Format_ARGB32_Premultiplied;
    QTest::newRow("imagetype=ARGB8565_Pre") << (int)QImage::Format_ARGB8565_Premultiplied;
}

void tst_QPainter::drawTransformedTransparentImage()
{
    QFETCH(int, imageType);

    //setup image
    QImage transImage(200, 200, (QImage::Format)imageType);
    transImage.fill(0);

    //setup surface
    QImage surface(200, 200, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.setWorldTransform(QTransform(0.956957, 0, 0.000704124, 0, 1, 0, 16.141, 0, 0.735953));
        p.drawImage(0,0, transImage);
    }
}

void tst_QPainter::drawTransformedSemiTransparentImage_data()
{
    QTest::addColumn<int>("imageType");

    QTest::newRow("imagetype=ARGB32_Pre") << (int)QImage::Format_ARGB32_Premultiplied;
    QTest::newRow("imagetype=ARGB8565_Pre") << (int)QImage::Format_ARGB8565_Premultiplied;
}

void tst_QPainter::drawTransformedSemiTransparentImage()
{
    QFETCH(int, imageType);

    //setup image
    QImage transImage(200, 200, (QImage::Format)imageType);
    transImage.fill(QColor(0,0,0, 128).rgba());

    //setup surface
    QImage surface(200, 200, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.setWorldTransform(QTransform(0.956957, 0, 0.000704124, 0, 1, 0, 16.141, 0, 0.735953));
        p.drawImage(0,0, transImage);
    }
}

void tst_QPainter::drawTransformedFilledImage_data()
{
    QTest::addColumn<int>("imageType");

    QTest::newRow("imagetype=ARGB32_Pre") << (int)QImage::Format_ARGB32_Premultiplied;
    QTest::newRow("imagetype=ARGB8565_Pre") << (int)QImage::Format_ARGB8565_Premultiplied;
}

void tst_QPainter::drawTransformedFilledImage()
{
    QFETCH(int, imageType);

    //setup image
    QImage filledImage(200, 200, (QImage::Format)imageType);
    filledImage.fill(QColor(0,0,0).rgb());

    //setup surface
    QImage surface(200, 200, QImage::Format_RGB16);
    surface.fill(QColor(255,255,255).rgb());
    QPainter p(&surface);

    QBENCHMARK {
        p.setWorldTransform(QTransform(0.956957, 0, 0.000704124, 0, 1, 0, 16.141, 0, 0.735953));
        p.drawImage(0,0, filledImage);
    }
}

void tst_QPainter::drawPathExceedingDevice_data()
{
    QTest::addColumn<int>("dim");
    QTest::addColumn<QPainterPath>("path");

    const int dim = 512;
    QPainterPath p;
    const int ext = 10 * dim;
    for (int i = 0; i < ext; i += (ext / 50)) {
        p.lineTo(ext, i);
        p.lineTo(0,  dim);
        p.moveTo(0, 0);
    }

    {
        QPainterPath preClip;
        preClip.addRect(0, 0, dim, dim);
        QTest::newRow("devicesize") << dim << p.intersected(preClip);
    }

    {
        QPainterPath preClip;
        preClip.addRect(0, 0, 2*dim, 2*dim);
        QTest::newRow("devicesizex2") << dim << p.intersected(preClip);
    }

    {
        QPainterPath preClip;
        preClip.addRect(0, 0, 5*dim, 5*dim);
        QTest::newRow("devicesizex5") << dim << p.intersected(preClip);
    }

    QTest::newRow("devicesizex10") << dim << p;
}

void tst_QPainter::drawPathExceedingDevice()
{
    QFETCH(int, dim);
    QFETCH(QPainterPath, path);

    QImage img(dim, dim, QImage::Format_RGB32);
    QPainter p(&img);
    p.setRenderHint(QPainter::Antialiasing);
    p.setPen(QPen(Qt::black, 3));
    p.setBrush(Qt::NoBrush);

    QBENCHMARK {
        p.drawPath(path);
    }
}

void tst_QPainter::setPen()
{
    QImage img(20, 20, QImage::Format_RGB32);
    QPainter p(&img);

    QBENCHMARK {
        p.setPen(Qt::black);
        p.setPen(Qt::red);
    }
}

void tst_QPainter::setBrush()
{
    QImage img(20, 20, QImage::Format_RGB32);
    QPainter p(&img);

    QBENCHMARK {
        p.setBrush(Qt::black);
        p.setBrush(Qt::red);
    }
}


QTEST_MAIN(tst_QPainter)

#include "tst_qpainter.moc"
