source: trunk/src/canvas/qcanvas.cpp@ 73

Last change on this file since 73 was 73, checked in by dmik, 19 years ago

Fixed all the drawing artefacts and distortions seen when applying non-identity transformations to canvas views.

  • Property svn:keywords set to Id
File size: 150.0 KB
Line 
1/**********************************************************************
2** $Id: qcanvas.cpp 73 2006-03-23 23:46:08Z dmik $
3**
4** Implementation of QCanvas and associated classes
5**
6** Created : 991211
7**
8** Copyright (C) 1999-2002 Trolltech AS. All rights reserved.
9**
10** This file is part of the canvas module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition licenses may use this
22** file in accordance with the Qt Commercial License Agreement provided
23** with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qcanvas.h"
39#ifndef QT_NO_CANVAS
40#include "qapplication.h"
41#include "qbitmap.h"
42#include "qimage.h"
43#include "qptrdict.h"
44#include "qpainter.h"
45#include "qpolygonscanner.h"
46#include "qtimer.h"
47#include "qtl.h"
48
49#include <stdlib.h>
50
51//#define QT_NO_CANVAS_ROUNDING_FIX
52
53#if !defined(QT_NO_TRANSFORMATIONS) && !defined(QT_NO_CANVAS_ROUNDING_FIX)
54#include <math.h>
55#endif
56
57class QCanvasData {
58public:
59 QCanvasData() :
60 itemDict(1013), animDict(503)
61 {
62 }
63
64 QPtrList<QCanvasView> viewList;
65 QPtrDict<void> itemDict;
66 QPtrDict<void> animDict;
67};
68
69class QCanvasViewData {
70public:
71 QCanvasViewData() : repaint_from_moving( FALSE ) {}
72#ifndef QT_NO_TRANSFORMATIONS
73 QWMatrix xform;
74 QWMatrix ixform;
75#endif
76 bool repaint_from_moving;
77};
78
79// clusterizer
80
81class QCanvasClusterizer {
82public:
83 QCanvasClusterizer(int maxclusters);
84 ~QCanvasClusterizer();
85
86 void add(int x, int y); // 1x1 rectangle (point)
87 void add(int x, int y, int w, int h);
88 void add(const QRect& rect);
89
90 void clear();
91 int clusters() { return count; }
92 const QRect& operator[](int i);
93
94private:
95 QRect* cluster;
96 int count;
97 const int maxcl;
98};
99
100static
101void include(QRect& r, const QRect& rect)
102{
103 if (rect.left()<r.left()) {
104 r.setLeft(rect.left());
105 }
106 if (rect.right()>r.right()) {
107 r.setRight(rect.right());
108 }
109 if (rect.top()<r.top()) {
110 r.setTop(rect.top());
111 }
112 if (rect.bottom()>r.bottom()) {
113 r.setBottom(rect.bottom());
114 }
115}
116
117#if !defined(QT_NO_TRANSFORMATIONS) && !defined(QT_NO_CANVAS_ROUNDING_FIX)
118
119/*!
120 * \internal
121 *
122 * Takes inclusive top left and exclusive bottom right poitns of the given
123 * rectangle, translates it using the given matrix, calculates the bounding
124 * rectangle and converts resulting doubles to integers so that points of the
125 * target raster partially covered by resulting double points are included
126 * to the resulting rectangle.
127 *
128 * Using this function instead of QWMatrix::map(const QRect&) eliminates many
129 * artefacts and distortions seen in canvas views that use world transformation.
130 * When scaling up, this function works merely as QWMatrix::map() with
131 * transformation mode set to Areas, but it also produces correct rectangles
132 * (as necessary to determine affected chunks) in all other cases.
133 */
134static
135QRect mapRectMax( const QWMatrix &m, const QRect &r )
136{
137 if ( m.isIdentity() ) {
138 return r;
139 }
140
141 QRect rect = r;
142
143 // make bottom right exclusive
144 ++ rect.rBottom();
145 ++ rect.rRight();
146
147 double x1, y1, x2, y2;
148
149 if ( m.m12() == 0.0 && m.m21() == 0.0 ) {
150 // no shear or rotation, map two coords
151 m.map( rect.left(), rect.top(), &x1, &y1 );
152 m.map( rect.right(), rect.bottom(), &x2, &y2 );
153 } else {
154 double xl = rect.left(), yt = rect.top();
155 double xr = rect.right(), yb = rect.bottom();
156 double x, y;
157 // map four coords and find min/max
158 m.map( xl, yt, &x, &y );
159 x1 = x;
160 y1 = y;
161 x2 = x;
162 y2 = y;
163 m.map( xr, yt, &x, &y );
164 x1 = QMIN( x1, x );
165 y1 = QMIN( y1, y );
166 x2 = QMAX( x2, x );
167 y2 = QMAX( y2, y );
168 m.map( xl, yb, &x, &y );
169 x1 = QMIN( x1, x );
170 y1 = QMIN( y1, y );
171 x2 = QMAX( x2, x );
172 y2 = QMAX( y2, y );
173 m.map( xr, yb, &x, &y );
174 x1 = QMIN( x1, x );
175 y1 = QMIN( y1, y );
176 x2 = QMAX( x2, x );
177 y2 = QMAX( y2, y );
178 }
179
180 // get integers (partially covered points are included)
181 x1 = floor( x1 );
182 y1 = floor( y1 );
183 x2 = ceil( x2 );
184 y2 = ceil( y2 );
185
186 // compose the result
187 rect.setCoords( int( x1 ), int( y1 ), int( x2 ), int( y2 ) );
188 // make bottom right inclusive again
189 -- rect.rBottom();
190 -- rect.rRight();
191
192 return rect;
193}
194
195/*!
196 * \internal
197 *
198 * Similar to mapRectMax, but with the opposite meaning. Tries to return
199 * a region that is completely inside the given rectangle after transformation.
200 * \a br is the target bounding rectangle (to minimize calculations).
201 *
202 * Using this function to clip out areas of the canvas view not covered by the
203 * canvas eliminates garbage at canvas edges that can be well seen when the
204 * canvas is rotated in the view.
205 */
206static
207QRegion mapRectMin( const QWMatrix &m, const QRect &r, const QRect &br )
208{
209 QRegion rgn;
210
211 if ( m.isIdentity() ) {
212 rgn = QRegion( r );
213 return rgn;
214 }
215
216 if ( m.m12() == 0.0 && m.m21() == 0.0 ) {
217 QRect rect = r;
218 // make bottom right exclusive
219 ++ rect.rBottom();
220 ++ rect.rRight();
221 double x1, y1, x2, y2;
222 // no shear or rotation, map two coords
223 m.map( rect.left(), rect.top(), &x1, &y1 );
224 m.map( rect.right(), rect.bottom(), &x2, &y2 );
225 // get integers (partially covered points are excluded)
226 x1 = ceil( x1 );
227 y1 = ceil( y1 );
228 x2 = floor( x2 );
229 y2 = floor( y2 );
230 // compose the result
231 rect.setCoords( int( x1 ), int( y1 ), int( x2 ), int( y2 ) );
232 // make bottom right inclusive again
233 -- rect.rBottom();
234 -- rect.rRight();
235 rgn = QRegion( rect );
236 } else {
237 // make the rectangle smaller to compensate for differences in
238 // line drawing routines of the underlying OS (when defining regions
239 // from polygons) and by Qt tansformations (when transforming images
240 // or when determining invalid chunks)
241 QPointArray pa( r );
242 pa = m.map( pa );
243 rgn = QRegion( pa );
244 // limit to the bounding rect (expanded to compensate shifts)
245 QRect rect = br;
246 rect.addCoords( -1, 0, 1, 0 );
247 rgn &= QRegion( rect );
248 // create a copy of the region and shift each towards other by 1 pixel
249 // to compensate for (clip out) differences in line drawing routines of
250 // the underlying OS (when defining regions from polygons) and in Qt
251 // tansformation routines (when transforming images or when determining
252 // invalid chunks)
253 QRegion rgn2 = rgn;
254 rgn.translate( 1, 0 );
255 rgn2.translate( -1, 0 );
256 // take an intersection
257 rgn &= rgn2;
258 }
259
260 return rgn;
261}
262
263#endif
264
265/*
266A QCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
267by a merging heuristic.
268*/
269QCanvasClusterizer::QCanvasClusterizer(int maxclusters) :
270 cluster(new QRect[maxclusters]),
271 count(0),
272 maxcl(maxclusters)
273{ }
274
275QCanvasClusterizer::~QCanvasClusterizer()
276{
277 delete [] cluster;
278}
279
280void QCanvasClusterizer::clear()
281{
282 count=0;
283}
284
285void QCanvasClusterizer::add(int x, int y)
286{
287 add(QRect(x,y,1,1));
288}
289
290void QCanvasClusterizer::add(int x, int y, int w, int h)
291{
292 add(QRect(x,y,w,h));
293}
294
295void QCanvasClusterizer::add(const QRect& rect)
296{
297 QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2);
298
299 //assert(rect.width()>0 && rect.height()>0);
300
301 int cursor;
302
303 for (cursor=0; cursor<count; cursor++) {
304 if (cluster[cursor].contains(rect)) {
305 // Wholly contained already.
306 return;
307 }
308 }
309
310 int lowestcost=9999999;
311 int cheapest=-1;
312 cursor = 0;
313 while( cursor<count ) {
314 if (cluster[cursor].intersects(biggerrect)) {
315 QRect larger=cluster[cursor];
316 include(larger,rect);
317 int cost = larger.width()*larger.height() -
318 cluster[cursor].width()*cluster[cursor].height();
319
320 if (cost < lowestcost) {
321 bool bad=FALSE;
322 for (int c=0; c<count && !bad; c++) {
323 bad=cluster[c].intersects(larger) && c!=cursor;
324 }
325 if (!bad) {
326 cheapest=cursor;
327 lowestcost=cost;
328 }
329 }
330 }
331 cursor++;
332 }
333
334 if (cheapest>=0) {
335 include(cluster[cheapest],rect);
336 return;
337 }
338
339 if (count < maxcl) {
340 cluster[count++]=rect;
341 return;
342 }
343
344 // Do cheapest of:
345 // add to closest cluster
346 // do cheapest cluster merge, add to new cluster
347
348 lowestcost=9999999;
349 cheapest=-1;
350 cursor=0;
351 while( cursor<count ) {
352 QRect larger=cluster[cursor];
353 include(larger,rect);
354 int cost=larger.width()*larger.height()
355 - cluster[cursor].width()*cluster[cursor].height();
356 if (cost < lowestcost) {
357 bool bad=FALSE;
358 for (int c=0; c<count && !bad; c++) {
359 bad=cluster[c].intersects(larger) && c!=cursor;
360 }
361 if (!bad) {
362 cheapest=cursor;
363 lowestcost=cost;
364 }
365 }
366 cursor++;
367 }
368
369 // ###
370 // could make an heuristic guess as to whether we need to bother
371 // looking for a cheap merge.
372
373 int cheapestmerge1 = -1;
374 int cheapestmerge2 = -1;
375
376 int merge1 = 0;
377 while( merge1 < count ) {
378 int merge2=0;
379 while( merge2 < count ) {
380 if( merge1!=merge2) {
381 QRect larger=cluster[merge1];
382 include(larger,cluster[merge2]);
383 int cost=larger.width()*larger.height()
384 - cluster[merge1].width()*cluster[merge1].height()
385 - cluster[merge2].width()*cluster[merge2].height();
386 if (cost < lowestcost) {
387 bool bad=FALSE;
388 for (int c=0; c<count && !bad; c++) {
389 bad=cluster[c].intersects(larger) && c!=cursor;
390 }
391 if (!bad) {
392 cheapestmerge1=merge1;
393 cheapestmerge2=merge2;
394 lowestcost=cost;
395 }
396 }
397 }
398 merge2++;
399 }
400 merge1++;
401 }
402
403 if (cheapestmerge1>=0) {
404 include(cluster[cheapestmerge1],cluster[cheapestmerge2]);
405 cluster[cheapestmerge2]=cluster[count--];
406 } else {
407 // if (!cheapest) debugRectangles(rect);
408 include(cluster[cheapest],rect);
409 }
410
411 // NB: clusters do not intersect (or intersection will
412 // overwrite). This is a result of the above algorithm,
413 // given the assumption that (x,y) are ordered topleft
414 // to bottomright.
415
416 // ###
417 //
418 // add explicit x/y ordering to that comment, move it to the top
419 // and rephrase it as pre-/post-conditions.
420}
421
422const QRect& QCanvasClusterizer::operator[](int i)
423{
424 return cluster[i];
425}
426
427// end of clusterizer
428
429
430
431class QM_EXPORT_CANVAS QCanvasItemPtr {
432public:
433 QCanvasItemPtr() : ptr(0) { }
434 QCanvasItemPtr( QCanvasItem* p ) : ptr(p) { }
435
436 bool operator<=(const QCanvasItemPtr& that) const
437 {
438 // Order same-z objects by identity.
439 if (that.ptr->z()==ptr->z())
440 return that.ptr <= ptr;
441 return that.ptr->z() <= ptr->z();
442 }
443 bool operator<(const QCanvasItemPtr& that) const
444 {
445 // Order same-z objects by identity.
446 if (that.ptr->z()==ptr->z())
447 return that.ptr < ptr;
448 return that.ptr->z() < ptr->z();
449 }
450 bool operator>(const QCanvasItemPtr& that) const
451 {
452 // Order same-z objects by identity.
453 if (that.ptr->z()==ptr->z())
454 return that.ptr > ptr;
455 return that.ptr->z() > ptr->z();
456 }
457 bool operator==(const QCanvasItemPtr& that) const
458 {
459 return that.ptr == ptr;
460 }
461 operator QCanvasItem*() const { return ptr; }
462
463private:
464 QCanvasItem* ptr;
465};
466
467
468/*!
469 \class QCanvasItemList
470 \brief The QCanvasItemList class is a list of QCanvasItems.
471\if defined(commercial)
472 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
473\endif
474
475 \module canvas
476 \ingroup graphics
477 \ingroup images
478
479 QCanvasItemList is a QValueList of pointers to \l{QCanvasItem}s.
480 This class is used by some methods in QCanvas that need to return
481 a list of canvas items.
482
483 The \l QValueList documentation describes how to use this list.
484*/
485
486/*!
487 \internal
488*/
489void QCanvasItemList::sort()
490{
491 qHeapSort(*((QValueList<QCanvasItemPtr>*)this));
492}
493
494/*!
495 \internal
496*/
497void QCanvasItemList::drawUnique( QPainter& painter )
498{
499 QCanvasItem* prev=0;
500 for (Iterator it=fromLast(); it!=end(); --it) {
501 QCanvasItem *g=*it;
502 if (g!=prev) {
503 g->draw(painter);
504 prev=g;
505 }
506 }
507}
508
509/*!
510 Returns the concatenation of this list and list \a l.
511*/
512QCanvasItemList QCanvasItemList::operator+(const QCanvasItemList &l) const
513{
514 QCanvasItemList l2(*this);
515 for(const_iterator it = l.begin(); it != l.end(); ++it)
516 l2.append(*it);
517 return l2;
518}
519
520class QCanvasChunk {
521public:
522 QCanvasChunk() : changed(TRUE) { }
523 // Other code assumes lists are not deleted. Assignment is also
524 // done on ChunkRecs. So don't add that sort of thing here.
525
526 void sort()
527 {
528 list.sort();
529 }
530
531 const QCanvasItemList* listPtr() const
532 {
533 return &list;
534 }
535
536 void add(QCanvasItem* item)
537 {
538 list.prepend(item);
539 changed = TRUE;
540 }
541
542 void remove(QCanvasItem* item)
543 {
544 list.remove(item);
545 changed = TRUE;
546 }
547
548 void change()
549 {
550 changed = TRUE;
551 }
552
553 bool hasChanged() const
554 {
555 return changed;
556 }
557
558 bool takeChange()
559 {
560 bool y = changed;
561 changed = FALSE;
562 return y;
563 }
564
565private:
566 QCanvasItemList list;
567 bool changed;
568};
569
570
571static int gcd(int a, int b)
572{
573 int r;
574 while ( (r = a%b) ) {
575 a=b;
576 b=r;
577 }
578 return b;
579}
580
581static int scm(int a, int b)
582{
583 int g = gcd(a,b);
584 return a/g*b;
585}
586
587
588
589/*!
590 \class QCanvas qcanvas.h
591 \brief The QCanvas class provides a 2D area that can contain QCanvasItem objects.
592\if defined(commercial)
593 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
594\endif
595
596 \ingroup abstractwidgets
597 \ingroup graphics
598 \ingroup images
599 \mainclass
600 \module canvas
601
602 The QCanvas class manages its 2D graphic area and all the canvas
603 items the area contains. The canvas has no visual appearance of
604 its own. Instead, it is displayed on screen using a QCanvasView.
605 Multiple QCanvasView widgets may be associated with a canvas to
606 provide multiple views of the same canvas.
607
608 The canvas is optimized for large numbers of items, particularly
609 where only a small percentage of the items change at any
610 one time. If the entire display changes very regularly, you should
611 consider using your own custom QScrollView subclass.
612
613 Qt provides a rich
614 set of canvas item classes, e.g. QCanvasEllipse, QCanvasLine,
615 QCanvasPolygon, QCanvasPolygonalItem, QCanvasRectangle, QCanvasSpline,
616 QCanvasSprite and QCanvasText. You can subclass to create your own
617 canvas items; QCanvasPolygonalItem is the most common base class used
618 for this purpose.
619
620 Items appear on the canvas after their \link QCanvasItem::show()
621 show()\endlink function has been called (or \link
622 QCanvasItem::setVisible() setVisible(TRUE)\endlink), and \e after
623 update() has been called. The canvas only shows items that are
624 \link QCanvasItem::setVisible() visible\endlink, and then only if
625 \l update() is called. (By default the canvas is white and so are
626 canvas items, so if nothing appears try changing colors.)
627
628 If you created the canvas without passing a width and height to
629 the constructor you must also call resize().
630
631 Although a canvas may appear to be similar to a widget with child
632 widgets, there are several notable differences:
633
634 \list
635 \i Canvas items are usually much faster to manipulate and redraw than
636 child widgets, with the speed advantage becoming especially great when
637 there are \e many canvas items and non-rectangular items. In most
638 situations canvas items are also a lot more memory efficient than child
639 widgets.
640
641 \i It's easy to detect overlapping items (collision detection).
642
643 \i The canvas can be larger than a widget. A million-by-million canvas
644 is perfectly possible. At such a size a widget might be very
645 inefficient, and some window systems might not support it at all,
646 whereas QCanvas scales well. Even with a billion pixels and a million
647 items, finding a particular canvas item, detecting collisions, etc.,
648 is still fast (though the memory consumption may be prohibitive
649 at such extremes).
650
651 \i Two or more QCanvasView objects can view the same canvas.
652
653 \i An arbitrary transformation matrix can be set on each QCanvasView
654 which makes it easy to zoom, rotate or shear the viewed canvas.
655
656 \i Widgets provide a lot more functionality, such as input (QKeyEvent,
657 QMouseEvent etc.) and layout management (QGridLayout etc.).
658
659 \endlist
660
661 A canvas consists of a background, a number of canvas items organized by
662 x, y and z coordinates, and a foreground. A canvas item's z coordinate
663 can be treated as a layer number -- canvas items with a higher z
664 coordinate appear in front of canvas items with a lower z coordinate.
665
666 The background is white by default, but can be set to a different color
667 using setBackgroundColor(), or to a repeated pixmap using
668 setBackgroundPixmap() or to a mosaic of smaller pixmaps using
669 setTiles(). Individual tiles can be set with setTile(). There
670 are corresponding get functions, e.g. backgroundColor() and
671 backgroundPixmap().
672
673 Note that QCanvas does not inherit from QWidget, even though it has some
674 functions which provide the same functionality as those in QWidget. One
675 of these is setBackgroundPixmap(); some others are resize(), size(),
676 width() and height(). \l QCanvasView is the widget used to display a
677 canvas on the screen.
678
679 Canvas items are added to a canvas by constructing them and passing the
680 canvas to the canvas item's constructor. An item can be moved to a
681 different canvas using QCanvasItem::setCanvas().
682
683 Canvas items are movable (and in the case of QCanvasSprites, animated)
684 objects that inherit QCanvasItem. Each canvas item has a position on the
685 canvas (x, y coordinates) and a height (z coordinate), all of which are
686 held as floating-point numbers. Moving canvas items also have x and y
687 velocities. It's possible for a canvas item to be outside the canvas
688 (for example QCanvasItem::x() is greater than width()). When a canvas
689 item is off the canvas, onCanvas() returns FALSE and the canvas
690 disregards the item. (Canvas items off the canvas do not slow down any
691 of the common operations on the canvas.)
692
693 Canvas items can be moved with QCanvasItem::move(). The advance()
694 function moves all QCanvasItem::animated() canvas items and
695 setAdvancePeriod() makes QCanvas move them automatically on a periodic
696 basis. In the context of the QCanvas classes, to `animate' a canvas item
697 is to set it in motion, i.e. using QCanvasItem::setVelocity(). Animation
698 of a canvas item itself, i.e. items which change over time, is enabled
699 by calling QCanvasSprite::setFrameAnimation(), or more generally by
700 subclassing and reimplementing QCanvasItem::advance(). To detect collisions
701 use one of the QCanvasItem::collisions() functions.
702
703 The changed parts of the canvas are redrawn (if they are visible in a
704 canvas view) whenever update() is called. You can either call update()
705 manually after having changed the contents of the canvas, or force
706 periodic updates using setUpdatePeriod(). If you have moving objects on
707 the canvas, you must call advance() every time the objects should
708 move one step further. Periodic calls to advance() can be forced using
709 setAdvancePeriod(). The advance() function will call
710 QCanvasItem::advance() on every item that is \link
711 QCanvasItem::animated() animated\endlink and trigger an update of the
712 affected areas afterwards. (A canvas item that is `animated' is simply
713 a canvas item that is in motion.)
714
715 QCanvas organizes its canvas items into \e chunks; these are areas on
716 the canvas that are used to speed up most operations. Many operations
717 start by eliminating most chunks (i.e. those which haven't changed)
718 and then process only the canvas items that are in the few interesting
719 (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on
720 the canvas.
721
722 The chunk size is a key factor to QCanvas's speed: if there are too many
723 chunks, the speed benefit of grouping canvas items into chunks is
724 reduced. If the chunks are too large, it takes too long to process each
725 one. The QCanvas constructor tries to pick a suitable size, but you
726 can call retune() to change it at any time. The chunkSize() function
727 returns the current chunk size. The canvas items always make sure
728 they're in the right chunks; all you need to make sure of is that
729 the canvas uses the right chunk size. A good rule of thumb is that
730 the size should be a bit smaller than the average canvas item
731 size. If you have moving objects, the chunk size should be a bit
732 smaller than the average size of the moving items.
733
734 The foreground is normally nothing, but if you reimplement
735 drawForeground(), you can draw things in front of all the canvas
736 items.
737
738 Areas can be set as changed with setChanged() and set unchanged with
739 setUnchanged(). The entire canvas can be set as changed with
740 setAllChanged(). A list of all the items on the canvas is returned by
741 allItems().
742
743 An area can be copied (painted) to a QPainter with drawArea().
744
745 If the canvas is resized it emits the resized() signal.
746
747 The examples/canvas application and the 2D graphics page of the
748 examples/demo application demonstrate many of QCanvas's facilities.
749
750 \sa QCanvasView QCanvasItem
751*/
752void QCanvas::init(int w, int h, int chunksze, int mxclusters)
753{
754 d = new QCanvasData;
755 awidth=w;
756 aheight=h;
757 chunksize=chunksze;
758 maxclusters=mxclusters;
759 chwidth=(w+chunksize-1)/chunksize;
760 chheight=(h+chunksize-1)/chunksize;
761 chunks=new QCanvasChunk[chwidth*chheight];
762 update_timer = 0;
763 bgcolor = white;
764 grid = 0;
765 htiles = 0;
766 vtiles = 0;
767 dblbuf = TRUE;
768 debug_redraw_areas = FALSE;
769}
770
771/*!
772 Create a QCanvas with no size. \a parent and \a name are passed to
773 the QObject superclass.
774
775 \warning You \e must call resize() at some time after creation to
776 be able to use the canvas.
777*/
778QCanvas::QCanvas( QObject* parent, const char* name )
779 : QObject( parent, name )
780{
781 init(0,0);
782}
783
784/*!
785 Constructs a QCanvas that is \a w pixels wide and \a h pixels high.
786*/
787QCanvas::QCanvas(int w, int h)
788{
789 init(w,h);
790}
791
792/*!
793 Constructs a QCanvas which will be composed of \a h tiles
794 horizontally and \a v tiles vertically. Each tile will be an image
795 \a tilewidth by \a tileheight pixels taken from pixmap \a p.
796
797 The pixmap \a p is a list of tiles, arranged left to right, (and
798 in the case of pixmaps that have multiple rows of tiles, top to
799 bottom), with tile 0 in the top-left corner, tile 1 next to the
800 right, and so on, e.g.
801
802 \table
803 \row \i 0 \i 1 \i 2 \i 3
804 \row \i 4 \i 5 \i 6 \i 7
805 \endtable
806
807 The QCanvas is initially sized to show exactly the given number of
808 tiles horizontally and vertically. If it is resized to be larger,
809 the entire matrix of tiles will be repeated as often as necessary
810 to cover the area. If it is smaller, tiles to the right and bottom
811 will not be visible.
812
813 \sa setTiles()
814*/
815QCanvas::QCanvas( QPixmap p,
816 int h, int v, int tilewidth, int tileheight )
817{
818 init(h*tilewidth, v*tileheight, scm(tilewidth,tileheight) );
819 setTiles( p, h, v, tilewidth, tileheight );
820}
821
822void qt_unview(QCanvas* c)
823{
824 for (QCanvasView* view=c->d->viewList.first(); view != 0; view=c->d->viewList.next()) {
825 view->viewing = 0;
826 }
827}
828
829/*!
830 Destroys the canvas and all the canvas's canvas items.
831*/
832QCanvas::~QCanvas()
833{
834 qt_unview(this);
835 QCanvasItemList all = allItems();
836 for (QCanvasItemList::Iterator it=all.begin(); it!=all.end(); ++it)
837 delete *it;
838 delete [] chunks;
839 delete [] grid;
840 delete d;
841}
842
843/*!
844\internal
845Returns the chunk at a chunk position \a i, \a j.
846*/
847QCanvasChunk& QCanvas::chunk(int i, int j) const
848{
849 return chunks[i+chwidth*j];
850}
851
852/*!
853\internal
854Returns the chunk at a pixel position \a x, \a y.
855*/
856QCanvasChunk& QCanvas::chunkContaining(int x, int y) const
857{
858 return chunk(x/chunksize,y/chunksize);
859}
860
861/*!
862 Returns a list of all the items in the canvas.
863*/
864QCanvasItemList QCanvas::allItems()
865{
866 QCanvasItemList list;
867 for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
868 list.prepend((QCanvasItem*)it.currentKey());
869 }
870 return list;
871}
872
873
874/*!
875 Changes the size of the canvas to have a width of \a w and a
876 height of \a h. This is a slow operation.
877*/
878void QCanvas::resize(int w, int h)
879{
880 if (awidth==w && aheight==h)
881 return;
882
883 QCanvasItem* item;
884 QPtrList<QCanvasItem> hidden;
885 for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
886 if (((QCanvasItem*)it.currentKey())->isVisible()) {
887 ((QCanvasItem*)it.currentKey())->hide();
888 hidden.append(((QCanvasItem*)it.currentKey()));
889 }
890 }
891
892 int nchwidth=(w+chunksize-1)/chunksize;
893 int nchheight=(h+chunksize-1)/chunksize;
894
895 QCanvasChunk* newchunks = new QCanvasChunk[nchwidth*nchheight];
896
897 // Commit the new values.
898 //
899 awidth=w;
900 aheight=h;
901 chwidth=nchwidth;
902 chheight=nchheight;
903 delete [] chunks;
904 chunks=newchunks;
905
906 for (item=hidden.first(); item != 0; item=hidden.next()) {
907 item->show();
908 }
909
910 setAllChanged();
911
912 emit resized();
913}
914
915/*!
916 \fn void QCanvas::resized()
917
918 This signal is emitted whenever the canvas is resized. Each
919 QCanvasView connects to this signal to keep the scrollview's size
920 correct.
921*/
922
923/*!
924 Change the efficiency tuning parameters to \a mxclusters clusters,
925 each of size \a chunksze. This is a slow operation if there are
926 many objects on the canvas.
927
928 The canvas is divided into chunks which are rectangular areas \a
929 chunksze wide by \a chunksze high. Use a chunk size which is about
930 the average size of the canvas items. If you choose a chunk size
931 which is too small it will increase the amount of calculation
932 required when drawing since each change will affect many chunks.
933 If you choose a chunk size which is too large the amount of
934 drawing required will increase because for each change, a lot of
935 drawing will be required since there will be many (unchanged)
936 canvas items which are in the same chunk as the changed canvas
937 items.
938
939 Internally, a canvas uses a low-resolution "chunk matrix" to keep
940 track of all the items in the canvas. A 64x64 chunk matrix is the
941 default for a 1024x1024 pixel canvas, where each chunk collects
942 canvas items in a 16x16 pixel square. This default is also
943 affected by setTiles(). You can tune this default using this
944 function. For example if you have a very large canvas and want to
945 trade off speed for memory then you might set the chunk size to 32
946 or 64.
947
948 The \a mxclusters argument is the number of rectangular groups of
949 chunks that will be separately drawn. If the canvas has a large
950 number of small, dispersed items, this should be about that
951 number. Our testing suggests that a large number of clusters is
952 almost always best.
953
954*/
955void QCanvas::retune(int chunksze, int mxclusters)
956{
957 maxclusters=mxclusters;
958
959 if ( chunksize!=chunksze ) {
960 QPtrList<QCanvasItem> hidden;
961 for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
962 if (((QCanvasItem*)it.currentKey())->isVisible()) {
963 ((QCanvasItem*)it.currentKey())->hide();
964 hidden.append(((QCanvasItem*)it.currentKey()));
965 }
966 }
967
968 chunksize=chunksze;
969
970 int nchwidth=(awidth+chunksize-1)/chunksize;
971 int nchheight=(aheight+chunksize-1)/chunksize;
972
973 QCanvasChunk* newchunks = new QCanvasChunk[nchwidth*nchheight];
974
975 // Commit the new values.
976 //
977 chwidth=nchwidth;
978 chheight=nchheight;
979 delete [] chunks;
980 chunks=newchunks;
981
982 for (QCanvasItem* item=hidden.first(); item != 0; item=hidden.next()) {
983 item->show();
984 }
985 }
986}
987
988/*!
989 \fn int QCanvas::width() const
990
991 Returns the width of the canvas, in pixels.
992*/
993
994/*!
995 \fn int QCanvas::height() const
996
997 Returns the height of the canvas, in pixels.
998*/
999
1000/*!
1001 \fn QSize QCanvas::size() const
1002
1003 Returns the size of the canvas, in pixels.
1004*/
1005
1006/*!
1007 \fn QRect QCanvas::rect() const
1008
1009 Returns a rectangle the size of the canvas.
1010*/
1011
1012
1013/*!
1014 \fn bool QCanvas::onCanvas( int x, int y ) const
1015
1016 Returns TRUE if the pixel position (\a x, \a y) is on the canvas;
1017 otherwise returns FALSE.
1018
1019 \sa validChunk()
1020*/
1021
1022/*!
1023 \fn bool QCanvas::onCanvas( const QPoint& p ) const
1024 \overload
1025
1026 Returns TRUE if the pixel position \a p is on the canvas;
1027 otherwise returns FALSE.
1028
1029 \sa validChunk()
1030*/
1031
1032/*!
1033 \fn bool QCanvas::validChunk( int x, int y ) const
1034
1035 Returns TRUE if the chunk position (\a x, \a y) is on the canvas;
1036 otherwise returns FALSE.
1037
1038 \sa onCanvas()
1039*/
1040
1041/*!
1042 \fn bool QCanvas::validChunk( const QPoint& p ) const
1043 \overload
1044
1045 Returns TRUE if the chunk position \a p is on the canvas; otherwise
1046 returns FALSE.
1047
1048 \sa onCanvas()
1049*/
1050
1051/*!
1052 \fn int QCanvas::chunkSize() const
1053
1054 Returns the chunk size of the canvas.
1055
1056 \sa retune()
1057*/
1058
1059/*!
1060\fn bool QCanvas::sameChunk(int x1, int y1, int x2, int y2) const
1061\internal
1062Tells if the points ( \a x1, \a y1 ) and ( \a x2, \a y2 ) are within the same chunk.
1063*/
1064
1065/*!
1066\internal
1067This method adds an the item \a item to the list of QCanvasItem objects
1068in the QCanvas. The QCanvasItem class calls this.
1069*/
1070void QCanvas::addItem(QCanvasItem* item)
1071{
1072 d->itemDict.insert((void*)item,(void*)1);
1073}
1074
1075/*!
1076\internal
1077This method adds the item \a item to the list of QCanvasItem objects
1078to be moved. The QCanvasItem class calls this.
1079*/
1080void QCanvas::addAnimation(QCanvasItem* item)
1081{
1082 d->animDict.insert((void*)item,(void*)1);
1083}
1084
1085/*!
1086\internal
1087This method adds the item \a item to the list of QCanvasItem objects
1088which are no longer to be moved. The QCanvasItem class calls this.
1089*/
1090void QCanvas::removeAnimation(QCanvasItem* item)
1091{
1092 d->animDict.remove((void*)item);
1093}
1094
1095/*!
1096\internal
1097This method removes the item \a item from the list of QCanvasItem objects
1098in this QCanvas. The QCanvasItem class calls this.
1099*/
1100void QCanvas::removeItem(QCanvasItem* item)
1101{
1102 d->itemDict.remove((void*)item);
1103}
1104
1105/*!
1106\internal
1107This method adds the view \a view to the list of QCanvasView objects
1108viewing this QCanvas. The QCanvasView class calls this.
1109*/
1110void QCanvas::addView(QCanvasView* view)
1111{
1112 d->viewList.append(view);
1113 if ( htiles>1 || vtiles>1 || pm.isNull() )
1114 view->viewport()->setBackgroundColor(backgroundColor());
1115}
1116
1117/*!
1118\internal
1119This method removes the view \a view from the list of QCanvasView objects
1120viewing this QCanvas. The QCanvasView class calls this.
1121*/
1122void QCanvas::removeView(QCanvasView* view)
1123{
1124 d->viewList.removeRef(view);
1125}
1126
1127/*!
1128 Sets the canvas to call advance() every \a ms milliseconds. Any
1129 previous setting by setAdvancePeriod() or setUpdatePeriod() is
1130 overridden.
1131
1132 If \a ms is less than 0 advancing will be stopped.
1133*/
1134void QCanvas::setAdvancePeriod(int ms)
1135{
1136 if ( ms<0 ) {
1137 if ( update_timer )
1138 update_timer->stop();
1139 } else {
1140 if ( update_timer )
1141 delete update_timer;
1142 update_timer = new QTimer(this);
1143 connect(update_timer,SIGNAL(timeout()),this,SLOT(advance()));
1144 update_timer->start(ms);
1145 }
1146}
1147
1148/*!
1149 Sets the canvas to call update() every \a ms milliseconds. Any
1150 previous setting by setAdvancePeriod() or setUpdatePeriod() is
1151 overridden.
1152
1153 If \a ms is less than 0 automatic updating will be stopped.
1154*/
1155void QCanvas::setUpdatePeriod(int ms)
1156{
1157 if ( ms<0 ) {
1158 if ( update_timer )
1159 update_timer->stop();
1160 } else {
1161 if ( update_timer )
1162 delete update_timer;
1163 update_timer = new QTimer(this);
1164 connect(update_timer,SIGNAL(timeout()),this,SLOT(update()));
1165 update_timer->start(ms);
1166 }
1167}
1168
1169/*!
1170 Moves all QCanvasItem::animated() canvas items on the canvas and
1171 refreshes all changes to all views of the canvas. (An `animated'
1172 item is an item that is in motion; see setVelocity().)
1173
1174 The advance takes place in two phases. In phase 0, the
1175 QCanvasItem::advance() function of each QCanvasItem::animated()
1176 canvas item is called with paramater 0. Then all these canvas
1177 items are called again, with parameter 1. In phase 0, the canvas
1178 items should not change position, merely examine other items on
1179 the canvas for which special processing is required, such as
1180 collisions between items. In phase 1, all canvas items should
1181 change positions, ignoring any other items on the canvas. This
1182 two-phase approach allows for considerations of "fairness",
1183 although no QCanvasItem subclasses supplied with Qt do anything
1184 interesting in phase 0.
1185
1186 The canvas can be configured to call this function periodically
1187 with setAdvancePeriod().
1188
1189 \sa update()
1190*/
1191void QCanvas::advance()
1192{
1193 QPtrDictIterator<void> it=d->animDict;
1194 while ( it.current() ) {
1195 QCanvasItem* i = (QCanvasItem*)(void*)it.currentKey();
1196 ++it;
1197 if ( i )
1198 i->advance(0);
1199 }
1200 // we expect the dict contains the exact same items as in the
1201 // first pass.
1202 it.toFirst();
1203 while ( it.current() ) {
1204 QCanvasItem* i = (QCanvasItem*)(void*)it.currentKey();
1205 ++it;
1206 if ( i )
1207 i->advance(1);
1208 }
1209 update();
1210}
1211
1212// Don't call this unless you know what you're doing.
1213// p is in the content's co-ordinate example.
1214/*!
1215 \internal
1216*/
1217void QCanvas::drawViewArea( QCanvasView* view, QPainter* p, const QRect& vr, bool dbuf )
1218{
1219 QPoint tl = view->contentsToViewport(QPoint(0,0));
1220
1221#ifndef QT_NO_TRANSFORMATIONS
1222 QWMatrix wm = view->worldMatrix();
1223 QWMatrix iwm = wm.invert();
1224 // ivr = covers all chunks in vr
1225#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1226 QRect ivr = vr;
1227 if ( wm.m12() != 0.0 || wm.m21() != 0.0 ) {
1228 // compensate for rounding errors happening on the path
1229 // update() -> drawViewArea() after many transformations (can be seen
1230 // when moving items in a canvas view that makes the canvas smaller
1231 // and rotates it to ~270 degress clockwise)
1232 ivr.addCoords( -1, -1, 1, 1 );
1233 }
1234 ivr = mapRectMax( iwm, ivr );
1235#else
1236 QRect ivr = iwm.map(vr);
1237#endif
1238 QWMatrix twm;
1239 twm.translate(tl.x(),tl.y());
1240#else
1241 QRect ivr = vr;
1242#endif
1243
1244 QRect all(0,0,width(),height());
1245
1246 if ( !all.contains(ivr) ) {
1247 // Need to clip with edge of canvas.
1248
1249#ifndef QT_NO_TRANSFORMATIONS
1250#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1251 QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
1252 QRegion ra = mapRectMin( wm * twm, all, cvr );
1253#else
1254#if !defined(Q_WS_PM)
1255 // For translation-only transformation, it is safe to include the right
1256 // and bottom edges, but otherwise, these must be excluded since they
1257 // are not precisely defined (different bresenham paths).
1258 QPointArray a;
1259 if ( wm.m12()==0.0 && wm.m21()==0.0 && wm.m11() == 1.0 && wm.m22() == 1.0 )
1260 a = QPointArray( QRect(all.x(),all.y(),all.width()+1,all.height()+1) );
1261 else
1262 a = QPointArray( all );
1263#else
1264 // Polygons in OS/2 already include the right and bottom edges
1265 // (why shouldn't they do that?) Indeed, this important moment is not
1266 // defined in Qt at all.
1267 QPointArray a( all );
1268#endif
1269 a = (wm*twm).map(a);
1270#endif
1271#else
1272#if !defined(Q_WS_PM)
1273 QPointArray a( QRect(all.x(),all.y(),all.width()+1,all.height()+1) );
1274#else
1275 QPointArray a( all );
1276#endif
1277#endif
1278 if ( view->viewport()->backgroundMode() == NoBackground ) {
1279#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1280 p->setClipRegion( QRegion( cvr ) - ra );
1281#else
1282 QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
1283 p->setClipRegion(QRegion(cvr)-QRegion(a));
1284#endif
1285 p->fillRect(vr,view->viewport()->palette()
1286 .brush(QPalette::Active,QColorGroup::Background));
1287 }
1288#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1289 p->setClipRegion( ra );
1290#else
1291 p->setClipRegion(a);
1292#endif
1293 }
1294
1295 if ( dbuf ) {
1296 ensureOffScrSize( vr.width(), vr.height() );
1297 QPainter dbp(&offscr);
1298#ifndef QT_NO_TRANSFORMATIONS
1299 twm.translate(-vr.x(),-vr.y());
1300 twm.translate(-tl.x(),-tl.y());
1301 dbp.setWorldMatrix( wm*twm, TRUE );
1302#else
1303 dbp.translate(-vr.x()-tl.x(),-vr.y()-tl.y());
1304#endif
1305 dbp.setClipRect(0,0,vr.width(), vr.height());
1306 drawCanvasArea(ivr,&dbp,FALSE);
1307 p->drawPixmap(vr.x(), vr.y(), offscr, 0, 0, vr.width(), vr.height());
1308 } else {
1309 QRect r = vr; r.moveBy(tl.x(),tl.y()); // move to untransformed co-ords
1310 if ( !all.contains(ivr) ) {
1311 QRegion inside = p->clipRegion() & r;
1312 //QRegion outside = p->clipRegion() - r;
1313 //p->setClipRegion(outside);
1314 //p->fillRect(outside.boundingRect(),red);
1315 p->setClipRegion(inside);
1316 } else {
1317 p->setClipRect(r);
1318 }
1319#ifndef QT_NO_TRANSFORMATIONS
1320 p->setWorldMatrix( wm*twm );
1321#else
1322#endif
1323 p->setBrushOrigin(tl.x(), tl.y());
1324 drawCanvasArea(ivr,p,FALSE);
1325 }
1326}
1327
1328/*!
1329 Repaints changed areas in all views of the canvas.
1330
1331 \sa advance()
1332*/
1333void QCanvas::update()
1334{
1335 QCanvasClusterizer clusterizer(d->viewList.count());
1336#ifndef QT_NO_TRANSFORMATIONS
1337 QPtrList<QRect> doneareas;
1338 doneareas.setAutoDelete(TRUE);
1339#endif
1340
1341 QPtrListIterator<QCanvasView> it(d->viewList);
1342 QCanvasView* view;
1343 while( (view=it.current()) != 0 ) {
1344 ++it;
1345#ifndef QT_NO_TRANSFORMATIONS
1346 QWMatrix wm = view->worldMatrix();
1347#endif
1348 QRect area(view->contentsX(),view->contentsY(),
1349 view->visibleWidth(),view->visibleHeight());
1350 if (area.width()>0 && area.height()>0) {
1351#ifndef QT_NO_TRANSFORMATIONS
1352 if ( !wm.isIdentity() ) {
1353 // r = Visible area of the canvas where there are changes
1354#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1355 QRect r = changeBounds( mapRectMax( view->inverseWorldMatrix(),
1356 area ) );
1357#else
1358 QRect r = changeBounds(view->inverseWorldMatrix().map(area));
1359#endif
1360 if ( !r.isEmpty() ) {
1361 QPainter p(view->viewport());
1362 // Translate to the coordinate system of drawViewArea().
1363 QPoint tl = view->contentsToViewport(QPoint(0,0));
1364 p.translate(tl.x(),tl.y());
1365#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
1366 drawViewArea( view, &p, mapRectMax( wm, r ), dblbuf );
1367#else
1368 drawViewArea( view, &p, wm.map(r), dblbuf );
1369#endif
1370 doneareas.append(new QRect(r));
1371 }
1372 } else
1373#endif
1374 {
1375 clusterizer.add(area);
1376 }
1377 }
1378 }
1379
1380 for (int i=0; i<clusterizer.clusters(); i++)
1381 drawChanges(clusterizer[i]);
1382
1383#ifndef QT_NO_TRANSFORMATIONS
1384 for ( QRect* r=doneareas.first(); r != 0; r=doneareas.next() )
1385 setUnchanged(*r);
1386#endif
1387}
1388
1389
1390// ### warwick - setAllChanged() is not a set function. please rename
1391// it. ditto setChanged(). markChanged(), perhaps?
1392// ### unfortunately this function is virtual, which makes renaming more difficult. Lars
1393
1394/*!
1395 Marks the whole canvas as changed.
1396 All views of the canvas will be entirely redrawn when
1397 update() is called next.
1398*/
1399void QCanvas::setAllChanged()
1400{
1401 setChanged(QRect(0,0,width(),height()));
1402}
1403
1404/*!
1405 Marks \a area as changed. This \a area will be redrawn in all
1406 views that are showing it when update() is called next.
1407*/
1408void QCanvas::setChanged(const QRect& area)
1409{
1410 QRect thearea = area.intersect(QRect(0,0,width(),height()));
1411
1412 int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
1413 int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
1414 if (mx>chwidth)
1415 mx=chwidth;
1416 if (my>chheight)
1417 my=chheight;
1418
1419 int x=thearea.x()/chunksize;
1420 while( x<mx) {
1421 int y = thearea.y()/chunksize;
1422 while( y<my ) {
1423 chunk(x,y).change();
1424 y++;
1425 }
1426 x++;
1427 }
1428}
1429
1430/*!
1431 Marks \a area as \e unchanged. The area will \e not be redrawn in
1432 the views for the next update(), unless it is marked or changed
1433 again before the next call to update().
1434*/
1435void QCanvas::setUnchanged(const QRect& area)
1436{
1437 QRect thearea = area.intersect(QRect(0,0,width(),height()));
1438
1439 int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
1440 int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
1441 if (mx>chwidth)
1442 mx=chwidth;
1443 if (my>chheight)
1444 my=chheight;
1445
1446 int x=thearea.x()/chunksize;
1447 while( x<mx) {
1448 int y = thearea.y()/chunksize;
1449 while( y<my ) {
1450 chunk(x,y).takeChange();
1451 y++;
1452 }
1453 x++;
1454 }
1455}
1456
1457
1458/*!
1459 \internal
1460*/
1461QRect QCanvas::changeBounds(const QRect& inarea)
1462{
1463 QRect area=inarea.intersect(QRect(0,0,width(),height()));
1464
1465 int mx = (area.x()+area.width()+chunksize)/chunksize;
1466 int my = (area.y()+area.height()+chunksize)/chunksize;
1467 if (mx > chwidth)
1468 mx=chwidth;
1469 if (my > chheight)
1470 my=chheight;
1471
1472 QRect result;
1473
1474 int x=area.x()/chunksize;
1475 while( x<mx ) {
1476 int y=area.y()/chunksize;
1477 while( y<my ) {
1478 QCanvasChunk& ch=chunk(x,y);
1479 if ( ch.hasChanged() )
1480 result |= QRect(x,y,1,1);
1481 y++;
1482 }
1483 x++;
1484 }
1485
1486 if ( !result.isEmpty() ) {
1487 result.rLeft() *= chunksize;
1488 result.rTop() *= chunksize;
1489 result.rRight() *= chunksize;
1490 result.rBottom() *= chunksize;
1491 result.rRight() += chunksize;
1492 result.rBottom() += chunksize;
1493 }
1494
1495 return result;
1496}
1497
1498/*!
1499\internal
1500Redraws the area \a inarea of the QCanvas.
1501*/
1502void QCanvas::drawChanges(const QRect& inarea)
1503{
1504 QRect area=inarea.intersect(QRect(0,0,width(),height()));
1505
1506 QCanvasClusterizer clusters(maxclusters);
1507
1508 int mx = (area.x()+area.width()+chunksize)/chunksize;
1509 int my = (area.y()+area.height()+chunksize)/chunksize;
1510 if (mx > chwidth)
1511 mx=chwidth;
1512 if (my > chheight)
1513 my=chheight;
1514
1515 int x=area.x()/chunksize;
1516 while( x<mx ) {
1517 int y=area.y()/chunksize;
1518 while( y<my ) {
1519 QCanvasChunk& ch=chunk(x,y);
1520 if ( ch.hasChanged() )
1521 clusters.add(x,y);
1522 y++;
1523 }
1524 x++;
1525 }
1526
1527 for (int i=0; i<clusters.clusters(); i++) {
1528 QRect elarea=clusters[i];
1529 elarea.setRect(
1530 elarea.left()*chunksize,
1531 elarea.top()*chunksize,
1532 elarea.width()*chunksize,
1533 elarea.height()*chunksize
1534 );
1535 drawCanvasArea(elarea);
1536 }
1537}
1538
1539void QCanvas::ensureOffScrSize( int osw, int osh )
1540{
1541 if ( osw > offscr.width() || osh > offscr.height() )
1542 offscr.resize(QMAX(osw,offscr.width()),
1543 QMAX(osh,offscr.height()));
1544 else if ( offscr.width() == 0 || offscr.height() == 0 )
1545 offscr.resize( QMAX( offscr.width(), 1),
1546 QMAX( offscr.height(), 1 ) );
1547}
1548
1549/*!
1550 Paints all canvas items that are in the area \a clip to \a
1551 painter, using double-buffering if \a dbuf is TRUE.
1552
1553 e.g. to print the canvas to a printer:
1554 \code
1555 QPrinter pr;
1556 if ( pr.setup() ) {
1557 QPainter p(&pr);
1558 canvas.drawArea( canvas.rect(), &p );
1559 }
1560 \endcode
1561*/
1562void QCanvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf)
1563{
1564 if ( painter )
1565 drawCanvasArea( clip, painter, dbuf );
1566}
1567
1568/*!
1569 \internal
1570*/
1571void QCanvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool double_buffer)
1572{
1573 QRect area=inarea.intersect(QRect(0,0,width(),height()));
1574
1575 if ( !dblbuf )
1576 double_buffer = FALSE;
1577
1578 if (!d->viewList.first() && !p) return; // Nothing to do.
1579
1580 int lx=area.x()/chunksize;
1581 int ly=area.y()/chunksize;
1582 int mx=area.right()/chunksize;
1583 int my=area.bottom()/chunksize;
1584 if (mx>=chwidth)
1585 mx=chwidth-1;
1586 if (my>=chheight)
1587 my=chheight-1;
1588
1589 QCanvasItemList allvisible;
1590
1591 // Stores the region within area that need to be drawn. It is relative
1592 // to area.topLeft() (so as to keep within bounds of 16-bit XRegions)
1593 QRegion rgn;
1594
1595 for (int x=lx; x<=mx; x++) {
1596 for (int y=ly; y<=my; y++) {
1597 // Only reset change if all views updating, and
1598 // wholy within area. (conservative: ignore entire boundary)
1599 //
1600 // Disable this to help debugging.
1601 //
1602 if (!p) {
1603 if ( chunk(x,y).takeChange() ) {
1604 // ### should at least make bands
1605 rgn |= QRegion(x*chunksize-area.x(),y*chunksize-area.y(),
1606 chunksize,chunksize);
1607 allvisible += *chunk(x,y).listPtr();
1608 }
1609 } else {
1610 allvisible += *chunk(x,y).listPtr();
1611 }
1612 }
1613 }
1614 allvisible.sort();
1615
1616 if ( double_buffer )
1617 ensureOffScrSize( area.width(), area.height() );
1618
1619 if ( double_buffer && !offscr.isNull() ) {
1620 QPainter painter;
1621 painter.begin(&offscr);
1622 painter.translate(-area.x(),-area.y());
1623 if ( p ) {
1624 painter.setClipRect(QRect(0,0,area.width(),area.height()));
1625 } else {
1626 painter.setClipRegion(rgn);
1627 }
1628 drawBackground(painter,area);
1629 allvisible.drawUnique(painter);
1630 drawForeground(painter,area);
1631 painter.end();
1632 if ( p ) {
1633 p->drawPixmap( area.x(), area.y(), offscr,
1634 0, 0, area.width(), area.height() );
1635 return;
1636 }
1637 } else if ( p ) {
1638 drawBackground(*p,area);
1639 allvisible.drawUnique(*p);
1640 drawForeground(*p,area);
1641 return;
1642 }
1643
1644 QPoint trtr; // keeps track of total translation of rgn
1645
1646 trtr -= area.topLeft();
1647
1648 for (QCanvasView* view=d->viewList.first(); view; view=d->viewList.next()) {
1649#ifndef QT_NO_TRANSFORMATIONS
1650 if ( !view->worldMatrix().isIdentity() )
1651 continue; // Cannot paint those here (see callers).
1652#endif
1653 QPainter painter(view->viewport());
1654 QPoint tr = view->contentsToViewport(area.topLeft());
1655 QPoint nrtr = view->contentsToViewport(QPoint(0,0)); // new translation
1656 QPoint rtr = nrtr - trtr; // extra translation of rgn
1657 trtr += rtr; // add to total
1658 if (double_buffer) {
1659 rgn.translate(rtr.x(),rtr.y());
1660 painter.setClipRegion(rgn);
1661 painter.drawPixmap(tr,offscr, QRect(QPoint(0,0),area.size()));
1662 } else {
1663 painter.translate(nrtr.x(),nrtr.y());
1664 rgn.translate(rtr.x(),rtr.y());
1665 painter.setClipRegion(rgn);
1666 drawBackground(painter,area);
1667 allvisible.drawUnique(painter);
1668 drawForeground(painter,area);
1669 painter.translate(-nrtr.x(),-nrtr.y());
1670 }
1671 }
1672}
1673
1674/*!
1675\internal
1676This method to informs the QCanvas that a given chunk is
1677`dirty' and needs to be redrawn in the next Update.
1678
1679(\a x,\a y) is a chunk location.
1680
1681The sprite classes call this. Any new derived class of QCanvasItem
1682must do so too. SetChangedChunkContaining can be used instead.
1683*/
1684void QCanvas::setChangedChunk(int x, int y)
1685{
1686 if (validChunk(x,y)) {
1687 QCanvasChunk& ch=chunk(x,y);
1688 ch.change();
1689 }
1690}
1691
1692/*!
1693\internal
1694This method to informs the QCanvas that the chunk containing a given
1695pixel is `dirty' and needs to be redrawn in the next Update.
1696
1697(\a x,\a y) is a pixel location.
1698
1699The item classes call this. Any new derived class of QCanvasItem must
1700do so too. SetChangedChunk can be used instead.
1701*/
1702void QCanvas::setChangedChunkContaining(int x, int y)
1703{
1704 if (x>=0 && x<width() && y>=0 && y<height()) {
1705 QCanvasChunk& chunk=chunkContaining(x,y);
1706 chunk.change();
1707 }
1708}
1709
1710/*!
1711\internal
1712This method adds the QCanvasItem \a g to the list of those which need to be
1713drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like
1714SetChangedChunk and SetChangedChunkContaining, this method marks the
1715chunk as `dirty'.
1716*/
1717void QCanvas::addItemToChunk(QCanvasItem* g, int x, int y)
1718{
1719 if (validChunk(x,y)) {
1720 chunk(x,y).add(g);
1721 }
1722}
1723
1724/*!
1725\internal
1726This method removes the QCanvasItem \a g from the list of those which need to
1727be drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like
1728SetChangedChunk and SetChangedChunkContaining, this method marks the chunk
1729as `dirty'.
1730*/
1731void QCanvas::removeItemFromChunk(QCanvasItem* g, int x, int y)
1732{
1733 if (validChunk(x,y)) {
1734 chunk(x,y).remove(g);
1735 }
1736}
1737
1738
1739/*!
1740\internal
1741This method adds the QCanvasItem \a g to the list of those which need to be
1742drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn. Like
1743SetChangedChunk and SetChangedChunkContaining, this method marks the
1744chunk as `dirty'.
1745*/
1746void QCanvas::addItemToChunkContaining(QCanvasItem* g, int x, int y)
1747{
1748 if (x>=0 && x<width() && y>=0 && y<height()) {
1749 chunkContaining(x,y).add(g);
1750 }
1751}
1752
1753/*!
1754\internal
1755This method removes the QCanvasItem \a g from the list of those which need to
1756be drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn.
1757Like SetChangedChunk and SetChangedChunkContaining, this method
1758marks the chunk as `dirty'.
1759*/
1760void QCanvas::removeItemFromChunkContaining(QCanvasItem* g, int x, int y)
1761{
1762 if (x>=0 && x<width() && y>=0 && y<height()) {
1763 chunkContaining(x,y).remove(g);
1764 }
1765}
1766
1767/*!
1768 Returns the color set by setBackgroundColor(). By default, this is
1769 white.
1770
1771 This function is not a reimplementation of
1772 QWidget::backgroundColor() (QCanvas is not a subclass of QWidget),
1773 but all QCanvasViews that are viewing the canvas will set their
1774 backgrounds to this color.
1775
1776 \sa setBackgroundColor(), backgroundPixmap()
1777*/
1778QColor QCanvas::backgroundColor() const
1779{
1780 return bgcolor;
1781}
1782
1783/*!
1784 Sets the solid background to be the color \a c.
1785
1786 \sa backgroundColor(), setBackgroundPixmap(), setTiles()
1787*/
1788void QCanvas::setBackgroundColor( const QColor& c )
1789{
1790 if ( bgcolor != c ) {
1791 bgcolor = c;
1792 QCanvasView* view=d->viewList.first();
1793 while ( view != 0 ) {
1794 /* XXX this doesn't look right. Shouldn't this
1795 be more like setBackgroundPixmap? : Ian */
1796 view->viewport()->setEraseColor( bgcolor );
1797 view=d->viewList.next();
1798 }
1799 setAllChanged();
1800 }
1801}
1802
1803/*!
1804 Returns the pixmap set by setBackgroundPixmap(). By default,
1805 this is a null pixmap.
1806
1807 \sa setBackgroundPixmap(), backgroundColor()
1808*/
1809QPixmap QCanvas::backgroundPixmap() const
1810{
1811 return pm;
1812}
1813
1814/*!
1815 Sets the solid background to be the pixmap \a p repeated as
1816 necessary to cover the entire canvas.
1817
1818 \sa backgroundPixmap(), setBackgroundColor(), setTiles()
1819*/
1820void QCanvas::setBackgroundPixmap( const QPixmap& p )
1821{
1822 setTiles(p, 1, 1, p.width(), p.height());
1823 QCanvasView* view = d->viewList.first();
1824 while ( view != 0 ) {
1825 view->updateContents();
1826 view = d->viewList.next();
1827 }
1828}
1829
1830/*!
1831 This virtual function is called for all updates of the canvas. It
1832 renders any background graphics using the painter \a painter, in
1833 the area \a clip. If the canvas has a background pixmap or a tiled
1834 background, that graphic is used, otherwise the canvas is cleared
1835 using the background color.
1836
1837 If the graphics for an area change, you must explicitly call
1838 setChanged(const QRect&) for the result to be visible when
1839 update() is next called.
1840
1841 \sa setBackgroundColor(), setBackgroundPixmap(), setTiles()
1842*/
1843void QCanvas::drawBackground(QPainter& painter, const QRect& clip)
1844{
1845 if ( pm.isNull() ) {
1846 painter.fillRect(clip,bgcolor);
1847 } else if ( !grid ) {
1848 for (int x=clip.x()/pm.width();
1849 x<(clip.x()+clip.width()+pm.width()-1)/pm.width(); x++)
1850 {
1851 for (int y=clip.y()/pm.height();
1852 y<(clip.y()+clip.height()+pm.height()-1)/pm.height(); y++)
1853 {
1854 painter.drawPixmap(x*pm.width(), y*pm.height(),pm);
1855 }
1856 }
1857 } else {
1858 const int x1 = clip.left()/tilew;
1859 int x2 = clip.right()/tilew;
1860 const int y1 = clip.top()/tileh;
1861 int y2 = clip.bottom()/tileh;
1862
1863 const int roww = pm.width()/tilew;
1864
1865 for (int j=y1; j<=y2; j++) {
1866 int jj = j%tilesVertically();
1867 for (int i=x1; i<=x2; i++) {
1868 int t = tile(i%tilesHorizontally(), jj);
1869 int tx = t % roww;
1870 int ty = t / roww;
1871 painter.drawPixmap( i*tilew, j*tileh, pm,
1872 tx*tilew, ty*tileh, tilew, tileh );
1873 }
1874 }
1875 }
1876}
1877
1878/*!
1879 This virtual function is called for all updates of the canvas. It
1880 renders any foreground graphics using the painter \a painter, in
1881 the area \a clip.
1882
1883 If the graphics for an area change, you must explicitly call
1884 setChanged(const QRect&) for the result to be visible when
1885 update() is next called.
1886
1887 The default is to draw nothing.
1888*/
1889void QCanvas::drawForeground(QPainter& painter, const QRect& clip)
1890{
1891 if ( debug_redraw_areas ) {
1892 painter.setPen(red);
1893 painter.setBrush(NoBrush);
1894 painter.drawRect(clip);
1895 }
1896}
1897
1898/*!
1899 If \a y is TRUE (the default) double-buffering is switched on;
1900 otherwise double-buffering is switched off.
1901
1902 Turning off double-buffering causes the redrawn areas to flicker a
1903 little and also gives a (usually small) performance improvement.
1904*/
1905void QCanvas::setDoubleBuffering(bool y)
1906{
1907 dblbuf = y;
1908}
1909
1910
1911/*!
1912 Sets the QCanvas to be composed of \a h tiles horizontally and \a
1913 v tiles vertically. Each tile will be an image \a tilewidth by \a
1914 tileheight pixels from pixmap \a p.
1915
1916 The pixmap \a p is a list of tiles, arranged left to right, (and
1917 in the case of pixmaps that have multiple rows of tiles, top to
1918 bottom), with tile 0 in the top-left corner, tile 1 next to the
1919 right, and so on, e.g.
1920
1921 \table
1922 \row \i 0 \i 1 \i 2 \i 3
1923 \row \i 4 \i 5 \i 6 \i 7
1924 \endtable
1925
1926 If the canvas is larger than the matrix of tiles, the entire
1927 matrix is repeated as necessary to cover the whole canvas. If it
1928 is smaller, tiles to the right and bottom are not visible.
1929
1930 The width and height of \a p must be a multiple of \a tilewidth
1931 and \a tileheight. If they are not the function will do nothing.
1932
1933 If you want to unset any tiling set, then just pass in a null
1934 pixmap and 0 for \a h, \a v, \a tilewidth, and
1935 \a tileheight.
1936*/
1937void QCanvas::setTiles( QPixmap p,
1938 int h, int v, int tilewidth, int tileheight )
1939{
1940 if ( !p.isNull() && (!tilewidth || !tileheight ||
1941 p.width() % tilewidth != 0 || p.height() % tileheight != 0 ) )
1942 return;
1943
1944 htiles = h;
1945 vtiles = v;
1946 delete[] grid;
1947 pm = p;
1948 if ( h && v && !p.isNull() ) {
1949 grid = new ushort[h*v];
1950 memset( grid, 0, h*v*sizeof(ushort) );
1951 tilew = tilewidth;
1952 tileh = tileheight;
1953 } else {
1954 grid = 0;
1955 }
1956 if ( h + v > 10 ) {
1957 int s = scm(tilewidth,tileheight);
1958 retune( s < 128 ? s : QMAX(tilewidth,tileheight) );
1959 }
1960 setAllChanged();
1961}
1962
1963/*!
1964 \fn int QCanvas::tile( int x, int y ) const
1965
1966 Returns the tile at position (\a x, \a y). Initially, all tiles
1967 are 0.
1968
1969 The parameters must be within range, i.e.
1970 0 \< \a x \< tilesHorizontally() and
1971 0 \< \a y \< tilesVertically().
1972
1973 \sa setTile()
1974*/
1975
1976/*!
1977 \fn int QCanvas::tilesHorizontally() const
1978
1979 Returns the number of tiles horizontally.
1980*/
1981
1982/*!
1983 \fn int QCanvas::tilesVertically() const
1984
1985 Returns the number of tiles vertically.
1986*/
1987
1988/*!
1989 \fn int QCanvas::tileWidth() const
1990
1991 Returns the width of each tile.
1992*/
1993
1994/*!
1995 \fn int QCanvas::tileHeight() const
1996
1997 Returns the height of each tile.
1998*/
1999
2000
2001/*!
2002 Sets the tile at (\a x, \a y) to use tile number \a tilenum, which
2003 is an index into the tile pixmaps. The canvas will update
2004 appropriately when update() is next called.
2005
2006 The images are taken from the pixmap set by setTiles() and are
2007 arranged left to right, (and in the case of pixmaps that have
2008 multiple rows of tiles, top to bottom), with tile 0 in the
2009 top-left corner, tile 1 next to the right, and so on, e.g.
2010
2011 \table
2012 \row \i 0 \i 1 \i 2 \i 3
2013 \row \i 4 \i 5 \i 6 \i 7
2014 \endtable
2015
2016 \sa tile() setTiles()
2017*/
2018void QCanvas::setTile( int x, int y, int tilenum )
2019{
2020 ushort& t = grid[x+y*htiles];
2021 if ( t != tilenum ) {
2022 t = tilenum;
2023 if ( tilew == tileh && tilew == chunksize )
2024 setChangedChunk( x, y ); // common case
2025 else
2026 setChanged( QRect(x*tilew,y*tileh,tilew,tileh) );
2027 }
2028}
2029
2030
2031// lesser-used data in canvas item, plus room for extension.
2032// Be careful adding to this - check all usages.
2033class QCanvasItemExtra {
2034 QCanvasItemExtra() : vx(0.0), vy(0.0) { }
2035 double vx,vy;
2036 friend class QCanvasItem;
2037};
2038
2039
2040/*!
2041 \class QCanvasItem qcanvas.h
2042 \brief The QCanvasItem class provides an abstract graphic object on a QCanvas.
2043\if defined(commercial)
2044 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
2045\endif
2046
2047 \module canvas
2048 \ingroup graphics
2049 \ingroup images
2050
2051 A variety of QCanvasItem subclasses provide immediately usable
2052 behaviour. This class is a pure abstract superclass providing the
2053 behaviour that is shared among all the concrete canvas item classes.
2054 QCanvasItem is not intended for direct subclassing. It is much easier
2055 to subclass one of its subclasses, e.g. QCanvasPolygonalItem (the
2056 commonest base class), QCanvasRectangle, QCanvasSprite, QCanvasEllipse
2057 or QCanvasText.
2058
2059 Canvas items are added to a canvas by constructing them and passing the
2060 canvas to the canvas item's constructor. An item can be moved to a
2061 different canvas using setCanvas().
2062
2063 Items appear on the canvas after their \link show() show()\endlink
2064 function has been called (or \link setVisible()
2065 setVisible(TRUE)\endlink), and \e after update() has been called. The
2066 canvas only shows items that are \link setVisible() visible\endlink,
2067 and then only if \l update() is called. If you created the canvas
2068 without passing a width and height to the constructor you'll also need
2069 to call \link QCanvas::resize() resize()\endlink. Since the canvas
2070 background defaults to white and canvas items default to white,
2071 you may need to change colors to see your items.
2072
2073 A QCanvasItem object can be moved in the x(), y() and z() dimensions
2074 using functions such as move(), moveBy(), setX(), setY() and setZ(). A
2075 canvas item can be set in motion, `animated', using setAnimated() and
2076 given a velocity in the x and y directions with setXVelocity() and
2077 setYVelocity() -- the same effect can be achieved by calling
2078 setVelocity(). Use the collidesWith() function to see if the canvas item
2079 will collide on the \e next advance(1) and use collisions() to see what
2080 collisions have occurred.
2081
2082 Use QCanvasSprite or your own subclass of QCanvasSprite to create canvas
2083 items which are animated, i.e. which change over time.
2084
2085 The size of a canvas item is given by boundingRect(). Use
2086 boundingRectAdvanced() to see what the size of the canvas item will be
2087 \e after the next advance(1) call.
2088
2089 The rtti() function is used for identifying subclasses of QCanvasItem.
2090 The canvas() function returns a pointer to the canvas which contains the
2091 canvas item.
2092
2093 QCanvasItem provides the show() and isVisible() functions like those in
2094 QWidget.
2095
2096 QCanvasItem also provides the setEnabled(), setActive() and
2097 setSelected() functions; these functions set the relevant boolean and
2098 cause a repaint but the boolean values they set are not used in
2099 QCanvasItem itself. You can make use of these booleans in your subclasses.
2100
2101 By default, canvas items have no velocity, no size, and are not in
2102 motion. The subclasses provided in Qt do not change these defaults
2103 except where noted.
2104
2105*/
2106
2107/*!
2108 \enum QCanvasItem::RttiValues
2109
2110 This enum is used to name the different types of canvas item.
2111
2112 \value Rtti_Item Canvas item abstract base class
2113 \value Rtti_Ellipse
2114 \value Rtti_Line
2115 \value Rtti_Polygon
2116 \value Rtti_PolygonalItem
2117 \value Rtti_Rectangle
2118 \value Rtti_Spline
2119 \value Rtti_Sprite
2120 \value Rtti_Text
2121
2122*/
2123
2124/*!
2125 \fn void QCanvasItem::update()
2126
2127 Call this function to repaint the canvas's changed chunks.
2128*/
2129
2130/*!
2131 Constructs a QCanvasItem on canvas \a canvas.
2132
2133 \sa setCanvas()
2134*/
2135QCanvasItem::QCanvasItem(QCanvas* canvas) :
2136 cnv(canvas),
2137 myx(0),myy(0),myz(0)
2138{
2139 ani=0;
2140 vis=0;
2141 val=0;
2142 sel=0;
2143 ena=0;
2144 act=0;
2145
2146 ext = 0;
2147 if (cnv) cnv->addItem(this);
2148}
2149
2150/*!
2151 Destroys the QCanvasItem and removes it from its canvas.
2152*/
2153QCanvasItem::~QCanvasItem()
2154{
2155 if (cnv) {
2156 cnv->removeItem(this);
2157 cnv->removeAnimation(this);
2158 }
2159 delete ext;
2160}
2161
2162QCanvasItemExtra& QCanvasItem::extra()
2163{
2164 if ( !ext )
2165 ext = new QCanvasItemExtra;
2166 return *ext;
2167}
2168
2169/*!
2170 \fn double QCanvasItem::x() const
2171
2172 Returns the horizontal position of the canvas item. Note that
2173 subclasses often have an origin other than the top-left corner.
2174*/
2175
2176/*!
2177 \fn double QCanvasItem::y() const
2178
2179 Returns the vertical position of the canvas item. Note that
2180 subclasses often have an origin other than the top-left corner.
2181*/
2182
2183/*!
2184 \fn double QCanvasItem::z() const
2185
2186 Returns the z index of the canvas item, which is used for visual
2187 order: higher-z items obscure (are in front of) lower-z items.
2188*/
2189
2190/*!
2191 \fn void QCanvasItem::setX(double x)
2192
2193 Moves the canvas item so that its x-position is \a x.
2194
2195 \sa x(), move()
2196*/
2197
2198/*!
2199 \fn void QCanvasItem::setY(double y)
2200
2201 Moves the canvas item so that its y-position is \a y.
2202
2203 \sa y(), move()
2204*/
2205
2206/*!
2207 \fn void QCanvasItem::setZ(double z)
2208
2209 Sets the z index of the canvas item to \a z. Higher-z items
2210 obscure (are in front of) lower-z items.
2211
2212 \sa z(), move()
2213*/
2214
2215
2216/*!
2217 Moves the canvas item relative to its current position by (\a dx,
2218 \a dy).
2219*/
2220void QCanvasItem::moveBy( double dx, double dy )
2221{
2222 if ( dx || dy ) {
2223 removeFromChunks();
2224 myx += dx;
2225 myy += dy;
2226 addToChunks();
2227 }
2228}
2229
2230
2231/*!
2232 Moves the canvas item to the absolute position (\a x, \a y).
2233*/
2234void QCanvasItem::move( double x, double y )
2235{
2236 moveBy( x-myx, y-myy );
2237}
2238
2239
2240/*!
2241 Returns TRUE if the canvas item is in motion; otherwise returns
2242 FALSE.
2243
2244 \sa setVelocity(), setAnimated()
2245*/
2246bool QCanvasItem::animated() const
2247{
2248 return (bool)ani;
2249}
2250
2251/*!
2252 Sets the canvas item to be in motion if \a y is TRUE, or not if \a
2253 y is FALSE. The speed and direction of the motion is set with
2254 setVelocity(), or with setXVelocity() and setYVelocity().
2255
2256 \sa advance(), QCanvas::advance()
2257*/
2258void QCanvasItem::setAnimated(bool y)
2259{
2260 if ( y != (bool)ani ) {
2261 ani = (uint)y;
2262 if ( y ) {
2263 cnv->addAnimation(this);
2264 } else {
2265 cnv->removeAnimation(this);
2266 }
2267 }
2268}
2269
2270/*!
2271 \fn void QCanvasItem::setXVelocity( double vx )
2272
2273 Sets the horizontal component of the canvas item's velocity to \a vx.
2274
2275 \sa setYVelocity() setVelocity()
2276*/
2277
2278/*!
2279 \fn void QCanvasItem::setYVelocity( double vy )
2280
2281 Sets the vertical component of the canvas item's velocity to \a vy.
2282
2283 \sa setXVelocity() setVelocity()
2284*/
2285
2286/*!
2287 Sets the canvas item to be in motion, moving by \a vx and \a vy
2288 pixels in the horizontal and vertical directions respectively.
2289
2290 \sa advance() setXVelocity() setYVelocity()
2291*/
2292void QCanvasItem::setVelocity( double vx, double vy)
2293{
2294 if ( ext || vx!=0.0 || vy!=0.0 ) {
2295 if ( !ani )
2296 setAnimated(TRUE);
2297 extra().vx = vx;
2298 extra().vy = vy;
2299 }
2300}
2301
2302/*!
2303 Returns the horizontal velocity component of the canvas item.
2304*/
2305double QCanvasItem::xVelocity() const
2306{
2307 return ext ? ext->vx : 0;
2308}
2309
2310/*!
2311 Returns the vertical velocity component of the canvas item.
2312*/
2313double QCanvasItem::yVelocity() const
2314{
2315 return ext ? ext->vy : 0;
2316}
2317
2318/*!
2319 The default implementation moves the canvas item, if it is
2320 animated(), by the preset velocity if \a phase is 1, and does
2321 nothing if \a phase is 0.
2322
2323 Note that if you reimplement this function, the reimplementation
2324 must not change the canvas in any way, for example it must not add
2325 or remove items.
2326
2327 \sa QCanvas::advance() setVelocity()
2328*/
2329void QCanvasItem::advance(int phase)
2330{
2331 if ( ext && phase==1 )
2332 moveBy(ext->vx,ext->vy);
2333}
2334
2335/*!
2336 \fn void QCanvasItem::draw(QPainter& painter)
2337
2338 This abstract virtual function draws the canvas item using \a painter.
2339*/
2340
2341/*!
2342 Sets the QCanvas upon which the canvas item is to be drawn to \a c.
2343
2344 \sa canvas()
2345*/
2346void QCanvasItem::setCanvas(QCanvas* c)
2347{
2348 bool v=isVisible();
2349 setVisible(FALSE);
2350 if (cnv) {
2351 cnv->removeItem(this);
2352 }
2353 cnv=c;
2354 if (cnv) {
2355 cnv->addItem(this);
2356 if ( ext )
2357 cnv->addAnimation(this);
2358 }
2359 setVisible(v);
2360}
2361
2362/*!
2363 \fn QCanvas* QCanvasItem::canvas() const
2364
2365 Returns the canvas containing the canvas item.
2366*/
2367
2368/*! Shorthand for setVisible(TRUE). */
2369void QCanvasItem::show()
2370{
2371 setVisible(TRUE);
2372}
2373
2374/*! Shorthand for setVisible(FALSE). */
2375void QCanvasItem::hide()
2376{
2377 setVisible(FALSE);
2378}
2379
2380/*!
2381 Makes the canvas item visible if \a yes is TRUE, or invisible if
2382 \a yes is FALSE. The change takes effect when QCanvas::update() is
2383 next called.
2384*/
2385void QCanvasItem::setVisible(bool yes)
2386{
2387 if ((bool)vis!=yes) {
2388 if (yes) {
2389 vis=(uint)yes;
2390 addToChunks();
2391 } else {
2392 removeFromChunks();
2393 vis=(uint)yes;
2394 }
2395 }
2396}
2397/*!
2398 \obsolete
2399 \fn bool QCanvasItem::visible() const
2400 Use isVisible() instead.
2401*/
2402
2403/*!
2404 \fn bool QCanvasItem::isVisible() const
2405
2406 Returns TRUE if the canvas item is visible; otherwise returns
2407 FALSE.
2408
2409 Note that in this context TRUE does \e not mean that the canvas
2410 item is currently in a view, merely that if a view is showing the
2411 area where the canvas item is positioned, and the item is not
2412 obscured by items with higher z values, and the view is not
2413 obscured by overlaying windows, it would be visible.
2414
2415 \sa setVisible(), z()
2416*/
2417
2418/*!
2419 \obsolete
2420 \fn bool QCanvasItem::selected() const
2421 Use isSelected() instead.
2422*/
2423
2424/*!
2425 \fn bool QCanvasItem::isSelected() const
2426
2427 Returns TRUE if the canvas item is selected; otherwise returns FALSE.
2428*/
2429
2430/*!
2431 Sets the selected flag of the item to \a yes. If this changes the
2432 item's selected state the item will be redrawn when
2433 QCanvas::update() is next called.
2434
2435 The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
2436 subclasses do not make use of this value. The setSelected()
2437 function is supplied because many applications need it, but it is
2438 up to you how you use the isSelected() value.
2439*/
2440void QCanvasItem::setSelected(bool yes)
2441{
2442 if ((bool)sel!=yes) {
2443 sel=(uint)yes;
2444 changeChunks();
2445 }
2446}
2447
2448/*!
2449 \obsolete
2450 \fn bool QCanvasItem::enabled() const
2451 Use isEnabled() instead.
2452*/
2453
2454/*!
2455 \fn bool QCanvasItem::isEnabled() const
2456
2457 Returns TRUE if the QCanvasItem is enabled; otherwise returns FALSE.
2458*/
2459
2460/*!
2461 Sets the enabled flag of the item to \a yes. If this changes the
2462 item's enabled state the item will be redrawn when
2463 QCanvas::update() is next called.
2464
2465 The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
2466 subclasses do not make use of this value. The setEnabled()
2467 function is supplied because many applications need it, but it is
2468 up to you how you use the isEnabled() value.
2469*/
2470void QCanvasItem::setEnabled(bool yes)
2471{
2472 if (ena!=(uint)yes) {
2473 ena=(uint)yes;
2474 changeChunks();
2475 }
2476}
2477
2478/*!
2479 \obsolete
2480 \fn bool QCanvasItem::active() const
2481 Use isActive() instead.
2482*/
2483
2484/*!
2485 \fn bool QCanvasItem::isActive() const
2486
2487 Returns TRUE if the QCanvasItem is active; otherwise returns FALSE.
2488*/
2489
2490/*!
2491 Sets the active flag of the item to \a yes. If this changes the
2492 item's active state the item will be redrawn when
2493 QCanvas::update() is next called.
2494
2495 The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
2496 subclasses do not make use of this value. The setActive() function
2497 is supplied because many applications need it, but it is up to you
2498 how you use the isActive() value.
2499*/
2500void QCanvasItem::setActive(bool yes)
2501{
2502 if (act!=(uint)yes) {
2503 act=(uint)yes;
2504 changeChunks();
2505 }
2506}
2507
2508bool qt_testCollision(const QCanvasSprite* s1, const QCanvasSprite* s2)
2509{
2510 const QImage* s2image = s2->imageAdvanced()->collision_mask;
2511 QRect s2area = s2->boundingRectAdvanced();
2512
2513 QRect cyourarea(s2area.x(),s2area.y(),
2514 s2area.width(),s2area.height());
2515
2516 QImage* s1image=s1->imageAdvanced()->collision_mask;
2517
2518 QRect s1area = s1->boundingRectAdvanced();
2519
2520 QRect ourarea = s1area.intersect(cyourarea);
2521
2522 if ( ourarea.isEmpty() )
2523 return FALSE;
2524
2525 int x2=ourarea.x()-cyourarea.x();
2526 int y2=ourarea.y()-cyourarea.y();
2527 int x1=ourarea.x()-s1area.x();
2528 int y1=ourarea.y()-s1area.y();
2529 int w=ourarea.width();
2530 int h=ourarea.height();
2531
2532 if ( !s2image ) {
2533 if ( !s1image )
2534 return w>0 && h>0;
2535 // swap everything around
2536 int t;
2537 t=x1; x1=x2; x2=t;
2538 t=y1; x1=y2; y2=t;
2539 s2image = s1image;
2540 s1image = 0;
2541 }
2542
2543 // s2image != 0
2544
2545 // A non-linear search may be more efficient.
2546 // Perhaps spiralling out from the center, or a simpler
2547 // vertical expansion from the centreline.
2548
2549 // We assume that sprite masks don't have
2550 // different bit orders.
2551 //
2552 // Q_ASSERT(s1image->bitOrder()==s2image->bitOrder());
2553
2554 if (s1image) {
2555 if (s1image->bitOrder() == QImage::LittleEndian) {
2556 for (int j=0; j<h; j++) {
2557 uchar* ml = s1image->scanLine(y1+j);
2558 uchar* yl = s2image->scanLine(y2+j);
2559 for (int i=0; i<w; i++) {
2560 if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))
2561 && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7)))
2562 {
2563 return TRUE;
2564 }
2565 }
2566 }
2567 } else {
2568 for (int j=0; j<h; j++) {
2569 uchar* ml = s1image->scanLine(y1+j);
2570 uchar* yl = s2image->scanLine(y2+j);
2571 for (int i=0; i<w; i++) {
2572 if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))
2573 && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7))))
2574 {
2575 return TRUE;
2576 }
2577 }
2578 }
2579 }
2580 } else {
2581 if (s2image->bitOrder() == QImage::LittleEndian) {
2582 for (int j=0; j<h; j++) {
2583 uchar* yl = s2image->scanLine(y2+j);
2584 for (int i=0; i<w; i++) {
2585 if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)))
2586 {
2587 return TRUE;
2588 }
2589 }
2590 }
2591 } else {
2592 for (int j=0; j<h; j++) {
2593 uchar* yl = s2image->scanLine(y2+j);
2594 for (int i=0; i<w; i++) {
2595 if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))))
2596 {
2597 return TRUE;
2598 }
2599 }
2600 }
2601 }
2602 }
2603
2604 return FALSE;
2605}
2606
2607static bool collision_double_dispatch( const QCanvasSprite* s1,
2608 const QCanvasPolygonalItem* p1,
2609 const QCanvasRectangle* r1,
2610 const QCanvasEllipse* e1,
2611 const QCanvasText* t1,
2612 const QCanvasSprite* s2,
2613 const QCanvasPolygonalItem* p2,
2614 const QCanvasRectangle* r2,
2615 const QCanvasEllipse* e2,
2616 const QCanvasText* t2 )
2617{
2618 const QCanvasItem* i1 = s1 ?
2619 (const QCanvasItem*)s1 : p1 ?
2620 (const QCanvasItem*)p1 : r1 ?
2621 (const QCanvasItem*)r1 : e1 ?
2622 (const QCanvasItem*)e1 : (const QCanvasItem*)t1;
2623 const QCanvasItem* i2 = s2 ?
2624 (const QCanvasItem*)s2 : p2 ?
2625 (const QCanvasItem*)p2 : r2 ?
2626 (const QCanvasItem*)r2 : e2 ?
2627 (const QCanvasItem*)e2 : (const QCanvasItem*)t2;
2628
2629 if ( s1 && s2 ) {
2630 // a
2631 return qt_testCollision(s1,s2);
2632 } else if ( (r1 || t1 || s1) && (r2 || t2 || s2) ) {
2633 // b
2634 QRect rc1 = i1->boundingRectAdvanced();
2635 QRect rc2 = i2->boundingRectAdvanced();
2636 return rc1.intersects(rc2);
2637 } else if ( e1 && e2
2638 && e1->angleLength()>=360*16 && e2->angleLength()>=360*16
2639 && e1->width()==e1->height()
2640 && e2->width()==e2->height() ) {
2641 // c
2642 double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity());
2643 double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity());
2644 double rd = (e1->width()+e2->width())/2;
2645 return xd*xd+yd*yd <= rd*rd;
2646 } else if ( p1 && (p2 || s2 || t2) ) {
2647 // d
2648 QPointArray pa1 = p1->areaPointsAdvanced();
2649 QPointArray pa2 = p2 ? p2->areaPointsAdvanced()
2650 : QPointArray(i2->boundingRectAdvanced());
2651 bool col= !(QRegion(pa1) & QRegion(pa2,TRUE)).isEmpty();
2652
2653 return col;
2654 } else {
2655 return collision_double_dispatch(s2,p2,r2,e2,t2,
2656 s1,p1,r1,e1,t1);
2657 }
2658}
2659
2660/*!
2661 \fn bool QCanvasItem::collidesWith( const QCanvasItem* other ) const
2662
2663 Returns TRUE if the canvas item will collide with the \a other
2664 item \e after they have moved by their current velocities;
2665 otherwise returns FALSE.
2666
2667 \sa collisions()
2668*/
2669
2670
2671/*!
2672 \class QCanvasSprite qcanvas.h
2673 \brief The QCanvasSprite class provides an animated canvas item on a QCanvas.
2674\if defined(commercial)
2675 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
2676\endif
2677
2678 \module canvas
2679 \ingroup graphics
2680 \ingroup images
2681
2682 A canvas sprite is an object which can contain any number of images
2683 (referred to as frames), only one of which is current, i.e.
2684 displayed, at any one time. The images can be passed in the
2685 constructor or set or changed later with setSequence(). If you
2686 subclass QCanvasSprite you can change the frame that is displayed
2687 periodically, e.g. whenever QCanvasItem::advance(1) is called to
2688 create the effect of animation.
2689
2690 The current frame can be set with setFrame() or with move(). The
2691 number of frames available is given by frameCount(). The bounding
2692 rectangle of the current frame is returned by boundingRect().
2693
2694 The current frame's image can be retrieved with image(); use
2695 imageAdvanced() to retrieve the image for the frame that will be
2696 shown after advance(1) is called. Use the image() overload passing
2697 it an integer index to retrieve a particular image from the list of
2698 frames.
2699
2700 Use width() and height() to retrieve the dimensions of the current
2701 frame.
2702
2703 Use leftEdge() and rightEdge() to retrieve the current frame's
2704 left-hand and right-hand x-coordinates respectively. Use
2705 bottomEdge() and topEdge() to retrieve the current frame's bottom
2706 and top y-coordinates respectively. These functions have an overload
2707 which will accept an integer frame number to retrieve the
2708 coordinates of a particular frame.
2709
2710 QCanvasSprite draws very quickly, at the expense of memory.
2711
2712 The current frame's image can be drawn on a painter with draw().
2713
2714 Like any other canvas item, canvas sprites can be moved with
2715 move() which sets the x and y coordinates and the frame number, as
2716 well as with QCanvasItem::move() and QCanvasItem::moveBy(), or by
2717 setting coordinates with QCanvasItem::setX(), QCanvasItem::setY()
2718 and QCanvasItem::setZ().
2719
2720*/
2721
2722
2723/*!
2724 \reimp
2725*/
2726bool QCanvasSprite::collidesWith( const QCanvasItem* i ) const
2727{
2728 return i->collidesWith(this,0,0,0,0);
2729}
2730
2731/*!
2732 Returns TRUE if the canvas item collides with any of the given
2733 items; otherwise returns FALSE. The parameters, \a s, \a p, \a r,
2734 \a e and \a t, are all the same object, this is just a type
2735 resolution trick.
2736*/
2737bool QCanvasSprite::collidesWith( const QCanvasSprite* s,
2738 const QCanvasPolygonalItem* p,
2739 const QCanvasRectangle* r,
2740 const QCanvasEllipse* e,
2741 const QCanvasText* t ) const
2742{
2743 return collision_double_dispatch(s,p,r,e,t,this,0,0,0,0);
2744}
2745
2746/*!
2747 \reimp
2748*/
2749bool QCanvasPolygonalItem::collidesWith( const QCanvasItem* i ) const
2750{
2751 return i->collidesWith(0,this,0,0,0);
2752}
2753
2754bool QCanvasPolygonalItem::collidesWith( const QCanvasSprite* s,
2755 const QCanvasPolygonalItem* p,
2756 const QCanvasRectangle* r,
2757 const QCanvasEllipse* e,
2758 const QCanvasText* t ) const
2759{
2760 return collision_double_dispatch(s,p,r,e,t,0,this,0,0,0);
2761}
2762
2763/*!
2764 \reimp
2765*/
2766bool QCanvasRectangle::collidesWith( const QCanvasItem* i ) const
2767{
2768 return i->collidesWith(0,this,this,0,0);
2769}
2770
2771bool QCanvasRectangle::collidesWith( const QCanvasSprite* s,
2772 const QCanvasPolygonalItem* p,
2773 const QCanvasRectangle* r,
2774 const QCanvasEllipse* e,
2775 const QCanvasText* t ) const
2776{
2777 return collision_double_dispatch(s,p,r,e,t,0,this,this,0,0);
2778}
2779
2780
2781/*!
2782 \reimp
2783*/
2784bool QCanvasEllipse::collidesWith( const QCanvasItem* i ) const
2785{
2786 return i->collidesWith(0,this,0,this,0);
2787}
2788
2789bool QCanvasEllipse::collidesWith( const QCanvasSprite* s,
2790 const QCanvasPolygonalItem* p,
2791 const QCanvasRectangle* r,
2792 const QCanvasEllipse* e,
2793 const QCanvasText* t ) const
2794{
2795 return collision_double_dispatch(s,p,r,e,t,0,this,0,this,0);
2796}
2797
2798/*!
2799 \reimp
2800*/
2801bool QCanvasText::collidesWith( const QCanvasItem* i ) const
2802{
2803 return i->collidesWith(0,0,0,0,this);
2804}
2805
2806bool QCanvasText::collidesWith( const QCanvasSprite* s,
2807 const QCanvasPolygonalItem* p,
2808 const QCanvasRectangle* r,
2809 const QCanvasEllipse* e,
2810 const QCanvasText* t ) const
2811{
2812 return collision_double_dispatch(s,p,r,e,t,0,0,0,0,this);
2813}
2814
2815/*!
2816 Returns the list of canvas items that this canvas item has
2817 collided with.
2818
2819 A collision is generally defined as occurring when the pixels of
2820 one item draw on the pixels of another item, but not all
2821 subclasses are so precise. Also, since pixel-wise collision
2822 detection can be slow, this function works in either exact or
2823 inexact mode, according to the \a exact parameter.
2824
2825 If \a exact is TRUE, the canvas items returned have been
2826 accurately tested for collision with the canvas item.
2827
2828 If \a exact is FALSE, the canvas items returned are \e near the
2829 canvas item. You can test the canvas items returned using
2830 collidesWith() if any are interesting collision candidates. By
2831 using this approach, you can ignore some canvas items for which
2832 collisions are not relevant.
2833
2834 The returned list is a list of QCanvasItems, but often you will
2835 need to cast the items to their subclass types. The safe way to do
2836 this is to use rtti() before casting. This provides some of the
2837 functionality of the standard C++ dynamic cast operation even on
2838 compilers where dynamic casts are not available.
2839
2840 Note that a canvas item may be `on' a canvas, e.g. it was created
2841 with the canvas as parameter, even though its coordinates place it
2842 beyond the edge of the canvas's area. Collision detection only
2843 works for canvas items which are wholly or partly within the
2844 canvas's area.
2845
2846 Note that if items have a velocity (see \l setVelocity()), then
2847 collision testing is done based on where the item \e will be when
2848 it moves, not its current location. For example, a "ball" item
2849 doesn't need to actually embed into a "wall" item before a
2850 collision is detected. For items without velocity, plain
2851 intersection is used.
2852*/
2853QCanvasItemList QCanvasItem::collisions(bool exact) const
2854{
2855 return canvas()->collisions(chunks(),this,exact);
2856}
2857
2858/*!
2859 Returns a list of canvas items that collide with the point \a p.
2860 The list is ordered by z coordinates, from highest z coordinate
2861 (front-most item) to lowest z coordinate (rear-most item).
2862*/
2863QCanvasItemList QCanvas::collisions(const QPoint& p) const
2864{
2865 return collisions(QRect(p,QSize(1,1)));
2866}
2867
2868/*!
2869 \overload
2870
2871 Returns a list of items which collide with the rectangle \a r. The
2872 list is ordered by z coordinates, from highest z coordinate
2873 (front-most item) to lowest z coordinate (rear-most item).
2874*/
2875QCanvasItemList QCanvas::collisions(const QRect& r) const
2876{
2877 QCanvasRectangle i(r,(QCanvas*)this);
2878 i.setPen(NoPen);
2879 i.show(); // doesn't actually show, since we destroy it
2880 QCanvasItemList l = i.collisions(TRUE);
2881 l.sort();
2882 return l;
2883}
2884
2885/*!
2886 \overload
2887
2888 Returns a list of canvas items which intersect with the chunks
2889 listed in \a chunklist, excluding \a item. If \a exact is TRUE,
2890 only those which actually \link QCanvasItem::collidesWith()
2891 collide with\endlink \a item are returned; otherwise canvas items
2892 are included just for being in the chunks.
2893
2894 This is a utility function mainly used to implement the simpler
2895 QCanvasItem::collisions() function.
2896*/
2897QCanvasItemList QCanvas::collisions(const QPointArray& chunklist,
2898 const QCanvasItem* item, bool exact) const
2899{
2900 QPtrDict<void> seen;
2901 QCanvasItemList result;
2902 for (int i=0; i<(int)chunklist.count(); i++) {
2903 int x = chunklist[i].x();
2904 int y = chunklist[i].y();
2905 if ( validChunk(x,y) ) {
2906 const QCanvasItemList* l = chunk(x,y).listPtr();
2907 for (QCanvasItemList::ConstIterator it=l->begin(); it!=l->end(); ++it) {
2908 QCanvasItem *g=*it;
2909 if ( g != item ) {
2910 if ( !seen.find(g) ) {
2911 seen.replace(g,(void*)1);
2912 if ( !exact || item->collidesWith(g) )
2913 result.append(g);
2914 }
2915 }
2916 }
2917 }
2918 }
2919 return result;
2920}
2921
2922/*!
2923 \internal
2924 Adds the item to all the chunks it covers.
2925*/
2926void QCanvasItem::addToChunks()
2927{
2928 if (isVisible() && canvas()) {
2929 QPointArray pa = chunks();
2930 for (int i=0; i<(int)pa.count(); i++)
2931 canvas()->addItemToChunk(this,pa[i].x(),pa[i].y());
2932 val=(uint)TRUE;
2933 }
2934}
2935
2936/*!
2937 \internal
2938 Removes the item from all the chunks it covers.
2939*/
2940void QCanvasItem::removeFromChunks()
2941{
2942 if (isVisible() && canvas()) {
2943 QPointArray pa = chunks();
2944 for (int i=0; i<(int)pa.count(); i++)
2945 canvas()->removeItemFromChunk(this,pa[i].x(),pa[i].y());
2946 }
2947}
2948
2949/*!
2950 \internal
2951 Sets all the chunks covered by the item to be refreshed with QCanvas::update()
2952 is next called.
2953*/
2954void QCanvasItem::changeChunks()
2955{
2956 if (isVisible() && canvas()) {
2957 if (!val)
2958 addToChunks();
2959 QPointArray pa = chunks();
2960 for (int i=0; i<(int)pa.count(); i++)
2961 canvas()->setChangedChunk(pa[i].x(),pa[i].y());
2962 }
2963}
2964
2965/*!
2966 \fn QRect QCanvasItem::boundingRect() const
2967
2968 Returns the bounding rectangle in pixels that the canvas item covers.
2969
2970 \sa boundingRectAdvanced()
2971*/
2972
2973/*!
2974 Returns the bounding rectangle of pixels that the canvas item \e
2975 will cover after advance(1) is called.
2976
2977 \sa boundingRect()
2978*/
2979QRect QCanvasItem::boundingRectAdvanced() const
2980{
2981 int dx = int(x()+xVelocity())-int(x());
2982 int dy = int(y()+yVelocity())-int(y());
2983 QRect r = boundingRect();
2984 r.moveBy(dx,dy);
2985 return r;
2986}
2987
2988/*!
2989 \class QCanvasPixmap qcanvas.h
2990 \brief The QCanvasPixmap class provides pixmaps for QCanvasSprites.
2991\if defined(commercial)
2992 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
2993\endif
2994
2995 \module canvas
2996 \ingroup graphics
2997 \ingroup images
2998
2999 If you want to show a single pixmap on a QCanvas use a
3000 QCanvasSprite with just one pixmap.
3001
3002 When pixmaps are inserted into a QCanvasPixmapArray they are held
3003 as QCanvasPixmaps. \l{QCanvasSprite}s are used to show pixmaps on
3004 \l{QCanvas}es and hold their pixmaps in a QCanvasPixmapArray. If
3005 you retrieve a frame (pixmap) from a QCanvasSprite it will be
3006 returned as a QCanvasPixmap.
3007
3008 The pixmap is a QPixmap and can only be set in the constructor.
3009 There are three different constructors, one taking a QPixmap, one
3010 a QImage and one a file name that refers to a file in any
3011 supported file format (see QImageIO).
3012
3013 QCanvasPixmap can have a hotspot which is defined in terms of an (x,
3014 y) offset. When you create a QCanvasPixmap from a PNG file or from
3015 a QImage that has a QImage::offset(), the offset() is initialized
3016 appropriately, otherwise the constructor leaves it at (0, 0). You
3017 can set it later using setOffset(). When the QCanvasPixmap is used
3018 in a QCanvasSprite, the offset position is the point at
3019 QCanvasItem::x() and QCanvasItem::y(), not the top-left corner of
3020 the pixmap.
3021
3022 Note that for QCanvasPixmap objects created by a QCanvasSprite, the
3023 position of each QCanvasPixmap object is set so that the hotspot
3024 stays in the same position.
3025
3026 \sa QCanvasPixmapArray QCanvasItem QCanvasSprite
3027*/
3028
3029#ifndef QT_NO_IMAGEIO
3030
3031/*!
3032 Constructs a QCanvasPixmap that uses the image stored in \a
3033 datafilename.
3034*/
3035QCanvasPixmap::QCanvasPixmap(const QString& datafilename)
3036{
3037 QImage image(datafilename);
3038 init(image);
3039}
3040
3041#endif
3042
3043/*!
3044 Constructs a QCanvasPixmap from the image \a image.
3045*/
3046QCanvasPixmap::QCanvasPixmap(const QImage& image)
3047{
3048 init(image);
3049}
3050/*!
3051 Constructs a QCanvasPixmap from the pixmap \a pm using the offset
3052 \a offset.
3053*/
3054QCanvasPixmap::QCanvasPixmap(const QPixmap& pm, const QPoint& offset)
3055{
3056 init(pm,offset.x(),offset.y());
3057}
3058
3059void QCanvasPixmap::init(const QImage& image)
3060{
3061 convertFromImage(image);
3062 hotx = image.offset().x();
3063 hoty = image.offset().y();
3064#ifndef QT_NO_IMAGE_DITHER_TO_1
3065 if( image.hasAlphaBuffer() ) {
3066 QImage i = image.createAlphaMask();
3067 collision_mask = new QImage(i);
3068 } else
3069#endif
3070 collision_mask = 0;
3071}
3072
3073void QCanvasPixmap::init(const QPixmap& pixmap, int hx, int hy)
3074{
3075 (QPixmap&)*this = pixmap;
3076 hotx = hx;
3077 hoty = hy;
3078 if( pixmap.mask() ) {
3079 QImage i = mask()->convertToImage();
3080 collision_mask = new QImage(i);
3081 } else
3082 collision_mask = 0;
3083}
3084
3085/*!
3086 Destroys the pixmap.
3087*/
3088QCanvasPixmap::~QCanvasPixmap()
3089{
3090 delete collision_mask;
3091}
3092
3093/*!
3094 \fn int QCanvasPixmap::offsetX() const
3095
3096 Returns the x-offset of the pixmap's hotspot.
3097
3098 \sa setOffset()
3099*/
3100
3101/*!
3102 \fn int QCanvasPixmap::offsetY() const
3103
3104 Returns the y-offset of the pixmap's hotspot.
3105
3106 \sa setOffset()
3107*/
3108
3109/*!
3110 \fn void QCanvasPixmap::setOffset(int x, int y)
3111
3112 Sets the offset of the pixmap's hotspot to (\a x, \a y).
3113
3114 \warning Do not call this function if any QCanvasSprites are
3115 currently showing this pixmap.
3116*/
3117
3118/*!
3119 \class QCanvasPixmapArray qcanvas.h
3120 \brief The QCanvasPixmapArray class provides an array of QCanvasPixmaps.
3121\if defined(commercial)
3122 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
3123\endif
3124
3125 \module canvas
3126 \ingroup graphics
3127 \ingroup images
3128
3129
3130 This class is used by QCanvasSprite to hold an array of pixmaps.
3131 It is used to implement animated sprites, i.e. images that change
3132 over time, with each pixmap in the array holding one frame.
3133
3134 Depending on the constructor you use you can load multiple pixmaps
3135 into the array either from a directory (specifying a wildcard
3136 pattern for the files), or from a list of QPixmaps. You can also
3137 read in a set of pixmaps after construction using readPixmaps().
3138
3139 Individual pixmaps can be set with setImage() and retrieved with
3140 image(). The number of pixmaps in the array is returned by
3141 count().
3142
3143 QCanvasSprite uses an image's mask for collision detection. You
3144 can change this by reading in a separate set of image masks using
3145 readCollisionMasks().
3146
3147*/
3148
3149/*!
3150 Constructs an invalid array (i.e. isValid() will return FALSE).
3151 You must call readPixmaps() before being able to use this
3152 QCanvasPixmapArray.
3153*/
3154QCanvasPixmapArray::QCanvasPixmapArray()
3155: framecount( 0 ), img( 0 )
3156{
3157}
3158
3159#ifndef QT_NO_IMAGEIO
3160/*!
3161 Constructs a QCanvasPixmapArray from files.
3162
3163 The \a fc parameter sets the number of frames to be loaded for
3164 this image.
3165
3166 If \a fc is not 0, \a datafilenamepattern should contain "%1",
3167 e.g. "foo%1.png". The actual filenames are formed by replacing the
3168 %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
3169 foo0001.png, foo0002.png, etc.
3170
3171 If \a fc is 0, \a datafilenamepattern is asssumed to be a
3172 filename, and the image contained in this file will be loaded as
3173 the first (and only) frame.
3174
3175 If \a datafilenamepattern does not exist, is not readable, isn't
3176 an image, or some other error occurs, the array ends up empty and
3177 isValid() returns FALSE.
3178*/
3179
3180QCanvasPixmapArray::QCanvasPixmapArray( const QString& datafilenamepattern,
3181 int fc )
3182: framecount( 0 ), img( 0 )
3183{
3184 readPixmaps(datafilenamepattern,fc);
3185}
3186#endif
3187
3188/*!
3189 \obsolete
3190 Use QCanvasPixmapArray::QCanvasPixmapArray( QValueList<QPixmap>, QPointArray )
3191 instead.
3192
3193 Constructs a QCanvasPixmapArray from the list of QPixmaps \a
3194 list. The \a hotspots list has to be of the same size as \a list.
3195*/
3196QCanvasPixmapArray::QCanvasPixmapArray(QPtrList<QPixmap> list, QPtrList<QPoint> hotspots) :
3197 framecount(list.count()),
3198 img(new QCanvasPixmap*[list.count()])
3199{
3200 if (list.count() != hotspots.count()) {
3201 qWarning("QCanvasPixmapArray: lists have different lengths");
3202 reset();
3203 img = 0;
3204 } else {
3205 list.first();
3206 hotspots.first();
3207 for (int i=0; i<framecount; i++) {
3208 img[i]=new QCanvasPixmap(*list.current(), *hotspots.current());
3209 list.next();
3210 hotspots.next();
3211 }
3212 }
3213}
3214
3215/*!
3216 Constructs a QCanvasPixmapArray from the list of QPixmaps in the
3217 \a list. Each pixmap will get a hotspot according to the \a
3218 hotspots array. If no hotspots are specified, each one is set to
3219 be at position (0, 0).
3220
3221 If an error occurs, isValid() will return FALSE.
3222*/
3223QCanvasPixmapArray::QCanvasPixmapArray(QValueList<QPixmap> list, QPointArray hotspots) :
3224 framecount((int)list.size()),
3225 img(new QCanvasPixmap*[list.size()])
3226{
3227 bool have_hotspots = ( hotspots.size() != 0 );
3228 if (have_hotspots && list.count() != hotspots.count()) {
3229 qWarning("QCanvasPixmapArray: lists have different lengths");
3230 reset();
3231 img = 0;
3232 } else {
3233 QValueList<QPixmap>::iterator it;
3234 it = list.begin();
3235 for (int i=0; i<framecount; i++) {
3236 QPoint hs = have_hotspots ? hotspots[i] : QPoint( 0, 0 );
3237 img[i]=new QCanvasPixmap( *it, hs );
3238 ++it;
3239 }
3240 }
3241}
3242
3243/*!
3244 Destroys the pixmap array and all the pixmaps it contains.
3245*/
3246QCanvasPixmapArray::~QCanvasPixmapArray()
3247{
3248 reset();
3249}
3250
3251void QCanvasPixmapArray::reset()
3252{
3253 for (int i=0; i<framecount; i++)
3254 delete img[i];
3255 delete [] img;
3256 img = 0;
3257 framecount = 0;
3258}
3259
3260#ifndef QT_NO_IMAGEIO
3261/*!
3262 Reads one or more pixmaps into the pixmap array.
3263
3264 If \a fc is not 0, \a filenamepattern should contain "%1", e.g.
3265 "foo%1.png". The actual filenames are formed by replacing the %1
3266 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
3267 foo0001.png, foo0002.png, etc.
3268
3269 If \a fc is 0, \a filenamepattern is asssumed to be a filename,
3270 and the image contained in this file will be loaded as the first
3271 (and only) frame.
3272
3273 If \a filenamepattern does not exist, is not readable, isn't an
3274 image, or some other error occurs, this function will return
3275 FALSE, and isValid() will return FALSE; otherwise this function
3276 will return TRUE.
3277
3278 \sa isValid()
3279*/
3280bool QCanvasPixmapArray::readPixmaps( const QString& filenamepattern,
3281 int fc)
3282{
3283 return readPixmaps(filenamepattern,fc,FALSE);
3284}
3285
3286/*!
3287 Reads new collision masks for the array.
3288
3289 By default, QCanvasSprite uses the image mask of a sprite to
3290 detect collisions. Use this function to set your own collision
3291 image masks.
3292
3293 If count() is 1 \a filename must specify a real filename to read
3294 the mask from. If count() is greater than 1, the \a filename must
3295 contain a "%1" that will get replaced by the number of the mask to
3296 be loaded, just like QCanvasPixmapArray::readPixmaps().
3297
3298 All collision masks must be 1-bit images or this function call
3299 will fail.
3300
3301 If the file isn't readable, contains the wrong number of images,
3302 or there is some other error, this function will return FALSE, and
3303 the array will be flagged as invalid; otherwise this function
3304 returns TRUE.
3305
3306 \sa isValid()
3307*/
3308bool QCanvasPixmapArray::readCollisionMasks(const QString& filename)
3309{
3310 return readPixmaps(filename,framecount,TRUE);
3311}
3312
3313
3314bool QCanvasPixmapArray::readPixmaps( const QString& datafilenamepattern,
3315 int fc, bool maskonly)
3316{
3317 if ( !maskonly ) {
3318 reset();
3319 framecount = fc;
3320 if ( !framecount )
3321 framecount=1;
3322 img = new QCanvasPixmap*[framecount];
3323 }
3324 bool ok = TRUE;
3325 bool arg = fc > 1;
3326 if ( !arg )
3327 framecount=1;
3328 for (int i=0; i<framecount; i++) {
3329 QString r;
3330 r.sprintf("%04d",i);
3331 if ( maskonly ) {
3332 img[i]->collision_mask->load(
3333 arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3334 ok = ok
3335 && !img[i]->collision_mask->isNull()
3336 && img[i]->collision_mask->depth()==1;
3337 } else {
3338 img[i]=new QCanvasPixmap(
3339 arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3340 ok = ok && !img[i]->isNull();
3341 }
3342 }
3343 if ( !ok ) {
3344 reset();
3345 }
3346 return ok;
3347}
3348#endif
3349
3350/*!
3351 \obsolete
3352
3353 Use isValid() instead.
3354
3355 This returns FALSE if the array is valid, and TRUE if it is not.
3356*/
3357bool QCanvasPixmapArray::operator!()
3358{
3359 return img==0;
3360}
3361
3362/*!
3363 Returns TRUE if the pixmap array is valid; otherwise returns
3364 FALSE.
3365*/
3366bool QCanvasPixmapArray::isValid() const
3367{
3368 return (img != 0);
3369}
3370
3371/*!
3372 \fn QCanvasPixmap* QCanvasPixmapArray::image(int i) const
3373
3374 Returns pixmap \a i in the array, if \a i is non-negative and less
3375 than than count(), and returns an unspecified value otherwise.
3376*/
3377
3378// ### wouldn't it be better to put empty QCanvasPixmaps in there instead of
3379// initializing the additional elements in the array to 0? Lars
3380/*!
3381 Replaces the pixmap at index \a i with pixmap \a p.
3382
3383 The array takes ownership of \a p and will delete \a p when the
3384 array itself is deleted.
3385
3386 If \a i is beyond the end of the array the array is extended to at
3387 least i+1 elements, with elements count() to i-1 being initialized
3388 to 0.
3389*/
3390void QCanvasPixmapArray::setImage(int i, QCanvasPixmap* p)
3391{
3392 if ( i >= framecount ) {
3393 QCanvasPixmap** newimg = new QCanvasPixmap*[i+1];
3394 memcpy(newimg, img, sizeof( QCanvasPixmap * )*framecount);
3395 memset(newimg + framecount, 0, sizeof( QCanvasPixmap * )*( i+1 - framecount ) );
3396 framecount = i+1;
3397 delete [] img;
3398 img = newimg;
3399 }
3400 delete img[i]; img[i]=p;
3401}
3402
3403/*!
3404 \fn uint QCanvasPixmapArray::count() const
3405
3406 Returns the number of pixmaps in the array.
3407*/
3408
3409/*!
3410 Returns the x-coordinate of the current left edge of the sprite.
3411 (This may change as the sprite animates since different frames may
3412 have different left edges.)
3413
3414 \sa rightEdge() bottomEdge() topEdge()
3415*/
3416int QCanvasSprite::leftEdge() const
3417{
3418 return int(x()) - image()->hotx;
3419}
3420
3421/*!
3422 \overload
3423
3424 Returns what the x-coordinate of the left edge of the sprite would
3425 be if the sprite (actually its hotspot) were moved to x-position
3426 \a nx.
3427
3428 \sa rightEdge() bottomEdge() topEdge()
3429*/
3430int QCanvasSprite::leftEdge(int nx) const
3431{
3432 return nx - image()->hotx;
3433}
3434
3435/*!
3436 Returns the y-coordinate of the top edge of the sprite. (This may
3437 change as the sprite animates since different frames may have
3438 different top edges.)
3439
3440 \sa leftEdge() rightEdge() bottomEdge()
3441*/
3442int QCanvasSprite::topEdge() const
3443{
3444 return int(y()) - image()->hoty;
3445}
3446
3447/*!
3448 \overload
3449
3450 Returns what the y-coordinate of the top edge of the sprite would
3451 be if the sprite (actually its hotspot) were moved to y-position
3452 \a ny.
3453
3454 \sa leftEdge() rightEdge() bottomEdge()
3455*/
3456int QCanvasSprite::topEdge(int ny) const
3457{
3458 return ny - image()->hoty;
3459}
3460
3461/*!
3462 Returns the x-coordinate of the current right edge of the sprite.
3463 (This may change as the sprite animates since different frames may
3464 have different right edges.)
3465
3466 \sa leftEdge() bottomEdge() topEdge()
3467*/
3468int QCanvasSprite::rightEdge() const
3469{
3470 return leftEdge() + image()->width()-1;
3471}
3472
3473/*!
3474 \overload
3475
3476 Returns what the x-coordinate of the right edge of the sprite
3477 would be if the sprite (actually its hotspot) were moved to
3478 x-position \a nx.
3479
3480 \sa leftEdge() bottomEdge() topEdge()
3481*/
3482int QCanvasSprite::rightEdge(int nx) const
3483{
3484 return leftEdge(nx) + image()->width()-1;
3485}
3486
3487/*!
3488 Returns the y-coordinate of the current bottom edge of the sprite.
3489 (This may change as the sprite animates since different frames may
3490 have different bottom edges.)
3491
3492 \sa leftEdge() rightEdge() topEdge()
3493*/
3494int QCanvasSprite::bottomEdge() const
3495{
3496 return topEdge() + image()->height()-1;
3497}
3498
3499/*!
3500 \overload
3501
3502 Returns what the y-coordinate of the top edge of the sprite would
3503 be if the sprite (actually its hotspot) were moved to y-position
3504 \a ny.
3505
3506 \sa leftEdge() rightEdge() topEdge()
3507*/
3508int QCanvasSprite::bottomEdge(int ny) const
3509{
3510 return topEdge(ny) + image()->height()-1;
3511}
3512
3513/*!
3514 \fn QCanvasPixmap* QCanvasSprite::image() const
3515
3516 Returns the current frame's image.
3517
3518 \sa frame(), setFrame()
3519*/
3520
3521/*!
3522 \fn QCanvasPixmap* QCanvasSprite::image(int f) const
3523 \overload
3524
3525 Returns the image for frame \a f. Does not do any bounds checking on \a f.
3526*/
3527
3528/*!
3529 Returns the image the sprite \e will have after advance(1) is
3530 called. By default this is the same as image().
3531*/
3532QCanvasPixmap* QCanvasSprite::imageAdvanced() const
3533{
3534 return image();
3535}
3536
3537/*!
3538 Returns the bounding rectangle for the image in the sprite's
3539 current frame. This assumes that the images are tightly cropped
3540 (i.e. do not have transparent pixels all along a side).
3541*/
3542QRect QCanvasSprite::boundingRect() const
3543{
3544 return QRect(leftEdge(), topEdge(), width(), height());
3545}
3546
3547
3548/*!
3549 \internal
3550 Returns the chunks covered by the item.
3551*/
3552QPointArray QCanvasItem::chunks() const
3553{
3554 QPointArray r;
3555 int n=0;
3556 QRect br = boundingRect();
3557 if (isVisible() && canvas()) {
3558 int chunksize=canvas()->chunkSize();
3559 br &= QRect(0,0,canvas()->width(),canvas()->height());
3560 if ( br.isValid() ) {
3561 r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2));
3562 for (int j=br.top()/chunksize; j<=br.bottom()/chunksize; j++) {
3563 for (int i=br.left()/chunksize; i<=br.right()/chunksize; i++) {
3564 r[n++] = QPoint(i,j);
3565 }
3566 }
3567 }
3568 }
3569 r.resize(n);
3570 return r;
3571}
3572
3573
3574/*!
3575 \internal
3576 Add the sprite to the chunks in its QCanvas which it overlaps.
3577*/
3578void QCanvasSprite::addToChunks()
3579{
3580 if (isVisible() && canvas()) {
3581 int chunksize=canvas()->chunkSize();
3582 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
3583 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
3584 canvas()->addItemToChunk(this,i,j);
3585 }
3586 }
3587 }
3588}
3589
3590/*!
3591 \internal
3592 Remove the sprite from the chunks in its QCanvas which it overlaps.
3593
3594 \sa addToChunks()
3595*/
3596void QCanvasSprite::removeFromChunks()
3597{
3598 if (isVisible() && canvas()) {
3599 int chunksize=canvas()->chunkSize();
3600 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
3601 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
3602 canvas()->removeItemFromChunk(this,i,j);
3603 }
3604 }
3605 }
3606}
3607
3608/*!
3609 The width of the sprite for the current frame's image.
3610
3611 \sa frame()
3612*/
3613//### mark: Why don't we have width(int) and height(int) to be
3614//consistent with leftEdge() and leftEdge(int)?
3615int QCanvasSprite::width() const
3616{
3617 return image()->width();
3618}
3619
3620/*!
3621 The height of the sprite for the current frame's image.
3622
3623 \sa frame()
3624*/
3625int QCanvasSprite::height() const
3626{
3627 return image()->height();
3628}
3629
3630
3631/*!
3632 Draws the current frame's image at the sprite's current position
3633 on painter \a painter.
3634*/
3635void QCanvasSprite::draw(QPainter& painter)
3636{
3637 painter.drawPixmap(leftEdge(),topEdge(),*image());
3638}
3639
3640/*!
3641 \class QCanvasView qcanvas.h
3642 \brief The QCanvasView class provides an on-screen view of a QCanvas.
3643\if defined(commercial)
3644 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
3645\endif
3646
3647 \module canvas
3648 \ingroup graphics
3649 \ingroup images
3650
3651 A QCanvasView is widget which provides a view of a QCanvas.
3652
3653 If you want users to be able to interact with a canvas view,
3654 subclass QCanvasView. You might then reimplement
3655 QScrollView::contentsMousePressEvent(). For example:
3656
3657 \code
3658 void MyCanvasView::contentsMousePressEvent( QMouseEvent* e )
3659 {
3660 QCanvasItemList l = canvas()->collisions(e->pos());
3661 for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
3662 if ( (*it)->rtti() == QCanvasRectangle::RTTI )
3663 qDebug("A QCanvasRectangle lies somewhere at this point");
3664 }
3665 }
3666 \endcode
3667
3668 The canvas view shows canvas canvas(); this can be changed using
3669 setCanvas().
3670
3671 A transformation matrix can be used to transform the view of the
3672 canvas in various ways, for example, zooming in or out or rotating.
3673 For example:
3674
3675 \code
3676 QWMatrix wm;
3677 wm.scale( 2, 2 ); // Zooms in by 2 times
3678 wm.rotate( 90 ); // Rotates 90 degrees counter clockwise
3679 // around the origin.
3680 wm.translate( 0, -canvas->height() );
3681 // moves the canvas down so what was visible
3682 // before is still visible.
3683 myCanvasView->setWorldMatrix( wm );
3684 \endcode
3685
3686 Use setWorldMatrix() to set the canvas view's world matrix: you must
3687 ensure that the world matrix is invertible. The current world matrix
3688 is retrievable with worldMatrix(), and its inversion is retrievable
3689 with inverseWorldMatrix().
3690
3691 Example:
3692
3693 The following code finds the part of the canvas that is visible in
3694 this view, i.e. the bounding rectangle of the view in canvas coordinates.
3695
3696 \code
3697 QRect rc = QRect( myCanvasView->contentsX(), myCanvasView->contentsY(),
3698 myCanvasView->visibleWidth(), myCanvasView->visibleHeight() );
3699 QRect canvasRect = myCanvasView->inverseWorldMatrix().mapRect(rc);
3700 \endcode
3701
3702 \sa QWMatrix QPainter::setWorldMatrix()
3703
3704*/
3705
3706/*!
3707 Constructs a QCanvasView with parent \a parent, and name \a name,
3708 using the widget flags \a f. The canvas view is not associated
3709 with a canvas, so you must to call setCanvas() to view a
3710 canvas.
3711*/
3712QCanvasView::QCanvasView(QWidget* parent, const char* name, WFlags f) :
3713 QScrollView(parent,name,f|WResizeNoErase|WStaticContents)
3714{
3715 d = new QCanvasViewData;
3716 viewing = 0;
3717 setCanvas(0);
3718 connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int)));
3719}
3720
3721/*!
3722 \overload
3723
3724 Constructs a QCanvasView which views canvas \a canvas, with parent
3725 \a parent, and name \a name, using the widget flags \a f.
3726*/
3727QCanvasView::QCanvasView(QCanvas* canvas, QWidget* parent, const char* name, WFlags f) :
3728 QScrollView(parent,name,f|WResizeNoErase|WStaticContents)
3729{
3730 d = new QCanvasViewData;
3731 viewing = 0;
3732 setCanvas(canvas);
3733
3734 connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int)));
3735}
3736
3737/*!
3738 Destroys the canvas view. The associated canvas is \e not deleted.
3739*/
3740QCanvasView::~QCanvasView()
3741{
3742 delete d;
3743 d = 0;
3744 setCanvas(0);
3745}
3746
3747/*!
3748 \fn QCanvas* QCanvasView::canvas() const
3749
3750 Returns a pointer to the canvas which the QCanvasView is currently
3751 showing.
3752*/
3753
3754
3755/*!
3756 Sets the canvas that the QCanvasView is showing to the canvas \a
3757 canvas.
3758*/
3759void QCanvasView::setCanvas(QCanvas* canvas)
3760{
3761 if (viewing) {
3762 disconnect(viewing);
3763 viewing->removeView(this);
3764 }
3765 viewing=canvas;
3766 if (viewing) {
3767 connect(viewing,SIGNAL(resized()), this, SLOT(updateContentsSize()));
3768 viewing->addView(this);
3769 }
3770 if ( d ) // called by d'tor
3771 updateContentsSize();
3772}
3773
3774#ifndef QT_NO_TRANSFORMATIONS
3775/*!
3776 Returns a reference to the canvas view's current transformation matrix.
3777
3778 \sa setWorldMatrix() inverseWorldMatrix()
3779*/
3780const QWMatrix &QCanvasView::worldMatrix() const
3781{
3782 return d->xform;
3783}
3784
3785/*!
3786 Returns a reference to the inverse of the canvas view's current
3787 transformation matrix.
3788
3789 \sa setWorldMatrix() worldMatrix()
3790*/
3791const QWMatrix &QCanvasView::inverseWorldMatrix() const
3792{
3793 return d->ixform;
3794}
3795
3796/*!
3797 Sets the transformation matrix of the QCanvasView to \a wm. The
3798 matrix must be invertible (i.e. if you create a world matrix that
3799 zooms out by 2 times, then the inverse of this matrix is one that
3800 will zoom in by 2 times).
3801
3802 When you use this, you should note that the performance of the
3803 QCanvasView will decrease considerably.
3804
3805 Returns FALSE if \a wm is not invertable; otherwise returns TRUE.
3806
3807 \sa worldMatrix() inverseWorldMatrix() QWMatrix::isInvertible()
3808*/
3809bool QCanvasView::setWorldMatrix( const QWMatrix & wm )
3810{
3811 bool ok = wm.isInvertible();
3812 if ( ok ) {
3813 d->xform = wm;
3814 d->ixform = wm.invert();
3815 updateContentsSize();
3816 viewport()->update();
3817 }
3818 return ok;
3819}
3820#endif
3821
3822void QCanvasView::updateContentsSize()
3823{
3824 if ( viewing ) {
3825 QRect br;
3826#ifndef QT_NO_TRANSFORMATIONS
3827 br = d->xform.map(QRect(0,0,viewing->width(),viewing->height()));
3828#else
3829 br = QRect(0,0,viewing->width(),viewing->height());
3830#endif
3831
3832 if ( br.width() < contentsWidth() ) {
3833 QRect r(contentsToViewport(QPoint(br.width(),0)),
3834 QSize(contentsWidth()-br.width(),contentsHeight()));
3835 viewport()->erase(r);
3836 }
3837 if ( br.height() < contentsHeight() ) {
3838 QRect r(contentsToViewport(QPoint(0,br.height())),
3839 QSize(contentsWidth(),contentsHeight()-br.height()));
3840 viewport()->erase(r);
3841 }
3842
3843 resizeContents(br.width(),br.height());
3844 } else {
3845 viewport()->erase();
3846 resizeContents(1,1);
3847 }
3848}
3849
3850void QCanvasView::cMoving(int x, int y)
3851{
3852 // A little kludge to smooth up repaints when scrolling
3853 int dx = x - contentsX();
3854 int dy = y - contentsY();
3855 d->repaint_from_moving = QABS(dx) < width()/8 && QABS(dy) < height()/8;
3856}
3857
3858/*!
3859 Repaints part of the QCanvas that the canvas view is showing
3860 starting at \a cx by \a cy, with a width of \a cw and a height of \a
3861 ch using the painter \a p.
3862*/
3863void QCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
3864{
3865 QRect r(cx,cy,cw,ch);
3866 if (viewing) {
3867 //viewing->drawViewArea(this,p,r,TRUE);
3868 viewing->drawViewArea(this,p,r,!d->repaint_from_moving);
3869 d->repaint_from_moving = FALSE;
3870 } else {
3871 p->eraseRect(r);
3872 }
3873}
3874
3875/*!
3876 \reimp
3877 \internal
3878
3879 (Implemented to get rid of a compiler warning.)
3880*/
3881void QCanvasView::drawContents( QPainter * )
3882{
3883}
3884
3885/*!
3886 Suggests a size sufficient to view the entire canvas.
3887*/
3888QSize QCanvasView::sizeHint() const
3889{
3890 if ( !canvas() )
3891 return QScrollView::sizeHint();
3892 // should maybe take transformations into account
3893 return ( canvas()->size() + 2 * QSize(frameWidth(), frameWidth()) )
3894 .boundedTo( 3 * QApplication::desktop()->size() / 4 );
3895}
3896
3897// ### Qt 4.0 customer request: operate on doubles rather than int.
3898// ### I know, almost impossible due to the use of QRegion etc.
3899/*!
3900 \class QCanvasPolygonalItem qcanvas.h
3901 \brief The QCanvasPolygonalItem class provides a polygonal canvas item
3902 on a QCanvas.
3903\if defined(commercial)
3904 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
3905\endif
3906
3907 \module canvas
3908 \ingroup graphics
3909 \ingroup images
3910
3911 The mostly rectangular classes, such as QCanvasSprite and
3912 QCanvasText, use the object's bounding rectangle for movement,
3913 repainting and collision calculations. For most other items, the
3914 bounding rectangle can be far too large -- a diagonal line being
3915 the worst case, and there are many other cases which are also bad.
3916 QCanvasPolygonalItem provides polygon-based bounding rectangle
3917 handling, etc., which is much faster for non-rectangular items.
3918
3919 Derived classes should try to define as small an area as possible
3920 to maximize efficiency, but the polygon must \e definitely be
3921 contained completely within the polygonal area. Calculating the
3922 exact requirements is usually difficult, but if you allow a small
3923 overestimate it can be easy and quick, while still getting almost
3924 all of QCanvasPolygonalItem's speed.
3925
3926 Note that all subclasses \e must call hide() in their destructor
3927 since hide() needs to be able to access areaPoints().
3928
3929 Normally, QCanvasPolygonalItem uses the odd-even algorithm for
3930 determining whether an object intersects this object. You can
3931 change this to the winding algorithm using setWinding().
3932
3933 The bounding rectangle is available using boundingRect(). The
3934 points bounding the polygonal item are retrieved with
3935 areaPoints(). Use areaPointsAdvanced() to retrieve the bounding
3936 points the polygonal item \e will have after
3937 QCanvasItem::advance(1) has been called.
3938
3939 If the shape of the polygonal item is about to change while the
3940 item is visible, call invalidate() before updating with a
3941 different result from \l areaPoints().
3942
3943 By default, QCanvasPolygonalItem objects have a black pen and no
3944 brush (the default QPen and QBrush constructors). You can change
3945 this with setPen() and setBrush(), but note that some
3946 QCanvasPolygonalItem subclasses only use the brush, ignoring the
3947 pen setting.
3948
3949 The polygonal item can be drawn on a painter with draw().
3950 Subclasses must reimplement drawShape() to draw themselves.
3951
3952 Like any other canvas item polygonal items can be moved with
3953 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting coordinates
3954 with QCanvasItem::setX(), QCanvasItem::setY() and QCanvasItem::setZ().
3955
3956*/
3957
3958
3959/*
3960 Since most polygonal items don't have a pen, the default is
3961 NoPen and a black brush.
3962*/
3963static const QPen& defaultPolygonPen()
3964{
3965 static QPen* dp=0;
3966 if ( !dp )
3967 dp = new QPen;
3968 return *dp;
3969}
3970
3971static const QBrush& defaultPolygonBrush()
3972{
3973 static QBrush* db=0;
3974 if ( !db )
3975 db = new QBrush;
3976 return *db;
3977}
3978
3979/*!
3980 Constructs a QCanvasPolygonalItem on the canvas \a canvas.
3981*/
3982QCanvasPolygonalItem::QCanvasPolygonalItem(QCanvas* canvas) :
3983 QCanvasItem(canvas),
3984 br(defaultPolygonBrush()),
3985 pn(defaultPolygonPen())
3986{
3987 wind=0;
3988}
3989
3990/*!
3991 Note that all subclasses \e must call hide() in their destructor
3992 since hide() needs to be able to access areaPoints().
3993*/
3994QCanvasPolygonalItem::~QCanvasPolygonalItem()
3995{
3996}
3997
3998/*!
3999 Returns TRUE if the polygonal item uses the winding algorithm to
4000 determine the "inside" of the polygon. Returns FALSE if it uses
4001 the odd-even algorithm.
4002
4003 The default is to use the odd-even algorithm.
4004
4005 \sa setWinding()
4006*/
4007bool QCanvasPolygonalItem::winding() const
4008{
4009 return wind;
4010}
4011
4012/*!
4013 If \a enable is TRUE, the polygonal item will use the winding
4014 algorithm to determine the "inside" of the polygon; otherwise the
4015 odd-even algorithm will be used.
4016
4017 The default is to use the odd-even algorithm.
4018
4019 \sa winding()
4020*/
4021void QCanvasPolygonalItem::setWinding(bool enable)
4022{
4023 wind = enable;
4024}
4025
4026/*!
4027 Invalidates all information about the area covered by the canvas
4028 item. The item will be updated automatically on the next call that
4029 changes the item's status, for example, move() or update(). Call
4030 this function if you are going to change the shape of the item (as
4031 returned by areaPoints()) while the item is visible.
4032*/
4033void QCanvasPolygonalItem::invalidate()
4034{
4035 val = (uint)FALSE;
4036 removeFromChunks();
4037}
4038
4039/*!
4040 \fn QCanvasPolygonalItem::isValid() const
4041
4042 Returns TRUE if the polygonal item's area information has been
4043 invalidated; otherwise returns FALSE.
4044
4045 \sa invalidate()
4046*/
4047
4048/*!
4049 Returns the points the polygonal item \e will have after
4050 QCanvasItem::advance(1) is called, i.e. what the points are when
4051 advanced by the current xVelocity() and yVelocity().
4052*/
4053QPointArray QCanvasPolygonalItem::areaPointsAdvanced() const
4054{
4055 int dx = int(x()+xVelocity())-int(x());
4056 int dy = int(y()+yVelocity())-int(y());
4057 QPointArray r = areaPoints();
4058 r.detach(); // Explicit sharing is stupid.
4059 if ( dx || dy )
4060 r.translate(dx,dy);
4061 return r;
4062}
4063
4064//#define QCANVAS_POLYGONS_DEBUG
4065#ifdef QCANVAS_POLYGONS_DEBUG
4066static QWidget* dbg_wid=0;
4067static QPainter* dbg_ptr=0;
4068#endif
4069
4070class QPolygonalProcessor {
4071public:
4072 QPolygonalProcessor(QCanvas* c, const QPointArray& pa) :
4073 canvas(c)
4074 {
4075 QRect pixelbounds = pa.boundingRect();
4076 int cs = canvas->chunkSize();
4077 bounds.setLeft(pixelbounds.left()/cs);
4078 bounds.setRight(pixelbounds.right()/cs);
4079 bounds.setTop(pixelbounds.top()/cs);
4080 bounds.setBottom(pixelbounds.bottom()/cs);
4081 bitmap = QImage(bounds.width(),bounds.height(),1,2,QImage::LittleEndian);
4082 pnt = 0;
4083 bitmap.fill(0);
4084#ifdef QCANVAS_POLYGONS_DEBUG
4085 dbg_start();
4086#endif
4087 }
4088
4089 inline void add(int x, int y)
4090 {
4091 if ( pnt >= (int)result.size() ) {
4092 result.resize(pnt*2+10);
4093 }
4094 result[pnt++] = QPoint(x+bounds.x(),y+bounds.y());
4095#ifdef QCANVAS_POLYGONS_DEBUG
4096 if ( dbg_ptr ) {
4097 int cs = canvas->chunkSize();
4098 QRect r(x*cs+bounds.x()*cs,y*cs+bounds.y()*cs,cs-1,cs-1);
4099 dbg_ptr->setPen(Qt::blue);
4100 dbg_ptr->drawRect(r);
4101 }
4102#endif
4103 }
4104
4105 inline void addBits(int x1, int x2, uchar newbits, int xo, int yo)
4106 {
4107 for (int i=x1; i<=x2; i++)
4108 if ( newbits & (1<<i) )
4109 add(xo+i,yo);
4110 }
4111
4112#ifdef QCANVAS_POLYGONS_DEBUG
4113 void dbg_start()
4114 {
4115 if ( !dbg_wid ) {
4116 dbg_wid = new QWidget;
4117 dbg_wid->resize(800,600);
4118 dbg_wid->show();
4119 dbg_ptr = new QPainter(dbg_wid);
4120 dbg_ptr->setBrush(Qt::NoBrush);
4121 }
4122 dbg_ptr->fillRect(dbg_wid->rect(),Qt::white);
4123 }
4124#endif
4125
4126 void doSpans(int n, QPoint* pt, int* w)
4127 {
4128 int cs = canvas->chunkSize();
4129 for (int j=0; j<n; j++) {
4130 int y = pt[j].y()/cs-bounds.y();
4131 uchar* l = bitmap.scanLine(y);
4132 int x = pt[j].x();
4133 int x1 = x/cs-bounds.x();
4134 int x2 = (x+w[j])/cs-bounds.x();
4135 int x1q = x1/8;
4136 int x1r = x1%8;
4137 int x2q = x2/8;
4138 int x2r = x2%8;
4139#ifdef QCANVAS_POLYGONS_DEBUG
4140 if ( dbg_ptr ) dbg_ptr->setPen(Qt::darkGray);
4141#endif
4142 if ( x1q == x2q ) {
4143 uchar newbits = (~l[x1q]) & (((2<<(x2r-x1r))-1)<<x1r);
4144 if ( newbits ) {
4145 addBits(x1r,x2r,newbits,x1q*8,y);
4146 l[x1q] |= newbits;
4147#ifdef QCANVAS_POLYGONS_DEBUG
4148 if ( dbg_ptr ) dbg_ptr->setPen(Qt::darkGreen);
4149#endif
4150 }
4151 } else {
4152 uchar newbits1 = (~l[x1q]) & (0xff<<x1r);
4153 if ( newbits1 ) {
4154 addBits(x1r,7,newbits1,x1q*8,y);
4155 l[x1q] |= newbits1;
4156#ifdef QCANVAS_POLYGONS_DEBUG
4157 if ( dbg_ptr ) dbg_ptr->setPen(Qt::green);
4158#endif
4159 }
4160 for (int i=x1q+1; i<x2q; i++) {
4161 if ( l[i] != 0xff ) {
4162 addBits(0,7,~l[i],i*8,y);
4163 l[i]=0xff;
4164#ifdef QCANVAS_POLYGONS_DEBUG
4165 if ( dbg_ptr ) dbg_ptr->setPen(Qt::black);
4166#endif
4167 }
4168 }
4169 uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r));
4170 if ( newbits2 ) {
4171 addBits(0,x2r,newbits2,x2q*8,y);
4172 l[x2q] |= newbits2;
4173#ifdef QCANVAS_POLYGONS_DEBUG
4174 if ( dbg_ptr ) dbg_ptr->setPen(Qt::red);
4175#endif
4176 }
4177 }
4178#ifdef QCANVAS_POLYGONS_DEBUG
4179 if ( dbg_ptr ) {
4180 dbg_ptr->drawLine(pt[j],pt[j]+QPoint(w[j],0));
4181 }
4182#endif
4183 }
4184 result.resize(pnt);
4185 }
4186
4187#if defined(Q_WS_PM)
4188 // We cannot use QPolygonScanner, because:
4189 // a) polygons in OS/2 are all-inclusive, but
4190 // b) specifying the Bottom edge inclusion in QPolygonScanner::scan()
4191 // doesn't actually work (seems that the code from Xserver always
4192 // internally assumes that right and bottom edges are excluded).
4193 // Instead of getting into and fixing the Xserver code, I decided to use
4194 // region's rectangles to determine polygon spans. It should be as fast
4195 // as the Xserver code, but gives us one more advantage: spans detected
4196 // this way precisely repeat the actual polygon pixels drawn on the screen
4197 // (because both regions and polygons are created/drawn by GPI using the
4198 // same line and fill routines), so all chunks will be correctly detected.
4199
4200 void scanPolygon( const QPointArray &pa, bool wind )
4201 {
4202 int cs = canvas->chunkSize();
4203 QRegion rgn = QRegion( pa, wind );
4204 QMemArray <QRect> rects = rgn.rects();
4205 for ( size_t i = 0; i < rects.size(); ++ i ) {
4206 const QRect &r = rects[i];
4207 int width = r.width() - 1;
4208 // I think I saw null rects in regions a couple of times...
4209 if ( width < 0 || r.height() == 0 )
4210 continue;
4211 QPoint pt( r.topLeft() );
4212 while ( 1 ) {
4213 doSpans( 1, &pt, &width );
4214#ifdef QCANVAS_POLYGONS_DEBUG
4215 if ( dbg_ptr ) {
4216 dbg_ptr->setPen( Qt::cyan );
4217 QPoint ptd = pt;
4218 ++ ptd.ry();
4219 int j = 1;
4220 for ( ; j < cs && ptd.y() < r.bottom(); ++ j, ++ ptd.ry() )
4221 dbg_ptr->drawLine( ptd, ptd + QPoint( width, 0 ) );
4222 }
4223#endif
4224 if ( pt.y() == r.bottom() )
4225 break;
4226 pt.ry() += cs;
4227 if ( pt.y() > r.bottom() )
4228 pt.ry() = r.bottom();
4229 }
4230 }
4231 }
4232#endif // !defined(Q_WS_PM)
4233
4234 int pnt;
4235 QPointArray result;
4236 QCanvas* canvas;
4237 QRect bounds;
4238 QImage bitmap;
4239};
4240
4241
4242QPointArray QCanvasPolygonalItem::chunks() const
4243{
4244 QPointArray pa = areaPoints();
4245
4246 if ( !pa.size() ) {
4247 pa.detach(); // Explicit sharing is stupid.
4248 return pa;
4249 }
4250
4251 QPolygonalProcessor processor(canvas(),pa);
4252
4253#if defined(Q_WS_PM)
4254 processor.scanPolygon( pa, wind );
4255#else
4256 scanPolygon(pa, wind, processor);
4257#endif
4258
4259 return processor.result;
4260}
4261/*!
4262 Simply calls QCanvasItem::chunks().
4263*/
4264QPointArray QCanvasRectangle::chunks() const
4265{
4266 // No need to do a polygon scan!
4267 return QCanvasItem::chunks();
4268}
4269
4270/*!
4271 Returns the bounding rectangle of the polygonal item, based on
4272 areaPoints().
4273*/
4274QRect QCanvasPolygonalItem::boundingRect() const
4275{
4276 return areaPoints().boundingRect();
4277}
4278
4279/*!
4280 Reimplemented from QCanvasItem, this draws the polygonal item by
4281 setting the pen and brush for the item on the painter \a p and
4282 calling drawShape().
4283*/
4284void QCanvasPolygonalItem::draw(QPainter & p)
4285{
4286 p.setPen(pn);
4287 p.setBrush(br);
4288 drawShape(p);
4289}
4290
4291/*!
4292 \fn void QCanvasPolygonalItem::drawShape(QPainter & p)
4293
4294 Subclasses must reimplement this function to draw their shape. The
4295 pen and brush of \a p are already set to pen() and brush() prior
4296 to calling this function.
4297
4298 \sa draw()
4299*/
4300
4301/*!
4302 \fn QPen QCanvasPolygonalItem::pen() const
4303
4304 Returns the QPen used to draw the outline of the item, if any.
4305
4306 \sa setPen()
4307*/
4308
4309/*!
4310 \fn QBrush QCanvasPolygonalItem::brush() const
4311
4312 Returns the QBrush used to fill the item, if filled.
4313
4314 \sa setBrush()
4315*/
4316
4317/*!
4318 Sets the QPen used when drawing the item to the pen \a p.
4319 Note that many QCanvasPolygonalItems do not use the pen value.
4320
4321 \sa setBrush(), pen(), drawShape()
4322*/
4323void QCanvasPolygonalItem::setPen(QPen p)
4324{
4325 if ( pn != p ) {
4326 removeFromChunks();
4327 pn = p;
4328 addToChunks();
4329 }
4330}
4331
4332/*!
4333 Sets the QBrush used when drawing the polygonal item to the brush \a b.
4334
4335 \sa setPen(), brush(), drawShape()
4336*/
4337void QCanvasPolygonalItem::setBrush(QBrush b)
4338{
4339 if ( br != b) {
4340 br = b;
4341 changeChunks();
4342 }
4343}
4344
4345
4346/*!
4347 \class QCanvasPolygon qcanvas.h
4348 \brief The QCanvasPolygon class provides a polygon on a QCanvas.
4349\if defined(commercial)
4350 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
4351\endif
4352
4353 \module canvas
4354 \ingroup graphics
4355 \ingroup images
4356
4357 Paints a polygon with a QBrush. The polygon's points can be set in
4358 the constructor or set or changed later using setPoints(). Use
4359 points() to retrieve the points, or areaPoints() to retrieve the
4360 points relative to the canvas's origin.
4361
4362 The polygon can be drawn on a painter with drawShape().
4363
4364 Like any other canvas item polygons can be moved with
4365 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
4366 coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
4367 QCanvasItem::setZ().
4368
4369 Note: QCanvasPolygon does not use the pen.
4370*/
4371
4372/*!
4373 Constructs a point-less polygon on the canvas \a canvas. You
4374 should call setPoints() before using it further.
4375*/
4376QCanvasPolygon::QCanvasPolygon(QCanvas* canvas) :
4377 QCanvasPolygonalItem(canvas)
4378{
4379}
4380
4381/*!
4382 Destroys the polygon.
4383*/
4384QCanvasPolygon::~QCanvasPolygon()
4385{
4386 hide();
4387}
4388
4389/*!
4390 Draws the polygon using the painter \a p.
4391
4392 Note that QCanvasPolygon does not support an outline (the pen is
4393 always NoPen).
4394*/
4395void QCanvasPolygon::drawShape(QPainter & p)
4396{
4397 // ### why can't we draw outlines? We could use drawPolyline for it. Lars
4398 // ### see other message. Warwick
4399
4400 p.setPen(NoPen); // since QRegion(QPointArray) excludes outline :-( )-:
4401 p.drawPolygon(poly);
4402}
4403
4404/*!
4405 Sets the points of the polygon to be \a pa. These points will have
4406 their x and y coordinates automatically translated by x(), y() as
4407 the polygon is moved.
4408*/
4409void QCanvasPolygon::setPoints(QPointArray pa)
4410{
4411 removeFromChunks();
4412 poly = pa;
4413 poly.detach(); // Explicit sharing is stupid.
4414 poly.translate((int)x(),(int)y());
4415 addToChunks();
4416}
4417
4418/*!
4419 \reimp
4420*/
4421void QCanvasPolygon::moveBy(double dx, double dy)
4422{
4423 // Note: does NOT call QCanvasPolygonalItem::moveBy(), since that
4424 // only does half this work.
4425 //
4426 int idx = int(x()+dx)-int(x());
4427 int idy = int(y()+dy)-int(y());
4428 if ( idx || idy ) {
4429 removeFromChunks();
4430 poly.translate(idx,idy);
4431 }
4432 myx+=dx;
4433 myy+=dy;
4434 if ( idx || idy ) {
4435 addToChunks();
4436 }
4437}
4438
4439/*!
4440 \class QCanvasSpline qcanvas.h
4441 \brief The QCanvasSpline class provides multi-bezier splines on a QCanvas.
4442\if defined(commercial)
4443 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
4444\endif
4445
4446 \module canvas
4447 \ingroup graphics
4448 \ingroup images
4449
4450 A QCanvasSpline is a sequence of 4-point bezier curves joined
4451 together to make a curved shape.
4452
4453 You set the control points of the spline with setControlPoints().
4454
4455 If the bezier is closed(), then the first control point will be
4456 re-used as the last control point. Therefore, a closed bezier must
4457 have a multiple of 3 control points and an open bezier must have
4458 one extra point.
4459
4460 The beziers are not necessarily joined "smoothly". To ensure this,
4461 set control points appropriately (general reference texts about
4462 beziers will explain this in detail).
4463
4464 Like any other canvas item splines can be moved with
4465 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
4466 coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
4467 QCanvasItem::setZ().
4468
4469*/
4470
4471/*!
4472 Create a spline with no control points on the canvas \a canvas.
4473
4474 \sa setControlPoints()
4475*/
4476QCanvasSpline::QCanvasSpline(QCanvas* canvas) :
4477 QCanvasPolygon(canvas),
4478 cl(TRUE)
4479{
4480}
4481
4482/*!
4483 Destroy the spline.
4484*/
4485QCanvasSpline::~QCanvasSpline()
4486{
4487}
4488
4489// ### shouldn't we handle errors more gracefully than with an assert? Lars
4490// ### no, since it's a programming error. Warwick
4491/*!
4492 Set the spline control points to \a ctrl.
4493
4494 If \a close is TRUE, then the first point in \a ctrl will be
4495 re-used as the last point, and the number of control points must
4496 be a multiple of 3. If \a close is FALSE, one additional control
4497 point is required, and the number of control points must be one of
4498 (4, 7, 10, 13, ...).
4499
4500 If the number of control points doesn't meet the above conditions,
4501 the number of points will be truncated to the largest number of
4502 points that do meet the requirement.
4503*/
4504void QCanvasSpline::setControlPoints(QPointArray ctrl, bool close)
4505{
4506 if ( (int)ctrl.count() % 3 != (close ? 0 : 1) ) {
4507 qWarning( "QCanvasSpline::setControlPoints(): Number of points doesn't fit." );
4508 int numCurves = (ctrl.count() - (close ? 0 : 1 ))/ 3;
4509 ctrl.resize( numCurves*3 + ( close ? 0 : 1 ) );
4510 }
4511
4512 cl = close;
4513 bez = ctrl;
4514 recalcPoly();
4515}
4516
4517/*!
4518 Returns the current set of control points.
4519
4520 \sa setControlPoints(), closed()
4521*/
4522QPointArray QCanvasSpline::controlPoints() const
4523{
4524 return bez;
4525}
4526
4527/*!
4528 Returns TRUE if the control points are a closed set; otherwise
4529 returns FALSE.
4530*/
4531bool QCanvasSpline::closed() const
4532{
4533 return cl;
4534}
4535
4536void QCanvasSpline::recalcPoly()
4537{
4538 QPtrList<QPointArray> segs;
4539 segs.setAutoDelete(TRUE);
4540 int n=0;
4541 for (int i=0; i<(int)bez.count()-1; i+=3) {
4542 QPointArray ctrl(4);
4543 ctrl[0] = bez[i+0];
4544 ctrl[1] = bez[i+1];
4545 ctrl[2] = bez[i+2];
4546 if ( cl )
4547 ctrl[3] = bez[(i+3)%(int)bez.count()];
4548 else
4549 ctrl[3] = bez[i+3];
4550 QPointArray *seg = new QPointArray(ctrl.cubicBezier());
4551 n += seg->count()-1;
4552 segs.append(seg);
4553 }
4554 QPointArray p(n+1);
4555 n=0;
4556 for (QPointArray* seg = segs.first(); seg; seg = segs.next()) {
4557 for (int i=0; i<(int)seg->count()-1; i++)
4558 p[n++] = seg->point(i);
4559 if ( n == (int)p.count()-1 )
4560 p[n] = seg->point(seg->count()-1);
4561 }
4562 QCanvasPolygon::setPoints(p);
4563}
4564
4565/*!
4566 \fn QPointArray QCanvasPolygonalItem::areaPoints() const
4567
4568 This function must be reimplemented by subclasses. It \e must
4569 return the points bounding (i.e. outside and not touching) the
4570 shape or drawing errors will occur.
4571*/
4572
4573/*!
4574 \fn QPointArray QCanvasPolygon::points() const
4575
4576 Returns the vertices of the polygon, not translated by the position.
4577
4578 \sa setPoints(), areaPoints()
4579*/
4580QPointArray QCanvasPolygon::points() const
4581{
4582 QPointArray pa = areaPoints();
4583 pa.translate(int(-x()),int(-y()));
4584 return pa;
4585}
4586
4587/*!
4588 Returns the vertices of the polygon translated by the polygon's
4589 current x(), y() position, i.e. relative to the canvas's origin.
4590
4591 \sa setPoints(), points()
4592*/
4593QPointArray QCanvasPolygon::areaPoints() const
4594{
4595 return poly.copy();
4596}
4597
4598// ### mark: Why don't we offer a constructor that lets the user set the
4599// points -- that way for some uses just the constructor call would be
4600// required?
4601/*!
4602 \class QCanvasLine qcanvas.h
4603 \brief The QCanvasLine class provides a line on a QCanvas.
4604\if defined(commercial)
4605 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
4606\endif
4607
4608 \module canvas
4609 \ingroup graphics
4610 \ingroup images
4611
4612 The line inherits functionality from QCanvasPolygonalItem, for
4613 example the setPen() function. The start and end points of the
4614 line are set with setPoints().
4615
4616 Like any other canvas item lines can be moved with
4617 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
4618 coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
4619 QCanvasItem::setZ().
4620*/
4621
4622/*!
4623 Constructs a line from (0,0) to (0,0) on \a canvas.
4624
4625 \sa setPoints().
4626*/
4627QCanvasLine::QCanvasLine(QCanvas* canvas) :
4628 QCanvasPolygonalItem(canvas)
4629{
4630 x1 = y1 = x2 = y2 = 0;
4631}
4632
4633/*!
4634 Destroys the line.
4635*/
4636QCanvasLine::~QCanvasLine()
4637{
4638 hide();
4639}
4640
4641/*!
4642 \reimp
4643*/
4644void QCanvasLine::setPen(QPen p)
4645{
4646 QCanvasPolygonalItem::setPen(p);
4647}
4648
4649/*!
4650 \fn QPoint QCanvasLine::startPoint () const
4651
4652 Returns the start point of the line.
4653
4654 \sa setPoints(), endPoint()
4655*/
4656
4657/*!
4658 \fn QPoint QCanvasLine::endPoint () const
4659
4660 Returns the end point of the line.
4661
4662 \sa setPoints(), startPoint()
4663*/
4664
4665/*!
4666 Sets the line's start point to (\a xa, \a ya) and its end point to
4667 (\a xb, \a yb).
4668*/
4669void QCanvasLine::setPoints(int xa, int ya, int xb, int yb)
4670{
4671 if ( x1 != xa || x2 != xb || y1 != ya || y2 != yb ) {
4672 removeFromChunks();
4673 x1 = xa;
4674 y1 = ya;
4675 x2 = xb;
4676 y2 = yb;
4677 addToChunks();
4678 }
4679}
4680
4681/*!
4682 \reimp
4683*/
4684void QCanvasLine::drawShape(QPainter &p)
4685{
4686 p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2));
4687}
4688
4689/*!
4690 \reimp
4691
4692 Note that the area defined by the line is somewhat thicker than
4693 the line that is actually drawn.
4694*/
4695QPointArray QCanvasLine::areaPoints() const
4696{
4697 QPointArray p(4);
4698 int xi = int(x());
4699 int yi = int(y());
4700 int pw = pen().width();
4701 int dx = QABS(x1-x2);
4702 int dy = QABS(y1-y2);
4703 pw = pw*4/3+2; // approx pw*sqrt(2)
4704 int px = x1<x2 ? -pw : pw ;
4705 int py = y1<y2 ? -pw : pw ;
4706 if ( dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2)) ) {
4707 // steep
4708 if ( px == py ) {
4709 p[0] = QPoint(x1+xi ,y1+yi+py);
4710 p[1] = QPoint(x2+xi-px,y2+yi );
4711 p[2] = QPoint(x2+xi ,y2+yi-py);
4712 p[3] = QPoint(x1+xi+px,y1+yi );
4713 } else {
4714 p[0] = QPoint(x1+xi+px,y1+yi );
4715 p[1] = QPoint(x2+xi ,y2+yi-py);
4716 p[2] = QPoint(x2+xi-px,y2+yi );
4717 p[3] = QPoint(x1+xi ,y1+yi+py);
4718 }
4719 } else if ( dx > dy ) {
4720 // horizontal
4721 p[0] = QPoint(x1+xi+px,y1+yi+py);
4722 p[1] = QPoint(x2+xi-px,y2+yi+py);
4723 p[2] = QPoint(x2+xi-px,y2+yi-py);
4724 p[3] = QPoint(x1+xi+px,y1+yi-py);
4725 } else {
4726 // vertical
4727 p[0] = QPoint(x1+xi+px,y1+yi+py);
4728 p[1] = QPoint(x2+xi+px,y2+yi-py);
4729 p[2] = QPoint(x2+xi-px,y2+yi-py);
4730 p[3] = QPoint(x1+xi-px,y1+yi+py);
4731 }
4732 return p;
4733}
4734
4735/*!
4736 \reimp
4737
4738*/
4739
4740void QCanvasLine::moveBy(double dx, double dy)
4741{
4742 QCanvasPolygonalItem::moveBy(dx, dy);
4743}
4744
4745/*!
4746 \class QCanvasRectangle qcanvas.h
4747 \brief The QCanvasRectangle class provides a rectangle on a QCanvas.
4748\if defined(commercial)
4749 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
4750\endif
4751
4752 \module canvas
4753 \ingroup graphics
4754 \ingroup images
4755
4756 This item paints a single rectangle which may have any pen() and
4757 brush(), but may not be tilted/rotated. For rotated rectangles,
4758 use QCanvasPolygon.
4759
4760 The rectangle's size and initial position can be set in the
4761 constructor. The size can be set or changed later using setSize().
4762 Use height() and width() to retrieve the rectangle's dimensions.
4763
4764 The rectangle can be drawn on a painter with drawShape().
4765
4766 Like any other canvas item rectangles can be moved with
4767 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
4768 coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
4769 QCanvasItem::setZ().
4770
4771*/
4772
4773/*!
4774 Constructs a rectangle at position (0,0) with both width and
4775 height set to 32 pixels on \a canvas.
4776*/
4777QCanvasRectangle::QCanvasRectangle(QCanvas* canvas) :
4778 QCanvasPolygonalItem(canvas),
4779 w(32), h(32)
4780{
4781}
4782
4783/*!
4784 Constructs a rectangle positioned and sized by \a r on \a canvas.
4785*/
4786QCanvasRectangle::QCanvasRectangle(const QRect& r, QCanvas* canvas) :
4787 QCanvasPolygonalItem(canvas),
4788 w(r.width()), h(r.height())
4789{
4790 move(r.x(),r.y());
4791}
4792
4793/*!
4794 Constructs a rectangle at position (\a x, \a y) and size \a width
4795 by \a height, on \a canvas.
4796*/
4797QCanvasRectangle::QCanvasRectangle(int x, int y, int width, int height,
4798 QCanvas* canvas) :
4799 QCanvasPolygonalItem(canvas),
4800 w(width), h(height)
4801{
4802 move(x,y);
4803}
4804
4805/*!
4806 Destroys the rectangle.
4807*/
4808QCanvasRectangle::~QCanvasRectangle()
4809{
4810 hide();
4811}
4812
4813
4814/*!
4815 Returns the width of the rectangle.
4816*/
4817int QCanvasRectangle::width() const
4818{
4819 return w;
4820}
4821
4822/*!
4823 Returns the height of the rectangle.
4824*/
4825int QCanvasRectangle::height() const
4826{
4827 return h;
4828}
4829
4830/*!
4831 Sets the \a width and \a height of the rectangle.
4832*/
4833void QCanvasRectangle::setSize(int width, int height)
4834{
4835 if ( w != width || h != height ) {
4836 removeFromChunks();
4837 w = width;
4838 h = height;
4839 addToChunks();
4840 }
4841}
4842
4843/*!
4844 \fn QSize QCanvasRectangle::size() const
4845
4846 Returns the width() and height() of the rectangle.
4847
4848 \sa rect(), setSize()
4849*/
4850
4851/*!
4852 \fn QRect QCanvasRectangle::rect() const
4853
4854 Returns the integer-converted x(), y() position and size() of the
4855 rectangle as a QRect.
4856*/
4857
4858/*!
4859 \reimp
4860*/
4861QPointArray QCanvasRectangle::areaPoints() const
4862{
4863 QPointArray pa(4);
4864 int pw = (pen().width()+1)/2;
4865 if ( pw < 1 ) pw = 1;
4866 if ( pen() == NoPen ) pw = 0;
4867 pa[0] = QPoint((int)x()-pw,(int)y()-pw);
4868 pa[1] = pa[0] + QPoint(w+pw*2,0);
4869 pa[2] = pa[1] + QPoint(0,h+pw*2);
4870 pa[3] = pa[0] + QPoint(0,h+pw*2);
4871 return pa;
4872}
4873
4874/*!
4875 Draws the rectangle on painter \a p.
4876*/
4877void QCanvasRectangle::drawShape(QPainter & p)
4878{
4879 p.drawRect((int)x(), (int)y(), w, h);
4880}
4881
4882
4883/*!
4884 \class QCanvasEllipse qcanvas.h
4885 \brief The QCanvasEllipse class provides an ellipse or ellipse segment on a QCanvas.
4886\if defined(commercial)
4887 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
4888\endif
4889
4890 \module canvas
4891 \ingroup graphics
4892 \ingroup images
4893
4894 A canvas item that paints an ellipse or ellipse segment with a QBrush.
4895 The ellipse's height, width, start angle and angle length can be set
4896 at construction time. The size can be changed at runtime with
4897 setSize(), and the angles can be changed (if you're displaying an
4898 ellipse segment rather than a whole ellipse) with setAngles().
4899
4900 Note that angles are specified in 16ths of a degree.
4901
4902 \target anglediagram
4903 \img qcanvasellipse.png Ellipse
4904
4905 If a start angle and length angle are set then an ellipse segment
4906 will be drawn. The start angle is the angle that goes from zero in a
4907 counter-clockwise direction (shown in green in the diagram). The
4908 length angle is the angle from the start angle in a
4909 counter-clockwise direction (shown in blue in the diagram). The blue
4910 segment is the segment of the ellipse that would be drawn. If no
4911 start angle and length angle are specified the entire ellipse is
4912 drawn.
4913
4914 The ellipse can be drawn on a painter with drawShape().
4915
4916 Like any other canvas item ellipses can be moved with move() and
4917 moveBy(), or by setting coordinates with setX(), setY() and setZ().
4918
4919 Note: QCanvasEllipse does not use the pen.
4920*/
4921
4922/*!
4923 Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas.
4924*/
4925QCanvasEllipse::QCanvasEllipse(QCanvas* canvas) :
4926 QCanvasPolygonalItem(canvas),
4927 w(32), h(32),
4928 a1(0), a2(360*16)
4929{
4930}
4931
4932/*!
4933 Constructs a \a width by \a height pixel ellipse, centered at
4934 (0, 0) on \a canvas.
4935*/
4936QCanvasEllipse::QCanvasEllipse(int width, int height, QCanvas* canvas) :
4937 QCanvasPolygonalItem(canvas),
4938 w(width),h(height),
4939 a1(0),a2(360*16)
4940{
4941}
4942
4943// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars
4944// ### it's how QPainter does it, so QCanvas does too for consistency. If it's
4945// ### a good idea, it should be added to QPainter, not just to QCanvas. Warwick
4946/*!
4947 Constructs a \a width by \a height pixel ellipse, centered at
4948 (0, 0) on \a canvas. Only a segment of the ellipse is drawn,
4949 starting at angle \a startangle, and extending for angle \a angle
4950 (the angle length).
4951
4952 Note that angles are specified in
4953 <small><sup>1</sup>/<sub>16</sub></small>ths of a degree.
4954*/
4955QCanvasEllipse::QCanvasEllipse(int width, int height,
4956 int startangle, int angle, QCanvas* canvas) :
4957 QCanvasPolygonalItem(canvas),
4958 w(width),h(height),
4959 a1(startangle),a2(angle)
4960{
4961}
4962
4963/*!
4964 Destroys the ellipse.
4965*/
4966QCanvasEllipse::~QCanvasEllipse()
4967{
4968 hide();
4969}
4970
4971/*!
4972 Returns the width of the ellipse.
4973*/
4974int QCanvasEllipse::width() const
4975{
4976 return w;
4977}
4978
4979/*!
4980 Returns the height of the ellipse.
4981*/
4982int QCanvasEllipse::height() const
4983{
4984 return h;
4985}
4986
4987/*!
4988 Sets the \a width and \a height of the ellipse.
4989*/
4990void QCanvasEllipse::setSize(int width, int height)
4991{
4992 if ( w != width || h != height ) {
4993 removeFromChunks();
4994 w = width;
4995 h = height;
4996 addToChunks();
4997 }
4998}
4999
5000/*!
5001 \fn int QCanvasEllipse::angleStart() const
5002
5003 Returns the start angle in 16ths of a degree. Initially
5004 this will be 0.
5005
5006 \sa setAngles(), angleLength()
5007*/
5008
5009/*!
5010 \fn int QCanvasEllipse::angleLength() const
5011
5012 Returns the length angle (the extent of the ellipse segment) in
5013 16ths of a degree. Initially this will be 360 * 16 (a complete
5014 ellipse).
5015
5016 \sa setAngles(), angleStart()
5017*/
5018
5019/*!
5020 Sets the angles for the ellipse. The start angle is \a start and
5021 the extent of the segment is \a length (the angle length) from the
5022 \a start. The angles are specified in 16ths of a degree. By
5023 default the ellipse will start at 0 and have an angle length of
5024 360 * 16 (a complete ellipse).
5025
5026 \sa angleStart(), angleLength()
5027*/
5028void QCanvasEllipse::setAngles(int start, int length)
5029{
5030 if ( a1 != start || a2 != length ) {
5031 removeFromChunks();
5032 a1 = start;
5033 a2 = length;
5034 addToChunks();
5035 }
5036}
5037
5038/*!
5039 \reimp
5040*/
5041QPointArray QCanvasEllipse::areaPoints() const
5042{
5043 QPointArray r;
5044 // makeArc at 0,0, then translate so that fixed point math doesn't overflow
5045 r.makeArc(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2);
5046 r.resize(r.size()+1);
5047 r.setPoint(r.size()-1,int(x()),int(y()));
5048 return r;
5049}
5050
5051// ### support outlines! Lars
5052// ### QRegion doesn't, so we cannot (try it). Warwick
5053/*!
5054 Draws the ellipse, centered at x(), y() using the painter \a p.
5055
5056 Note that QCanvasEllipse does not support an outline (the pen is
5057 always NoPen).
5058*/
5059void QCanvasEllipse::drawShape(QPainter & p)
5060{
5061 p.setPen(NoPen); // since QRegion(QPointArray) excludes outline :-( )-:
5062 if ( !a1 && a2 == 360*16 ) {
5063 p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h);
5064 } else {
5065 p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2);
5066 }
5067}
5068
5069
5070/*!
5071 \class QCanvasText qcanvas.h
5072 \brief The QCanvasText class provides a text object on a QCanvas.
5073\if defined(commercial)
5074 It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
5075\endif
5076
5077 \module canvas
5078 \ingroup graphics
5079 \ingroup images
5080
5081 A canvas text item has text with font, color and alignment
5082 attributes. The text and font can be set in the constructor or set
5083 or changed later with setText() and setFont(). The color is set
5084 with setColor() and the alignment with setTextFlags(). The text
5085 item's bounding rectangle is retrieved with boundingRect().
5086
5087 The text can be drawn on a painter with draw().
5088
5089 Like any other canvas item text items can be moved with
5090 QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
5091 coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
5092 QCanvasItem::setZ().
5093*/
5094
5095/*!
5096 Constructs a QCanvasText with the text "\<text\>", on \a canvas.
5097*/
5098QCanvasText::QCanvasText(QCanvas* canvas) :
5099 QCanvasItem(canvas),
5100 txt("<text>"), flags(0)
5101{
5102 setRect();
5103}
5104
5105// ### add textflags to the constructor? Lars
5106/*!
5107 Constructs a QCanvasText with the text \a t, on canvas \a canvas.
5108*/
5109QCanvasText::QCanvasText(const QString& t, QCanvas* canvas) :
5110 QCanvasItem(canvas),
5111 txt(t), flags(0)
5112{
5113 setRect();
5114}
5115
5116// ### see above
5117/*!
5118 Constructs a QCanvasText with the text \a t and font \a f, on the
5119 canvas \a canvas.
5120*/
5121QCanvasText::QCanvasText(const QString& t, QFont f, QCanvas* canvas) :
5122 QCanvasItem(canvas),
5123 txt(t), flags(0),
5124 fnt(f)
5125{
5126 setRect();
5127}
5128
5129/*!
5130 Destroys the canvas text item.
5131*/
5132QCanvasText::~QCanvasText()
5133{
5134 removeFromChunks();
5135}
5136
5137/*!
5138 Returns the bounding rectangle of the text.
5139*/
5140QRect QCanvasText::boundingRect() const { return brect; }
5141
5142void QCanvasText::setRect()
5143{
5144 brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt);
5145}
5146
5147/*!
5148 \fn int QCanvasText::textFlags() const
5149
5150 Returns the currently set alignment flags.
5151
5152 \sa setTextFlags() Qt::AlignmentFlags
5153*/
5154
5155
5156/*!
5157 Sets the alignment flags to \a f. These are a bitwise OR of the
5158 flags available to QPainter::drawText() -- see the
5159 \l{Qt::AlignmentFlags}.
5160
5161 \sa setFont() setColor()
5162*/
5163void QCanvasText::setTextFlags(int f)
5164{
5165 if ( flags != f ) {
5166 removeFromChunks();
5167 flags = f;
5168 setRect();
5169 addToChunks();
5170 }
5171}
5172
5173/*!
5174 Returns the text item's text.
5175
5176 \sa setText()
5177*/
5178QString QCanvasText::text() const
5179{
5180 return txt;
5181}
5182
5183
5184/*!
5185 Sets the text item's text to \a t. The text may contain newlines.
5186
5187 \sa text(), setFont(), setColor() setTextFlags()
5188*/
5189void QCanvasText::setText( const QString& t )
5190{
5191 if ( txt != t ) {
5192 removeFromChunks();
5193 txt = t;
5194 setRect();
5195 addToChunks();
5196 }
5197}
5198
5199/*!
5200 Returns the font in which the text is drawn.
5201
5202 \sa setFont()
5203*/
5204QFont QCanvasText::font() const
5205{
5206 return fnt;
5207}
5208
5209/*!
5210 Sets the font in which the text is drawn to font \a f.
5211
5212 \sa font()
5213*/
5214void QCanvasText::setFont( const QFont& f )
5215{
5216 if ( f != fnt ) {
5217 removeFromChunks();
5218 fnt = f;
5219 setRect();
5220 addToChunks();
5221 }
5222}
5223
5224/*!
5225 Returns the color of the text.
5226
5227 \sa setColor()
5228*/
5229QColor QCanvasText::color() const
5230{
5231 return col;
5232}
5233
5234/*!
5235 Sets the color of the text to the color \a c.
5236
5237 \sa color(), setFont()
5238*/
5239void QCanvasText::setColor(const QColor& c)
5240{
5241 col=c;
5242 changeChunks();
5243}
5244
5245
5246/*!
5247 \reimp
5248*/
5249void QCanvasText::moveBy(double dx, double dy)
5250{
5251 int idx = int(x()+dx)-int(x());
5252 int idy = int(y()+dy)-int(y());
5253 if ( idx || idy ) {
5254 removeFromChunks();
5255 }
5256 myx+=dx;
5257 myy+=dy;
5258 if ( idx || idy ) {
5259 brect.moveBy(idx,idy);
5260 addToChunks();
5261 }
5262}
5263
5264/*!
5265 Draws the text using the painter \a painter.
5266*/
5267void QCanvasText::draw(QPainter& painter)
5268{
5269 painter.setFont(fnt);
5270 painter.setPen(col);
5271 painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt);
5272}
5273
5274/*!
5275 \reimp
5276*/
5277void QCanvasText::changeChunks()
5278{
5279 if (isVisible() && canvas()) {
5280 int chunksize=canvas()->chunkSize();
5281 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
5282 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
5283 canvas()->setChangedChunk(i,j);
5284 }
5285 }
5286 }
5287}
5288
5289/*!
5290 Adds the text item to the appropriate chunks.
5291*/
5292void QCanvasText::addToChunks()
5293{
5294 if (isVisible() && canvas()) {
5295 int chunksize=canvas()->chunkSize();
5296 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
5297 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
5298 canvas()->addItemToChunk(this,i,j);
5299 }
5300 }
5301 }
5302}
5303
5304/*!
5305 Removes the text item from the appropriate chunks.
5306*/
5307void QCanvasText::removeFromChunks()
5308{
5309 if (isVisible() && canvas()) {
5310 int chunksize=canvas()->chunkSize();
5311 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
5312 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
5313 canvas()->removeItemFromChunk(this,i,j);
5314 }
5315 }
5316 }
5317}
5318
5319
5320/*!
5321 Returns 0 (QCanvasItem::Rtti_Item).
5322
5323 Make your derived classes return their own values for rtti(), so
5324 that you can distinguish between objects returned by
5325 QCanvas::at(). You should use values greater than 1000 to allow
5326 for extensions to this class.
5327
5328 Overuse of this functionality can damage it's extensibility. For
5329 example, once you have identified a base class of a QCanvasItem
5330 found by QCanvas::at(), cast it to that type and call meaningful
5331 methods rather than acting upon the object based on its rtti
5332 value.
5333
5334 For example:
5335
5336 \code
5337 QCanvasItem* item;
5338 // Find an item, e.g. with QCanvasItem::collisions().
5339 ...
5340 if (item->rtti() == MySprite::RTTI ) {
5341 MySprite* s = (MySprite*)item;
5342 if (s->isDamagable()) s->loseHitPoints(1000);
5343 if (s->isHot()) myself->loseHitPoints(1000);
5344 ...
5345 }
5346 \endcode
5347*/
5348int QCanvasItem::rtti() const { return RTTI; }
5349int QCanvasItem::RTTI = Rtti_Item;
5350
5351/*!
5352 Returns 1 (QCanvasItem::Rtti_Sprite).
5353
5354 \sa QCanvasItem::rtti()
5355*/
5356int QCanvasSprite::rtti() const { return RTTI; }
5357int QCanvasSprite::RTTI = Rtti_Sprite;
5358
5359/*!
5360 Returns 2 (QCanvasItem::Rtti_PolygonalItem).
5361
5362 \sa QCanvasItem::rtti()
5363*/
5364int QCanvasPolygonalItem::rtti() const { return RTTI; }
5365int QCanvasPolygonalItem::RTTI = Rtti_PolygonalItem;
5366
5367/*!
5368 Returns 3 (QCanvasItem::Rtti_Text).
5369
5370 \sa QCanvasItem::rtti()
5371*/
5372int QCanvasText::rtti() const { return Rtti_Text; }
5373int QCanvasText::RTTI = Rtti_Text;
5374
5375/*!
5376 Returns 4 (QCanvasItem::Rtti_Polygon).
5377
5378 \sa QCanvasItem::rtti()
5379*/
5380int QCanvasPolygon::rtti() const { return RTTI; }
5381int QCanvasPolygon::RTTI = Rtti_Polygon;
5382
5383/*!
5384 Returns 5 (QCanvasItem::Rtti_Rectangle).
5385
5386 \sa QCanvasItem::rtti()
5387*/
5388int QCanvasRectangle::rtti() const { return RTTI; }
5389int QCanvasRectangle::RTTI = Rtti_Rectangle;
5390
5391/*!
5392 Returns 6 (QCanvasItem::Rtti_Ellipse).
5393
5394 \sa QCanvasItem::rtti()
5395*/
5396int QCanvasEllipse::rtti() const { return RTTI; }
5397int QCanvasEllipse::RTTI = Rtti_Ellipse;
5398
5399/*!
5400 Returns 7 (QCanvasItem::Rtti_Line).
5401
5402 \sa QCanvasItem::rtti()
5403*/
5404int QCanvasLine::rtti() const { return RTTI; }
5405int QCanvasLine::RTTI = Rtti_Line;
5406
5407/*!
5408 Returns 8 (QCanvasItem::Rtti_Spline).
5409
5410 \sa QCanvasItem::rtti()
5411*/
5412int QCanvasSpline::rtti() const { return RTTI; }
5413int QCanvasSpline::RTTI = Rtti_Spline;
5414
5415/*!
5416 Constructs a QCanvasSprite which uses images from the
5417 QCanvasPixmapArray \a a.
5418
5419 The sprite in initially positioned at (0, 0) on \a canvas, using
5420 frame 0.
5421*/
5422QCanvasSprite::QCanvasSprite(QCanvasPixmapArray* a, QCanvas* canvas) :
5423 QCanvasItem(canvas),
5424 frm(0),
5425 anim_val(0),
5426 anim_state(0),
5427 anim_type(0),
5428 images(a)
5429{
5430}
5431
5432
5433/*!
5434 Set the array of images used for displaying the sprite to the
5435 QCanvasPixmapArray \a a.
5436
5437 If the current frame() is larger than the number of images in \a
5438 a, the current frame will be reset to 0.
5439*/
5440void QCanvasSprite::setSequence(QCanvasPixmapArray* a)
5441{
5442 bool isvisible = isVisible();
5443 if ( isvisible && images )
5444 hide();
5445 images = a;
5446 if ( frm >= (int)images->count() )
5447 frm = 0;
5448 if ( isvisible )
5449 show();
5450}
5451
5452/*!
5453\internal
5454
5455Marks any chunks the sprite touches as changed.
5456*/
5457void QCanvasSprite::changeChunks()
5458{
5459 if (isVisible() && canvas()) {
5460 int chunksize=canvas()->chunkSize();
5461 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
5462 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
5463 canvas()->setChangedChunk(i,j);
5464 }
5465 }
5466 }
5467}
5468
5469/*!
5470 Destroys the sprite and removes it from the canvas. Does \e not
5471 delete the images.
5472*/
5473QCanvasSprite::~QCanvasSprite()
5474{
5475 removeFromChunks();
5476}
5477
5478/*!
5479 Sets the animation frame used for displaying the sprite to \a f,
5480 an index into the QCanvasSprite's QCanvasPixmapArray. The call
5481 will be ignored if \a f is larger than frameCount() or smaller
5482 than 0.
5483
5484 \sa frame() move()
5485*/
5486void QCanvasSprite::setFrame(int f)
5487{
5488 move(x(),y(),f);
5489}
5490
5491/*!
5492 \enum QCanvasSprite::FrameAnimationType
5493
5494 This enum is used to identify the different types of frame
5495 animation offered by QCanvasSprite.
5496
5497 \value Cycle at each advance the frame number will be incremented by
5498 1 (modulo the frame count).
5499 \value Oscillate at each advance the frame number will be
5500 incremented by 1 up to the frame count then decremented to by 1 to
5501 0, repeating this sequence forever.
5502*/
5503
5504/*!
5505 Sets the animation characteristics for the sprite.
5506
5507 For \a type == \c Cycle, the frames will increase by \a step
5508 at each advance, modulo the frameCount().
5509
5510 For \a type == \c Oscillate, the frames will increase by \a step
5511 at each advance, up to the frameCount(), then decrease by \a step
5512 back to 0, repeating forever.
5513
5514 The \a state parameter is for internal use.
5515*/
5516void QCanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state)
5517{
5518 anim_val = step;
5519 anim_type = type;
5520 anim_state = state;
5521 setAnimated(TRUE);
5522}
5523
5524/*!
5525 Extends the default QCanvasItem implementation to provide the
5526 functionality of setFrameAnimation().
5527
5528 The \a phase is 0 or 1: see QCanvasItem::advance() for details.
5529
5530 \sa QCanvasItem::advance() setVelocity()
5531*/
5532void QCanvasSprite::advance(int phase)
5533{
5534 if ( phase==1 ) {
5535 int nf = frame();
5536 if ( anim_type == Oscillate ) {
5537 if ( anim_state )
5538 nf += anim_val;
5539 else
5540 nf -= anim_val;
5541 if ( nf < 0 ) {
5542 nf = abs(anim_val);
5543 anim_state = !anim_state;
5544 } else if ( nf >= frameCount() ) {
5545 nf = frameCount()-1-abs(anim_val);
5546 anim_state = !anim_state;
5547 }
5548 } else {
5549 nf = (nf + anim_val + frameCount()) % frameCount();
5550 }
5551 move(x()+xVelocity(),y()+yVelocity(),nf);
5552 }
5553}
5554
5555
5556/*!
5557 \fn int QCanvasSprite::frame() const
5558
5559 Returns the index of the current animation frame in the
5560 QCanvasSprite's QCanvasPixmapArray.
5561
5562 \sa setFrame(), move()
5563*/
5564
5565/*!
5566 \fn int QCanvasSprite::frameCount() const
5567
5568 Returns the number of frames in the QCanvasSprite's
5569 QCanvasPixmapArray.
5570*/
5571
5572
5573/*!
5574 \reimp
5575 \internal
5576 Moves the sprite to the position \a x, \a y.
5577 Keep it visible.
5578*/
5579void QCanvasSprite::move(double x, double y) { QCanvasItem::move(x,y); }
5580
5581/*!
5582 \fn void QCanvasSprite::move(double nx, double ny, int nf)
5583
5584 Set the position of the sprite to \a nx, \a ny and the current
5585 frame to \a nf. \a nf will be ignored if it is larger than
5586 frameCount() or smaller than 0.
5587*/
5588void QCanvasSprite::move(double nx, double ny, int nf)
5589{
5590 if (isVisible() && canvas()) {
5591 hide();
5592 QCanvasItem::move(nx,ny);
5593 if ( nf >= 0 && nf < frameCount() )
5594 frm=nf;
5595 show();
5596 } else {
5597 QCanvasItem::move(nx,ny);
5598 if ( nf >= 0 && nf < frameCount() )
5599 frm=nf;
5600 }
5601}
5602
5603#if !defined(Q_WS_PM)
5604
5605class QCanvasPolygonScanner : public QPolygonScanner {
5606 QPolygonalProcessor& processor;
5607public:
5608 QCanvasPolygonScanner(QPolygonalProcessor& p) :
5609 processor(p)
5610 {
5611 }
5612 void processSpans( int n, QPoint* point, int* width )
5613 {
5614 processor.doSpans(n,point,width);
5615 }
5616};
5617
5618void QCanvasPolygonalItem::scanPolygon(const QPointArray& pa, int winding, QPolygonalProcessor& process) const
5619{
5620 QCanvasPolygonScanner scanner(process);
5621 scanner.scan(pa,winding);
5622}
5623
5624#endif
5625
5626#endif // QT_NO_CANVAS
Note: See TracBrowser for help on using the repository browser.