tests for canvas and a few bug fixes
authorCharles Yin <yinyunqiao@gmail.com>
Thu, 22 Sep 2011 10:01:29 +0000 (20:01 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 30 Sep 2011 08:37:05 +0000 (10:37 +0200)
Change-Id: Icbbc7f2a0fe3b908963ce18afef51e25ea0170a0
Reviewed-on: http://codereview.qt-project.org/5805
Reviewed-by: Charles Yin <charles.yin@nokia.com>
25 files changed:
examples/declarative/canvas/bezierCurve/bezierCurve.qml
examples/declarative/canvas/quadraticCurveTo/quadraticCurveTo.qml
examples/declarative/canvas/roundedrect/roundedrect.qml
examples/declarative/canvas/smile/smile.qml
examples/declarative/canvas/squircle/squircle.qml
examples/declarative/canvas/tiger/tiger.qml
examples/declarative/canvas/twitterfriends/TwitterUser.qml
src/declarative/items/context2d/qsgcanvasitem.cpp
src/declarative/items/context2d/qsgcanvasitem_p.h
src/declarative/items/context2d/qsgcontext2d.cpp
src/declarative/items/context2d/qsgcontext2d_p.h
src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp
src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h
src/declarative/items/context2d/qsgcontext2dtexture.cpp
src/declarative/items/context2d/qsgcontext2dtexture_p.h
src/declarative/items/context2d/qsgcontext2dtile.cpp
src/declarative/items/context2d/qsgcontext2dtile_p.h
tests/auto/declarative/declarative.pro
tests/auto/declarative/qsgcanvasitem/data/testhelper.js [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/data/tst_colors.qml [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/data/tst_fillStyle.qml [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/data/tst_fillrect.qml [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/data/tst_strokeStyle.qml [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/qsgcanvasitem.pro [new file with mode: 0644]
tests/auto/declarative/qsgcanvasitem/tst_qsgcanvasitem.cpp [new file with mode: 0644]

index 3a7fbe7..7db84a1 100644 (file)
@@ -124,4 +124,4 @@ Item {
         }
     }
   }
-}
+}
\ No newline at end of file
index 5ec9204..0c53a2b 100644 (file)
@@ -125,4 +125,4 @@ Item {
         }
     }
   }
-}
+}
\ No newline at end of file
index 2ecd322..50cbc7e 100644 (file)
@@ -216,6 +216,7 @@ void QSGCanvasItem::setCanvasSize(const QSizeF & size)
         d->canvasSize = size;
         emit canvasSizeChanged();
         polish();
+        update();
     }
 }
 
@@ -249,6 +250,7 @@ void QSGCanvasItem::setTileSize(const QSize & size)
 
         emit tileSizeChanged();
         polish();
+        update();
     }
 }
 
@@ -279,6 +281,7 @@ void QSGCanvasItem::setCanvasWindow(const QRectF& rect)
         d->hasCanvasWindow = true;
         emit canvasWindowChanged();
         polish();
+        update();
     }
 }
 
@@ -378,6 +381,7 @@ void QSGCanvasItem::setRenderInThread(bool renderInThread)
             disconnect(this, SIGNAL(painted()), this, SLOT(update()));
         emit renderInThreadChanged();
         polish();
+        update();
     }
 }
 
@@ -406,26 +410,29 @@ void QSGCanvasItem::geometryChanged(const QRectF &newGeometry,
     }
 
     polish();
