Files correlati : cg0.exe cg0700a.msk cg0700b.msk cg3.exe cg4.exe Bug : Commento: Merge 1.0 libraries
825 lines
22 KiB
C++
825 lines
22 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the examples of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
** You may use this file under the terms of the BSD license as follows:
|
|
**
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions are
|
|
** met:
|
|
** * Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** * Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in
|
|
** the documentation and/or other materials provided with the
|
|
** distribution.
|
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
|
** contributors may be used to endorse or promote products derived
|
|
** from this software without specific prior written permission.
|
|
**
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "context2d.h"
|
|
|
|
#include <QVariant>
|
|
|
|
#include <math.h>
|
|
static const double Q_PI = 3.14159265358979323846; // pi
|
|
|
|
#define DEGREES(t) ((t) * 180.0 / Q_PI)
|
|
|
|
#define qClamp(val, min, max) qMin(qMax(val, min), max)
|
|
static QList<qreal> parseNumbersList(QString::const_iterator &itr)
|
|
{
|
|
QList<qreal> points;
|
|
QString temp;
|
|
while ((*itr).isSpace())
|
|
++itr;
|
|
while ((*itr).isNumber() ||
|
|
(*itr) == '-' || (*itr) == '+' || (*itr) == '.') {
|
|
temp = QString();
|
|
|
|
if ((*itr) == '-')
|
|
temp += *itr++;
|
|
else if ((*itr) == '+')
|
|
temp += *itr++;
|
|
while ((*itr).isDigit())
|
|
temp += *itr++;
|
|
if ((*itr) == '.')
|
|
temp += *itr++;
|
|
while ((*itr).isDigit())
|
|
temp += *itr++;
|
|
while ((*itr).isSpace())
|
|
++itr;
|
|
if ((*itr) == ',')
|
|
++itr;
|
|
points.append(temp.toDouble());
|
|
//eat spaces
|
|
while ((*itr).isSpace())
|
|
++itr;
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
QColor colorFromString(const QString &name)
|
|
{
|
|
QString::const_iterator itr = name.constBegin();
|
|
QList<qreal> compo;
|
|
if (name.startsWith("rgba(")) {
|
|
++itr; ++itr; ++itr; ++itr; ++itr;
|
|
compo = parseNumbersList(itr);
|
|
if (compo.size() != 4) {
|
|
return QColor();
|
|
}
|
|
//alpha seems to be always between 0-1
|
|
compo[3] *= 255;
|
|
return QColor((int)compo[0], (int)compo[1],
|
|
(int)compo[2], (int)compo[3]);
|
|
} else if (name.startsWith("rgb(")) {
|
|
++itr; ++itr; ++itr; ++itr;
|
|
compo = parseNumbersList(itr);
|
|
if (compo.size() != 3) {
|
|
return QColor();
|
|
}
|
|
return QColor((int)qClamp(compo[0], qreal(0), qreal(255)),
|
|
(int)qClamp(compo[1], qreal(0), qreal(255)),
|
|
(int)qClamp(compo[2], qreal(0), qreal(255)));
|
|
} else {
|
|
//QRgb color;
|
|
//CSSParser::parseColor(name, color);
|
|
return QColor(name);
|
|
}
|
|
}
|
|
|
|
|
|
static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
|
|
{
|
|
if ( compositeOperator == "source-over" ) {
|
|
return QPainter::CompositionMode_SourceOver;
|
|
} else if ( compositeOperator == "source-out" ) {
|
|
return QPainter::CompositionMode_SourceOut;
|
|
} else if ( compositeOperator == "source-in" ) {
|
|
return QPainter::CompositionMode_SourceIn;
|
|
} else if ( compositeOperator == "source-atop" ) {
|
|
return QPainter::CompositionMode_SourceAtop;
|
|
} else if ( compositeOperator == "destination-atop" ) {
|
|
return QPainter::CompositionMode_DestinationAtop;
|
|
} else if ( compositeOperator == "destination-in" ) {
|
|
return QPainter::CompositionMode_DestinationIn;
|
|
} else if ( compositeOperator == "destination-out" ) {
|
|
return QPainter::CompositionMode_DestinationOut;
|
|
} else if ( compositeOperator == "destination-over" ) {
|
|
return QPainter::CompositionMode_DestinationOver;
|
|
} else if ( compositeOperator == "darker" ) {
|
|
return QPainter::CompositionMode_SourceOver;
|
|
} else if ( compositeOperator == "lighter" ) {
|
|
return QPainter::CompositionMode_SourceOver;
|
|
} else if ( compositeOperator == "copy" ) {
|
|
return QPainter::CompositionMode_Source;
|
|
} else if ( compositeOperator == "xor" ) {
|
|
return QPainter::CompositionMode_Xor;
|
|
}
|
|
|
|
return QPainter::CompositionMode_SourceOver;
|
|
}
|
|
|
|
static QString compositeOperatorToString(QPainter::CompositionMode op)
|
|
{
|
|
switch (op) {
|
|
case QPainter::CompositionMode_SourceOver:
|
|
return "source-over";
|
|
case QPainter::CompositionMode_DestinationOver:
|
|
return "destination-over";
|
|
case QPainter::CompositionMode_Clear:
|
|
return "clear";
|
|
case QPainter::CompositionMode_Source:
|
|
return "source";
|
|
case QPainter::CompositionMode_Destination:
|
|
return "destination";
|
|
case QPainter::CompositionMode_SourceIn:
|
|
return "source-in";
|
|
case QPainter::CompositionMode_DestinationIn:
|
|
return "destination-in";
|
|
case QPainter::CompositionMode_SourceOut:
|
|
return "source-out";
|
|
case QPainter::CompositionMode_DestinationOut:
|
|
return "destination-out";
|
|
case QPainter::CompositionMode_SourceAtop:
|
|
return "source-atop";
|
|
case QPainter::CompositionMode_DestinationAtop:
|
|
return "destination-atop";
|
|
case QPainter::CompositionMode_Xor:
|
|
return "xor";
|
|
case QPainter::CompositionMode_Plus:
|
|
return "plus";
|
|
case QPainter::CompositionMode_Multiply:
|
|
return "multiply";
|
|
case QPainter::CompositionMode_Screen:
|
|
return "screen";
|
|
case QPainter::CompositionMode_Overlay:
|
|
return "overlay";
|
|
case QPainter::CompositionMode_Darken:
|
|
return "darken";
|
|
case QPainter::CompositionMode_Lighten:
|
|
return "lighten";
|
|
case QPainter::CompositionMode_ColorDodge:
|
|
return "color-dodge";
|
|
case QPainter::CompositionMode_ColorBurn:
|
|
return "color-burn";
|
|
case QPainter::CompositionMode_HardLight:
|
|
return "hard-light";
|
|
case QPainter::CompositionMode_SoftLight:
|
|
return "soft-light";
|
|
case QPainter::CompositionMode_Difference:
|
|
return "difference";
|
|
case QPainter::CompositionMode_Exclusion:
|
|
return "exclusion";
|
|
default:
|
|
break;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void Context2D::save()
|
|
{
|
|
m_stateStack.push(m_state);
|
|
}
|
|
|
|
|
|
void Context2D::restore()
|
|
{
|
|
if (!m_stateStack.isEmpty()) {
|
|
m_state = m_stateStack.pop();
|
|
m_state.flags = AllIsFullOfDirt;
|
|
}
|
|
}
|
|
|
|
|
|
void Context2D::scale(qreal x, qreal y)
|
|
{
|
|
m_state.matrix.scale(x, y);
|
|
m_state.flags |= DirtyTransformationMatrix;
|
|
}
|
|
|
|
|
|
void Context2D::rotate(qreal angle)
|
|
{
|
|
m_state.matrix.rotate(DEGREES(angle));
|
|
m_state.flags |= DirtyTransformationMatrix;
|
|
}
|
|
|
|
|
|
void Context2D::translate(qreal x, qreal y)
|
|
{
|
|
m_state.matrix.translate(x, y);
|
|
m_state.flags |= DirtyTransformationMatrix;
|
|
}
|
|
|
|
|
|
void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
|
|
qreal dx, qreal dy)
|
|
{
|
|
QMatrix mat(m11, m12,
|
|
m21, m22,
|
|
dx, dy);
|
|
m_state.matrix *= mat;
|
|
m_state.flags |= DirtyTransformationMatrix;
|
|
}
|
|
|
|
|
|
void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
|
|
qreal dx, qreal dy)
|
|
{
|
|
QMatrix mat(m11, m12,
|
|
m21, m22,
|
|
dx, dy);
|
|
m_state.matrix = mat;
|
|
m_state.flags |= DirtyTransformationMatrix;
|
|
}
|
|
|
|
|
|
QString Context2D::globalCompositeOperation() const
|
|
{
|
|
return compositeOperatorToString(m_state.globalCompositeOperation);
|
|
}
|
|
|
|
void Context2D::setGlobalCompositeOperation(const QString &op)
|
|
{
|
|
QPainter::CompositionMode mode =
|
|
compositeOperatorFromString(op);
|
|
m_state.globalCompositeOperation = mode;
|
|
m_state.flags |= DirtyGlobalCompositeOperation;
|
|
}
|
|
|
|
QVariant Context2D::strokeStyle() const
|
|
{
|
|
return m_state.strokeStyle;
|
|
}
|
|
|
|
void Context2D::setStrokeStyle(const QVariant &style)
|
|
{
|
|
if (style.canConvert<CanvasGradient>()) {
|
|
CanvasGradient cg = qvariant_cast<CanvasGradient>(style);
|
|
m_state.strokeStyle = cg.value;
|
|
} else {
|
|
QColor color = colorFromString(style.toString());
|
|
m_state.strokeStyle = color;
|
|
}
|
|
m_state.flags |= DirtyStrokeStyle;
|
|
}
|
|
|
|
QVariant Context2D::fillStyle() const
|
|
{
|
|
return m_state.fillStyle;
|
|
}
|
|
|
|
//! [3]
|
|
void Context2D::setFillStyle(const QVariant &style)
|
|
{
|
|
if (style.canConvert<CanvasGradient>()) {
|
|
CanvasGradient cg = qvariant_cast<CanvasGradient>(style);
|
|
m_state.fillStyle = cg.value;
|
|
} else {
|
|
QColor color = colorFromString(style.toString());
|
|
m_state.fillStyle = color;
|
|
}
|
|
m_state.flags |= DirtyFillStyle;
|
|
}
|
|
//! [3]
|
|
|
|
qreal Context2D::globalAlpha() const
|
|
{
|
|
return m_state.globalAlpha;
|
|
}
|
|
|
|
void Context2D::setGlobalAlpha(qreal alpha)
|
|
{
|
|
m_state.globalAlpha = alpha;
|
|
m_state.flags |= DirtyGlobalAlpha;
|
|
}
|
|
|
|
|
|
CanvasGradient Context2D::createLinearGradient(qreal x0, qreal y0,
|
|
qreal x1, qreal y1)
|
|
{
|
|
QLinearGradient g(x0, y0, x1, y1);
|
|
return CanvasGradient(g);
|
|
}
|
|
|
|
|
|
CanvasGradient Context2D::createRadialGradient(qreal x0, qreal y0,
|
|
qreal r0, qreal x1,
|
|
qreal y1, qreal r1)
|
|
{
|
|
QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
|
|
return CanvasGradient(g);
|
|
}
|
|
|
|
qreal Context2D::lineWidth() const
|
|
{
|
|
return m_state.lineWidth;
|
|
}
|
|
|
|
void Context2D::setLineWidth(qreal w)
|
|
{
|
|
m_state.lineWidth = w;
|
|
m_state.flags |= DirtyLineWidth;
|
|
}
|
|
|
|
//! [0]
|
|
QString Context2D::lineCap() const
|
|
{
|
|
switch (m_state.lineCap) {
|
|
case Qt::FlatCap:
|
|
return "butt";
|
|
case Qt::SquareCap:
|
|
return "square";
|
|
case Qt::RoundCap:
|
|
return "round";
|
|
default: ;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void Context2D::setLineCap(const QString &capString)
|
|
{
|
|
Qt::PenCapStyle style;
|
|
if (capString == "round")
|
|
style = Qt::RoundCap;
|
|
else if (capString == "square")
|
|
style = Qt::SquareCap;
|
|
else //if (capString == "butt")
|
|
style = Qt::FlatCap;
|
|
m_state.lineCap = style;
|
|
m_state.flags |= DirtyLineCap;
|
|
}
|
|
//! [0]
|
|
|
|
QString Context2D::lineJoin() const
|
|
{
|
|
switch (m_state.lineJoin) {
|
|
case Qt::RoundJoin:
|
|
return "round";
|
|
case Qt::BevelJoin:
|
|
return "bevel";
|
|
case Qt::MiterJoin:
|
|
return "miter";
|
|
default: ;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void Context2D::setLineJoin(const QString &joinString)
|
|
{
|
|
Qt::PenJoinStyle style;
|
|
if (joinString == "round")
|
|
style = Qt::RoundJoin;
|
|
else if (joinString == "bevel")
|
|
style = Qt::BevelJoin;
|
|
else //if (joinString == "miter")
|
|
style = Qt::MiterJoin;
|
|
m_state.lineJoin = style;
|
|
m_state.flags |= DirtyLineJoin;
|
|
}
|
|
|
|
qreal Context2D::miterLimit() const
|
|
{
|
|
return m_state.miterLimit;
|
|
}
|
|
|
|
void Context2D::setMiterLimit(qreal m)
|
|
{
|
|
m_state.miterLimit = m;
|
|
m_state.flags |= DirtyMiterLimit;
|
|
}
|
|
|
|
void Context2D::setShadowOffsetX(qreal x)
|
|
{
|
|
m_state.shadowOffsetX = x;
|
|
m_state.flags |= DirtyShadowOffsetX;
|
|
}
|
|
|
|
void Context2D::setShadowOffsetY(qreal y)
|
|
{
|
|
m_state.shadowOffsetY = y;
|
|
m_state.flags |= DirtyShadowOffsetY;
|
|
}
|
|
|
|
void Context2D::setShadowBlur(qreal b)
|
|
{
|
|
m_state.shadowBlur = b;
|
|
m_state.flags |= DirtyShadowBlur;
|
|
}
|
|
|
|
void Context2D::setShadowColor(const QString &str)
|
|
{
|
|
m_state.shadowColor = colorFromString(str);
|
|
m_state.flags |= DirtyShadowColor;
|
|
}
|
|
|
|
qreal Context2D::shadowOffsetX() const
|
|
{
|
|
return m_state.shadowOffsetX;
|
|
}
|
|
|
|
qreal Context2D::shadowOffsetY() const
|
|
{
|
|
return m_state.shadowOffsetY;
|
|
}
|
|
|
|
|
|
qreal Context2D::shadowBlur() const
|
|
{
|
|
return m_state.shadowBlur;
|
|
}
|
|
|
|
|
|
QString Context2D::shadowColor() const
|
|
{
|
|
return m_state.shadowColor.name();
|
|
}
|
|
|
|
|
|
void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h)
|
|
{
|
|
beginPainting();
|
|
m_painter.save();
|
|
m_painter.setMatrix(m_state.matrix, false);
|
|
m_painter.setCompositionMode(QPainter::CompositionMode_Source);
|
|
m_painter.fillRect(QRectF(x, y, w, h), QColor(0, 0, 0, 0));
|
|
m_painter.restore();
|
|
scheduleChange();
|
|
}
|
|
|
|
|
|
//! [1]
|
|
void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h)
|
|
{
|
|
beginPainting();
|
|
m_painter.save();
|
|
m_painter.setMatrix(m_state.matrix, false);
|
|
m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush());
|
|
m_painter.restore();
|
|
scheduleChange();
|
|
}
|
|
//! [1]
|
|
|
|
|
|
void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
|
|
{
|
|
QPainterPath path;
|
|
path.addRect(x, y, w, h);
|
|
beginPainting();
|
|
m_painter.save();
|
|
m_painter.setMatrix(m_state.matrix, false);
|
|
m_painter.strokePath(path, m_painter.pen());
|
|
m_painter.restore();
|
|
scheduleChange();
|
|
}
|
|
|
|
|
|
void Context2D::beginPath()
|
|
{
|
|
m_path = QPainterPath();
|
|
}
|
|
|
|
|
|
void Context2D::closePath()
|
|
{
|
|
m_path.closeSubpath();
|
|
}
|
|
|
|
|
|
void Context2D::moveTo(qreal x, qreal y)
|
|
{
|
|
QPointF pt = m_state.matrix.map(QPointF(x, y));
|
|
m_path.moveTo(pt);
|
|
}
|
|
|
|
|
|
void Context2D::lineTo(qreal x, qreal y)
|
|
{
|
|
QPointF pt = m_state.matrix.map(QPointF(x, y));
|
|
m_path.lineTo(pt);
|
|
}
|
|
|
|
|
|
void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
|
|
{
|
|
QPointF cp = m_state.matrix.map(QPointF(cpx, cpy));
|
|
QPointF xy = m_state.matrix.map(QPointF(x, y));
|
|
m_path.quadTo(cp, xy);
|
|
}
|
|
|
|
|
|
void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y,
|
|
qreal cp2x, qreal cp2y, qreal x, qreal y)
|
|
{
|
|
QPointF cp1 = m_state.matrix.map(QPointF(cp1x, cp1y));
|
|
QPointF cp2 = m_state.matrix.map(QPointF(cp2x, cp2y));
|
|
QPointF end = m_state.matrix.map(QPointF(x, y));
|
|
m_path.cubicTo(cp1, cp2, end);
|
|
}
|
|
|
|
|
|
void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
|
|
{
|
|
//FIXME: this is surely busted
|
|
QPointF st = m_state.matrix.map(QPointF(x1, y1));
|
|
QPointF end = m_state.matrix.map(QPointF(x2, y2));
|
|
m_path.arcTo(st.x(), st.y(),
|
|
end.x()-st.x(), end.y()-st.y(),
|
|
radius, 90);
|
|
}
|
|
|
|
|
|
void Context2D::rect(qreal x, qreal y, qreal w, qreal h)
|
|
{
|
|
QPainterPath path; path.addRect(x, y, w, h);
|
|
path = m_state.matrix.map(path);
|
|
m_path.addPath(path);
|
|
}
|
|
|
|
void Context2D::arc(qreal xc, qreal yc, qreal radius,
|
|
qreal sar, qreal ear,
|
|
bool anticlockwise)
|
|
{
|
|
//### 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;
|
|
}
|
|
|
|
QPainterPath path;
|
|
path.moveTo(QPointF(xc + radius * cos(sar),
|
|
yc - radius * sin(sar)));
|
|
|
|
path.arcTo(xs, ys, width, height, sa, span);
|
|
path = m_state.matrix.map(path);
|
|
m_path.addPath(path);
|
|
}
|
|
|
|
|
|
void Context2D::fill()
|
|
{
|
|
beginPainting();
|
|
m_painter.fillPath(m_path, m_painter.brush());
|
|
scheduleChange();
|
|
}
|
|
|
|
|
|
void Context2D::stroke()
|
|
{
|
|
beginPainting();
|
|
m_painter.save();
|
|
m_painter.setMatrix(m_state.matrix, false);
|
|
QPainterPath tmp = m_state.matrix.inverted().map(m_path);
|
|
m_painter.strokePath(tmp, m_painter.pen());
|
|
m_painter.restore();
|
|
scheduleChange();
|
|
}
|
|
|
|
|
|
void Context2D::clip()
|
|
{
|
|
m_state.clipPath = m_path;
|
|
m_state.flags |= DirtyClippingRegion;
|
|
}
|
|
|
|
|
|
bool Context2D::isPointInPath(qreal x, qreal y) const
|
|
{
|
|
return m_path.contains(QPointF(x, y));
|
|
}
|
|
|
|
|
|
ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
|
|
{
|
|
Q_UNUSED(sx);
|
|
Q_UNUSED(sy);
|
|
Q_UNUSED(sw);
|
|
Q_UNUSED(sh);
|
|
return ImageData();
|
|
}
|
|
|
|
|
|
void Context2D::putImageData(ImageData image, qreal dx, qreal dy)
|
|
{
|
|
Q_UNUSED(image);
|
|
Q_UNUSED(dx);
|
|
Q_UNUSED(dy);
|
|
}
|
|
|
|
Context2D::Context2D(QObject *parent)
|
|
: QObject(parent), m_changeTimerId(-1)
|
|
{
|
|
reset();
|
|
}
|
|
|
|
const QImage &Context2D::endPainting()
|
|
{
|
|
if (m_painter.isActive())
|
|
m_painter.end();
|
|
return m_image;
|
|
}
|
|
|
|
void Context2D::beginPainting()
|
|
{
|
|
if (!m_painter.isActive()) {
|
|
m_painter.begin(&m_image);
|
|
m_painter.setRenderHint(QPainter::Antialiasing);
|
|
if (!m_state.clipPath.isEmpty())
|
|
m_painter.setClipPath(m_state.clipPath);
|
|
m_painter.setBrush(m_state.fillStyle);
|
|
m_painter.setOpacity(m_state.globalAlpha);
|
|
QPen pen;
|
|
pen.setBrush(m_state.strokeStyle);
|
|
if (pen.style() == Qt::NoPen)
|
|
pen.setStyle(Qt::SolidLine);
|
|
pen.setCapStyle(m_state.lineCap);
|
|
pen.setJoinStyle(m_state.lineJoin);
|
|
pen.setWidthF(m_state.lineWidth);
|
|
pen.setMiterLimit(m_state.miterLimit);
|
|
m_painter.setPen(pen);
|
|
} else {
|
|
if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty())
|
|
m_painter.setClipPath(m_state.clipPath);
|
|
if (m_state.flags & DirtyFillStyle)
|
|
m_painter.setBrush(m_state.fillStyle);
|
|
if (m_state.flags & DirtyGlobalAlpha)
|
|
m_painter.setOpacity(m_state.globalAlpha);
|
|
if (m_state.flags & DirtyGlobalCompositeOperation)
|
|
m_painter.setCompositionMode(m_state.globalCompositeOperation);
|
|
if (m_state.flags & MDirtyPen) {
|
|
QPen pen = m_painter.pen();
|
|
if (m_state.flags & DirtyStrokeStyle)
|
|
pen.setBrush(m_state.strokeStyle);
|
|
if (m_state.flags & DirtyLineWidth)
|
|
pen.setWidthF(m_state.lineWidth);
|
|
if (m_state.flags & DirtyLineCap)
|
|
pen.setCapStyle(m_state.lineCap);
|
|
if (m_state.flags & DirtyLineJoin)
|
|
pen.setJoinStyle(m_state.lineJoin);
|
|
if (m_state.flags & DirtyMiterLimit)
|
|
pen.setMiterLimit(m_state.miterLimit);
|
|
m_painter.setPen(pen);
|
|
}
|
|
m_state.flags = 0;
|
|
}
|
|
}
|
|
|
|
void Context2D::clear()
|
|
{
|
|
endPainting();
|
|
m_image.fill(qRgba(0,0,0,0));
|
|
scheduleChange();
|
|
}
|
|
|
|
void Context2D::reset()
|
|
{
|
|
m_stateStack.clear();
|
|
m_state.matrix = QMatrix();
|
|
m_state.clipPath = QPainterPath();
|
|
m_state.globalAlpha = 1.0;
|
|
m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
|
|
m_state.strokeStyle = Qt::black;
|
|
m_state.fillStyle = Qt::black;
|
|
m_state.lineWidth = 1;
|
|
m_state.lineCap = Qt::FlatCap;
|
|
m_state.lineJoin = Qt::MiterJoin;
|
|
m_state.miterLimit = 10;
|
|
m_state.shadowOffsetX = 0;
|
|
m_state.shadowOffsetY = 0;
|
|
m_state.shadowBlur = 0;
|
|
m_state.shadowColor = qRgba(0, 0, 0, 0);
|
|
m_state.flags = AllIsFullOfDirt;
|
|
clear();
|
|
}
|
|
|
|
void Context2D::setSize(int width, int height)
|
|
{
|
|
endPainting();
|
|
QImage newi(width, height, QImage::Format_ARGB32_Premultiplied);
|
|
newi.fill(qRgba(0,0,0,0));
|
|
QPainter p(&newi);
|
|
p.drawImage(0, 0, m_image);
|
|
p.end();
|
|
m_image = newi;
|
|
scheduleChange();
|
|
}
|
|
|
|
void Context2D::setSize(const QSize &size)
|
|
{
|
|
setSize(size.width(), size.height());
|
|
}
|
|
|
|
QSize Context2D::size() const
|
|
{
|
|
return m_image.size();
|
|
}
|
|
|
|
void Context2D::drawImage(DomImage *image, qreal dx, qreal dy)
|
|
{
|
|
if (!image)
|
|
return;
|
|
if (dx < 0) {
|
|
qreal sx = qAbs(dx);
|
|
qreal sy = qAbs(dy);
|
|
qreal sw = image->width() - sx;
|
|
qreal sh = image->height() - sy;
|
|
|
|
drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh);
|
|
} else {
|
|
beginPainting();
|
|
m_painter.drawImage(QPointF(dx, dy), image->image());
|
|
scheduleChange();
|
|
}
|
|
}
|
|
|
|
void Context2D::drawImage(DomImage *image, qreal dx, qreal dy,
|
|
qreal dw, qreal dh)
|
|
{
|
|
if (!image)
|
|
return;
|
|
beginPainting();
|
|
m_painter.drawImage(QRectF(dx, dy, dw, dh).toRect(), image->image());
|
|
scheduleChange();
|
|
}
|
|
|
|
void Context2D::drawImage(DomImage *image, qreal sx, qreal sy,
|
|
qreal sw, qreal sh, qreal dx, qreal dy,
|
|
qreal dw, qreal dh)
|
|
{
|
|
if (!image)
|
|
return;
|
|
beginPainting();
|
|
m_painter.drawImage(QRectF(dx, dy, dw, dh), image->image(),
|
|
QRectF(sx, sy, sw, sh));
|
|
scheduleChange();
|
|
}
|
|
|
|
//! [2]
|
|
void Context2D::scheduleChange()
|
|
{
|
|
if (m_changeTimerId == -1)
|
|
m_changeTimerId = startTimer(0);
|
|
}
|
|
|
|
void Context2D::timerEvent(QTimerEvent *e)
|
|
{
|
|
if (e->timerId() == m_changeTimerId) {
|
|
killTimer(m_changeTimerId);
|
|
m_changeTimerId = -1;
|
|
emit changed(endPainting());
|
|
} else {
|
|
QObject::timerEvent(e);
|
|
}
|
|
}
|
|
//! [2]
|