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

source code of qtscript/examples/script/context2d/context2d.cpp