/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qsgcontext2d_p.h" #include "qsgcontext2d_p_p.h" #include "private/qsgadaptationlayer_p.h" #include "qsgcanvasitem_p.h" #include #include #include "private/qsgcontext_p.h" #include #include #include #include #include #include "qdeclarativepixmapcache_p.h" #include QT_BEGIN_NAMESPACE 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]; } } // Note, this is exported but in a private header as qtopengl depends on it. // But it really should be considered private API void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); #define DEGREES(t) ((t) * 180.0 / Q_PI) #define qClamp(val, min, max) qMin(qMax(val, min), max) static inline int extractInt(const char **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; return result; } 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; 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 colorFromString(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; } return QColor(name); } static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator) { if (compositeOperator == QLatin1String("source-over")) { return QPainter::CompositionMode_SourceOver; } else if (compositeOperator == QLatin1String("source-out")) { return QPainter::CompositionMode_SourceOut; } else if (compositeOperator == QLatin1String("source-in")) { return QPainter::CompositionMode_SourceIn; } else if (compositeOperator == QLatin1String("source-atop")) { return QPainter::CompositionMode_SourceAtop; } else if (compositeOperator == QLatin1String("destination-atop")) { return QPainter::CompositionMode_DestinationAtop; } else if (compositeOperator == QLatin1String("destination-in")) { return QPainter::CompositionMode_DestinationIn; } else if (compositeOperator == QLatin1String("destination-out")) { return QPainter::CompositionMode_DestinationOut; } else if (compositeOperator == QLatin1String("destination-over")) { return QPainter::CompositionMode_DestinationOver; } else if (compositeOperator == QLatin1String("darker")) { return QPainter::CompositionMode_SourceOver; } else if (compositeOperator == QLatin1String("lighter")) { return QPainter::CompositionMode_SourceOver; } else if (compositeOperator == QLatin1String("copy")) { return QPainter::CompositionMode_Source; } else if (compositeOperator == QLatin1String("xor")) { return QPainter::CompositionMode_Xor; } return QPainter::CompositionMode_SourceOver; } static QString compositeOperatorToString(QPainter::CompositionMode op) { switch (op) { case QPainter::CompositionMode_SourceOver: return QLatin1String("source-over"); case QPainter::CompositionMode_DestinationOver: return QLatin1String("destination-over"); case QPainter::CompositionMode_Clear: return QLatin1String("clear"); case QPainter::CompositionMode_Source: return QLatin1String("source"); case QPainter::CompositionMode_Destination: return QLatin1String("destination"); case QPainter::CompositionMode_SourceIn: return QLatin1String("source-in"); case QPainter::CompositionMode_DestinationIn: return QLatin1String("destination-in"); case QPainter::CompositionMode_SourceOut: return QLatin1String("source-out"); case QPainter::CompositionMode_DestinationOut: return QLatin1String("destination-out"); case QPainter::CompositionMode_SourceAtop: return QLatin1String("source-atop"); case QPainter::CompositionMode_DestinationAtop: return QLatin1String("destination-atop"); case QPainter::CompositionMode_Xor: return QLatin1String("xor"); case QPainter::CompositionMode_Plus: return QLatin1String("plus"); case QPainter::CompositionMode_Multiply: return QLatin1String("multiply"); case QPainter::CompositionMode_Screen: return QLatin1String("screen"); case QPainter::CompositionMode_Overlay: return QLatin1String("overlay"); case QPainter::CompositionMode_Darken: return QLatin1String("darken"); case QPainter::CompositionMode_Lighten: return QLatin1String("lighten"); case QPainter::CompositionMode_ColorDodge: return QLatin1String("color-dodge"); case QPainter::CompositionMode_ColorBurn: return QLatin1String("color-burn"); case QPainter::CompositionMode_HardLight: return QLatin1String("hard-light"); case QPainter::CompositionMode_SoftLight: return QLatin1String("soft-light"); case QPainter::CompositionMode_Difference: return QLatin1String("difference"); case QPainter::CompositionMode_Exclusion: return QLatin1String("exclusion"); default: break; } return QString(); } bool QSGContext2DPrivate::hasShadow() const { return state.shadowColor.isValid() && state.shadowColor.alpha() && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY); } void QSGContext2DPrivate::clearShadow() { state.shadowOffsetX = 0; state.shadowOffsetY = 0; state.shadowBlur = 0; state.shadowColor = QColor(); } QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix) { 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; tmpPainter.drawPixmap(shadowX, shadowY, pix); tmpPainter.end(); // 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; } // blacken the image with shadow color... tmpPainter.begin(&shadowImg); tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); tmpPainter.fillRect(shadowImg.rect(), state.shadowColor); tmpPainter.end(); return shadowImg; } void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect) { QRectF r = shadowRect; r.moveTo(0, 0); 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)); qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0); qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0); p->drawImage(dx, dy, shadowImage); p->fillRect(shadowRect, p->brush()); } void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path) { 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(); 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()); } void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path) { 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()); } void QSGContext2DPrivate::clear() { clearRect(0, 0, size.width(), size.height()); } void QSGContext2DPrivate::reset() { 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(); } void QSGContext2DPrivate::updateMatrix(const QMatrix& m) { commands.push_back(QSGContext2D::UpdateMatrix); matrixes.push_back(m); } void QSGContext2DPrivate::save() { stateStack.push(state); } void QSGContext2DPrivate::restore() { if (!stateStack.isEmpty()) { bool update = false; QSGContext2D::State s = stateStack.pop(); if (state.matrix != s.matrix) { updateMatrix(s.matrix); update = true; } if (s.pen != state.pen) { commands.push_back(QSGContext2D::UpdatePen); pens.push_back(s.pen); update = true; } if (s.globalAlpha != state.globalAlpha) { commands.push_back(QSGContext2D::GlobalAlpha); reals.push_back(s.globalAlpha); update = true; } if (s.globalCompositeOperation != state.globalCompositeOperation) { commands.push_back(QSGContext2D::GlobalCompositeOperation); ints.push_back(s.globalCompositeOperation); update = true; } if (s.font != state.font) { commands.push_back(QSGContext2D::Font); fonts.push_back(s.font); update = true; } if (s.fillStyle != state.fillStyle) { commands.push_back(QSGContext2D::FillStyle); brushes.push_back(s.fillStyle); update = true; } if (s.clipPath != state.clipPath) { //commands.push_back(QSGContext2D::ClipPath); update = true; } if (s.textAlign != state.textAlign) { commands.push_back(QSGContext2D::TextAlign); update = true; } if (s.textBaseline != state.textBaseline) { commands.push_back(QSGContext2D::TextBaseline); update = true; } if (s.shadowBlur != state.shadowBlur || s.shadowColor != state.shadowColor || s.shadowOffsetX != state.shadowOffsetX || s.shadowOffsetY != state.shadowOffsetY) { update = true; } if (update) state = s; } } void QSGContext2DPrivate::scale(qreal x, qreal y) { state.matrix.scale(x, y); updateMatrix(state.matrix); } void QSGContext2DPrivate::rotate(qreal angle) { state.matrix.rotate(DEGREES(angle)); updateMatrix(state.matrix); } void QSGContext2DPrivate::translate(qreal x, qreal y) { state.matrix.translate(x, y); updateMatrix(state.matrix); } void QSGContext2DPrivate::transform( qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) { QMatrix matrix(m11, m12, m21, m22, dx, dy); state.matrix *= matrix; updateMatrix(state.matrix); } void QSGContext2DPrivate::setTransform( qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) { QMatrix matrix(m11, m12, m21, m22, dx, dy); state.matrix = matrix; updateMatrix(state.matrix); } void QSGContext2DPrivate::clearRect(qreal x, qreal y, qreal w, qreal h) { commands.push_back(QSGContext2D::ClearRect); reals.push_back(x); reals.push_back(y); reals.push_back(w); reals.push_back(h); } void QSGContext2DPrivate::fillRect(qreal x, qreal y, qreal w, qreal h) { commands.push_back(QSGContext2D::FillRect); reals.push_back(x); reals.push_back(y); reals.push_back(w); reals.push_back(h); } void QSGContext2DPrivate::strokeRect(qreal x, qreal y, qreal w, qreal h) { QPainterPath path; path.addRect(x, y, w, h); commands.push_back(QSGContext2D::Stroke); pathes.push_back(path); } void QSGContext2DPrivate::beginPath() { path = QPainterPath(); } void QSGContext2DPrivate::closePath() { path.closeSubpath(); } void QSGContext2DPrivate::moveTo( qreal x, qreal y) { path.moveTo(state.matrix.map(QPointF(x, y))); } void QSGContext2DPrivate::lineTo( qreal x, qreal y) { path.lineTo(state.matrix.map(QPointF(x, y))); } void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) { path.quadTo(state.matrix.map(QPointF(cpx, cpy)), state.matrix.map(QPointF(x, y))); } void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y, qreal cp2x, qreal cp2y, qreal x, qreal y) { path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), state.matrix.map(QPointF(cp2x, cp2y)), state.matrix.map(QPointF(x, y))); } void QSGContext2DPrivate::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius) { 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); } void QSGContext2DPrivate::rect(qreal x, qreal y, qreal w, qreal h) { QPainterPath path; path.addRect(QRectF(x, y, w, h)); path.addPath(state.matrix.map(path)); } void QSGContext2DPrivate::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise) { QPainterPath path; //### HACK // In Qt we don't switch the coordinate system for degrees // and still use the 0,0 as bottom left for degrees so we need // to switch sar = -sar; ear = -ear; antiClockWise = !antiClockWise; //end hack float sa = DEGREES(sar); float ea = DEGREES(ear); double span = 0; double xs = xc - radius; double ys = yc - radius; double width = radius*2; double height = radius*2; if (!antiClockWise && (ea < sa)) { span += 360; } else if (antiClockWise && (sa < ea)) { span -= 360; } //### this is also due to switched coordinate system // we would end up with a 0 span instead of 360 if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) && qFuzzyCompare(qAbs(span), 360))) { span += ea - sa; } path.moveTo(QPointF(xc + radius * qCos(sar), yc - radius * qSin(sar))); path.arcTo(xs, ys, width, height, sa, span); path.addPath(state.matrix.map(path)); } void QSGContext2DPrivate::fill() { commands.push_back(QSGContext2D::Fill); pathes.push_back(path); } void QSGContext2DPrivate::stroke() { commands.push_back(QSGContext2D::Stroke); pathes.push_back(path); // painter->setMatrix(state.matrix, false); // QPainterPath tmp = state.matrix.inverted().map(path); //why? // painter->strokePath(tmp, painter->pen()); } void QSGContext2DPrivate::clip() { state.clipPath = path; pathes.push_back(state.clipPath); commands.push_back(QSGContext2D::Clip); } void QSGContext2DPrivate::setGlobalAlpha( qreal alpha) { state.globalAlpha = alpha; commands.push_back(QSGContext2D::GlobalAlpha); reals.push_back(state.globalAlpha); } void QSGContext2DPrivate::setGlobalCompositeOperation( const QString &op) { state.globalCompositeOperation = compositeOperatorFromString(op); commands.push_back(QSGContext2D::GlobalCompositeOperation); ints.push_back(state.globalCompositeOperation); } void QSGContext2DPrivate::setStrokeStyle( const QVariant &style) { QSGCanvasGradient *gradient= qobject_cast(style.value()); QBrush b; if (gradient) { b = gradient->value(); } else { b = colorFromString(style.toString()); } if (state.strokeStyle != b) { state.strokeStyle = b; state.pen.setBrush(state.strokeStyle); commands.push_back(QSGContext2D::UpdatePen); pens.push_back(state.pen); } } void QSGContext2DPrivate::setStrokeColor(const QColor& color) { if (state.strokeStyle != color) { state.strokeStyle = color; commands.push_back(QSGContext2D::UpdatePen); QPen pen; pen.setBrush(state.strokeStyle); pens.push_back(pen); } } void QSGContext2DPrivate::setFillColor(const QColor& color) { if (state.fillStyle != color) { state.fillStyle = color; commands.push_back(QSGContext2D::UpdateBrush); brushes.push_back(state.fillStyle); } } void QSGContext2DPrivate::setFillStyle( const QVariant &style) { QSGCanvasGradient *gradient= qobject_cast(style.value()); QBrush b; if (gradient) { b = gradient->value(); } else { b = colorFromString(style.toString()); } if (state.fillStyle != b) { state.fillStyle = b; commands.push_back(QSGContext2D::UpdateBrush); brushes.push_back(b); } } void QSGContext2DPrivate::setLineWidth( qreal w) { if (state.lineWidth != w) { state.pen.setWidthF(w); state.lineWidth = w; commands.push_back(QSGContext2D::UpdatePen); pens.push_back(state.pen); } } void QSGContext2DPrivate::setLineCap( const QString& cap) { Qt::PenCapStyle style; if (cap == QLatin1String("round")) style = Qt::RoundCap; else if (cap == QLatin1String("square")) style = Qt::SquareCap; else //if (capString == "butt") style = Qt::FlatCap; if (state.lineCap != style) { state.pen.setCapStyle(style); state.lineCap = style; commands.push_back(QSGContext2D::UpdatePen); pens.push_back(state.pen); } } void QSGContext2DPrivate::setLineJoin( const QString& join) { Qt::PenJoinStyle style; if (join == QLatin1String("round")) style = Qt::RoundJoin; else if (join == QLatin1String("bevel")) style = Qt::BevelJoin; else //if (joinString == "miter") style = Qt::MiterJoin; if (state.lineJoin != style) { state.lineJoin = style; state.pen.setJoinStyle(style); commands.push_back(QSGContext2D::UpdatePen); pens.push_back(state.pen); } } void QSGContext2DPrivate::setMiterLimit( qreal limit) { if (state.miterLimit != limit) { state.pen.setMiterLimit(limit); state.miterLimit = limit; commands.push_back(QSGContext2D::UpdatePen); pens.push_back(state.pen); } } void QSGContext2DPrivate::setShadowOffsetX( qreal x) { if (state.shadowOffsetX != x) { state.shadowOffsetX = x; commands.push_back(QSGContext2D::ShadowOffsetX); reals.push_back(x); } } void QSGContext2DPrivate::setShadowOffsetY( qreal y) { if (state.shadowOffsetY != y) { state.shadowOffsetY = y; commands.push_back(QSGContext2D::ShadowOffsetY); reals.push_back(y); } } void QSGContext2DPrivate::setShadowBlur( qreal b) { if (state.shadowBlur != b) { state.shadowBlur = b; commands.push_back(QSGContext2D::ShadowBlur); reals.push_back(b); } } void QSGContext2DPrivate::setShadowColor( const QString& color) { QColor c = colorFromString(color); if (state.shadowColor != c) { state.shadowColor = c; commands.push_back(QSGContext2D::ShadowColor); colors.push_back(c); } } void QSGContext2DPrivate::setFont( const QString& fontString) { QFont font; // ### this is simplified and incomplete // ### TODO:get code from Qt webkit QStringList tokens = fontString.split(QLatin1String(" ")); foreach (const QString &token, tokens) { if (token == QLatin1String("italic")) font.setItalic(true); else if (token == QLatin1String("bold")) font.setBold(true); else if (token.endsWith(QLatin1String("px"))) { QString number = token; number.remove(QLatin1String("px")); font.setPointSizeF(number.trimmed().toFloat()); } else font.setFamily(token); } if (state.font != font) { state.font = font; commands.push_back(QSGContext2D::Font); fonts.push_back(font); } } void QSGContext2DPrivate::setTextBaseline( const QString& baseline) { QSGContext2D::TextBaseLineType tbl; if (baseline==QLatin1String("alphabetic")) tbl = QSGContext2D::Alphabetic; else if (baseline == QLatin1String("hanging")) tbl = QSGContext2D::Hanging; else if (baseline == QLatin1String("top")) tbl = QSGContext2D::Top; else if (baseline == QLatin1String("bottom")) tbl = QSGContext2D::Bottom; else if (baseline == QLatin1String("middle")) tbl = QSGContext2D::Middle; else { tbl = QSGContext2D::Alphabetic; Q_Q(QSGContext2D); qmlInfo(q) << "QSGContext2D: invalid baseline:" << baseline; } if (state.textBaseline != tbl) { state.textBaseline = tbl; commands.push_back(QSGContext2D::TextBaseline); ints.push_back(tbl); } } void QSGContext2DPrivate::setTextAlign(const QString& align) { QSGContext2D::TextAlignType ta; if (align==QLatin1String("start")) ta = QSGContext2D::Start; else if (align == QLatin1String("end")) ta = QSGContext2D::End; else if (align == QLatin1String("left")) ta = QSGContext2D::Left; else if (align == QLatin1String("right")) ta = QSGContext2D::Right; else if (align == QLatin1String("center")) ta = QSGContext2D::Center; else { ta = QSGContext2D::Start; Q_Q(QSGContext2D); qmlInfo(q) << "QSGContext2D: invalid text align:" << align; } if (state.textAlign != ta) { state.textAlign = ta; commands.push_back(QSGContext2D::TextAlign); ints.push_back(ta); } } void QSGContext2DPrivate::fillText(const QString& text, qreal x, qreal y) { commands.push_back(QSGContext2D::FillText); strings.push_back(text); reals.push_back(x); reals.push_back(y); ints.push_back(state.textAlign); ints.push_back(state.textBaseline); } void QSGContext2DPrivate::strokeText( const QString& text, qreal x, qreal y) { commands.push_back(QSGContext2D::StrokeText); strings.push_back(text); reals.push_back(x); reals.push_back(y); ints.push_back(state.textAlign); ints.push_back(state.textBaseline); } void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy) { commands.push_back(QSGContext2D::DrawImage1); strings.push_back(url); reals.push_back(dx); reals.push_back(dy); } void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh) { commands.push_back(QSGContext2D::DrawImage2); strings.push_back(url); reals.push_back(dx); reals.push_back(dy); reals.push_back(dw); reals.push_back(dh); } void QSGContext2DPrivate::drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh) { commands.push_back(QSGContext2D::DrawImage3); strings.push_back(url); reals.push_back(sx); reals.push_back(sy); reals.push_back(sw); reals.push_back(sh); reals.push_back(dx); reals.push_back(dy); reals.push_back(dw); reals.push_back(dh); } QList QSGContext2DPrivate::getImageData(qreal sx, qreal sy, qreal sw, qreal sh) { Q_Q(QSGContext2D); waitingForPainting = true; commands.push_back(QSGContext2D::GetImageData); reals.push_back(sx); reals.push_back(sy); reals.push_back(sw); reals.push_back(sh); q->sync(); return imageData; } void QSGContext2DPrivate::putImageData(const QVariantList& imageData, qreal dx, qreal dy, qreal w, qreal h) { QImage image = cachedImage.copy(dx, dy, w, h); uchar* data = image.bits(); int i = 0; while(i< imageData.size() && i < image.byteCount()) { //the stored order in QImage:BGRA //the stored order in Canvas:RGBA *(data+i) = imageData[i+2].toInt();//B *(data+i+1) = imageData[i+1].toInt();//G *(data+i+2) = imageData[i].toInt();//R *(data+i+3) = imageData[i+3].toInt();//A i+=4; } commands.push_back(QSGContext2D::PutImageData); images.push_back(image); reals.push_back(dx); reals.push_back(dy); } void QSGContext2D::save() { Q_D(QSGContext2D); d->save(); } void QSGContext2D::restore() { Q_D(QSGContext2D); d->restore(); } void QSGContext2D::scale(qreal x, qreal y) { Q_D(QSGContext2D); d->scale(x, y); } void QSGContext2D::rotate(qreal angle) { Q_D(QSGContext2D); d->rotate(angle); } void QSGContext2D::translate(qreal x, qreal y) { Q_D(QSGContext2D); d->translate(x, y); } void QSGContext2D::transform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) { Q_D(QSGContext2D); d->transform(m11, m12, m21, m22, dx, dy); } void QSGContext2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) { Q_D(QSGContext2D); d->setTransform(m11, m12, m21, m22, dx, dy); } QString QSGContext2D::globalCompositeOperation() const { Q_D(const QSGContext2D); return compositeOperatorToString(d->state.globalCompositeOperation); } void QSGContext2D::setGlobalCompositeOperation(const QString &op) { Q_D(QSGContext2D); d->setGlobalCompositeOperation(op); } QVariant QSGContext2D::strokeStyle() const { Q_D(const QSGContext2D); return d->state.strokeStyle; } void QSGContext2D::setStrokeStyle(const QVariant &style) { Q_D(QSGContext2D); d->setStrokeStyle(style); } QVariant QSGContext2D::fillStyle() const { Q_D(const QSGContext2D); return d->state.fillStyle; } QColor QSGContext2D::strokeColor() const { Q_D(const QSGContext2D); return d->state.strokeStyle.color(); } QColor QSGContext2D::fillColor() const { Q_D(const QSGContext2D); return d->state.fillStyle.color(); } void QSGContext2D::setFillStyle(const QVariant &style) { Q_D(QSGContext2D); d->setFillStyle(style); } void QSGContext2D::setStrokeColor(const QColor& color) { Q_D(QSGContext2D); d->setStrokeColor(color); } void QSGContext2D::setFillColor(const QColor& color) { Q_D(QSGContext2D); d->setFillColor(color); } qreal QSGContext2D::globalAlpha() const { Q_D(const QSGContext2D); return d->state.globalAlpha; } void QSGContext2D::setGlobalAlpha(qreal alpha) { Q_D(QSGContext2D); d->setGlobalAlpha(alpha); } QSGImage *QSGContext2D::createImage(const QString &url) { Q_D(QSGContext2D); //### cache image QSGImage* img = new QSGImage(d->canvas); img->setSource(QUrl(url)); return img; } QSGCanvasGradient *QSGContext2D::createLinearGradient(qreal x0, qreal y0, qreal x1, qreal y1) { QLinearGradient g(x0, y0, x1, y1); return new QSGCanvasGradient(g); } QSGCanvasGradient *QSGContext2D::createRadialGradient(qreal x0, qreal y0, qreal r0, qreal x1, qreal y1, qreal r1) { QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0)); return new QSGCanvasGradient(g); } qreal QSGContext2D::lineWidth() const { Q_D(const QSGContext2D); return d->state.lineWidth; } void QSGContext2D::setLineWidth(qreal w) { Q_D(QSGContext2D); d->setLineWidth(w); } QString QSGContext2D::lineCap() const { Q_D(const QSGContext2D); switch(d->state.lineCap) { case Qt::RoundCap: return QLatin1String("round"); case Qt::FlatCap: return QLatin1String("butt"); case Qt::SquareCap: return QLatin1String("square"); default: break; } return QLatin1String(""); } void QSGContext2D::setLineCap(const QString &capString) { Q_D(QSGContext2D); d->setLineCap(capString); } QString QSGContext2D::lineJoin() const { Q_D(const QSGContext2D); switch (d->state.lineJoin) { case Qt::RoundJoin: return QLatin1String("round"); case Qt::BevelJoin: return QLatin1String("bevel"); case Qt::MiterJoin: return QLatin1String("miter"); default: break; } return QLatin1String(""); } void QSGContext2D::setLineJoin(const QString &joinString) { Q_D(QSGContext2D); d->setLineJoin(joinString); } qreal QSGContext2D::miterLimit() const { Q_D(const QSGContext2D); return d->state.miterLimit; } void QSGContext2D::setMiterLimit(qreal m) { Q_D(QSGContext2D); d->setMiterLimit(m); } void QSGContext2D::setShadowOffsetX(qreal x) { Q_D(QSGContext2D); d->setShadowOffsetX(x); } void QSGContext2D::setShadowOffsetY(qreal y) { Q_D(QSGContext2D); d->setShadowOffsetY(y); } void QSGContext2D::setShadowBlur(qreal b) { Q_D(QSGContext2D); d->setShadowBlur(b); } void QSGContext2D::setShadowColor(const QString &str) { Q_D(QSGContext2D); d->setShadowColor(str); } QString QSGContext2D::textBaseline() const { Q_D(const QSGContext2D); switch(d->state.textBaseline) { case QSGContext2D::Alphabetic: return QLatin1String("alphabetic"); case QSGContext2D::Hanging: return QLatin1String("hanging"); case QSGContext2D::Top: return QLatin1String("top"); case QSGContext2D::Bottom: return QLatin1String("bottom"); case QSGContext2D::Middle: return QLatin1String("middle"); default: break; } return QLatin1String("alphabetic"); } void QSGContext2D::setTextBaseline(const QString &baseline) { Q_D(QSGContext2D); d->setTextBaseline(baseline); } QString QSGContext2D::textAlign() const { Q_D(const QSGContext2D); switch(d->state.textAlign) { case QSGContext2D::Start: return QLatin1String("start"); case QSGContext2D::End: return QLatin1String("end"); case QSGContext2D::Left: return QLatin1String("left"); case QSGContext2D::Right: return QLatin1String("right"); case QSGContext2D::Center: return QLatin1String("center"); default: break; } return QLatin1String("start"); } void QSGContext2D::setTextAlign(const QString &align) { Q_D(QSGContext2D); d->setTextAlign(align); d->commands.push_back(QSGContext2D::TextAlign); d->ints.push_back(d->state.textAlign); } void QSGContext2D::setFont(const QString &fontString) { Q_D(QSGContext2D); d->setFont(fontString); } QString QSGContext2D::font() const { //### TODO Q_D(const QSGContext2D); return d->state.font.toString(); } qreal QSGContext2D::shadowOffsetX() const { Q_D(const QSGContext2D); return d->state.shadowOffsetX; } qreal QSGContext2D::shadowOffsetY() const { Q_D(const QSGContext2D); return d->state.shadowOffsetY; } qreal QSGContext2D::shadowBlur() const { Q_D(const QSGContext2D); return d->state.shadowBlur; } QString QSGContext2D::shadowColor() const { Q_D(const QSGContext2D); return d->state.shadowColor.name(); } void QSGContext2D::clearRect(qreal x, qreal y, qreal w, qreal h) { Q_D(QSGContext2D); d->clearRect(x, y, w, h); } void QSGContext2D::fillRect(qreal x, qreal y, qreal w, qreal h) { Q_D(QSGContext2D); d->fillRect(x, y, w, h); } int QSGContext2DPrivate::baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics) { int offset = 0; switch (value) { case QSGContext2D::Top: break; case QSGContext2D::Alphabetic: case QSGContext2D::Middle: case QSGContext2D::Hanging: offset = metrics.ascent(); break; case QSGContext2D::Bottom: offset = metrics.height(); break; } return offset; } int QSGContext2DPrivate::textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text) { int offset = 0; if (value == QSGContext2D::Start) value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right; else if (value == QSGContext2D::End) value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left; switch (value) { case QSGContext2D::Center: offset = metrics.width(text)/2; break; case QSGContext2D::Right: offset = metrics.width(text); case QSGContext2D::Left: default: break; } return offset; } void QSGContext2D::fillText(const QString &text, qreal x, qreal y) { Q_D(QSGContext2D); d->fillText(text, x, y); } void QSGContext2D::strokeText(const QString &text, qreal x, qreal y) { Q_D(QSGContext2D); d->strokeText(text, x, y); } void QSGContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h) { Q_D(QSGContext2D); d->strokeRect(x, y, w, h); } void QSGContext2D::beginPath() { Q_D(QSGContext2D); d->beginPath(); } void QSGContext2D::closePath() { Q_D(QSGContext2D); d->closePath(); } void QSGContext2D::moveTo(qreal x, qreal y) { Q_D(QSGContext2D); d->moveTo(x, y); } void QSGContext2D::lineTo(qreal x, qreal y) { Q_D(QSGContext2D); d->lineTo(x, y); } void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) { Q_D(QSGContext2D); d->quadraticCurveTo(cpx, cpy, x, y); } void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y, qreal cp2x, qreal cp2y, qreal x, qreal y) { Q_D(QSGContext2D); d->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); } void QSGContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius) { Q_D(QSGContext2D); d->arcTo(x1, y1, x2, y2, radius); } void QSGContext2D::rect(qreal x, qreal y, qreal w, qreal h) { Q_D(QSGContext2D); d->rect(x, y, w, h); } void QSGContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool anticlockwise) { Q_D(QSGContext2D); d->arc(xc, yc, radius, sar, ear, anticlockwise); } void QSGContext2D::fill() { Q_D(QSGContext2D); d->fill(); } void QSGContext2D::stroke() { Q_D(QSGContext2D); d->stroke(); } void QSGContext2D::clip() { Q_D(QSGContext2D); d->clip(); } bool QSGContext2D::isPointInPath(qreal x, qreal y) const { Q_D(const QSGContext2D); return d->path.contains(QPointF(x, y)); } QList QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh) { Q_D(QSGContext2D); return d->getImageData(sx, sy, sw, sh); } void QSGContext2D::putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h) { Q_D(QSGContext2D); return d->putImageData(imageData.toList(), x, y, w, h); } QSGContext2D::QSGContext2D(QObject *parent) : QObject(*(new QSGContext2DPrivate()), parent) { Q_D(QSGContext2D); d->canvas = qobject_cast(parent); } QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentData) : QObject(*(new QSGContext2DPrivate()), 0) { Q_D(QSGContext2D); d->agent = 0; d->agentData = agentData; if (d->agentData) { d->agentData->orig = orig; } d->canvas = qobject_cast(orig); } QSGContext2D::~QSGContext2D() { Q_D(QSGContext2D); if (d->agent) { d->agentData->syncDone.wakeAll(); d->agent->release(); } } bool QSGContext2D::isDirty() const { Q_D(const QSGContext2D); return !d->commands.isEmpty(); } QScriptValue QSGContext2D::scriptValue() const { Q_D(const QSGContext2D); return d->scriptValue; } void QSGContext2D::setScriptEngine(QScriptEngine *eng) { Q_D(QSGContext2D); if (d->scriptEngine != eng) { d->scriptEngine = eng; // QScriptValue agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent")); // if (!agent.isValid()) { // d->scriptEngine->evaluate(QLatin1String( // "(function CanvasImageData(w, h, d) {" // " this.widht = w;" // " this.height = h;" // " this.data = d;" // " })")); // d->scriptEngine->evaluate(agentScript()); // agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent")); // if (!agent.isValid()) { // qWarning() << "QSGContext2D:error when evaluating context2d script value!"; // d->scriptValue = QScriptValue(); // return; // } // } // QScriptValue o = d->scriptEngine->newQObject(this); // d->scriptValue = agent.construct(QScriptValueList() << o); } } QScriptEngine *QSGContext2D::scriptEngine() const { Q_D(const QSGContext2D); return d->scriptEngine; } void QSGContext2D::addref() { Q_D(QSGContext2D); Q_ASSERT(d->agentData); d->agentData->ref.ref(); } void QSGContext2D::release() { Q_D(QSGContext2D); Q_ASSERT(d->agentData); if (!d->agentData->ref.deref()) { deleteLater(); } } bool QSGContext2D::inWorkerThread() const { Q_D(const QSGContext2D); return d->agentData != 0; } const QString& QSGContext2D::agentScript() const { static QString script; if (script.isEmpty()) { script = QString::fromLatin1( "function CanvasImageData(w, h, d) {" " this.width = w;" " this.height = h;" " this.data = d;" "}" "function Context2DAgent(_ctx2d) {" " this._ctx = _ctx2d;" " this._fillColor = '#000000';" " this._fillStyle = '#000000';" " this._strokeColor = '#000000';" " this._strokeStyle = '#000000';" " this._globalCompositeOperation = \"source-over\";" " this._commands = [];" " this.createImageData = function() {" " var d = null;" " if (arguments.length == 1 && arguments[0] instanceof CanvasImageData) {" " d = new CanvasImageData(arguments[0].width," " arguments[0].height," " new Array(arguments[0].width * arguments[0].height * 4));" " } else if (arguments.length == 2) {" " d = new CanvasImageData(arguments[0], arguments[1], new Array(arguments[0] * arguments[1] * 4));" " }" " if (d)" " for (var i=0; i= dirtyX && x < dirtyX+dirtyWidth" " && y >= dirtyY && y < dirtyY+dirtyHeight;" " });" " this._commands.push([%2, filteredData, dx, dy, dirtyWidth, dirtyHeight]);" " }" "};").arg(PutImageData).arg(PutImageData)); script.append(QString::fromLatin1("}")); } return script; } QSGContext2D *QSGContext2D::agent() { Q_D(QSGContext2D); if (d->agent) return d->agent; d->agent = new QSGContext2D(this, new QSGContext2DWorkerAgent); connect(this, SIGNAL(painted()), d->agent, SIGNAL(painted())); d->agent->setSize(size()); return d->agent; } void QSGContext2D::processCommands(const QScriptValue& commands) { #ifdef QSGCANVASITEM_DEBUG QElapsedTimer t; t.start(); #endif int ii = 0; if (commands.isArray()) { QScriptValue cmd = commands.property(ii); while(cmd.isValid()) { processCommand(cmd); ii++; cmd = commands.property(ii); } } #ifdef QSGCANVASITEM_DEBUG qDebug() << "processed" << ii << "commands in " << t.nsecsElapsed() << "nsecs"; #endif sync(); } void QSGContext2D::sync() { Q_D(QSGContext2D); #ifdef QSGCANVASITEM_DEBUG QElapsedTimer t; t.start(); #endif if (d->agentData) { if (d->agentData->ref == 1) return; Sync *s = new Sync; s->data = d->agentData; d->agentData->mutex.lock(); QCoreApplication::postEvent(this, s); d->agentData->syncDone.wait(&d->agentData->mutex); d->agentData->mutex.unlock(); } else { //qmlInfo(this) << "Context2D sync() can only be called from a WorkerScript;"; emit changed(); } #ifdef QSGCANVASITEM_DEBUG qDebug() << "syncing time:" << t.nsecsElapsed(); #endif } bool QSGContext2D::event(QEvent *e) { Q_D(QSGContext2D); if (e->type() == QEvent::User && d->agentData) { QMutexLocker locker(&d->agentData->mutex); Sync *s = static_cast(e); QSGContext2DPrivate* origin_d = static_cast(s->data->orig->d_func()); //quick copy memcpy_vector(&origin_d->commands, d->commands); memcpy_vector(&origin_d->ints, d->ints); memcpy_vector(&origin_d->reals, d->reals); memcpy_vector(&origin_d->colors, d->colors); memcpy_vector(&origin_d->matrixes, d->matrixes); memcpy_vector(&origin_d->sizes, d->sizes); //slow copy copy_vector(&origin_d->strings, d->strings); copy_vector(&origin_d->variants, d->variants); copy_vector(&origin_d->pens, d->pens); copy_vector(&origin_d->brushes, d->brushes); copy_vector(&origin_d->pathes, d->pathes); copy_vector(&origin_d->fonts, d->fonts); copy_vector(&origin_d->images, d->images); origin_d->state = d->state; d->clearCommands(); if (d->waitingForPainting) { d->imageData.clear(); origin_d->imageData.clear(); emit s->data->orig->changed(); while(origin_d->imageData.isEmpty()) { QCoreApplication::processEvents(); } d->imageData = origin_d->imageData; d->waitingForPainting = false; qDebug() << "imageData size:" << d->imageData.size(); } else { emit s->data->orig->changed(); } d->agentData->syncDone.wakeAll(); return true; } return QObject::event(e); } void QSGContext2D::processCommand(const QScriptValue& cmd) { int action = cmd.property(0).toInt32(); switch (action) { case QSGContext2D::Save: save(); break; case QSGContext2D::Restore: restore(); break; case QSGContext2D::Scale: scale(cmd.property(1).toNumber(), cmd.property(2).toNumber()); break; case QSGContext2D::Rotate: rotate(cmd.property(1).toNumber()); break; case QSGContext2D::Translate: translate(cmd.property(1).toNumber(), cmd.property(2).toNumber()); break; case QSGContext2D::Transform: transform(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber(), cmd.property(6).toNumber()); break; case QSGContext2D::SetTransform: setTransform(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber(), cmd.property(6).toNumber()); break; case QSGContext2D::ClearRect: clearRect(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber()); break; case QSGContext2D::FillRect: fillRect(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber()); break; case QSGContext2D::StrokeRect: strokeRect(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber()); break; case QSGContext2D::BeginPath: beginPath(); break; case QSGContext2D::ClosePath: closePath(); break; case QSGContext2D::MoveTo: moveTo(cmd.property(1).toNumber(), cmd.property(2).toNumber()); break; case QSGContext2D::LineTo: lineTo(cmd.property(1).toNumber(), cmd.property(2).toNumber()); break; case QSGContext2D::QuadraticCurveTo: quadraticCurveTo(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber()); break; case QSGContext2D::BezierCurveTo: bezierCurveTo(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber(), cmd.property(6).toNumber()); break; case QSGContext2D::ArcTo: arcTo(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber()); break; case QSGContext2D::Rect: rect(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber()); break; case QSGContext2D::Arc: arc(cmd.property(1).toNumber(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber(), cmd.property(6).toBool()); break; case QSGContext2D::Fill: fill(); break; case QSGContext2D::Stroke: stroke(); break; case QSGContext2D::Clip: clip(); break; case QSGContext2D::GlobalAlpha: setGlobalAlpha(cmd.property(1).toNumber()); break; case QSGContext2D::GlobalCompositeOperation: setGlobalCompositeOperation(cmd.property(1).toString()); break; case QSGContext2D::StrokeStyle: setStrokeStyle(cmd.property(1).toVariant()); break; case QSGContext2D::FillStyle: setFillStyle(cmd.property(1).toVariant()); break; case QSGContext2D::FillColor: setFillColor(cmd.property(1).toVariant().value()); break; case QSGContext2D::StrokeColor: setStrokeColor(cmd.property(1).toVariant().value()); break; case QSGContext2D::LineWidth: setLineWidth(cmd.property(1).toNumber()); break; case QSGContext2D::LineCap: setLineCap(cmd.property(1).toString()); break; case QSGContext2D::LineJoin: setLineJoin(cmd.property(1).toString()); break; case QSGContext2D::MiterLimit: setMiterLimit(cmd.property(1).toNumber()); break; case QSGContext2D::ShadowOffsetX: setShadowOffsetX(cmd.property(1).toNumber()); break; case QSGContext2D::ShadowOffsetY: setShadowOffsetY(cmd.property(1).toNumber()); break; case QSGContext2D::ShadowBlur: setShadowBlur(cmd.property(1).toNumber()); break; case QSGContext2D::ShadowColor: setShadowColor(cmd.property(1).toString()); break; case QSGContext2D::Font: setFont(cmd.property(1).toString()); break; case QSGContext2D::TextBaseline: setTextBaseline(cmd.property(1).toString()); break; case QSGContext2D::TextAlign: setTextAlign(cmd.property(1).toString()); break; case QSGContext2D::FillText: fillText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber()); break; case QSGContext2D::StrokeText: strokeText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber()); break; case QSGContext2D::DrawImage1: { drawImage(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber()); break; } case QSGContext2D::DrawImage2: drawImage(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber()); break; case QSGContext2D::DrawImage3: drawImage(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber(), cmd.property(6).toNumber(), cmd.property(7).toNumber(), cmd.property(8).toNumber(), cmd.property(9).toNumber()); break; case QSGContext2D::PutImageData: putImageData(cmd.property(1).toVariant(), cmd.property(2).toNumber(), cmd.property(3).toNumber(), cmd.property(4).toNumber(), cmd.property(5).toNumber()); break; default: break; } } void QSGContext2D::paint(QPainter* p) { Q_D(QSGContext2D); QTransform transform = p->worldTransform(); if (!d->commands.isEmpty()) { int matrix_idx, real_idx, int_idx, variant_idx, string_idx,color_idx,cmd_idx, pen_idx, brush_idx, font_idx, path_idx, image_idx, size_idx; matrix_idx = real_idx = int_idx = variant_idx = string_idx =color_idx = cmd_idx = pen_idx = brush_idx = font_idx = path_idx = image_idx = size_idx = 0; foreach(PaintCommand cmd, d->commands) { switch (cmd) { case UpdateMatrix: { // qDebug() << "update matrix from " << d->state.matrix << " to " << d->matrixes[matrix_idx]; //p->setWorldTransform(transform * QTransform(d->matrixes[matrix_idx++]), false); //p->setMatrix(d->matrixes[matrix_idx++]); d->state.matrix = d->matrixes[matrix_idx++]; break; } case ClearRect: { qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; qreal w = d->reals[real_idx++]; qreal h = d->reals[real_idx++]; p->eraseRect(QRectF(x, y, w, h)); break; } case FillRect: { qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; qreal w = d->reals[real_idx++]; qreal h = d->reals[real_idx++]; if (d->hasShadow()) d->fillRectShadow(p, QRectF(x, y, w, h)); else p->fillRect(QRectF(x, y, w, h), p->brush()); break; } case ShadowColor: { QColor c = d->colors[color_idx++]; d->state.shadowColor = c; break; } case ShadowBlur: { qreal blur = d->reals[real_idx++]; d->state.shadowBlur = blur; break; } case ShadowOffsetX: { qreal x = d->reals[real_idx++]; d->state.shadowOffsetX = x; break; } case ShadowOffsetY: { qreal y = d->reals[real_idx++]; d->state.shadowOffsetY = y; break; } case Fill: { QPainterPath path = d->pathes[path_idx++]; if (d->hasShadow()) d->fillShadowPath(p,path); else p->fillPath(path, p->brush()); break; } case Stroke: { p->setMatrix(d->state.matrix); QPainterPath path = d->state.matrix.inverted().map(d->pathes[path_idx++]); if (d->hasShadow()) d->strokeShadowPath(p,path); else p->strokePath(path, p->pen()); break; } case Clip: { QPainterPath clipPath = d->pathes[path_idx++]; p->setClipPath(clipPath); p->setClipping(true); break; } case UpdateBrush: { p->setBrush(d->brushes[brush_idx++]); break; } case UpdatePen: { p->setPen(d->pens[pen_idx++]); break; } case GlobalAlpha: { p->setOpacity(d->reals[real_idx++]); break; } case GlobalCompositeOperation: { p->setCompositionMode(static_cast(d->ints[int_idx++])); break; } case Font: { p->setFont(d->fonts[font_idx++]); break; } case StrokeText: { QString text = d->strings[string_idx++]; qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; int align = d->ints[int_idx++]; int baseline = d->ints[int_idx++]; QPen oldPen = p->pen(); p->setPen(QPen(p->brush(),0)); //p->setMatrix(state.matrix, false); // always set? QPainterPath textPath; QFont oldFont = p->font(); QFont font = p->font(); font.setStyleStrategy(QFont::ForceOutline); p->setFont(font); const QFontMetrics &metrics = p->fontMetrics(); int yoffset = d->baseLineOffset(static_cast(baseline), metrics); int xoffset = d->textAlignOffset(static_cast(align), metrics, text); textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text); if (d->hasShadow()) d->strokeShadowPath(p,textPath); p->strokePath(textPath, QPen(p->brush(), p->pen().widthF())); //reset old font p->setFont(oldFont); p->setPen(oldPen); break; } case FillText: { QString text = d->strings[string_idx++]; qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; int align = d->ints[int_idx++]; int baseline = d->ints[int_idx++]; QFont oldFont = p->font(); QPen oldPen = p->pen(); p->setPen(QPen(p->brush(), p->pen().widthF())); //p->setMatrix(state.matrix, false); //QFont font = p->font(); QFont font = d->state.font; font.setBold(true); p->setFont(font); int yoffset = d->baseLineOffset(static_cast(baseline), p->fontMetrics()); int xoffset = d->textAlignOffset(static_cast(align), p->fontMetrics(), text); QTextOption opt; // Adjust baseLine etc if (d->hasShadow()) { const QFontMetrics &metrics = p->fontMetrics(); QPainterPath textPath; textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text); d->fillShadowPath(p,textPath); } //p->drawText(QRectF(x - xoffset, y - yoffset, QWIDGETSIZE_MAX, p->fontMetrics().height()), text, opt); p->setFont(oldFont); p->setPen(oldPen); break; } case DrawImage1: { QUrl url(d->strings[string_idx++]); qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; QDeclarativePixmap px(qmlEngine(d->canvas), url); qDebug() << "draw image:" << url << px.pixmap().size(); if (px.isReady()) { QPixmap pixmap = px.pixmap(); if (d->hasShadow()) { QImage shadow = d->makeShadowImage(pixmap); qreal dx = x + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0); qreal dy = y + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0); p->drawImage(QPointF(dx, dy), shadow); } p->drawPixmap(QPointF(x, y), pixmap); } break; } case DrawImage2: { qreal dx = d->reals[real_idx++]; qreal dy = d->reals[real_idx++]; qreal dw = d->reals[real_idx++]; qreal dh = d->reals[real_idx++]; QUrl url(d->strings[string_idx++]); QDeclarativePixmap px(qmlEngine(d->canvas), url); if (px.isReady()) { QPixmap pixmap = px.pixmap().scaled(dw, dh); if (d->hasShadow()) { QImage shadow = d->makeShadowImage(pixmap); qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0); qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0); p->drawImage(QPointF(shadow_dx, shadow_dy), shadow); } p->drawPixmap(QPointF(dx, dy), pixmap); } break; } case DrawImage3: { qreal sx = d->reals[real_idx++]; qreal sy = d->reals[real_idx++]; qreal sw = d->reals[real_idx++]; qreal sh = d->reals[real_idx++]; qreal dx = d->reals[real_idx++]; qreal dy = d->reals[real_idx++]; qreal dw = d->reals[real_idx++]; qreal dh = d->reals[real_idx++]; QUrl url(d->strings[string_idx++]); QDeclarativePixmap px(qmlEngine(d->canvas), url); if (px.isReady()) { QPixmap pixmap = px.pixmap().copy(sx, sy, sw, sh).scaled(dw, dh); if (d->hasShadow()) { QImage shadow = d->makeShadowImage(pixmap); qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0); qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0); p->drawImage(QPointF(shadow_dx, shadow_dy), shadow); } p->drawPixmap(QPointF(dx, dy), pixmap); } break; } case GetImageData: { qreal sx = d->reals[real_idx++]; qreal sy = d->reals[real_idx++]; qreal sw = d->reals[real_idx++]; qreal sh = d->reals[real_idx++]; QImage img = toImage().copy(sx, sy, sw, sh); const uchar* data = img.constBits(); int i = 0; while(i< img.byteCount()) { //the stored order in QImage:BGRA d->imageData << *(data+i+2);//R d->imageData << *(data+i+1);//G d->imageData << *(data+i);//B d->imageData << *(data+i+3);//A i+=4; } break; } case PutImageData: { QImage image = d->images[image_idx++]; qreal x = d->reals[real_idx++]; qreal y = d->reals[real_idx++]; p->drawImage(QPointF(x, y), image); break; } default: break; } } d->clearCommands(); } } QPaintDevice* QSGContext2D::paintDevice() { Q_D(QSGContext2D); return &d->cachedImage; } const QImage& QSGContext2D::toImage() const { Q_D(const QSGContext2D); return d->cachedImage; } bool QSGContext2D::requireCachedImage() const { Q_D(const QSGContext2D); return d->waitingForPainting; } void QSGContext2D::setCachedImage(const QImage& image) { Q_D(QSGContext2D); #ifndef QSGCANVASITEM_PAINTING_ON_IMAGE if (d->waitingForPainting) { d->cachedImage = image; d->waitingForPainting = false; } #endif if (inWorkerThread()) { d->agent->setCachedImage(image); } } void QSGContext2D::clear() { Q_D(QSGContext2D); d->clear(); } void QSGContext2D::reset() { Q_D(QSGContext2D); d->reset(); } void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy) { Q_D(QSGContext2D); if (!imgUrl.isEmpty()) d->drawImage(imgUrl, dx, dy); } void QSGContext2D::drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh) { Q_D(QSGContext2D); if (!imgUrl.isEmpty()) d->drawImage(imgUrl, sx, sy, sw, sh, dx, dy, dw, dh); } void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy, qreal dw, qreal dh) { Q_D(QSGContext2D); if (!imgUrl.isEmpty()) d->drawImage(imgUrl, dx, dy, dw, dh); } void QSGContext2D::setSize(int width, int height) { QSize size(width, height); setSize(size); } void QSGContext2D::setSize(const QSize &size) { Q_D(QSGContext2D); if (d->size == size) return; d->setSize(size); emit changed(); } QSize QSGContext2D::size() const { Q_D(const QSGContext2D); return d->size; } QMatrix QSGContext2D::worldMatrix() const { Q_D(const QSGContext2D); return d->state.matrix; } QT_END_NAMESPACE