diff options
Diffstat (limited to 'src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp')
-rw-r--r-- | src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp new file mode 100644 index 0000000000..476e7e2cf8 --- /dev/null +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2dcommandbuffer_p.h" +#include "qquickcanvasitem_p.h" +#include <qdeclarative.h> +#include <QtCore/QMutex> + +#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY)) + +QT_BEGIN_NAMESPACE + +void qt_image_boxblur(QImage& image, int radius, bool quality); + +static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QImage shadowImg(image.width() + blur + qAbs(offsetX), + image.height() + blur + qAbs(offsetY), + QImage::Format_ARGB32_Premultiplied); + shadowImg.fill(0); + QPainter tmpPainter(&shadowImg); + tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); + qreal shadowX = offsetX > 0? offsetX : 0; + qreal shadowY = offsetY > 0? offsetY : 0; + + tmpPainter.drawImage(shadowX, shadowY, image); + tmpPainter.end(); + + if (blur > 0) + qt_image_boxblur(shadowImg, blur/2, true); + + // blacken the image with shadow color... + tmpPainter.begin(&shadowImg); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(shadowImg.rect(), color); + tmpPainter.end(); + return shadowImg; +} + +static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = shadowRect; + r.moveTo(0, 0); + + QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied); + QPainter tp; + tp.begin(&shadowImage); + tp.fillRect(r, p->brush()); + tp.end(); + shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color); + + qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0); + qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0); + + p->drawImage(dx, dy, shadowImage); + p->fillRect(shadowRect, p->brush()); +} + +static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = path.boundingRect(); + QImage img(r.size().width() + r.left() + 1, + r.size().height() + r.top() + 1, + QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter tp(&img); + tp.fillPath(path.translated(0, 0), p->brush()); + tp.end(); + + QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); + qreal dx = r.left() + (offsetX < 0? offsetX:0); + qreal dy = r.top() + (offsetY < 0? offsetY:0); + + p->drawImage(dx, dy, shadowImage); + p->fillPath(path, p->brush()); +} + +static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = path.boundingRect(); + QImage img(r.size().width() + r.left() + 1, + r.size().height() + r.top() + 1, + QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter tp(&img); + tp.strokePath(path, p->pen()); + tp.end(); + + QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); + qreal dx = r.left() + (offsetX < 0? offsetX:0); + qreal dy = r.top() + (offsetY < 0? offsetY:0); + p->drawImage(dx, dy, shadowImage); + p->strokePath(path, p->pen()); +} +static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY) +{ + // Patterns must be painted so that the top left of the first image is anchored at + // the origin of the coordinate space + if (!image.isNull()) { + int w = image.width(); + int h = image.height(); + int startX, startY; + QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height())); + + // startX, startY is the coordinate of the first image we need to put on the left-top of the rect + if (repeatX && repeatY) { + // repeat + // startX, startY is at the left top side of the left-top of the rect + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else { + if (!repeatX && !repeatY) { + // no-repeat + // only draw the image once at orgin once, check if need to draw + QRect imageRect(0, 0, w, h); + if (imageRect.intersects(r)) { + startX = 0; + startY = 0; + } else + return; + } else if (repeatX && !repeatY) { + // repeat-x + // startY is fixed, but startX change based on the left-top of the rect + QRect imageRect(r.x(), 0, r.width(), h); + if (imageRect.intersects(r)) { + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = 0; + } else + return; + } else { + // repeat-y + // startX is fixed, but startY change based on the left-top of the rect + QRect imageRect(0, r.y(), w, r.height()); + if (imageRect.intersects(r)) { + startX = 0; + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else + return; + } + } + + int x = startX; + int y = startY; + do { + // repeat Y + do { + // repeat X + QRect imageRect(x, y, w, h); + QRect intersectRect = imageRect.intersected(r); + QPoint destStart(intersectRect.x(), intersectRect.y()); + QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height()); + + p->drawImage(destStart, image, sourceRect); + x += w; + } while (repeatX && x < r.x() + r.width()); + x = startX; + y += h; + } while (repeatY && y < r.y() + r.height()); + } +} + +QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state) +{ + QPen pen; + pen.setWidthF(state.lineWidth); + pen.setCapStyle(state.lineCap); + pen.setJoinStyle(state.lineJoin); + pen.setMiterLimit(state.miterLimit); + pen.setBrush(state.strokeStyle); + return pen; +} + +void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickContext2D::State& state, const QPen& pen) +{ + p->setTransform(state.matrix * p->transform()); + + if (pen != p->pen()) + p->setPen(pen); + + if (state.fillStyle != p->brush()) + p->setBrush(state.fillStyle); + + if (state.font != p->font()) + p->setFont(state.font); + + if (state.globalAlpha != p->opacity()) { + p->setOpacity(state.globalAlpha); + } + + if (state.globalCompositeOperation != p->compositionMode()) + p->setCompositionMode(state.globalCompositeOperation); +} + +void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& state) +{ + if (!p) + return; + + reset(); + + QTransform originMatrix = p->transform(); + + QPen pen = makePen(state); + setPainterState(p, state, pen); + + while (hasNext()) { + QQuickContext2D::PaintCommand cmd = takeNextCommand(); + switch (cmd) { + case QQuickContext2D::UpdateMatrix: + { + state.matrix = takeMatrix(); + p->setTransform(state.matrix * originMatrix); + break; + } + case QQuickContext2D::ClearRect: + { + QPainter::CompositionMode cm = p->compositionMode(); + qreal alpha = p->opacity(); + p->setCompositionMode(QPainter::CompositionMode_Source); + p->setOpacity(0); + p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0))); + p->setCompositionMode(cm); + p->setOpacity(alpha); + break; + } + case QQuickContext2D::FillRect: + { + QRectF r = takeRect(); + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->fillRect(r, p->brush()); + break; + } + case QQuickContext2D::ShadowColor: + { + state.shadowColor = takeColor(); + break; + } + case QQuickContext2D::ShadowBlur: + { + state.shadowBlur = takeShadowBlur(); + break; + } + case QQuickContext2D::ShadowOffsetX: + { + state.shadowOffsetX = takeShadowOffsetX(); + break; + } + case QQuickContext2D::ShadowOffsetY: + { + state.shadowOffsetY = takeShadowOffsetY(); + break; + } + case QQuickContext2D::FillStyle: + { + state.fillStyle = takeFillStyle(); + state.fillPatternRepeatX = takeBool(); + state.fillPatternRepeatY = takeBool(); + p->setBrush(state.fillStyle); + break; + } + case QQuickContext2D::StrokeStyle: + { + state.strokeStyle = takeStrokeStyle(); + state.strokePatternRepeatX = takeBool(); + state.strokePatternRepeatY = takeBool(); + pen.setBrush(state.strokeStyle); + p->setPen(pen); + break; + } + case QQuickContext2D::LineWidth: + { + state.lineWidth = takeLineWidth(); + pen.setWidth(state.lineWidth); + p->setPen(pen); + break; + } + case QQuickContext2D::LineCap: + { + state.lineCap = takeLineCap(); + pen.setCapStyle(state.lineCap); + p->setPen(pen); + break; + } + case QQuickContext2D::LineJoin: + { + state.lineJoin = takeLineJoin(); + pen.setJoinStyle(state.lineJoin); + p->setPen(pen); + break; + } + case QQuickContext2D::MiterLimit: + { + state.miterLimit = takeMiterLimit(); + pen.setMiterLimit(state.miterLimit); + p->setPen(pen); + break; + } + case QQuickContext2D::TextAlign: + case QQuickContext2D::TextBaseline: + break; + case QQuickContext2D::Fill: + { + QPainterPath path = takePath(); + path.closeSubpath(); + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->fillPath(path, p->brush()); + break; + } + case QQuickContext2D::Stroke: + { + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->strokePath(takePath(), p->pen()); + break; + } + case QQuickContext2D::Clip: + { + state.clipPath = takePath(); + p->setClipping(true); + p->setClipPath(state.clipPath); + break; + } + case QQuickContext2D::GlobalAlpha: + { + state.globalAlpha = takeGlobalAlpha(); + p->setOpacity(state.globalAlpha); + break; + } + case QQuickContext2D::GlobalCompositeOperation: + { + state.globalCompositeOperation = takeGlobalCompositeOperation(); + p->setCompositionMode(state.globalCompositeOperation); + break; + } + case QQuickContext2D::DrawImage: + { + qreal sx = takeReal(); + qreal sy = takeReal(); + qreal sw = takeReal(); + qreal sh = takeReal(); + qreal dx = takeReal(); + qreal dy = takeReal(); + qreal dw = takeReal(); + qreal dh = takeReal(); + QImage image = takeImage(); + + if (!image.isNull()) { + if (sw == -1 || sh == -1) { + sw = image.width(); + sh = image.height(); + } + if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height()) + image = image.copy(sx, sy, sw, sh); + + image = image.scaled(dw, dh); + + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) { + QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0); + qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0); + p->drawImage(shadow_dx, shadow_dy, shadow); + } + p->drawImage(dx, dy, image); + } + break; + } + case QQuickContext2D::GetImageData: + { + //TODO: + break; + } + default: + break; + } + } + + p->end(); +} + +QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer() + : cmdIdx(0) + , intIdx(0) + , boolIdx(0) + , realIdx(0) + , colorIdx(0) + , matrixIdx(0) + , brushIdx(0) + , pathIdx(0) + , imageIdx(0) +{ +} + + +QQuickContext2DCommandBuffer::~QQuickContext2DCommandBuffer() +{ +} + +void QQuickContext2DCommandBuffer::clear() +{ + commands.clear(); + ints.clear(); + bools.clear(); + reals.clear(); + colors.clear(); + matrixes.clear(); + brushes.clear(); + pathes.clear(); + images.clear(); + reset(); +} + +void QQuickContext2DCommandBuffer::reset() +{ + cmdIdx = 0; + intIdx = 0; + boolIdx = 0; + realIdx = 0; + colorIdx = 0; + matrixIdx = 0; + brushIdx = 0; + pathIdx = 0; + imageIdx = 0; +} + +QT_END_NAMESPACE + |