+    update();
 }
 
 void QSGCanvasItem::componentComplete()
 {
     Q_D(QSGCanvasItem);
 
-    createContext();
+    if (!d->context)
+        createContext();
     createTexture();
 
-    markDirty(d->canvasWindow);
+
+    _doPainting(canvasWindow());
     QSGItem::componentComplete();
 
     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
     d->componentCompleted = true;
+    update();
 }
 
 void QSGCanvasItem::updatePolish()
 {
     Q_D(QSGCanvasItem);
-
     QSGItem::updatePolish();
     if (d->texture) {
         if (!d->renderInThread && d->dirtyRect.isValid())
@@ -507,9 +514,9 @@ QDeclarativeV8Handle QSGCanvasItem::getContext(const QString &contextId)
     Q_D(QSGCanvasItem);
     Q_UNUSED(contextId);
 
-    if (d->context)
-       return QDeclarativeV8Handle::fromHandle(d->context->v8value());
-    return QDeclarativeV8Handle::fromHandle(v8::Undefined());
+    if (!d->context)
+        createContext();
+    return QDeclarativeV8Handle::fromHandle(d->context->v8value());
 }
 
 /*!
@@ -527,6 +534,7 @@ void QSGCanvasItem::markDirty(const QRectF& region)
     Q_D(QSGCanvasItem);
     d->dirtyRect |= region;
     polish();
+    update();
 }
 
 
@@ -717,4 +725,4 @@ QString QSGCanvasItem::toDataURL(const QString& mimeType) const
     the Canvas is actually rendered.
 */
 
-QT_END_NAMESPACE
\ No newline at end of file
+QT_END_NAMESPACE
index 8d6441a..a2dfb79 100644 (file)
@@ -45,8 +45,6 @@
 #include "qsgitem.h"
 #include <private/qv8engine_p.h>
 
-
-
 QT_BEGIN_HEADER
 
 QT_BEGIN_NAMESPACE
@@ -54,7 +52,7 @@ QT_BEGIN_NAMESPACE
 QT_MODULE(Declarative)
 class QSGContext2D;
 class QSGCanvasItemPrivate;
-class QSGCanvasItem : public QSGItem
+class Q_DECLARATIVE_EXPORT QSGCanvasItem : public QSGItem
 {
     Q_OBJECT
     Q_ENUMS(RenderTarget)
@@ -147,4 +145,4 @@ QML_DECLARE_TYPE(QSGCanvasItem)
 
 QT_END_HEADER
 
-#endif //QSGCANVASITEM_P_H
\ No newline at end of file
+#endif //QSGCANVASITEM_P_H
index 2058098..71cc9dc 100644 (file)
@@ -95,205 +95,85 @@ QT_BEGIN_NAMESPACE
 static const double Q_PI   = 3.14159265358979323846;   // pi
 
 #define DEGREES(t) ((t) * 180.0 / Q_PI)
-#define qClamp(val, min, max) qMin(qMax(val, min), max)
 
 #define CHECK_CONTEXT(r)     if (!r || !r->context || !r->context->buffer()) \
                                 V8THROW_ERROR("Not a Context2D object");
 
 #define CHECK_CONTEXT_SETTER(r)     if (!r || !r->context || !r->context->buffer()) \
                                        V8THROW_ERROR_SETTER("Not a Context2D object");
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
 
-static inline int extractInt(const char **name)
+QColor qt_color_from_string(const QString& name)
 {
-    int result = 0;
-    bool negative = false;
-
-    //eat leading whitespace
-    while (isspace(*name[0]))
-        ++*name;
-
-    if (*name[0] == '-') {
-        ++*name;
-        negative = true;
-    } /*else if (name[0] == '+')
-        ++name;     //ignore*/
-
-    //construct number
-    while (isdigit(*name[0])) {
-        result = result * 10 + (*name[0] - '0');
-        ++*name;
-    }
-    if (negative)
-        result = -result;
-
-    //handle optional percentage
-    if (*name[0] == '%')
-        result *= qreal(255)/100;  //### floor or round?
-
-    //eat trailing whitespace
-    while (isspace(*name[0]))
-        ++*name;
+    //rgb/hsl color string has at least 7 characters
+    if (name.isEmpty() || name.size() > 255 || name.size() <= 7)
+        return QColor(name);
+    else {
+        const char* data = name.toLatin1().constData();
+        bool isRgb = false, isHsl = false, hasAlpha = false;
+
+        int pos = 0;
+        while (isspace(data[pos])) pos++;
+
+        if (strncmp(&(data[pos]), "rgb", 3) == 0)
+            isRgb = true;
+        else if (strncmp(&(data[pos]), "hsl", 3) == 0)
+            isHsl = true;
+        else
+            return QColor(name);
+        pos+=3;
+        if (data[pos] == 'a')
+            hasAlpha = true;
+
+        int rh, gs, bl, alpha = 255;
+
+        const int len = name.size();
+        while (pos < len && (data[pos] != '(' || isspace(data[pos]))) pos++;
+        if (pos >= len) return QColor();
+
+        //red
+        while (pos < len && !isdigit(data[pos])) pos++;
+        if (pos >= len) return QColor();
+        rh = atoi(&(data[pos]));
+        while (pos < len && ((data[pos] != ',' && data[pos] != '%') || isspace(data[pos]))) pos++;
+        if (data[pos] == '%') {
+            rh = qRound(rh/100.0 * 255);
+            pos++;
+        }
+        //green
+        while (pos < len && !isdigit(data[pos])) pos++;
+        if (pos >= len) return QColor();
+        gs = atoi(&(data[pos]));
+        while (pos < len && ((data[pos] != ',' && data[pos] != '%') || isspace(data[pos]))) pos++;
+        if (data[pos] == '%') {
+            gs = qRound(gs/100.0 * 255);
+            pos++;
+        }
 
-    return result;
-}
+        //blue
+        while (pos < len && !isdigit(data[pos])) pos++;
+        if (pos >= len)
+            return QColor();
+        bl = atoi(&(data[pos]));
+        while (pos < len && ((data[pos] != ',' && data[pos] != '%') || isspace(data[pos]))) pos++;
+        if (data[pos] == '%') {
+            bl = qRound(bl/100.0 * 255);
+            pos++;
+        }
 
-static bool qt_get_rgb(const QString &string, QRgb *rgb)
-{
-    const char *name = string.toLatin1().constData();
-    int len = qstrlen(name);
-
-    if (len < 5)
-        return false;
+        if (hasAlpha) {
+            while (pos < len && !isdigit(data[pos])) pos++;
+            if (pos >= len)
+                return QColor();
+            alpha = qRound(strtof(&(data[pos]), 0) * 255);
+        }
 
-    bool handleAlpha = false;
-
-    if (name[0] != 'r')
-        return false;
-    if (name[1] != 'g')
-        return false;
-    if (name[2] != 'b')
-        return false;
-    if (name[3] == 'a') {
-        handleAlpha = true;
-        if(name[3] != '(')
-            return false;
-    } else if (name[3] != '(')
-        return false;
-
-    name += 4;
-
-    int r, g, b, a = 1;
-    int result;
-
-    //red
-    result = extractInt(&name);
-    if (name[0] == ',') {
-        r = result;
-        ++name;
-    } else
-        return false;
-
-    //green
-    result = extractInt(&name);
-    if (name[0] == ',') {
-        g = result;
-        ++name;
-    } else
-        return false;
-
-    char nextChar = handleAlpha ? ',' : ')';
-
-    //blue
-    result = extractInt(&name);
-    if (name[0] == nextChar) {
-        b = result;
-        ++name;
-    } else
-        return false;
-
-    //alpha
-    if (handleAlpha) {
-        result = extractInt(&name);
-        if (name[0] == ')') {
-            a = result * 255;   //map 0-1 to 0-255
-            ++name;
-        } else
-            return false;
-    }
-
-    if (name[0] != '\0')
-        return false;
-
-    *rgb = qRgba(qClamp(r,0,255), qClamp(g,0,255), qClamp(b,0,255), qClamp(a,0,255));
-    return true;
-}
-
-//### unify with qt_get_rgb?
-static bool qt_get_hsl(const QString &string, QColor *color)
-{
-    const char *name = string.toLatin1().constData();
-    int len = qstrlen(name);
-
-    if (len < 5)
-        return false;
-
-    bool handleAlpha = false;
-
-    if (name[0] != 'h')
-        return false;
-    if (name[1] != 's')
-        return false;
-    if (name[2] != 'l')
-        return false;
-    if (name[3] == 'a') {
-        handleAlpha = true;
-        if(name[3] != '(')
-            return false;
-    } else if (name[3] != '(')
-        return false;
-
-    name += 4;
-
-    int h, s, l, a = 1;
-    int result;
-
-    //hue
-    result = extractInt(&name);
-    if (name[0] == ',') {
-        h = result;
-        ++name;
-    } else
-        return false;
-
-    //saturation
-    result = extractInt(&name);
-    if (name[0] == ',') {
-        s = result;
-        ++name;
-    } else
-        return false;
-
-    char nextChar = handleAlpha ? ',' : ')';
-
-    //lightness
-    result = extractInt(&name);
-    if (name[0] == nextChar) {
-        l = result;
-        ++name;
-    } else
-        return false;
-
-    //alpha
-    if (handleAlpha) {
-        result = extractInt(&name);
-        if (name[0] == ')') {
-            a = result * 255;   //map 0-1 to 0-255
-            ++name;
-        } else
-            return false;
-    }
-
-    if (name[0] != '\0')
-        return false;
-
-    *color = QColor::fromHsl(qClamp(h,0,255), qClamp(s,0,255), qClamp(l,0,255), qClamp(a,0,255));
-    return true;
-}
-
-//### optimize further
-QColor qt_color_from_string(const QString &name)
-{
-    if (name.startsWith(QLatin1String("rgb"))) {
-        QRgb rgb;
-        if (qt_get_rgb(name, &rgb))
-            return QColor(rgb);
-    } else if (name.startsWith(QLatin1String("hsl"))) {
-        QColor color;
-        if (qt_get_hsl(name, &color))
-            return color;
+        if (isRgb)
+            return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
+        else
+            return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
     }
-
-    return QColor(name);
+    return QColor();
 }
 
 QFont qt_font_from_string(const QString& fontString) {
@@ -347,8 +227,14 @@ class QV8Context2DStyleResource : public QV8ObjectResource
 {
     V8_RESOURCE_TYPE(Context2DStyleType)
 public:
-    QV8Context2DStyleResource(QV8Engine *e) : QV8ObjectResource(e) {}
+    QV8Context2DStyleResource(QV8Engine *e)
+      : QV8ObjectResource(e)
+      , patternRepeatX(false)
+      , patternRepeatY(false)
+    {}
     QBrush brush;
+    bool patternRepeatX:1;
+    bool patternRepeatY:1;
 };
 
 class QV8Context2DPixelArrayResource : public QV8ObjectResource
@@ -527,10 +413,10 @@ static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* e
     QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
     if (image.isNull()) {
         r->image = QImage(w, h, QImage::Format_ARGB32);
-        r->image.fill(Qt::transparent);
+        r->image.fill(0x00000000);
     } else {
         Q_ASSERT(image.width() == w && image.height() == h);
-        r->image = image;
+        r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
     }
     v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
     pixelData->SetExternalResource(r);
@@ -936,6 +822,19 @@ static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::Ac
     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
     CHECK_CONTEXT(r)
 
+    QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+    QColor color = r->context->state.fillStyle.color();
+    if (color.isValid()) {
+        if (color.alpha() == 255)
+            return engine->toString(color.name());
+        QString alphaString = QString::number(color.alphaF(), 'f');
+        while (alphaString.endsWith('0'))
+            alphaString.chop(1);
+        if (alphaString.endsWith('.'))
+            alphaString += '0';
+        return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
+    }
     return r->context->m_fillStyle;
 }
 
@@ -946,17 +845,20 @@ static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> valu
 
     QV8Engine *engine = V8ENGINE_ACCESSOR();
 
-   r->context->m_fillStyle = value;
    if (value->IsObject()) {
        QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
        if (color.isValid()) {
            r->context->state.fillStyle = color;
            r->context->buffer()->setFillStyle(color);
+           r->context->m_fillStyle = value;
        } else {
            QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
            if (style && style->brush != r->context->state.fillStyle) {
                r->context->state.fillStyle = style->brush;
-               r->context->buffer()->setFillStyle(style->brush);
+               r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
+               r->context->m_fillStyle = value;
+               r->context->state.fillPatternRepeatX = style->patternRepeatX;
+               r->context->state.fillPatternRepeatY = style->patternRepeatY;
            }
        }
    } else if (value->IsString()) {
@@ -964,6 +866,7 @@ static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> valu
        if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
             r->context->state.fillStyle = QBrush(color);
             r->context->buffer()->setFillStyle(r->context->state.fillStyle);
+            r->context->m_fillStyle = value;
        }
    }
 }
@@ -1024,7 +927,19 @@ v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::Accesso
     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
     CHECK_CONTEXT(r)
 
+    QV8Engine *engine = V8ENGINE_ACCESSOR();
 
+    QColor color = r->context->state.strokeStyle.color();
+    if (color.isValid()) {
+        if (color.alpha() == 255)
+            return engine->toString(color.name());
+        QString alphaString = QString::number(color.alphaF(), 'f');
+        while (alphaString.endsWith('0'))
+            alphaString.chop(1);
+        if (alphaString.endsWith('.'))
+            alphaString += '0';
+        return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
+    }
     return r->context->m_strokeStyle;
 }
 
