source: vendor/trolltech/current/src/canvas/qcanvas.cpp

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

Imported canvas and session manager sources from the official release 3.3.1 from Trolltech.

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