source: trunk/examples/script/context2d/context2d.cpp@ 1168

Last change on this file since 1168 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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