@@ -1035,17 +950,21 @@ static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> va
 
     QV8Engine *engine = V8ENGINE_ACCESSOR();
 
-    r->context->m_strokeStyle = value;
     if (value->IsObject()) {
         QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
         if (color.isValid()) {
             r->context->state.fillStyle = color;
             r->context->buffer()->setStrokeStyle(color);
+            r->context->m_strokeStyle = value;
         } else {
             QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
             if (style && style->brush != r->context->state.strokeStyle) {
                 r->context->state.strokeStyle = style->brush;
-                r->context->buffer()->setStrokeStyle(style->brush);
+                r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
+                r->context->m_strokeStyle = value;
+                r->context->state.strokePatternRepeatX = style->patternRepeatX;
+                r->context->state.strokePatternRepeatY = style->patternRepeatY;
+
             }
         }
     } else if (value->IsString()) {
@@ -1053,6 +972,7 @@ static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> va
         if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
              r->context->state.strokeStyle = QBrush(color);
              r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
+             r->context->m_strokeStyle = value;
         }
     }
 }
@@ -1068,6 +988,7 @@ static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> va
 
     \sa QtQuick2::Context2D::CanvasGradient::addColorStop
     \sa QtQuick2::Context2D::createRadialGradient
+    \sa QtQuick2::Context2D::ctx2d_createConicalGradient
     \sa QtQuick2::Context2D::createPattern
     \sa QtQuick2::Context2D::fillStyle
     \sa QtQuick2::Context2D::strokeStyle
