From 8f69461c480e10424401e95b16b507eec3e28e54 Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Thu, 22 Sep 2011 20:01:29 +1000 Subject: [PATCH] tests for canvas and a few bug fixes Change-Id: Icbbc7f2a0fe3b908963ce18afef51e25ea0170a0 Reviewed-on: http://codereview.qt-project.org/5805 Reviewed-by: Charles Yin --- .../declarative/canvas/bezierCurve/bezierCurve.qml | 2 +- .../canvas/quadraticCurveTo/quadraticCurveTo.qml | 2 +- .../declarative/canvas/roundedrect/roundedrect.qml | 2 +- examples/declarative/canvas/smile/smile.qml | 2 +- examples/declarative/canvas/squircle/squircle.qml | 2 +- examples/declarative/canvas/tiger/tiger.qml | 2 +- .../canvas/twitterfriends/TwitterUser.qml | 2 +- src/declarative/items/context2d/qsgcanvasitem.cpp | 22 +- src/declarative/items/context2d/qsgcanvasitem_p.h | 6 +- src/declarative/items/context2d/qsgcontext2d.cpp | 457 ++++++++++----------- src/declarative/items/context2d/qsgcontext2d_p.h | 6 +- .../items/context2d/qsgcontext2dcommandbuffer.cpp | 91 +++- .../items/context2d/qsgcontext2dcommandbuffer_p.h | 11 +- .../items/context2d/qsgcontext2dtexture.cpp | 43 +- .../items/context2d/qsgcontext2dtexture_p.h | 1 + .../items/context2d/qsgcontext2dtile.cpp | 12 +- .../items/context2d/qsgcontext2dtile_p.h | 5 +- tests/auto/declarative/declarative.pro | 2 +- .../declarative/qsgcanvasitem/data/testhelper.js | 18 + .../declarative/qsgcanvasitem/data/tst_colors.qml | 19 + .../qsgcanvasitem/data/tst_fillStyle.qml | 113 +++++ .../qsgcanvasitem/data/tst_fillrect.qml | 23 ++ .../qsgcanvasitem/data/tst_strokeStyle.qml | 48 +++ .../declarative/qsgcanvasitem/qsgcanvasitem.pro | 5 + .../qsgcanvasitem/tst_qsgcanvasitem.cpp | 42 ++ 25 files changed, 643 insertions(+), 295 deletions(-) create mode 100644 tests/auto/declarative/qsgcanvasitem/data/testhelper.js create mode 100644 tests/auto/declarative/qsgcanvasitem/data/tst_colors.qml create mode 100644 tests/auto/declarative/qsgcanvasitem/data/tst_fillStyle.qml create mode 100644 tests/auto/declarative/qsgcanvasitem/data/tst_fillrect.qml create mode 100644 tests/auto/declarative/qsgcanvasitem/data/tst_strokeStyle.qml create mode 100644 tests/auto/declarative/qsgcanvasitem/qsgcanvasitem.pro create mode 100644 tests/auto/declarative/qsgcanvasitem/tst_qsgcanvasitem.cpp diff --git a/examples/declarative/canvas/bezierCurve/bezierCurve.qml b/examples/declarative/canvas/bezierCurve/bezierCurve.qml index 4d30c4d..cfd3e1f 100644 --- a/examples/declarative/canvas/bezierCurve/bezierCurve.qml +++ b/examples/declarative/canvas/bezierCurve/bezierCurve.qml @@ -120,4 +120,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/quadraticCurveTo/quadraticCurveTo.qml b/examples/declarative/canvas/quadraticCurveTo/quadraticCurveTo.qml index 00d9e9d..7bd9546 100644 --- a/examples/declarative/canvas/quadraticCurveTo/quadraticCurveTo.qml +++ b/examples/declarative/canvas/quadraticCurveTo/quadraticCurveTo.qml @@ -124,4 +124,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/roundedrect/roundedrect.qml b/examples/declarative/canvas/roundedrect/roundedrect.qml index 50c07ea..c657e31 100644 --- a/examples/declarative/canvas/roundedrect/roundedrect.qml +++ b/examples/declarative/canvas/roundedrect/roundedrect.qml @@ -121,4 +121,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/smile/smile.qml b/examples/declarative/canvas/smile/smile.qml index 3a7fbe7..7db84a1 100644 --- a/examples/declarative/canvas/smile/smile.qml +++ b/examples/declarative/canvas/smile/smile.qml @@ -124,4 +124,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/squircle/squircle.qml b/examples/declarative/canvas/squircle/squircle.qml index f9845e8..9f69dcf 100644 --- a/examples/declarative/canvas/squircle/squircle.qml +++ b/examples/declarative/canvas/squircle/squircle.qml @@ -151,4 +151,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/tiger/tiger.qml b/examples/declarative/canvas/tiger/tiger.qml index 5ec9204..0c53a2b 100644 --- a/examples/declarative/canvas/tiger/tiger.qml +++ b/examples/declarative/canvas/tiger/tiger.qml @@ -125,4 +125,4 @@ Item { } } } -} +} \ No newline at end of file diff --git a/examples/declarative/canvas/twitterfriends/TwitterUser.qml b/examples/declarative/canvas/twitterfriends/TwitterUser.qml index 7b16581..8f98505 100644 --- a/examples/declarative/canvas/twitterfriends/TwitterUser.qml +++ b/examples/declarative/canvas/twitterfriends/TwitterUser.qml @@ -291,4 +291,4 @@ Item { x.send(); } } -} +} \ No newline at end of file diff --git a/src/declarative/items/context2d/qsgcanvasitem.cpp b/src/declarative/items/context2d/qsgcanvasitem.cpp index 2ecd322..50cbc7e 100644 --- a/src/declarative/items/context2d/qsgcanvasitem.cpp +++ b/src/declarative/items/context2d/qsgcanvasitem.cpp @@ -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 diff --git a/src/declarative/items/context2d/qsgcanvasitem_p.h b/src/declarative/items/context2d/qsgcanvasitem_p.h index 8d6441a..a2dfb79 100644 --- a/src/declarative/items/context2d/qsgcanvasitem_p.h +++ b/src/declarative/items/context2d/qsgcanvasitem_p.h @@ -45,8 +45,6 @@ #include "qsgitem.h" #include - - 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 diff --git a/src/declarative/items/context2d/qsgcontext2d.cpp b/src/declarative/items/context2d/qsgcontext2d.cpp index 2058098..71cc9dc 100644 --- a/src/declarative/items/context2d/qsgcontext2d.cpp +++ b/src/declarative/items/context2d/qsgcontext2d.cpp @@ -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 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 pixelData = ed->constructorPixelArray->NewInstance(); pixelData->SetExternalResource(r); @@ -936,6 +822,19 @@ static v8::Handle ctx2d_fillStyle(v8::Local, const v8::Ac QV8Context2DResource *r = v8_resource_cast(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::Local valu QV8Engine *engine = V8ENGINE_ACCESSOR(); - r->context->m_fillStyle = value; if (value->IsObject()) { QColor color = engine->toVariant(value, qMetaTypeId()).value(); if (color.isValid()) { r->context->state.fillStyle = color; r->context->buffer()->setFillStyle(color); + r->context->m_fillStyle = value; } else { QV8Context2DStyleResource *style = v8_resource_cast(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::Local 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 ctx2d_strokeStyle(v8::Local, const v8::Accesso QV8Context2DResource *r = v8_resource_cast(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::Local va QV8Engine *engine = V8ENGINE_ACCESSOR(); - r->context->m_strokeStyle = value; if (value->IsObject()) { QColor color = engine->toVariant(value, qMetaTypeId()).value(); if (color.isValid()) { r->context->state.fillStyle = color; r->context->buffer()->setStrokeStyle(color); + r->context->m_strokeStyle = value; } else { QV8Context2DStyleResource *style = v8_resource_cast(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::Local 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::Local 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 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 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 ctx2d_createConicalGradient(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 6) { + QSGContext2DEngineData *ed = engineData(engine); + v8::Local 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 ctx2d_createPattern(const v8::Arguments &args) QV8Engine *engine = V8ENGINE(); - //FIXME:: - -// if (args.Length() == 2) { -// QSGContext2DEngineData *ed = engineData(engine); -// v8::Local pattern = ed->constructorPattern->NewInstance(); -// QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); - -// QImage img; - -// QSGItem* item = qobject_cast(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()).value(); + if (color.isValid()) { + int patternMode = args[1]->IntegerValue(); + Qt::BrushStyle style = Qt::SolidPattern; + if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) { + style = static_cast(patternMode); + } + styleResouce->brush = QBrush(color, style); + } else { + QImage patternTexture; + + if (args[0]->IsObject()) { + QV8Context2DPixelArrayResource *pixelData = v8_resource_cast(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 pattern = ed->constructorPattern->NewInstance(); + pattern->SetExternalResource(styleResouce); + return pattern; + + } + return v8::Undefined(); } // line styles @@ -2490,7 +2465,7 @@ static v8::Handle 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 ctx2d_pixelArray_indexed(uint32_t index, const v8::Accesso { QV8Context2DPixelArrayResource *r = v8_resource_cast(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(r->image.constScanLine(row)); @@ -2636,7 +2610,7 @@ v8::Handle ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local(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 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 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 diff --git a/src/declarative/items/context2d/qsgcontext2d_p.h b/src/declarative/items/context2d/qsgcontext2d_p.h index 830a185..5354876 100644 --- a/src/declarative/items/context2d/qsgcontext2d_p.h +++ b/src/declarative/items/context2d/qsgcontext2d_p.h @@ -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; diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp b/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp index 550ad77..51730d4 100644 --- a/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp +++ b/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp @@ -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(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(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; diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h index d95adee..95d0e96 100644 --- a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h +++ b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h @@ -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 commands; QVector ints; + QVector bools; QVector reals; QVector colors; QVector matrixes; diff --git a/src/declarative/items/context2d/qsgcontext2dtexture.cpp b/src/declarative/items/context2d/qsgcontext2dtexture.cpp index 3f7692c..455a468 100644 --- a/src/declarative/items/context2d/qsgcontext2dtexture.cpp +++ b/src/declarative/items/context2d/qsgcontext2dtexture.cpp @@ -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; diff --git a/src/declarative/items/context2d/qsgcontext2dtexture_p.h b/src/declarative/items/context2d/qsgcontext2dtexture_p.h index c91d3fb..fc251cd 100644 --- a/src/declarative/items/context2d/qsgcontext2dtexture_p.h +++ b/src/declarative/items/context2d/qsgcontext2dtexture_p.h @@ -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;} diff --git a/src/declarative/items/context2d/qsgcontext2dtile.cpp b/src/declarative/items/context2d/qsgcontext2dtile.cpp index 14051b6..8c8ef83 100644 --- a/src/declarative/items/context2d/qsgcontext2dtile.cpp +++ b/src/declarative/items/context2d/qsgcontext2dtile.cpp @@ -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 diff --git a/src/declarative/items/context2d/qsgcontext2dtile_p.h b/src/declarative/items/context2d/qsgcontext2dtile_p.h index d5317f9..57b68ef 100644 --- a/src/declarative/items/context2d/qsgcontext2dtile_p.h +++ b/src/declarative/items/context2d/qsgcontext2dtile_p.h @@ -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: diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 52abb02..d8567db 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -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 index 0000000..bac0210 --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/data/testhelper.js @@ -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 index 0000000..a20a7df --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/data/tst_colors.qml @@ -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 index 0000000..a4b77ec --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/data/tst_fillStyle.qml @@ -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 index 0000000..07ddc59 --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/data/tst_fillrect.qml @@ -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 index 0000000..84f830d --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/data/tst_strokeStyle.qml @@ -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 index 0000000..b22c49f --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/qsgcanvasitem.pro @@ -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 index 0000000..680e452 --- /dev/null +++ b/tests/auto/declarative/qsgcanvasitem/tst_qsgcanvasitem.cpp @@ -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 +QUICK_TEST_MAIN(qsgcanvasitem) -- 2.7.4