diff options
author | Charles Yin <yinyunqiao@gmail.com> | 2011-09-22 20:01:29 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-09-30 10:37:05 +0200 |
commit | 8f69461c480e10424401e95b16b507eec3e28e54 (patch) | |
tree | 58e13d4c7a470485ab5e14642a55bc35bd0ae2c3 /src | |
parent | 20fb62f6040ad8415828092a2b09bd374433505f (diff) |
tests for canvas and a few bug fixes
Change-Id: Icbbc7f2a0fe3b908963ce18afef51e25ea0170a0
Reviewed-on: http://codereview.qt-project.org/5805
Reviewed-by: Charles Yin <charles.yin@nokia.com>
Diffstat (limited to 'src')
10 files changed, 367 insertions, 287 deletions
diff --git a/src/declarative/items/context2d/qsgcanvasitem.cpp b/src/declarative/items/context2d/qsgcanvasitem.cpp index 2ecd322288..50cbc7e2f1 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 8d6441ac60..a2dfb7968a 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 <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 diff --git a/src/declarative/items/context2d/qsgcontext2d.cpp b/src/declarative/items/context2d/qsgcontext2d.cpp index 205809861b..71cc9dc0c9 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<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 diff --git a/src/declarative/items/context2d/qsgcontext2d_p.h b/src/declarative/items/context2d/qsgcontext2d_p.h index 830a185a31..5354876d5d 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 550ad77515..51730d41ec 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<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; diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h index d95adeeafd..95d0e9685e 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<QSGContext2D::PaintCommand> commands; QVector<int> ints; + QVector<bool> bools; QVector<qreal> reals; QVector<QColor> colors; QVector<QTransform> matrixes; diff --git a/src/declarative/items/context2d/qsgcontext2dtexture.cpp b/src/declarative/items/context2d/qsgcontext2dtexture.cpp index 3f7692c0ba..455a46884b 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 c91d3fb2ba..fc251cde7b 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 14051b62c1..8c8ef836b0 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 d5317f9357..57b68ef67d 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: |