@@ -1104,6 +1025,7 @@ static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &arg
 
     \sa QtQuick2::Context2D::CanvasGradient::addColorStop
     \sa QtQuick2::Context2D::createLinearGradient
+    \sa QtQuick2::Context2D::ctx2d_createConicalGradient
     \sa QtQuick2::Context2D::createPattern
     \sa QtQuick2::Context2D::fillStyle
     \sa QtQuick2::Context2D::strokeStyle
@@ -1137,6 +1059,45 @@ static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &arg
 
     return args.This();
 }
+
+/*!
+  \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
+   Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
+   with start angle \c angle in units of radians.
+
+    \sa QtQuick2::Context2D::CanvasGradient::addColorStop
+    \sa QtQuick2::Context2D::createLinearGradient
+    \sa QtQuick2::Context2D::ctx2d_createRadialGradient
+    \sa QtQuick2::Context2D::createPattern
+    \sa QtQuick2::Context2D::fillStyle
+    \sa QtQuick2::Context2D::strokeStyle
+  */
+
+static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
+{
+    QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+    CHECK_CONTEXT(r)
+
+
+    QV8Engine *engine = V8ENGINE();
+
+    if (args.Length() == 6) {
+        QSGContext2DEngineData *ed = engineData(engine);
+        v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
+        QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+
+        qreal x = args[0]->NumberValue();
+        qreal y = args[1]->NumberValue();
+        qreal angle = DEGREES(args[2]->NumberValue());
+        //TODO:infinite or NaN, a NOT_SUPPORTED_ERR exception must be raised.
+        //If either of r0 or r1 are negative, an INDEX_SIZE_ERR exception must be raised.
+        r->brush = QConicalGradient(x, y, angle);
+        gradient->SetExternalResource(r);
+        return gradient;
+    }
+
+    return args.This();
+}
 /*!
   \qmlmethod variant createPattern(Color color, enumeration patternMode)
   This is a overload function.
@@ -1188,43 +1149,57 @@ static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
 
     QV8Engine *engine = V8ENGINE();
 
-    //FIXME::
-
-//    if (args.Length() == 2) {
-//        QSGContext2DEngineData *ed = engineData(engine);
-//        v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
-//        QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
-
-//        QImage img;
-
-//        QSGItem* item = qobject_cast<QSGItem*>(engine->toQObject(args[0]));
-//        if (item) {
-//            img = qt_item_to_image(item);
-//            if (img.isNull()) {
-//                //exception: INVALID_STATE_ERR
-//            }
-//        } /*else {
-//            //exception: TYPE_MISMATCH_ERR
-//        }*/
-
-//        QString repetition = engine->toString(args[1]);
-
-//        if (repetition == "repeat" || repetition.isEmpty()) {
-//            //TODO
-//        } else if (repetition == "repeat-x") {
-//            //TODO
-//        } else if (repetition == "repeat-y") {
-//            //TODO
-//        } else if (repetition == "no-repeat") {
-//            //TODO
-//        } else {
-//            //TODO: exception: SYNTAX_ERR
-//        }
-//        r->brush = img;
-//        pattern->SetExternalResource(r);
- //       return pattern;
-//    }
-    return v8::Null();
+    if (args.Length() == 2) {
+        QSGContext2DEngineData *ed = engineData(engine);
+        QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
+
+        QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
+        if (color.isValid()) {
+            int patternMode = args[1]->IntegerValue();
+            Qt::BrushStyle style = Qt::SolidPattern;
+            if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
+                style = static_cast<Qt::BrushStyle>(patternMode);
+            }
+            styleResouce->brush = QBrush(color, style);
+        } else {
+            QImage patternTexture;
+
+            if (args[0]->IsObject()) {
+                QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
+                if (pixelData) {
+                    patternTexture = pixelData->image;
+                }
+            } else {
+                patternTexture = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+            }
+
+            if (!patternTexture.isNull()) {
+                styleResouce->brush.setTextureImage(patternTexture);
+
+                QString repetition = engine->toString(args[1]);
+                if (repetition == "repeat" || repetition.isEmpty()) {
+                    styleResouce->patternRepeatX = true;
+                    styleResouce->patternRepeatY = true;
+                } else if (repetition == "repeat-x") {
+                    styleResouce->patternRepeatX = true;
+                } else if (repetition == "repeat-y") {
+                    styleResouce->patternRepeatY = true;
+                } else if (repetition == "no-repeat") {
+                    styleResouce->patternRepeatY = false;
+                    styleResouce->patternRepeatY = false;
+                } else {
+                    //TODO: exception: SYNTAX_ERR
+                }
+
+            }
+        }
+
+        v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
+        pattern->SetExternalResource(styleResouce);
+        return pattern;
+
+    }
+    return v8::Undefined();
 }
 
 // line styles
