****************************************************************************/
#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 <QtGui/qopenglframebufferobject.h>
+
#include <QtCore/qdebug.h>
#include "private/qsgcontext_p.h"
#include "private/qdeclarativesvgparser_p.h"
+#include "private/qdeclarativepath_p.h"
+
+#include "private/qsgimage_p_p.h"
+ #include <QtGui/qguiapplication.h>
#include <qdeclarativeinfo.h>
#include <QtCore/qmath.h>
-#include "qdeclarativepixmapcache_p.h"
-
-#include <private/qv8engine_p.h>
-#include "qvarlengtharray.h"
+#include "qv8engine_p.h"
+#include <QtOpenGL/QGLFramebufferObjectFormat>
+#include <QtOpenGL/QGLFramebufferObject>
+#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 <class T>
-void memcpy_vector(QVector<T>* dst, const QVector<T>& src)
-{
- int pos = dst->size();
- dst->resize(pos + src.size());
- memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size());
-}
-template <class T>
-void copy_vector(QVector<T>* dst, const QVector<T>& 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)
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::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(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<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
}
// pixel manipulation
-static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
-{
- //#TODO
- return v8::Undefined();
-}
+/*!
+ \qmlclass QtQuick2::CanvasImageData
-static v8::Handle<v8::Value> 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<v8::Value> ctx2d_imageData_width(v8::Local<v8::String>, const v8::AccessorInfo &args)
{
- //#TODO
- return v8::Undefined();
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
+ if (!r)
+ return v8::Integer::New(0);
+ return v8::Integer::New(r->image.width());
}
-static v8::Handle<v8::Value> 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<v8::Value> ctx2d_imageData_height(v8::Local<v8::String>, const v8::AccessorInfo &args)
{
- //#TODO
- return v8::Undefined();
-}
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(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<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, 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<v8::Value> 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<QV8Context2DPixelArrayResource>(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<v8::Value> ctx2d_imageData_filter(const v8::Arguments &args)
{
- QRectF r = shadowRect;
- r.moveTo(0, 0);
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(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<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]);
+ QVector<int> 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<sides; cy++) {
+ for (int cx=0; cx<sides; cx++) {
+ int scy = sy + cy - half;
+ int scx = sx + cx - half;
+ if (scy >= 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<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, 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<QV8Context2DPixelArrayResource>(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<v8::Value> 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<QV8Context2DPixelArrayResource>(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<const QRgb*>(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<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> 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<QV8Context2DPixelArrayResource>(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<QRgb*>(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<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+ QV8Engine *engine = V8ENGINE();
+ if (args.Length() == 1) {
+ if (args[0]->IsObject()) {
+ v8::Local<v8::Object> imgData = args[0]->ToObject();
+ QV8Context2DPixelArrayResource *pa = v8_resource_cast<QV8Context2DPixelArrayResource>(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<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
{
- stateStack.push(state);
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(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<v8::Object> 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<v8::Value> 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<QV8Context2DResource>(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<v8::Object> imageData = args[0]->ToObject();
+ QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast<QV8Context2DPixelArrayResource>(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<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
+{
+ QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(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