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

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

On OS/2, use regions to scan polygons in order to determine which chunks they intersect with. This should be as fast as QPolygonScanner, but produces much better results (and no artefacts).

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