@@ -2490,7 +2465,7 @@ static v8::Handle<v8::Value> ctx2d_imageData_filter(const v8::Arguments &args)
         switch(filterFlag) {
         case QSGCanvasItem::Mono :
         {
-            r->image = r->image.convertToFormat(QImage::Format_Mono).convertToFormat(QImage::Format_ARGB32);
+            r->image = r->image.convertToFormat(QImage::Format_Mono).convertToFormat(QImage::Format_ARGB32_Premultiplied);
         }
             break;
         case QSGCanvasItem::GrayScale :
@@ -2610,9 +2585,8 @@ v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::Accesso
 {
     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
 
-    if (r && index && index < r->image.width() * r->image.height() * 4) {
+    if (r && index >= 0 && index < r->image.width() * r->image.height() * 4) {
         const int w = r->image.width();
-        const int h = r->image.height();
         const int row = (index / 4) / w;
         const int col = (index / 4) % w;
         const QRgb* pixel = reinterpret_cast<const QRgb*>(r->image.constScanLine(row));
@@ -2636,7 +2610,7 @@ v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8:
     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
 
     const int v = value->Uint32Value();
-    if (r && index > 0 && index < r->image.width() * r->image.height() * 4 && v > 0 && v <= 255) {
+    if (r && index >= 0 && index < r->image.width() * r->image.height() * 4 && v > 0 && v <= 255) {
         const int w = r->image.width();
         const int row = (index / 4) / w;
         const int col = (index / 4) % w;
@@ -2722,8 +2696,6 @@ static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
         qreal w = args[2]->NumberValue();
         qreal h = args[3]->NumberValue();
         QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
-        if (image.format() != QImage::Format_ARGB32)
-            image = image.convertToFormat(QImage::Format_ARGB32);
         v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
 
         return imageData;
@@ -3181,6 +3153,7 @@ QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *engine)
     ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
     ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
     ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
+    ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
     ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
@@ -3326,8 +3299,12 @@ void QSGContext2D::reset()
     newState.clipPath = defaultClipPath;
     newState.clipPath.setFillRule(Qt::WindingFill);
 
-    newState.strokeStyle = Qt::black;
-    newState.fillStyle = Qt::black;
+    newState.strokeStyle = QColor(qRgba(1,1,1,1));
+    newState.fillStyle = QColor(qRgba(1,1,1,1));
+    newState.fillPatternRepeatX = false;
+    newState.fillPatternRepeatY = false;
+    newState.strokePatternRepeatX = false;
+    newState.strokePatternRepeatY = false;
     newState.fillRule = Qt::WindingFill;
     newState.globalAlpha = 1.0;
     newState.lineWidth = 1;
@@ -3370,4 +3347,4 @@ void QSGContext2D::setV8Engine(QV8Engine *engine)
     }
 }
 
-QT_END_NAMESPACE
+QT_END_NAMESPACE
\ No newline at end of file
index 830a185..5354876 100644 (file)
@@ -69,7 +69,7 @@ class QSGCanvasItem;
 class QSGContext2DCommandBuffer;
 class QDeclarativePixmap;
 
-class QSGContext2D
+class Q_DECLARATIVE_EXPORT QSGContext2D
 {
 public:
     enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging};
@@ -111,6 +111,10 @@ public:
         QPainterPath clipPath;
         QBrush strokeStyle;
         QBrush fillStyle;
+        bool fillPatternRepeatX:1;
+        bool fillPatternRepeatY:1;
+        bool strokePatternRepeatX:1;
+        bool strokePatternRepeatY:1;
         Qt::FillRule fillRule;
         qreal globalAlpha;
         qreal lineWidth;
index 550ad77..51730d4 100644 (file)
@@ -54,7 +54,7 @@ static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY,
 {
     QImage shadowImg(image.width() + blur + qAbs(offsetX),
                      image.height() + blur + qAbs(offsetY),
-                     QImage::Format_ARGB32);
+                     QImage::Format_ARGB32_Premultiplied);
     shadowImg.fill(0);
     QPainter tmpPainter(&shadowImg);
     tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
@@ -80,7 +80,7 @@ static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal
     QRectF r = shadowRect;
     r.moveTo(0, 0);
 
-    QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
+    QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied);
     QPainter tp;
     tp.begin(&shadowImage);
     tp.fillRect(r, p->brush());
@@ -99,7 +99,7 @@ static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX,
     QRectF r = path.boundingRect();
     QImage img(r.size().width() + r.left() + 1,
                r.size().height() + r.top() + 1,
-               QImage::Format_ARGB32);
+               QImage::Format_ARGB32_Premultiplied);
     img.fill(0);
     QPainter tp(&img);
     tp.fillPath(path.translated(0, 0), p->brush());
@@ -118,7 +118,7 @@ static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offset
     QRectF r = path.boundingRect();
     QImage img(r.size().width() + r.left() + 1,
                r.size().height() + r.top() + 1,
-               QImage::Format_ARGB32);
+               QImage::Format_ARGB32_Premultiplied);
     img.fill(0);
     QPainter tp(&img);
     tp.strokePath(path, p->pen());
@@ -130,6 +130,72 @@ static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offset
     p->drawImage(dx, dy, shadowImage);
     p->strokePath(path, p->pen());
 }
+static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY)
+{
+    // Patterns must be painted so that the top left of the first image is anchored at
+    // the origin of the coordinate space
+    if (!image.isNull()) {
+        int w = image.width();
+        int h = image.height();
+        int startX, startY;
+        QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
+
+        // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
+        if (repeatX && repeatY) {
+            // repeat
+            // startX, startY is at the left top side of the left-top of the rect
+            startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
+            startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
+        } else {
+           if (!repeatX && !repeatY) {
+               // no-repeat
+               // only draw the image once at orgin once, check if need to draw
+               QRect imageRect(0, 0, w, h);
+               if (imageRect.intersects(r)) {
+                   startX = 0;
+                   startY = 0;
+               } else
+                   return;
+           } else if (repeatX && !repeatY) {
+               // repeat-x
+               // startY is fixed, but startX change based on the left-top of the rect
+               QRect imageRect(r.x(), 0, r.width(), h);
+               if (imageRect.intersects(r)) {
+                   startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
+                   startY = 0;
+               } else
+                   return;
+           } else {
+               // repeat-y
+               // startX is fixed, but startY change based on the left-top of the rect
+               QRect imageRect(0, r.y(), w, r.height());
+               if (imageRect.intersects(r)) {
+                   startX = 0;
+                   startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
+               } else
+                   return;
+           }
+        }
+
+        int x = startX;
+        int y = startY;
+        do {
+            // repeat Y
+            do {
+                // repeat X
+                QRect   imageRect(x, y, w, h);
+                QRect   intersectRect = imageRect.intersected(r);
+                QPoint  destStart(intersectRect.x(), intersectRect.y());
+                QRect   sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
+
+                p->drawImage(destStart, image, sourceRect);
+                x += w;
+            } while (repeatX && x < r.x() + r.width());
+            x = startX;
+            y += h;
+        } while (repeatY && y < r.y() + r.height());
+    }
+}
 
 QPen QSGContext2DCommandBuffer::makePen(QSGContext2D::State state)
 {
@@ -186,7 +252,7 @@ QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D:
         }
         case QSGContext2D::ClearRect:
         {
-            p->eraseRect(takeRect());
+            p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0)));
             break;
         }
         case QSGContext2D::FillRect:
