From: Gunnar Sletta Date: Tue, 13 Sep 2011 08:21:53 +0000 (+0200) Subject: Merge branch 'refactor' X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=ce5ac7d67b59deb7c0e261e9ee21a475e6cb2e41;p=konrad%2Fqtdeclarative.git Merge branch 'refactor' Conflicts: src/declarative/items/context2d/qsgcanvasitem.cpp src/declarative/items/context2d/qsgcontext2d.cpp src/declarative/items/context2d/qsgcontext2d_p_p.h src/declarative/particles/qsgcustomparticle.cpp src/declarative/particles/qsgparticlesystem.cpp Change-Id: I24e81d3652368c5031305ffa7f969f9f2c249c6c --- ce5ac7d67b59deb7c0e261e9ee21a475e6cb2e41 diff --cc src/declarative/items/context2d/qsgcontext2d.cpp index 91bd5b5,e5f2eca..50ce1e4 --- a/src/declarative/items/context2d/qsgcontext2d.cpp +++ b/src/declarative/items/context2d/qsgcontext2d.cpp @@@ -40,35 -40,45 +40,39 @@@ ****************************************************************************/ #include "qsgcontext2d_p.h" -#include "qsgcontext2d_p_p.h" -#include "private/qsgadaptationlayer_p.h" +#include "qsgcontext2dcommandbuffer_p.h" #include "qsgcanvasitem_p.h" +#include "qsgitem_p.h" +#include "qsgshadereffectsource_p.h" + #include + #include #include "private/qsgcontext_p.h" #include "private/qdeclarativesvgparser_p.h" +#include "private/qdeclarativepath_p.h" + +#include "private/qsgimage_p_p.h" + #include #include #include -#include "qdeclarativepixmapcache_p.h" - -#include -#include "qvarlengtharray.h" +#include "qv8engine_p.h" +#include +#include +#include "qdeclarativeengine.h" QT_BEGIN_NAMESPACE - +/*! + \qmlclass Context2D QSGContext2D + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Context2D element allows you to draw 2d graphic shapes on Canvas item. +*/ static const double Q_PI = 3.14159265358979323846; // pi -template -void memcpy_vector(QVector* dst, const QVector& src) -{ - int pos = dst->size(); - dst->resize(pos + src.size()); - memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size()); -} -template -void copy_vector(QVector* dst, const QVector& src) -{ - int pos = dst->size(); - dst->resize(pos + src.size()); - for (int i = 0; i < src.size(); i++) { - (*dst)[pos + i] = src[i]; - } -} + static bool parsePathDataFast(const QString &dataStr, QPainterPath &path); + #define DEGREES(t) ((t) * 180.0 / Q_PI) #define qClamp(val, min, max) qMin(qMax(val, min), max) @@@ -720,44 -541,14 +724,51 @@@ static v8::Handle ctx2d_glob CHECK_CONTEXT(r) ++<<<<<<< HEAD + return v8::Number::New(r->context->state.globalAlpha); ++======= + QV8Engine *engine = V8ENGINE_ACCESSOR(); + Q_UNUSED(engine) + + return v8::Boolean::New(r->context->valid()); ++>>>>>>> refactor } +static void ctx2d_globalAlpha_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) -// string getter/setter default "source-over" + qreal globalAlpha = value->NumberValue(); + + if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) { + r->context->state.globalAlpha = globalAlpha; + r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha); + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::globalCompositeOperation + Holds the the current the current composition operation, from the list below: + \list + \o source-atop - A atop B. Display the source image wherever both images are opaque. + Display the destination image wherever the destination image is opaque but the source image is transparent. + Display transparency elsewhere. + \o source-in - A in B. Display the source image wherever both the source image and destination image are opaque. + Display transparency elsewhere. + \o source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent. + Display transparency elsewhere. + \o source-over - (default) A over B. Display the source image wherever the source image is opaque. + Display the destination image elsewhere. + \o destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa. + \o destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa. + \o destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa. + \o destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa. + \o lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit. + \o copy - A (B is ignored). Display the source image instead of the destination image. + \o xor - A xor B. Exclusive OR of the source image and destination image. + \endlist +*/ static v8::Handle ctx2d_globalCompositeOperation(v8::Local, const v8::AccessorInfo &info) { QV8Context2DResource *r = v8_resource_cast(info.This()); @@@ -2107,632 -1271,391 +2118,646 @@@ static v8::Handle ctx2d_draw } // pixel manipulation -static v8::Handle ctx2d_createImageData(const v8::Arguments &args) -{ - //#TODO - return v8::Undefined(); -} +/*! + \qmlclass QtQuick2::CanvasImageData -static v8::Handle ctx2d_getImageData(const v8::Arguments &args) + */ +/*! + \qmlproperty QtQuick2::CanvasImageData::width + Holds the actual width dimension of the data in the ImageData object, in device pixels. + */ +v8::Handle ctx2d_imageData_width(v8::Local, const v8::AccessorInfo &args) { - //#TODO - return v8::Undefined(); + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + if (!r) + return v8::Integer::New(0); + return v8::Integer::New(r->image.width()); } -static v8::Handle ctx2d_putImageData(const v8::Arguments &args) +/*! + \qmlproperty QtQuick2::CanvasImageData::height + Holds the actual height dimension of the data in the ImageData object, in device pixels. + */ +v8::Handle ctx2d_imageData_height(v8::Local, const v8::AccessorInfo &args) { - //#TODO - return v8::Undefined(); -} + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + if (!r) + return v8::Integer::New(0); -bool QSGContext2DPrivate::hasShadow() const -{ - return state.shadowColor.isValid() - && state.shadowColor.alpha() - && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY); + return v8::Integer::New(r->image.height()); } -void QSGContext2DPrivate::clearShadow() +/*! + \qmlproperty QtQuick2::CanvasImageData::data + Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255. + */ +v8::Handle ctx2d_imageData_data(v8::Local, const v8::AccessorInfo &args) { - state.shadowOffsetX = 0; - state.shadowOffsetY = 0; - state.shadowBlur = 0; - state.shadowColor = QColor(); + return args.This()->GetInternalField(0); } -QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix) +static v8::Handle ctx2d_imageData_mirror(const v8::Arguments &args) { - QImage shadowImg(pix.width() + state.shadowBlur * 2 + qAbs(state.shadowOffsetX), - pix.height() + state.shadowBlur *2 + qAbs(state.shadowOffsetY), - QImage::Format_ARGB32); - shadowImg.fill(0); - QPainter tmpPainter(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); - qreal shadowX = state.shadowOffsetX > 0? state.shadowOffsetX : 0; - qreal shadowY = state.shadowOffsetY > 0? state.shadowOffsetY : 0; + bool horizontal = false, vertical = true; + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + + if (!r) { + //error + return v8::Undefined(); + } - tmpPainter.drawPixmap(shadowX, shadowY, pix); - tmpPainter.end(); + if (args.Length() > 2) { + //error + return v8::Undefined(); + } ++<<<<<<< HEAD + if (args.Length() == 1) { + horizontal = args[0]->BooleanValue(); + } else if (args.Length() == 2) { + horizontal = args[0]->BooleanValue(); + vertical = args[1]->BooleanValue(); ++======= + #if 0 + // ### refactor + // blur the alpha channel + if (state.shadowBlur > 0) { + QImage blurred(shadowImg.size(), QImage::Format_ARGB32); + blurred.fill(0); + QPainter blurPainter(&blurred); + qt_blurImage(&blurPainter, shadowImg, state.shadowBlur, false, true); + blurPainter.end(); + shadowImg = blurred; ++>>>>>>> refactor } + #endif - // blacken the image with shadow color... - tmpPainter.begin(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - tmpPainter.fillRect(shadowImg.rect(), state.shadowColor); - tmpPainter.end(); - return shadowImg; + r->image = r->image.mirrored(horizontal, vertical); + return args.This(); } -void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect) + +static v8::Handle ctx2d_imageData_filter(const v8::Arguments &args) { - QRectF r = shadowRect; - r.moveTo(0, 0); + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); - QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32); - QPainter tp; - tp.begin(&shadowImage); - tp.fillRect(r, p->brush()); - tp.end(); - shadowImage = makeShadowImage(QPixmap::fromImage(shadowImage)); + if (!r) { + //error + return v8::Undefined(); + } - qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0); - qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0); + if (args.Length() >= 1) { + int filterFlag = args[0]->IntegerValue(); + switch(filterFlag) { + case QSGCanvasItem::GrayScale : + { + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + unsigned char* rgb = ((unsigned char*)&row[x]); + rgb[0] = rgb[1] = rgb[2] = qGray(rgb[0], rgb[1], rgb[2]); + } + } + } + break; + case QSGCanvasItem::Threshold : + { + int threshold = 127; + if (args.Length() > 1) + threshold = args[1]->IntegerValue(); + + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + unsigned char* rgb = ((unsigned char*)&row[x]); + unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold ? 255 : 0; + rgb[0] = rgb[1] = rgb[2] = v; + } + } + } + break; + case QSGCanvasItem::Brightness : + { + int adjustment = 1; + if (args.Length() > 1) + adjustment = args[1]->IntegerValue(); + + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + ((unsigned char*)&row[x])[0] += adjustment; + ((unsigned char*)&row[x])[1] += adjustment; + ((unsigned char*)&row[x])[2] += adjustment; + } + } + } + break; + case QSGCanvasItem::Invert : + { + r->image.invertPixels(); + } + break; + case QSGCanvasItem::Blur : + { + QImage blurred(r->image.size(), QImage::Format_ARGB32); + qreal blur = 10; + if (args.Length() > 1) + blur = args[1]->NumberValue(); + + blurred.fill(Qt::transparent); + QPainter blurPainter(&blurred); + qt_blurImage(&blurPainter, r->image, blur, true, false); + blurPainter.end(); + r->image = blurred; + } + break; + case QSGCanvasItem::Opaque : + { + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + ((unsigned char*)&row[x])[3] = 255; + } + } + } + break; + case QSGCanvasItem::Convolute : + { + if (args.Length() > 1 && args[1]->IsArray()) { + v8::Local array = v8::Local::Cast(args[1]); + QVector weights; + for (uint32_t i = 0; i < array->Length(); ++i) + weights.append(array->Get(i)->NumberValue()); + int sides = qRound(qSqrt(weights.size())); + int half = qFloor(sides/2); + + QImage image = QImage(r->image.size(), QImage::Format_ARGB32); + int w = r->image.width(); + int h = r->image.height(); + for (int y = 0; y < image.height(); ++y) { + QRgb *dRow = (QRgb*)image.scanLine(y); + for (int x = 0; x < image.width(); ++x) { + unsigned char* dRgb = ((unsigned char*)&dRow[x]); + unsigned char red=0, green=0, blue=0, alpha=0; + int sy = y; + int sx = x; + + for (int cy=0; cy= 0 && scy < w && scx >= 0 && scx < h) { + QRgb *sRow = (QRgb*)(r->image.scanLine(scy)); + unsigned char* sRgb = ((unsigned char*)&sRow[scx]); + int wt = weights[cy*sides+cx]; + red += sRgb[0] * wt; + green += sRgb[1] * wt; + blue += sRgb[2] * wt; + alpha += sRgb[3] * wt; + } + } + } + dRgb[0] = red; + dRgb[1] = green; + dRgb[2] = blue; + dRgb[3] = alpha; + } + } + r->image = image; + } else { + //error + } + } + break; + default: + break; + } + } - p->drawImage(dx, dy, shadowImage); - p->fillRect(shadowRect, p->brush()); + return args.This(); } +/*! + \qmlclass QtQuick2::CanvasPixelArray + The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data. + See http://www.w3.org/TR/2dcontext/#canvaspixelarray for more details. + */ -void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path) +/*! + \qmlproperty QtQuick2::CanvasPixelArray::length + The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData. + The length attribute of a CanvasPixelArray object must return this h×w×4 number value. + */ +v8::Handle ctx2d_pixelArray_length(v8::Local, const v8::AccessorInfo &args) { - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32); - img.fill(0); - QPainter tp(&img); - tp.fillPath(path.translated(0, 0), p->brush()); - tp.end(); + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); + if (!r || r->image.isNull()) return v8::Undefined(); - QImage shadowImage = makeShadowImage(QPixmap::fromImage(img)); - qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0); - qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0); - - p->drawImage(dx, dy, shadowImage); - p->fillPath(path, p->brush()); + return v8::Integer::New(r->image.width() * r->image.height() * 4); } -void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path) +v8::Handle ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args) { - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32); - img.fill(0); - QPainter tp(&img); - tp.strokePath(path, p->pen()); - tp.end(); - - QImage shadowImage = makeShadowImage(QPixmap::fromImage(img)); - qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0); - qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0); - p->drawImage(dx, dy, shadowImage); - p->strokePath(path, p->pen()); -} + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); -void QSGContext2DPrivate::clear() -{ - clearRect(0, 0, size.width(), size.height()); + if (r && index && 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)); + pixel += col; + switch (index % 4) { + case 0: + return v8::Integer::New(qRed(*pixel)); + case 1: + return v8::Integer::New(qGreen(*pixel)); + case 2: + return v8::Integer::New(qBlue(*pixel)); + case 3: + return v8::Integer::New(qAlpha(*pixel)); + } + } + return v8::Undefined(); } -void QSGContext2DPrivate::reset() +v8::Handle ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local value, const v8::AccessorInfo& info) { - stateStack.clear(); - state.matrix = QMatrix(); - state.clipPath = QPainterPath(); - state.strokeStyle = Qt::black; - state.fillStyle = Qt::black; - state.globalAlpha = 1.0; - state.lineWidth = 1; - state.lineCap = Qt::FlatCap; - state.lineJoin = Qt::MiterJoin; - state.miterLimit = 10; - state.shadowOffsetX = 0; - state.shadowOffsetY = 0; - state.shadowBlur = 0; - state.shadowColor = qRgba(0, 0, 0, 0); - state.globalCompositeOperation = QPainter::CompositionMode_SourceOver; - state.font = QFont(); - state.textAlign = QSGContext2D::Start; - state.textBaseline = QSGContext2D::Alphabetic; - clear(); -} + QV8Context2DPixelArrayResource *r = v8_resource_cast(info.This()); + const int v = value->Uint32Value(); + if (r && index > 0 && index < r->image.width() * r->image.height() * 4 && v > 0 && v <= 255) { + const int w = r->image.width(); + const int h = r->image.height(); + const int row = (index / 4) / w; + const int col = (index / 4) % w; -void QSGContext2DPrivate::updateMatrix(const QMatrix& m) -{ - commands.push_back(QSGContext2D::UpdateMatrix); - matrixes.push_back(m); + QRgb* pixel = reinterpret_cast(r->image.scanLine(row)); + pixel += col; + switch (index % 4) { + case 0: + *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel)); + break; + case 1: + *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel)); + break; + case 2: + *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel)); + break; + case 3: + *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v); + break; + } + } + return v8::Undefined(); } +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh) + Creates a CanvasImageData object with the given dimensions(\a sw, \a sh). + */ +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData) + Creates a CanvasImageData object with the same dimensions as the argument. + */ +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl) + Creates a CanvasImageData object with the given image loaded from \a imageUrl. + Note:The \a imageUrl must be already loaded before this function call, if not, an empty + CanvasImageData obect will be returned. + + \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded + */ +static v8::Handle ctx2d_createImageData(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 1) { + if (args[0]->IsObject()) { + v8::Local imgData = args[0]->ToObject(); + QV8Context2DPixelArrayResource *pa = v8_resource_cast(imgData->GetInternalField(0)->ToObject()); + if (pa) { + qreal w = imgData->Get(v8::String::New("width"))->NumberValue(); + qreal h = imgData->Get(v8::String::New("height"))->NumberValue(); + return qt_create_image_data(w, h, engine, QImage()); + } + } else if (args[0]->IsString()) { + QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString()))); + return qt_create_image_data(image.width(), image.height(), engine, image); + } + } else if (args.Length() == 2) { + qreal w = args[0]->NumberValue(); + qreal h = args[1]->NumberValue(); + if (w > 0 && h > 0) + return qt_create_image_data(w, h, engine, QImage()); + } + return v8::Undefined(); +} -void QSGContext2DPrivate::save() +/*! + \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh) + Returns an CanvasImageData object containing the image data for the given rectangle of the canvas. + */ +static v8::Handle ctx2d_getImageData(const v8::Arguments &args) { - stateStack.push(state); + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + 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; + } + return v8::Null(); } -void QSGContext2DPrivate::restore() +/*! + \qmlmethod QtQuick2::Context2D putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight) + Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted. + */ +static v8::Handle ctx2d_putImageData(const v8::Arguments &args) { - if (!stateStack.isEmpty()) { - bool update = false; - QSGContext2D::State s = stateStack.pop(); - if (state.matrix != s.matrix) { - updateMatrix(s.matrix); - update = true; - } + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + if (args.Length() != 3 && args.Length() != 7) + return v8::Undefined(); + + if (args[0]->IsNull() || !args[0]->IsObject()) { + V8THROW_ERROR("Context2D::putImageData, the image data type mismatch"); + } + qreal dx = args[1]->NumberValue(); + qreal dy = args[2]->NumberValue(); + qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight; + + v8::Local imageData = args[0]->ToObject(); + QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast(imageData->Get(v8::String::New("data"))->ToObject()); + if (pixelArray) { + w = imageData->Get(v8::String::New("width"))->NumberValue(); + h = imageData->Get(v8::String::New("height"))->NumberValue(); + + if (args.Length() == 7) { + dirtyX = args[3]->NumberValue(); + dirtyY = args[4]->NumberValue(); + dirtyWidth = args[5]->NumberValue(); + dirtyHeight = args[6]->NumberValue(); + if (dirtyWidth < 0) { + dirtyX = dirtyX+dirtyWidth; + dirtyWidth = -dirtyWidth; + } - if (s.pen != state.pen) { - commands.push_back(QSGContext2D::UpdatePen); - pens.push_back(s.pen); - update = true; - } + if (dirtyHeight < 0) { + dirtyY = dirtyY+dirtyHeight; + dirtyHeight = -dirtyHeight; + } - if (s.globalAlpha != state.globalAlpha) { - commands.push_back(QSGContext2D::GlobalAlpha); - reals.push_back(s.globalAlpha); - update = true; - } + if (dirtyX < 0) { + dirtyWidth = dirtyWidth+dirtyX; + dirtyX = 0; + } - if (s.globalCompositeOperation != state.globalCompositeOperation) { - commands.push_back(QSGContext2D::GlobalCompositeOperation); - ints.push_back(s.globalCompositeOperation); - update = true; - } + if (dirtyY < 0) { + dirtyHeight = dirtyHeight+dirtyY; + dirtyY = 0; + } - if (s.font != state.font) { - commands.push_back(QSGContext2D::Font); - fonts.push_back(s.font); - update = true; - } + if (dirtyX+dirtyWidth > w) { + dirtyWidth = w - dirtyX; + } - if (s.fillStyle != state.fillStyle) { - commands.push_back(QSGContext2D::FillStyle); - brushes.push_back(s.fillStyle); - update = true; - } + if (dirtyY+dirtyHeight > h) { + dirtyHeight = h - dirtyY; + } - if (s.clipPath != state.clipPath) { - //commands.push_back(QSGContext2D::ClipPath); - update = true; + if (dirtyWidth <=0 || dirtyHeight <= 0) + return args.This(); + } else { + dirtyX = 0; + dirtyY = 0; + dirtyWidth = w; + dirtyHeight = h; } - if (s.textAlign != state.textAlign) { - commands.push_back(QSGContext2D::TextAlign); - update = true; - } + QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight); + r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight); + } + return args.This(); +} - if (s.textBaseline != state.textBaseline) { - commands.push_back(QSGContext2D::TextBaseline); - update = true; - } +/*! + \qmlclass QtQuick2::CanvasGradient + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Context2D opaque CanvasGradient interface. + */ - if (s.shadowBlur != state.shadowBlur - || s.shadowColor != state.shadowColor - || s.shadowOffsetX != state.shadowOffsetX - || s.shadowOffsetY != state.shadowOffsetY) { - update = true; +/*! + \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color) + Adds a color stop with the given color to the gradient at the given offset. + 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end. + */ +static v8::Handle ctx2d_gradient_addColorStop(const v8::Arguments &args) +{ + QV8Context2DStyleResource *style = v8_resource_cast(args.This()); + if (!style) + V8THROW_ERROR("Not a CanvasGradient object"); + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 2) { + + if (!style->brush.gradient()) + V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information"); + QGradient gradient = *(style->brush.gradient()); + qreal pos = args[0]->NumberValue(); + QColor color = qt_color_from_string(engine->toString(args[1])); + if (pos < 0.0 || pos > 1.0) { + //Throws an INDEX_SIZE_ERR exception + V8THROW_ERROR("CanvasGradient: parameter offset out of range"); } - if (update) - state = s; + if (color.isValid()) { + gradient.setColorAt(pos, color); + } else { + //Throws a SYNTAX_ERR exception + V8THROW_ERROR("CanvasGradient: parameter color is not a valid color string"); + } + style->brush = gradient; } + + return args.This(); } -void QSGContext2DPrivate::scale(qreal x, qreal y) + +void QSGContext2D::beginPath() { - state.matrix.scale(x, y); - updateMatrix(state.matrix); + m_path = QPainterPath(); + m_path.setFillRule(state.fillRule); } -void QSGContext2DPrivate::rotate(qreal angle) +void QSGContext2D::closePath() { - state.matrix.rotate(DEGREES(angle)); - updateMatrix(state.matrix); -} + if (m_path.isEmpty()) + return; + QRectF boundRect = m_path.boundingRect(); + if (boundRect.width() || boundRect.height()) + m_path.closeSubpath(); +} -void QSGContext2DPrivate::translate(qreal x, qreal y) +void QSGContext2D::moveTo( qreal x, qreal y) { - state.matrix.translate(x, y); - updateMatrix(state.matrix); + m_path.moveTo(state.matrix.map(QPointF(x, y))); } -void QSGContext2DPrivate::transform( - qreal m11, qreal m12, - qreal m21, qreal m22, - qreal dx, qreal dy) +void QSGContext2D::lineTo( qreal x, qreal y) { - QMatrix matrix(m11, m12, m21, m22, dx, dy); - state.matrix *= matrix; - updateMatrix(state.matrix); + m_path.lineTo(state.matrix.map(QPointF(x, y))); } -void QSGContext2DPrivate::setTransform( - qreal m11, qreal m12, - qreal m21, qreal m22, - qreal dx, qreal dy) +void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, + qreal x, qreal y) { - QMatrix matrix(m11, m12, m21, m22, dx, dy); - state.matrix = matrix; - updateMatrix(state.matrix); + m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)), + state.matrix.map(QPointF(x, y))); } -void QSGContext2DPrivate::clearRect(qreal x, qreal y, - qreal w, qreal h) +void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, + qreal x, qreal y) { - commands.push_back(QSGContext2D::ClearRect); - reals.push_back(x); - reals.push_back(y); - reals.push_back(w); - reals.push_back(h); + m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), + state.matrix.map(QPointF(cp2x, cp2y)), + state.matrix.map(QPointF(x, y))); } -void QSGContext2DPrivate::fillRect(qreal x, qreal y, - qreal w, qreal h) +void QSGContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius) { - QRectF rect; - if (tileRect.isValid()) { - rect = tileRect.intersect(QRect(x, y, w, h)); - } else { - rect = QRectF(x, y, w, h); - } + QPointF p0(m_path.currentPosition()); - if (rect.isValid()) { - commands.push_back(QSGContext2D::FillRect); - reals.push_back(rect.x()); - reals.push_back(rect.y()); - reals.push_back(rect.width()); - reals.push_back(rect.height()); - } -} + QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y())); + QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y())); + float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); -void QSGContext2DPrivate::strokeRect(qreal x, qreal y, - qreal w, qreal h) -{ - QPainterPath p; - p.addRect(x, y, w, h); + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); - if (tileRect.isValid()) { - QPainterPath tr; - tr.addRect(tileRect); - p = p.intersected(tr); + // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8) + // We could have used areCollinear() here, but since we're reusing + // the variables computed above later on we keep this logic. + if (qFuzzyCompare(qAbs(cos_phi), 1.0)) { + m_path.lineTo(p1); + return; } - if (!p.isEmpty()) { - commands.push_back(QSGContext2D::Stroke); - pathes.push_back(p); - } -} + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); -void QSGContext2DPrivate::beginPath() -{ - path = QPainterPath(); -} + QPointF orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; -void QSGContext2DPrivate::closePath() -{ - path.closeSubpath(); -} + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); -void QSGContext2DPrivate::moveTo( qreal x, qreal y) -{ - path.moveTo(state.matrix.map(QPointF(x, y))); + QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * Q_PI - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * Q_PI - ea; + if ((sa > ea) && ((sa - ea) < Q_PI)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > Q_PI)) + anticlockwise = true; + + arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false); } -void QSGContext2DPrivate::lineTo( qreal x, qreal y) +void QSGContext2D::arcTo(qreal x1, qreal y1, + qreal x2, qreal y2, + qreal radius) { - path.lineTo(state.matrix.map(QPointF(x, y))); + QPointF st = state.matrix.map(QPointF(x1, y1)); + QPointF end = state.matrix.map(QPointF(x2, y2)); + + if (!m_path.elementCount()) { + m_path.moveTo(st); + } else if (st == m_path.currentPosition() || st == end || !radius) { + m_path.lineTo(st); + } else { + addArcTo(st, end, radius); + } } -void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy, - qreal x, qreal y) +void QSGContext2D::rect(qreal x, qreal y, + qreal w, qreal h) { - path.quadTo(state.matrix.map(QPointF(cpx, cpy)), - state.matrix.map(QPointF(x, y))); + m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h))); } -void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y, - qreal cp2x, qreal cp2y, - qreal x, qreal y) +void QSGContext2D::roundedRect(qreal x, qreal y, + qreal w, qreal h, + qreal xr, qreal yr) { - path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), - state.matrix.map(QPointF(cp2x, cp2y)), - state.matrix.map(QPointF(x, y))); + QPainterPath path; + path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize); + m_path.addPath(state.matrix.map(path)); } -void QSGContext2DPrivate::arcTo(qreal x1, qreal y1, - qreal x2, qreal y2, - qreal radius) +void QSGContext2D::ellipse(qreal x, qreal y, + qreal w, qreal h) { - QPointF st = state.matrix.map(QPoint(x1, y1)); - QPointF end = state.matrix.map(QPoint(x2, y2)); - - path.arcTo(st.x(), st.y(), - end.x()-st.x(), end.y()-st.y(), - radius, 90); + QPainterPath path; + path.addEllipse(x, y, w, h); + m_path.addPath(state.matrix.map(path)); } -void QSGContext2DPrivate::rect(qreal x, qreal y, - qreal w, qreal h) +void QSGContext2D::text(const QString& str, qreal x, qreal y) { QPainterPath path; - path.addRect(QRectF(x, y, w, h)); - path.addPath(state.matrix.map(path)); + path.addText(x, y, state.font, str); + m_path.addPath(state.matrix.map(path)); } -void QSGContext2DPrivate::arc(qreal xc, - qreal yc, - qreal radius, - qreal sar, - qreal ear, - bool antiClockWise) +void QSGContext2D::arc(qreal xc, + qreal yc, + qreal radius, + qreal sar, + qreal ear, + bool antiClockWise, + bool transform) { - QPainterPath p; + if (transform) { + QPointF point = state.matrix.map(QPointF(xc, yc)); + xc = point.x(); + yc = point.y(); + } //### HACK // In Qt we don't switch the coordinate system for degrees diff --cc src/declarative/particles/qsgcustomparticle.cpp index a1d65a5,e1999f2..e0bbf28 --- a/src/declarative/particles/qsgcustomparticle.cpp +++ b/src/declarative/particles/qsgcustomparticle.cpp @@@ -463,9 -470,15 +471,20 @@@ QSGShaderEffectNode* QSGCustomParticle: s.fragmentCode = qt_particles_default_fragment_code; if (s.vertexCode.isEmpty()) s.vertexCode = qt_particles_default_vertex_code; + + if (!m_material) { + m_material = new QSGShaderEffectMaterialObject; + } + s.vertexCode = qt_particles_template_vertex_code + s.vertexCode; ++<<<<<<< HEAD + m_material.setProgramSource(s); + foreach (const QString &str, m_groups){ ++======= + m_material->setProgramSource(s); + + foreach (const QString &str, m_particles){ ++>>>>>>> refactor int gIdx = m_system->m_groupIds[str]; int count = m_system->m_groupData[gIdx]->size(); //Create Particle Geometry