examples/script/context2d/context2d.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the examples of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "context2d.h"
       
    43 
       
    44 #include <QVariant>
       
    45 
       
    46 #include <math.h>
       
    47 static const double Q_PI   = 3.14159265358979323846;   // pi
       
    48 
       
    49 #define DEGREES(t) ((t) * 180.0 / Q_PI)
       
    50 
       
    51 #define qClamp(val, min, max) qMin(qMax(val, min), max)
       
    52 static QList<qreal> parseNumbersList(QString::const_iterator &itr)
       
    53 {
       
    54     QList<qreal> points;
       
    55     QString temp;
       
    56     while ((*itr).isSpace())
       
    57         ++itr;
       
    58     while ((*itr).isNumber() ||
       
    59            (*itr) == '-' || (*itr) == '+' || (*itr) == '.') {
       
    60         temp = QString();
       
    61 
       
    62         if ((*itr) == '-')
       
    63             temp += *itr++;
       
    64         else if ((*itr) == '+')
       
    65             temp += *itr++;
       
    66         while ((*itr).isDigit())
       
    67             temp += *itr++;
       
    68         if ((*itr) == '.')
       
    69             temp += *itr++;
       
    70         while ((*itr).isDigit())
       
    71             temp += *itr++;
       
    72         while ((*itr).isSpace())
       
    73             ++itr;
       
    74         if ((*itr) == ',')
       
    75             ++itr;
       
    76         points.append(temp.toDouble());
       
    77         //eat spaces
       
    78         while ((*itr).isSpace())
       
    79             ++itr;
       
    80     }
       
    81 
       
    82     return points;
       
    83 }
       
    84 
       
    85 QColor colorFromString(const QString &name)
       
    86 {
       
    87     QString::const_iterator itr = name.constBegin();
       
    88     QList<qreal> compo;
       
    89     if (name.startsWith("rgba(")) {
       
    90         ++itr; ++itr; ++itr; ++itr; ++itr;
       
    91         compo = parseNumbersList(itr);
       
    92         if (compo.size() != 4) {
       
    93             return QColor();
       
    94         }
       
    95         //alpha seems to be always between 0-1
       
    96         compo[3] *= 255;
       
    97         return QColor((int)compo[0], (int)compo[1],
       
    98                       (int)compo[2], (int)compo[3]);
       
    99     } else if (name.startsWith("rgb(")) {
       
   100         ++itr; ++itr; ++itr; ++itr;
       
   101         compo = parseNumbersList(itr);
       
   102         if (compo.size() != 3) {
       
   103             return QColor();
       
   104         }
       
   105         return QColor((int)qClamp(compo[0], qreal(0), qreal(255)),
       
   106                       (int)qClamp(compo[1], qreal(0), qreal(255)),
       
   107                       (int)qClamp(compo[2], qreal(0), qreal(255)));
       
   108     } else {
       
   109         //QRgb color;
       
   110         //CSSParser::parseColor(name, color);
       
   111         return QColor(name);
       
   112     }
       
   113 }
       
   114 
       
   115 
       
   116 static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
       
   117 {
       
   118     if ( compositeOperator == "source-over" ) {
       
   119         return QPainter::CompositionMode_SourceOver;
       
   120     } else if ( compositeOperator == "source-out" ) {
       
   121         return QPainter::CompositionMode_SourceOut;
       
   122     } else if ( compositeOperator == "source-in" ) {
       
   123         return QPainter::CompositionMode_SourceIn;
       
   124     } else if ( compositeOperator == "source-atop" ) {
       
   125         return QPainter::CompositionMode_SourceAtop;
       
   126     } else if ( compositeOperator == "destination-atop" ) {
       
   127         return QPainter::CompositionMode_DestinationAtop;
       
   128     } else if ( compositeOperator == "destination-in" ) {
       
   129         return QPainter::CompositionMode_DestinationIn;
       
   130     } else if ( compositeOperator == "destination-out" ) {
       
   131         return QPainter::CompositionMode_DestinationOut;
       
   132     } else if ( compositeOperator == "destination-over" ) {
       
   133         return QPainter::CompositionMode_DestinationOver;
       
   134     } else if ( compositeOperator == "darker" ) {
       
   135         return QPainter::CompositionMode_SourceOver;
       
   136     } else if ( compositeOperator == "lighter" ) {
       
   137         return QPainter::CompositionMode_SourceOver;
       
   138     } else if ( compositeOperator == "copy" ) {
       
   139         return QPainter::CompositionMode_Source;
       
   140     } else if ( compositeOperator == "xor" ) {
       
   141         return QPainter::CompositionMode_Xor;
       
   142     }
       
   143 
       
   144     return QPainter::CompositionMode_SourceOver;
       
   145 }
       
   146 
       
   147 static QString compositeOperatorToString(QPainter::CompositionMode op)
       
   148 {
       
   149     switch (op) {
       
   150     case QPainter::CompositionMode_SourceOver:
       
   151         return "source-over";
       
   152     case QPainter::CompositionMode_DestinationOver:
       
   153         return "destination-over";
       
   154     case QPainter::CompositionMode_Clear:
       
   155         return "clear";
       
   156     case QPainter::CompositionMode_Source:
       
   157         return "source";
       
   158     case QPainter::CompositionMode_Destination:
       
   159         return "destination";
       
   160     case QPainter::CompositionMode_SourceIn:
       
   161         return "source-in";
       
   162     case QPainter::CompositionMode_DestinationIn:
       
   163         return "destination-in";
       
   164     case QPainter::CompositionMode_SourceOut:
       
   165         return "source-out";
       
   166     case QPainter::CompositionMode_DestinationOut:
       
   167         return "destination-out";
       
   168     case QPainter::CompositionMode_SourceAtop:
       
   169         return "source-atop";
       
   170     case QPainter::CompositionMode_DestinationAtop:
       
   171         return "destination-atop";
       
   172     case QPainter::CompositionMode_Xor:
       
   173         return "xor";
       
   174     case QPainter::CompositionMode_Plus:
       
   175         return "plus";
       
   176     case QPainter::CompositionMode_Multiply:
       
   177         return "multiply";
       
   178     case QPainter::CompositionMode_Screen:
       
   179         return "screen";
       
   180     case QPainter::CompositionMode_Overlay:
       
   181         return "overlay";
       
   182     case QPainter::CompositionMode_Darken:
       
   183         return "darken";
       
   184     case QPainter::CompositionMode_Lighten:
       
   185         return "lighten";
       
   186     case QPainter::CompositionMode_ColorDodge:
       
   187         return "color-dodge";
       
   188     case QPainter::CompositionMode_ColorBurn:
       
   189         return "color-burn";
       
   190     case QPainter::CompositionMode_HardLight:
       
   191         return "hard-light";
       
   192     case QPainter::CompositionMode_SoftLight:
       
   193         return "soft-light";
       
   194     case QPainter::CompositionMode_Difference:
       
   195         return "difference";
       
   196     case QPainter::CompositionMode_Exclusion:
       
   197         return "exclusion";
       
   198     default:
       
   199         break;
       
   200     }
       
   201     return QString();
       
   202 }
       
   203 
       
   204 void Context2D::save()
       
   205 {
       
   206     m_stateStack.push(m_state);
       
   207 }
       
   208 
       
   209 
       
   210 void Context2D::restore()
       
   211 {
       
   212     if (!m_stateStack.isEmpty()) {
       
   213         m_state = m_stateStack.pop();
       
   214         m_state.flags = AllIsFullOfDirt;
       
   215     }
       
   216 }
       
   217 
       
   218 
       
   219 void Context2D::scale(qreal x, qreal y)
       
   220 {
       
   221     m_state.matrix.scale(x, y);
       
   222     m_state.flags |= DirtyTransformationMatrix;
       
   223 }
       
   224 
       
   225 
       
   226 void Context2D::rotate(qreal angle)
       
   227 {
       
   228     m_state.matrix.rotate(DEGREES(angle));
       
   229     m_state.flags |= DirtyTransformationMatrix;
       
   230 }
       
   231 
       
   232 
       
   233 void Context2D::translate(qreal x, qreal y)
       
   234 {
       
   235     m_state.matrix.translate(x, y);
       
   236     m_state.flags |= DirtyTransformationMatrix;
       
   237 }
       
   238 
       
   239 
       
   240 void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
       
   241                           qreal dx, qreal dy)
       
   242 {
       
   243     QMatrix mat(m11, m12,
       
   244                 m21, m22,
       
   245                 dx, dy);
       
   246     m_state.matrix *= mat;
       
   247     m_state.flags |= DirtyTransformationMatrix;
       
   248 }
       
   249 
       
   250 
       
   251 void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
       
   252                              qreal dx, qreal dy)
       
   253 {
       
   254     QMatrix mat(m11, m12,
       
   255                 m21, m22,
       
   256                 dx, dy);
       
   257     m_state.matrix = mat;
       
   258     m_state.flags |= DirtyTransformationMatrix;
       
   259 }
       
   260 
       
   261 
       
   262 QString Context2D::globalCompositeOperation() const
       
   263 {
       
   264     return compositeOperatorToString(m_state.globalCompositeOperation);
       
   265 }
       
   266 
       
   267 void Context2D::setGlobalCompositeOperation(const QString &op)
       
   268 {
       
   269     QPainter::CompositionMode mode =
       
   270         compositeOperatorFromString(op);
       
   271     m_state.globalCompositeOperation = mode;
       
   272     m_state.flags |= DirtyGlobalCompositeOperation;
       
   273 }
       
   274 
       
   275 QVariant Context2D::strokeStyle() const
       
   276 {
       
   277     return m_state.strokeStyle;
       
   278 }
       
   279 
       
   280 void Context2D::setStrokeStyle(const QVariant &style)
       
   281 {
       
   282     if (qVariantCanConvert<CanvasGradient>(style)) {
       
   283         CanvasGradient cg = qvariant_cast<CanvasGradient>(style);
       
   284         m_state.strokeStyle = cg.value;
       
   285     } else {
       
   286         QColor color = colorFromString(style.toString());
       
   287         m_state.strokeStyle = color;
       
   288     }
       
   289     m_state.flags |= DirtyStrokeStyle;
       
   290 }
       
   291 
       
   292 QVariant Context2D::fillStyle() const
       
   293 {
       
   294     return m_state.fillStyle;
       
   295 }
       
   296 
       
   297 //! [3]
       
   298 void Context2D::setFillStyle(const QVariant &style)
       
   299 {
       
   300     if (qVariantCanConvert<CanvasGradient>(style)) {
       
   301         CanvasGradient cg = qvariant_cast<CanvasGradient>(style);
       
   302         m_state.fillStyle = cg.value;
       
   303     } else {
       
   304         QColor color = colorFromString(style.toString());
       
   305         m_state.fillStyle = color;
       
   306     }
       
   307     m_state.flags |= DirtyFillStyle;
       
   308 }
       
   309 //! [3]
       
   310 
       
   311 qreal Context2D::globalAlpha() const
       
   312 {
       
   313     return m_state.globalAlpha;
       
   314 }
       
   315 
       
   316 void Context2D::setGlobalAlpha(qreal alpha)
       
   317 {
       
   318     m_state.globalAlpha = alpha;
       
   319     m_state.flags |= DirtyGlobalAlpha;
       
   320 }
       
   321 
       
   322 
       
   323 CanvasGradient Context2D::createLinearGradient(qreal x0, qreal y0,
       
   324                                                qreal x1, qreal y1)
       
   325 {
       
   326     QLinearGradient g(x0, y0, x1, y1);
       
   327     return CanvasGradient(g);
       
   328 }
       
   329 
       
   330 
       
   331 CanvasGradient Context2D::createRadialGradient(qreal x0, qreal y0,
       
   332                                                qreal r0, qreal x1,
       
   333                                                qreal y1, qreal r1)
       
   334 {
       
   335     QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
       
   336     return CanvasGradient(g);
       
   337 }
       
   338 
       
   339 qreal Context2D::lineWidth() const
       
   340 {
       
   341     return m_state.lineWidth;
       
   342 }
       
   343 
       
   344 void Context2D::setLineWidth(qreal w)
       
   345 {
       
   346     m_state.lineWidth = w;
       
   347     m_state.flags |= DirtyLineWidth;
       
   348 }
       
   349 
       
   350 //! [0]
       
   351 QString Context2D::lineCap() const
       
   352 {
       
   353     switch (m_state.lineCap) {
       
   354     case Qt::FlatCap:
       
   355         return "butt";
       
   356     case Qt::SquareCap:
       
   357         return "square";
       
   358     case Qt::RoundCap:
       
   359         return "round";
       
   360     default: ;
       
   361     }
       
   362     return QString();
       
   363 }
       
   364 
       
   365 void Context2D::setLineCap(const QString &capString)
       
   366 {
       
   367     Qt::PenCapStyle style;
       
   368     if (capString == "round")
       
   369         style = Qt::RoundCap;
       
   370     else if (capString == "square")
       
   371         style = Qt::SquareCap;
       
   372     else //if (capString == "butt")
       
   373         style = Qt::FlatCap;
       
   374     m_state.lineCap = style;
       
   375     m_state.flags |= DirtyLineCap;
       
   376 }
       
   377 //! [0]
       
   378 
       
   379 QString Context2D::lineJoin() const
       
   380 {
       
   381     switch (m_state.lineJoin) {
       
   382     case Qt::RoundJoin:
       
   383         return "round";
       
   384     case Qt::BevelJoin:
       
   385         return "bevel";
       
   386     case Qt::MiterJoin:
       
   387         return "miter";
       
   388     default: ;
       
   389     }
       
   390     return QString();
       
   391 }
       
   392 
       
   393 void Context2D::setLineJoin(const QString &joinString)
       
   394 {
       
   395     Qt::PenJoinStyle style;
       
   396     if (joinString == "round")
       
   397         style = Qt::RoundJoin;
       
   398     else if (joinString == "bevel")
       
   399         style = Qt::BevelJoin;
       
   400     else //if (joinString == "miter")
       
   401         style = Qt::MiterJoin;
       
   402     m_state.lineJoin = style;
       
   403     m_state.flags |= DirtyLineJoin;
       
   404 }
       
   405 
       
   406 qreal Context2D::miterLimit() const
       
   407 {
       
   408     return m_state.miterLimit;
       
   409 }
       
   410 
       
   411 void Context2D::setMiterLimit(qreal m)
       
   412 {
       
   413     m_state.miterLimit = m;
       
   414     m_state.flags |= DirtyMiterLimit;
       
   415 }
       
   416 
       
   417 void Context2D::setShadowOffsetX(qreal x)
       
   418 {
       
   419     m_state.shadowOffsetX = x;
       
   420     m_state.flags |= DirtyShadowOffsetX;
       
   421 }
       
   422 
       
   423 void Context2D::setShadowOffsetY(qreal y)
       
   424 {
       
   425     m_state.shadowOffsetY = y;
       
   426     m_state.flags |= DirtyShadowOffsetY;
       
   427 }
       
   428 
       
   429 void Context2D::setShadowBlur(qreal b)
       
   430 {
       
   431     m_state.shadowBlur = b;
       
   432     m_state.flags |= DirtyShadowBlur;
       
   433 }
       
   434 
       
   435 void Context2D::setShadowColor(const QString &str)
       
   436 {
       
   437     m_state.shadowColor = colorFromString(str);
       
   438     m_state.flags |= DirtyShadowColor;
       
   439 }
       
   440 
       
   441 qreal Context2D::shadowOffsetX() const
       
   442 {
       
   443     return m_state.shadowOffsetX;
       
   444 }
       
   445 
       
   446 qreal Context2D::shadowOffsetY() const
       
   447 {
       
   448     return m_state.shadowOffsetY;
       
   449 }
       
   450 
       
   451 
       
   452 qreal Context2D::shadowBlur() const
       
   453 {
       
   454     return m_state.shadowBlur;
       
   455 }
       
   456 
       
   457 
       
   458 QString Context2D::shadowColor() const
       
   459 {
       
   460     return m_state.shadowColor.name();
       
   461 }
       
   462 
       
   463 
       
   464 void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h)
       
   465 {
       
   466     beginPainting();
       
   467     m_painter.save();
       
   468     m_painter.setMatrix(m_state.matrix, false);
       
   469     m_painter.setCompositionMode(QPainter::CompositionMode_Source);
       
   470     m_painter.fillRect(QRectF(x, y, w, h), QColor(0, 0, 0, 0));
       
   471     m_painter.restore();
       
   472     scheduleChange();
       
   473 }
       
   474 
       
   475 
       
   476 //! [1]
       
   477 void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h)
       
   478 {
       
   479     beginPainting();
       
   480     m_painter.save();
       
   481     m_painter.setMatrix(m_state.matrix, false);
       
   482     m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush());
       
   483     m_painter.restore();
       
   484     scheduleChange();
       
   485 }
       
   486 //! [1]
       
   487 
       
   488 
       
   489 void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
       
   490 {
       
   491     QPainterPath path;
       
   492     path.addRect(x, y, w, h);
       
   493     beginPainting();
       
   494     m_painter.save();
       
   495     m_painter.setMatrix(m_state.matrix, false);
       
   496     m_painter.strokePath(path, m_painter.pen());
       
   497     m_painter.restore();
       
   498     scheduleChange();
       
   499 }
       
   500 
       
   501 
       
   502 void Context2D::beginPath()
       
   503 {
       
   504     m_path = QPainterPath();
       
   505 }
       
   506 
       
   507 
       
   508 void Context2D::closePath()
       
   509 {
       
   510     m_path.closeSubpath();
       
   511 }
       
   512 
       
   513 
       
   514 void Context2D::moveTo(qreal x, qreal y)
       
   515 {
       
   516     QPointF pt = m_state.matrix.map(QPointF(x, y));
       
   517     m_path.moveTo(pt);
       
   518 }
       
   519 
       
   520 
       
   521 void Context2D::lineTo(qreal x, qreal y)
       
   522 {
       
   523     QPointF pt = m_state.matrix.map(QPointF(x, y));
       
   524     m_path.lineTo(pt);
       
   525 }
       
   526 
       
   527 
       
   528 void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
       
   529 {
       
   530     QPointF cp = m_state.matrix.map(QPointF(cpx, cpy));
       
   531     QPointF xy = m_state.matrix.map(QPointF(x, y));
       
   532     m_path.quadTo(cp, xy);
       
   533 }
       
   534 
       
   535 
       
   536 void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y,
       
   537                               qreal cp2x, qreal cp2y, qreal x, qreal y)
       
   538 {
       
   539     QPointF cp1 = m_state.matrix.map(QPointF(cp1x, cp1y));
       
   540     QPointF cp2 = m_state.matrix.map(QPointF(cp2x, cp2y));
       
   541     QPointF end = m_state.matrix.map(QPointF(x, y));
       
   542     m_path.cubicTo(cp1, cp2, end);
       
   543 }
       
   544 
       
   545 
       
   546 void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
       
   547 {
       
   548     //FIXME: this is surely busted
       
   549     QPointF st  = m_state.matrix.map(QPointF(x1, y1));
       
   550     QPointF end = m_state.matrix.map(QPointF(x2, y2));
       
   551     m_path.arcTo(st.x(), st.y(),
       
   552                  end.x()-st.x(), end.y()-st.y(),
       
   553                  radius, 90);
       
   554 }
       
   555 
       
   556 
       
   557 void Context2D::rect(qreal x, qreal y, qreal w, qreal h)
       
   558 {
       
   559     QPainterPath path; path.addRect(x, y, w, h);
       
   560     path = m_state.matrix.map(path);
       
   561     m_path.addPath(path);
       
   562 }
       
   563 
       
   564 void Context2D::arc(qreal xc, qreal yc, qreal radius,
       
   565                     qreal sar, qreal ear,
       
   566                     bool anticlockwise)
       
   567 {
       
   568     //### HACK
       
   569     // In Qt we don't switch the coordinate system for degrees
       
   570     // and still use the 0,0 as bottom left for degrees so we need
       
   571     // to switch
       
   572     sar = -sar;
       
   573     ear = -ear;
       
   574     anticlockwise = !anticlockwise;
       
   575     //end hack
       
   576 
       
   577     float sa = DEGREES(sar);
       
   578     float ea = DEGREES(ear);
       
   579 
       
   580     double span = 0;
       
   581 
       
   582     double xs     = xc - radius;
       
   583     double ys     = yc - radius;
       
   584     double width  = radius*2;
       
   585     double height = radius*2;
       
   586 
       
   587     if (!anticlockwise && (ea < sa)) {
       
   588         span += 360;
       
   589     } else if (anticlockwise && (sa < ea)) {
       
   590         span -= 360;
       
   591     }
       
   592 
       
   593     //### this is also due to switched coordinate system
       
   594     // we would end up with a 0 span instead of 360
       
   595     if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
       
   596           qFuzzyCompare(qAbs(span), 360))) {
       
   597         span   += ea - sa;
       
   598     }
       
   599 
       
   600     QPainterPath path;
       
   601     path.moveTo(QPointF(xc + radius  * cos(sar),
       
   602                                 yc - radius  * sin(sar)));
       
   603 
       
   604     path.arcTo(xs, ys, width, height, sa, span);
       
   605     path = m_state.matrix.map(path);
       
   606     m_path.addPath(path);
       
   607 }
       
   608 
       
   609 
       
   610 void Context2D::fill()
       
   611 {
       
   612     beginPainting();
       
   613     m_painter.fillPath(m_path, m_painter.brush());
       
   614     scheduleChange();
       
   615 }
       
   616 
       
   617 
       
   618 void Context2D::stroke()
       
   619 {
       
   620     beginPainting();
       
   621     m_painter.save();
       
   622     m_painter.setMatrix(m_state.matrix, false);
       
   623     QPainterPath tmp = m_state.matrix.inverted().map(m_path);
       
   624     m_painter.strokePath(tmp, m_painter.pen());
       
   625     m_painter.restore();
       
   626     scheduleChange();
       
   627 }
       
   628 
       
   629 
       
   630 void Context2D::clip()
       
   631 {
       
   632     m_state.clipPath = m_path;
       
   633     m_state.flags |= DirtyClippingRegion;
       
   634 }
       
   635 
       
   636 
       
   637 bool Context2D::isPointInPath(qreal x, qreal y) const
       
   638 {
       
   639     return m_path.contains(QPointF(x, y));
       
   640 }
       
   641 
       
   642 
       
   643 ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
       
   644 {
       
   645     Q_UNUSED(sx);
       
   646     Q_UNUSED(sy);
       
   647     Q_UNUSED(sw);
       
   648     Q_UNUSED(sh);
       
   649     return ImageData();
       
   650 }
       
   651 
       
   652 
       
   653 void Context2D::putImageData(ImageData image, qreal dx, qreal dy)
       
   654 {
       
   655     Q_UNUSED(image);
       
   656     Q_UNUSED(dx);
       
   657     Q_UNUSED(dy);
       
   658 }
       
   659 
       
   660 Context2D::Context2D(QObject *parent)
       
   661     : QObject(parent), m_changeTimerId(-1)
       
   662 {
       
   663     reset();
       
   664 }
       
   665 
       
   666 const QImage &Context2D::endPainting()
       
   667 {
       
   668     if (m_painter.isActive())
       
   669         m_painter.end();
       
   670     return m_image;
       
   671 }
       
   672 
       
   673 void Context2D::beginPainting()
       
   674 {
       
   675     if (!m_painter.isActive()) {
       
   676         m_painter.begin(&m_image);
       
   677         m_painter.setRenderHint(QPainter::Antialiasing);
       
   678         if (!m_state.clipPath.isEmpty())
       
   679             m_painter.setClipPath(m_state.clipPath);
       
   680         m_painter.setBrush(m_state.fillStyle);
       
   681         m_painter.setOpacity(m_state.globalAlpha);
       
   682         QPen pen;
       
   683         pen.setBrush(m_state.strokeStyle);
       
   684         if (pen.style() == Qt::NoPen)
       
   685             pen.setStyle(Qt::SolidLine);
       
   686         pen.setCapStyle(m_state.lineCap);
       
   687         pen.setJoinStyle(m_state.lineJoin);
       
   688         pen.setWidthF(m_state.lineWidth);
       
   689         pen.setMiterLimit(m_state.miterLimit);
       
   690         m_painter.setPen(pen);
       
   691     } else {
       
   692         if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty())
       
   693             m_painter.setClipPath(m_state.clipPath);
       
   694         if (m_state.flags & DirtyFillStyle)
       
   695             m_painter.setBrush(m_state.fillStyle);
       
   696         if (m_state.flags & DirtyGlobalAlpha)
       
   697             m_painter.setOpacity(m_state.globalAlpha);
       
   698         if (m_state.flags & DirtyGlobalCompositeOperation)
       
   699             m_painter.setCompositionMode(m_state.globalCompositeOperation);
       
   700         if (m_state.flags & MDirtyPen) {
       
   701             QPen pen = m_painter.pen();
       
   702             if (m_state.flags & DirtyStrokeStyle)
       
   703                 pen.setBrush(m_state.strokeStyle);
       
   704             if (m_state.flags & DirtyLineWidth)
       
   705                 pen.setWidthF(m_state.lineWidth);
       
   706             if (m_state.flags & DirtyLineCap)
       
   707                 pen.setCapStyle(m_state.lineCap);
       
   708             if (m_state.flags & DirtyLineJoin)
       
   709                 pen.setJoinStyle(m_state.lineJoin);
       
   710             if (m_state.flags & DirtyMiterLimit)
       
   711                 pen.setMiterLimit(m_state.miterLimit);
       
   712             m_painter.setPen(pen);
       
   713         }
       
   714         m_state.flags = 0;
       
   715     }
       
   716 }
       
   717 
       
   718 void Context2D::clear()
       
   719 {
       
   720     endPainting();
       
   721     m_image.fill(qRgba(0,0,0,0));
       
   722     scheduleChange();
       
   723 }
       
   724 
       
   725 void Context2D::reset()
       
   726 {
       
   727     m_stateStack.clear();
       
   728     m_state.matrix = QMatrix();
       
   729     m_state.clipPath = QPainterPath();
       
   730     m_state.globalAlpha = 1.0;
       
   731     m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
       
   732     m_state.strokeStyle = Qt::black;
       
   733     m_state.fillStyle = Qt::black;
       
   734     m_state.lineWidth = 1;
       
   735     m_state.lineCap = Qt::FlatCap;
       
   736     m_state.lineJoin = Qt::MiterJoin;
       
   737     m_state.miterLimit = 10;
       
   738     m_state.shadowOffsetX = 0;
       
   739     m_state.shadowOffsetY = 0;
       
   740     m_state.shadowBlur = 0;
       
   741     m_state.shadowColor = qRgba(0, 0, 0, 0);
       
   742     m_state.flags = AllIsFullOfDirt;
       
   743     clear();
       
   744 }
       
   745 
       
   746 void Context2D::setSize(int width, int height)
       
   747 {
       
   748     endPainting();
       
   749     QImage newi(width, height, QImage::Format_ARGB32_Premultiplied);
       
   750     newi.fill(qRgba(0,0,0,0));
       
   751     QPainter p(&newi);
       
   752     p.drawImage(0, 0, m_image);
       
   753     p.end();
       
   754     m_image = newi;
       
   755     scheduleChange();
       
   756 }
       
   757 
       
   758 void Context2D::setSize(const QSize &size)
       
   759 {
       
   760     setSize(size.width(), size.height());
       
   761 }
       
   762 
       
   763 QSize Context2D::size() const
       
   764 {
       
   765     return m_image.size();
       
   766 }
       
   767 
       
   768 void Context2D::drawImage(DomImage *image, qreal dx, qreal dy)
       
   769 {
       
   770     if (!image)
       
   771         return;
       
   772     if (dx < 0) {
       
   773         qreal sx = qAbs(dx);
       
   774         qreal sy = qAbs(dy);
       
   775         qreal sw = image->width() - sx;
       
   776         qreal sh = image->height() - sy;
       
   777 
       
   778         drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh);
       
   779     } else {
       
   780         beginPainting();
       
   781         m_painter.drawImage(QPointF(dx, dy), image->image());
       
   782         scheduleChange();
       
   783     }
       
   784 }
       
   785 
       
   786 void Context2D::drawImage(DomImage *image, qreal dx, qreal dy,
       
   787                           qreal dw, qreal dh)
       
   788 {
       
   789     if (!image)
       
   790         return;
       
   791     beginPainting();
       
   792     m_painter.drawImage(QRectF(dx, dy, dw, dh).toRect(), image->image());
       
   793     scheduleChange();
       
   794 }
       
   795 
       
   796 void Context2D::drawImage(DomImage *image, qreal sx, qreal sy,
       
   797                           qreal sw, qreal sh, qreal dx, qreal dy,
       
   798                           qreal dw, qreal dh)
       
   799 {
       
   800     if (!image)
       
   801         return;
       
   802     beginPainting();
       
   803     m_painter.drawImage(QRectF(dx, dy, dw, dh), image->image(),
       
   804                         QRectF(sx, sy, sw, sh));
       
   805     scheduleChange();
       
   806 }
       
   807 
       
   808 //! [2]
       
   809 void Context2D::scheduleChange()
       
   810 {
       
   811     if (m_changeTimerId == -1)
       
   812         m_changeTimerId = startTimer(0);
       
   813 }
       
   814 
       
   815 void Context2D::timerEvent(QTimerEvent *e)
       
   816 {
       
   817     if (e->timerId() == m_changeTimerId) {
       
   818         killTimer(m_changeTimerId);
       
   819         m_changeTimerId = -1;
       
   820         emit changed(endPainting());
       
   821     } else {
       
   822         QObject::timerEvent(e);
       
   823     }
       
   824 }
       
   825 //! [2]