@@ -221,12 +287,16 @@ QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D:
         case QSGContext2D::FillStyle:
         {
             state.fillStyle = takeFillStyle();
+            state.fillPatternRepeatX = takeBool();
+            state.fillPatternRepeatY = takeBool();
             p->setBrush(state.fillStyle);
             break;
         }
         case QSGContext2D::StrokeStyle:
         {
             state.strokeStyle = takeStrokeStyle();
+            state.strokePatternRepeatX = takeBool();
+            state.strokePatternRepeatY = takeBool();
             pen.setBrush(state.strokeStyle);
             p->setPen(pen);
             break;
@@ -264,6 +334,7 @@ QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D:
             break;
         case QSGContext2D::Fill:
         {
+            bool hasPattern = p->brush().style() == Qt::TexturePattern;
             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
                 fillShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
             else
@@ -289,13 +360,6 @@ QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D:
             p->setClipPath(clipPath);
             break;
         }
-        case QSGContext2D::UpdateBrush:
-        {
-            state.fillStyle = takeBrush();
-            p->setBrush(state.fillStyle);
-            break;
-        }
-
         case QSGContext2D::GlobalAlpha:
         {
             state.globalAlpha = takeGlobalAlpha();
@@ -357,6 +421,7 @@ QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D:
 QSGContext2DCommandBuffer::QSGContext2DCommandBuffer()
     : cmdIdx(0)
     , intIdx(0)
+    , boolIdx(0)
     , realIdx(0)
     , colorIdx(0)
     , matrixIdx(0)
@@ -375,6 +440,7 @@ void QSGContext2DCommandBuffer::clear()
 {
     commands.clear();
     ints.clear();
+    bools.clear();
     reals.clear();
     colors.clear();
     matrixes.clear();
@@ -388,6 +454,7 @@ void QSGContext2DCommandBuffer::reset()
 {
     cmdIdx = 0;
     intIdx = 0;
+    boolIdx = 0;
     realIdx = 0;
     colorIdx = 0;
     matrixIdx = 0;
index d95adee..95d0e96 100644 (file)
@@ -89,10 +89,11 @@ public:
         ints << cm;
     }
 
-    inline void setStrokeStyle(const QBrush &style)
+    inline void setStrokeStyle(const QBrush &style, bool repeatX = false, bool repeatY = false)
     {
         commands << QSGContext2D::StrokeStyle;
         brushes << style;
+        bools << repeatX << repeatY;
     }
 
     inline void drawImage(const QImage& image, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
@@ -157,10 +158,11 @@ public:
 
 
 
-    inline void setFillStyle(const QBrush &style)
+    inline void setFillStyle(const QBrush &style, bool repeatX = false, bool repeatY = false)
     {
-        commands << QSGContext2D::UpdateBrush;
+        commands << QSGContext2D::FillStyle;
         brushes << style;
+        bools << repeatX << repeatY;
     }
 
 
@@ -229,6 +231,7 @@ public:
     inline const QImage& takeImage() { return images[imageIdx++]; }
 
     inline int takeInt() { return ints[intIdx++]; }
+    inline bool takeBool() {return bools[boolIdx++]; }
     inline qreal takeReal() { return reals[realIdx++]; }
     inline QColor takeColor() { return colors[colorIdx++]; }
     inline QBrush takeBrush() { return brushes[brushIdx++]; }
@@ -239,6 +242,7 @@ private:
     void setPainterState(QPainter* painter, QSGContext2D::State state, const QPen& pen);
     int cmdIdx;
     int intIdx;
+    int boolIdx;
     int realIdx;
     int colorIdx;
     int matrixIdx;
@@ -248,6 +252,7 @@ private:
     QVector<QSGContext2D::PaintCommand> commands;
 
     QVector<int> ints;
+    QVector<bool> bools;
     QVector<qreal> reals;
     QVector<QColor> colors;
     QVector<QTransform> matrixes;
index 3f7692c..455a468 100644 (file)
@@ -163,11 +163,15 @@ bool QSGContext2DTexture::setCanvasWindow(const QRect& r)
 bool QSGContext2DTexture::setDirtyRect(const QRect &r)
 {
     bool doDirty = false;
-    foreach (QSGContext2DTile* t, m_tiles) {
-        bool dirty = t->rect().intersected(r).isValid();
-        t->markDirty(dirty);
-        if (dirty)
-            doDirty = true;
+    if (m_tiledCanvas) {
+        foreach (QSGContext2DTile* t, m_tiles) {
+            bool dirty = t->rect().intersected(r).isValid();
+            t->markDirty(dirty);
+            if (dirty)
+                doDirty = true;
+        }
+    } else {
+        doDirty = m_canvasWindow.intersected(r).isValid();
     }
     return doDirty;
 }
@@ -186,23 +190,24 @@ void QSGContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& ti
     bool canvasChanged = setCanvasSize(canvasSize);
     bool tileChanged = setTileSize(ts);
 
-    bool doDirty = false;
     if (canvasSize == canvasWindow.size()) {
         m_tiledCanvas = false;
         m_dirtyCanvas = false;
     } else {
         m_tiledCanvas = true;
-        if (dirtyRect.isValid())
-            doDirty = setDirtyRect(dirtyRect);
     }
 
-    bool windowChanged = setCanvasWindow(canvasWindow);
+    bool doDirty = false;
+    if (dirtyRect.isValid())
+        doDirty = setDirtyRect(dirtyRect);
 
+    bool windowChanged = setCanvasWindow(canvasWindow);
     if (windowChanged || doDirty) {
         if (m_threadRendering)
             QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection);
-        else if (supportDirectRendering())
-            QMetaObject::invokeMethod(this, "paint", Qt::DirectConnection);
+        else if (supportDirectRendering()) {
+           QMetaObject::invokeMethod(this, "paint", Qt::DirectConnection);
+        }
     }
 
     setSmooth(smooth);
@@ -312,7 +317,7 @@ void QSGContext2DTexture::paint()
                         return;
                     } else if (dirtyTile) {
                         m_state = ccb->replay(tile->createPainter(smooth), oldState);
-
+                        tile->drawFinished();
                         lock();
                         tile->markDirty(false);
                         unlock();
@@ -568,12 +573,20 @@ QPaintDevice* QSGContext2DFBOTexture::beginPainting()
 
     m_fbo->bind();
 
-    if (!m_paint_device)
-        m_paint_device = new QOpenGLPaintDevice(m_fbo->size());
+    if (!m_paint_device) {
+        QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size());
+        m_paint_device = gl_device;
+    }
 
     return m_paint_device;
 }
 
+void QSGContext2DFBOTexture::endPainting()
+{
+    QSGContext2DTexture::endPainting();
+    if (m_fbo)
+        m_fbo->release();
+}
 void qt_quit_context2d_render_thread()
 {
     QThread* thread = globalCanvasThreadRenderInstance();
@@ -698,7 +711,7 @@ QPaintDevice* QSGContext2DImageTexture::beginPainting()
     lock();
     if (m_image.size() != m_canvasWindow.size()) {
         m_image = QImage(m_canvasWindow.size(), QImage::Format_ARGB32_Premultiplied);
-        m_image.fill(Qt::transparent);
+        m_image.fill(0x00000000);
     }
     unlock();
     return &m_image;
index c91d3fb..fc251cd 100644 (file)
@@ -138,6 +138,7 @@ public:
     virtual QSGContext2DTile* createTile() const;
     virtual QImage toImage(const QRectF& region = QRectF());
     virtual QPaintDevice* beginPainting();
+    virtual void endPainting();
     QRectF textureSubRect() const;
     virtual bool supportThreadRendering() const {return false;}
     virtual bool supportDirectRendering() const {return false;}
index 14051b6..8c8ef83 100644 (file)
@@ -109,10 +109,18 @@ void QSGContext2DFBOTile::aboutToDraw()
 {
     m_fbo->bind();
     if (!m_device) {
-        m_device = new QOpenGLPaintDevice(rect().size());
+        QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(rect().size());
+        m_device = gl_device;
+        QPainter p(m_device);
+        p.fillRect(QRectF(0, 0, m_fbo->width(), m_fbo->height()), QColor(qRgba(0, 0, 0, 0)));
+        p.end();
     }
 }
 
+void QSGContext2DFBOTile::drawFinished()
+{
+    m_fbo->release();
+}
 
 void QSGContext2DFBOTile::setRect(const QRect& r)
 {
@@ -154,4 +162,4 @@ void QSGContext2DImageTile::setRect(const QRect& r)
         m_image = QImage(r.size(), QImage::Format_ARGB32_Premultiplied);
     }
     m_device = &m_image;
-}
+}
\ No newline at end of file
index d5317f9..57b68ef 100644 (file)
@@ -67,11 +67,10 @@ public:
 
     virtual void setRect(const QRect& r) = 0;
     virtual QPainter* createPainter(bool smooth = false);
-
+    virtual void drawFinished() {}
 
 protected:
     virtual void aboutToDraw() {}
-
     uint m_dirty : 1;
     QRect m_rect;
     QPaintDevice* m_device;
@@ -86,10 +85,10 @@ public:
     ~QSGContext2DFBOTile();
     virtual void setRect(const QRect& r);
     QOpenGLFramebufferObject* fbo() const {return m_fbo;}
+    void drawFinished();
 
 protected:
     void aboutToDraw();
-
 private:
 
 
index 52abb02..d8567db 100644 (file)
@@ -78,6 +78,7 @@ SGTESTS =  \
     qsgtextedit \
     qsgtextinput \
     qsgvisualdatamodel \
+    qsgcanvasitem \
 
 
 SUBDIRS += $$PUBLICTESTS
@@ -91,4 +92,3 @@ contains(QT_CONFIG, private_tests) {
 
 # Tests which should run in Pulse
 PULSE_TESTS = $$SUBDIRS
-
diff --git a/tests/auto/declarative/qsgcanvasitem/data/testhelper.js b/tests/auto/declarative/qsgcanvasitem/data/testhelper.js
new file mode 100644 (file)
index 0000000..bac0210
--- /dev/null
@@ -0,0 +1,18 @@
+function comparePixel(ctx,x,y,r,g,b,a, d)
+{
+   var c = ctx.getImageData(x,y,1,1).data;
+   if (d === undefined)
+      d = 0;
+   r = Math.round(r);
+   g = Math.round(g);
+   b = Math.round(b);
+   a = Math.round(a);
+
+   if (Math.abs(c[0]-r)>d || Math.abs(c[1]-g)>d || Math.abs(c[2]-b)>d || Math.abs(c[3]-a)>d) {
+      console.log('Pixel compare fail:\nactual  :[' + c[0]+','+c[1]+','+c[2]+','+c[3] + ']\nexpected:['+r+','+g+','+b+','+a+'] +/- '+d);
+      return false;
+   }
+   return true;
+}
+
+
diff --git a/tests/auto/declarative/qsgcanvasitem/data/tst_colors.qml b/tests/auto/declarative/qsgcanvasitem/data/tst_colors.qml
new file mode 100644 (file)
index 0000000..a20a7df
--- /dev/null
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+import QtTest 1.0
+import "testhelper.js" as Helper
+
+Canvas {
+   id:canvas; width:1;height:1
+   TestCase {
+       name: "Colors"; when: windowShown
+       function test_globalAlpha() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = Qt.rgba(1, 0.7, 0.2, 0.5);
+           ctx.globalAlpha = 0.5;
+           ctx.fillRect(0,0,1,1);
+           var d = ctx.getImageData(0,0,1,1).data;
+           verify(Helper.comparePixel(ctx, 0, 0, 255, 0.7 * 255, 0.2*255, 0.25 * 255));
+      }
+   }
+}
diff --git a/tests/auto/declarative/qsgcanvasitem/data/tst_fillStyle.qml b/tests/auto/declarative/qsgcanvasitem/data/tst_fillStyle.qml
new file mode 100644 (file)
index 0000000..a4b77ec
--- /dev/null
@@ -0,0 +1,113 @@
+import QtQuick 2.0
+import QtTest 1.0
+import "testhelper.js" as Helper
+
+Canvas {
+   id:canvas; width:1;height:1
+   TestCase {
+       name: "fillStyle"; when: windowShown
+       function test_default() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           verify(ctx.fillStyle, "#000000");
+           ctx.clearRect(0, 0, 1, 1);
+           compare(ctx.fillStyle, "#000000");
+       }
+       function test_get() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = '#fa0';
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = Qt.rgba(0,0,0,0);
+           compare(ctx.fillStyle, 'rgba(0, 0, 0, 0.0)');
+       }
+       function test_hex() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = '#f00';
+           compare(ctx.fillStyle, '#ff0000');
+           ctx.fillStyle = "#0f0";
+           compare(ctx.fillStyle, '#00ff00');
+           ctx.fillStyle = "#0fF";
+           compare(ctx.fillStyle, '#00ffff');
+           ctx.fillStyle = "#0aCCfb";
+           compare(ctx.fillStyle, '#0accfb');
+
+       }
+       function test_invalid() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = '#fa0';
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "invalid";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "rgb (1, 2, 3)";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "rgba(1, 2, 3)";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "rgb((3,4,1)";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "rgb(1, 3, 4, 0.5)";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "hsl(2, 3, 4, 0.8)";
+           compare(ctx.fillStyle, '#ffaa00');
+           ctx.fillStyle = "hsl(2, 3, 4";
+           compare(ctx.fillStyle, '#ffaa00');
+       }
+       function test_saverestore() {
+           var ctx = canvas.getContext('2d');
+           var old = ctx.fillStyle;
+           ctx.save();
+           ctx.fillStyle = "#ffaaff";
+           ctx.restore();
+           compare(ctx.fillStyle, old);
+
+           ctx.fillStyle = "#ffcc88";
+           old = ctx.fillStyle;
+           ctx.save();
+           compare(ctx.fillStyle, old);
+           ctx.restore();
+       }
+       function test_namedColor() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = "red";
+           ctx.fillRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,255,0,0,255));
+
+           ctx.fillStyle = "black";
+           ctx.fillRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,0,0,0,255));
+
+           ctx.fillStyle = "white";
+           ctx.fillRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,255,255,255,255));
+       }
+       function test_rgba() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = "rgb(-100, 300, 255)";
+           compare(ctx.fillStyle, "#00ffff");
+           ctx.fillStyle = "rgba(-100, 300, 255, 0.0)";
+           compare(ctx.fillStyle, "rgba(0, 255, 255, 0.0)");
+           ctx.fillStyle = "rgb(-10%, 110%, 50%)";
+           compare(ctx.fillStyle, "#00ff80");
+
+           ctx.clearRect(0, 0, 1, 1);
+           ctx.fillStyle = 'rgba(0%, 100%, 0%, 0.499)';
+           ctx.fillRect(0, 0, 1, 1);
+           //FIXME: currently we only return premultipled pixels
+           verify(Helper.comparePixel(ctx, 0,0, 0,127,0,255));
+           //verify(Helper.comparePixel(ctx, 0,0, 0,255,0,127));
+       }
+
+       function test_hsla() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.fillStyle = "hsla(120, 100%, 50%, 0.499)";
+           ctx.fillRect(0, 0, 1, 1);
+           verify(Helper.comparePixel(ctx,0,0,0,127,0,255));
+       }
+
+   }
+}
diff --git a/tests/auto/declarative/qsgcanvasitem/data/tst_fillrect.qml b/tests/auto/declarative/qsgcanvasitem/data/tst_fillrect.qml
new file mode 100644 (file)
index 0000000..07ddc59
--- /dev/null
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+import QtTest 1.0
+
+Canvas {
+   id:canvas; width:1;height:1
+   onPaint: {
+     context.fillStyle = "red";
+     context.fillRect(0, 0, canvas.width, canvas.height);
+   }
+   TestCase {
+       name: "FillRect"; when: windowShown
+       function test_fillRect() {
+           var ctx = canvas.getContext('2d');
+           var imageData = ctx.getImageData(0, 0, 1, 1);
+           var d = imageData.data;
+           verify(d.length == 4);
+           verify(d[0] == 255);
+           verify(d[1] == 0);
+           verify(d[2] == 0);
+           verify(d[3] == 255);
+      }
+   }
+}
diff --git a/tests/auto/declarative/qsgcanvasitem/data/tst_strokeStyle.qml b/tests/auto/declarative/qsgcanvasitem/data/tst_strokeStyle.qml
new file mode 100644 (file)
index 0000000..84f830d
--- /dev/null
@@ -0,0 +1,48 @@
+import QtQuick 2.0
+import QtTest 1.0
+import "testhelper.js" as Helper
+
+Canvas {
+   id:canvas; width:1;height:1
+   TestCase {
+       name: "strokeStyle"; when: windowShown
+       function test_default() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           compare(ctx.strokeStyle, "#000000")
+           ctx.clearRect(0, 0, 1, 1);
+           compare(ctx.strokeStyle, "#000000")
+       }
+       function test_saverestore() {
+           var ctx = canvas.getContext('2d');
+           var old = ctx.strokeStyle;
+           ctx.save();
+           ctx.strokeStyle = "#ffaaff";
+           ctx.restore();
+           compare(ctx.strokeStyle, old);
+
+           ctx.strokeStyle = "#ffcc88";
+           old = ctx.strokeStyle;
+           ctx.save();
+           compare(ctx.strokeStyle, old);
+           ctx.restore();
+       }
+       function test_namedColor() {
+           var ctx = canvas.getContext('2d');
+           ctx.reset();
+           ctx.strokeStyle = "red";
+           ctx.strokeRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,255,0,0,255));
+
+           ctx.strokeStyle = "black";
+           ctx.strokeRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,0,0,0,255));
+
+           ctx.strokeStyle = "white";
+           ctx.strokeRect(0,0,1,1);
+           verify(Helper.comparePixel(ctx,0,0,255,255,255,255));
+       }
+
+
+   }
+}
diff --git a/tests/auto/declarative/qsgcanvasitem/qsgcanvasitem.pro b/tests/auto/declarative/qsgcanvasitem/qsgcanvasitem.pro
new file mode 100644 (file)
index 0000000..b22c49f
--- /dev/null
@@ -0,0 +1,5 @@
+QT += core-private gui-private declarative-private widgets
+TE=app
+TARGET=tst_qsgcanvasitem
+CONFIG += warn_on qmltestcase
+SOURCES += tst_qsgcanvasitem.cpp
\ No newline at end of file
diff --git a/tests/auto/declarative/qsgcanvasitem/tst_qsgcanvasitem.cpp b/tests/auto/declarative/qsgcanvasitem/tst_qsgcanvasitem.cpp
new file mode 100644 (file)
index 0000000..680e452
--- /dev/null
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(qsgcanvasitem)