source: trunk/src/qt3support/canvas/q3canvas.cpp@ 1056

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

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

File size: 131.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt3Support module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "q3canvas.h"
43#include "qapplication.h"
44#include "qbitmap.h"
45#include "qdesktopwidget.h"
46#include "qimage.h"
47#include "q3ptrdict.h"
48#include "qpainter.h"
49#include "q3polygonscanner.h"
50#include "qtimer.h"
51#include "q3tl.h"
52
53#include <stdlib.h>
54
55QT_BEGIN_NAMESPACE
56
57using namespace Qt;
58
59class Q3CanvasData {
60public:
61 Q3CanvasData() :
62 itemDict(1013), animDict(503)
63 {
64 }
65
66 Q3PtrList<Q3CanvasView> viewList;
67 Q3PtrDict<void> itemDict;
68 Q3PtrDict<void> animDict;
69};
70
71class Q3CanvasViewData {
72public:
73 Q3CanvasViewData() {}
74#ifndef QT_NO_TRANSFORMATIONS
75 QMatrix xform;
76 QMatrix ixform;
77#endif
78 QRegion eraseRegion;
79};
80
81// clusterizer
82
83class Q3CanvasClusterizer {
84public:
85 Q3CanvasClusterizer(int maxclusters);
86 ~Q3CanvasClusterizer();
87
88 void add(int x, int y); // 1x1 rectangle (point)
89 void add(int x, int y, int w, int h);
90 void add(const QRect& rect);
91
92 void clear();
93 int clusters() const { return count; }
94 const QRect& operator[](int i) const;
95
96private:
97 QRect* cluster;
98 int count;
99 const int maxcl;
100};
101
102static
103void include(QRect& r, const QRect& rect)
104{
105 if (rect.left()<r.left()) {
106 r.setLeft(rect.left());
107 }
108 if (rect.right()>r.right()) {
109 r.setRight(rect.right());
110 }
111 if (rect.top()<r.top()) {
112 r.setTop(rect.top());
113 }
114 if (rect.bottom()>r.bottom()) {
115 r.setBottom(rect.bottom());
116 }
117}
118
119/*
120A Q3CanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
121by a merging heuristic.
122*/
123Q3CanvasClusterizer::Q3CanvasClusterizer(int maxclusters) :
124 cluster(new QRect[maxclusters]),
125 count(0),
126 maxcl(maxclusters)
127{ }
128
129Q3CanvasClusterizer::~Q3CanvasClusterizer()
130{
131 delete [] cluster;
132}
133
134void Q3CanvasClusterizer::clear()
135{
136 count=0;
137}
138
139void Q3CanvasClusterizer::add(int x, int y)
140{
141 add(QRect(x,y,1,1));
142}
143
144void Q3CanvasClusterizer::add(int x, int y, int w, int h)
145{
146 add(QRect(x,y,w,h));
147}
148
149void Q3CanvasClusterizer::add(const QRect& rect)
150{
151 QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2);
152
153 //Q_ASSERT(rect.width()>0 && rect.height()>0);
154
155 int cursor;
156
157 for (cursor=0; cursor<count; cursor++) {
158 if (cluster[cursor].contains(rect)) {
159 // Wholly contained already.
160 return;
161 }
162 }
163
164 int lowestcost=9999999;
165 int cheapest=-1;
166 cursor = 0;
167 while(cursor<count) {
168 if (cluster[cursor].intersects(biggerrect)) {
169 QRect larger=cluster[cursor];
170 include(larger,rect);
171 int cost = larger.width()*larger.height() -
172 cluster[cursor].width()*cluster[cursor].height();
173
174 if (cost < lowestcost) {
175 bool bad=false;
176 for (int c=0; c<count && !bad; c++) {
177 bad=cluster[c].intersects(larger) && c!=cursor;
178 }
179 if (!bad) {
180 cheapest=cursor;
181 lowestcost=cost;
182 }
183 }
184 }
185 cursor++;
186 }
187
188 if (cheapest>=0) {
189 include(cluster[cheapest],rect);
190 return;
191 }
192
193 if (count < maxcl) {
194 cluster[count++]=rect;
195 return;
196 }
197
198 // Do cheapest of:
199 // add to closest cluster
200 // do cheapest cluster merge, add to new cluster
201
202 lowestcost=9999999;
203 cheapest=-1;
204 cursor=0;
205 while(cursor<count) {
206 QRect larger=cluster[cursor];
207 include(larger,rect);
208 int cost=larger.width()*larger.height()
209 - cluster[cursor].width()*cluster[cursor].height();
210 if (cost < lowestcost) {
211 bool bad=false;
212 for (int c=0; c<count && !bad; c++) {
213 bad=cluster[c].intersects(larger) && c!=cursor;
214 }
215 if (!bad) {
216 cheapest=cursor;
217 lowestcost=cost;
218 }
219 }
220 cursor++;
221 }
222
223 // ###
224 // could make an heuristic guess as to whether we need to bother
225 // looking for a cheap merge.
226
227 int cheapestmerge1 = -1;
228 int cheapestmerge2 = -1;
229
230 int merge1 = 0;
231 while(merge1 < count) {
232 int merge2=0;
233 while(merge2 < count) {
234 if(merge1!=merge2) {
235 QRect larger=cluster[merge1];
236 include(larger,cluster[merge2]);
237 int cost=larger.width()*larger.height()
238 - cluster[merge1].width()*cluster[merge1].height()
239 - cluster[merge2].width()*cluster[merge2].height();
240 if (cost < lowestcost) {
241 bool bad=false;
242 for (int c=0; c<count && !bad; c++) {
243 bad=cluster[c].intersects(larger) && c!=cursor;
244 }
245 if (!bad) {
246 cheapestmerge1=merge1;
247 cheapestmerge2=merge2;
248 lowestcost=cost;
249 }
250 }
251 }
252 merge2++;
253 }
254 merge1++;
255 }
256
257 if (cheapestmerge1>=0) {
258 include(cluster[cheapestmerge1],cluster[cheapestmerge2]);
259 cluster[cheapestmerge2]=cluster[count--];
260 } else {
261 // if (!cheapest) debugRectangles(rect);
262 include(cluster[cheapest],rect);
263 }
264
265 // NB: clusters do not intersect (or intersection will
266 // overwrite). This is a result of the above algorithm,
267 // given the assumption that (x,y) are ordered topleft
268 // to bottomright.
269
270 // ###
271 //
272 // add explicit x/y ordering to that comment, move it to the top
273 // and rephrase it as pre-/post-conditions.
274}
275
276const QRect& Q3CanvasClusterizer::operator[](int i) const
277{
278 return cluster[i];
279}
280
281// end of clusterizer
282
283// there's no more device coordinate clipping done, so introduce these
284// clip setting compat functions
285
286static void qt_setclipregion(QPainter *p, const QRegion &r)
287{
288 QMatrix matrix = p->worldMatrix();
289 p->setWorldMatrix(QMatrix());
290 p->setClipRegion(r);
291 p->setWorldMatrix(matrix);
292}
293
294static void qt_setcliprect(QPainter *p, const QRect &r)
295{
296 qt_setclipregion(p, QRegion(r));
297}
298
299
300class Q_COMPAT_EXPORT Q3CanvasItemPtr {
301public:
302 Q3CanvasItemPtr() : ptr(0) { }
303 Q3CanvasItemPtr(Q3CanvasItem* p) : ptr(p) { }
304
305 bool operator<=(const Q3CanvasItemPtr& that) const
306 {
307 // Order same-z objects by identity.
308 if (that.ptr->z()==ptr->z())
309 return that.ptr <= ptr;
310 return that.ptr->z() <= ptr->z();
311 }
312 bool operator<(const Q3CanvasItemPtr& that) const
313 {
314 // Order same-z objects by identity.
315 if (that.ptr->z()==ptr->z())
316 return that.ptr < ptr;
317 return that.ptr->z() < ptr->z();
318 }
319 bool operator>(const Q3CanvasItemPtr& that) const
320 {
321 // Order same-z objects by identity.
322 if (that.ptr->z()==ptr->z())
323 return that.ptr > ptr;
324 return that.ptr->z() > ptr->z();
325 }
326 bool operator==(const Q3CanvasItemPtr& that) const
327 {
328 return that.ptr == ptr;
329 }
330 operator Q3CanvasItem*() const { return ptr; }
331
332private:
333 Q3CanvasItem* ptr;
334};
335
336
337/*!
338 \class Q3CanvasItemList
339 \compat
340 \brief The Q3CanvasItemList class is a list of Q3CanvasItems.
341
342 Q3CanvasItemList is a Q3ValueList of pointers to \l{Q3CanvasItem}s.
343 This class is used by some methods in Q3Canvas that need to return
344 a list of canvas items.
345
346 The \l Q3ValueList documentation describes how to use this list.
347
348 \sa QtCanvas, {Porting to Graphics View}
349*/
350
351/*!
352 \internal
353*/
354void Q3CanvasItemList::sort()
355{
356 qHeapSort(*((Q3ValueList<Q3CanvasItemPtr>*)this));
357}
358
359/*!
360 \internal
361*/
362void Q3CanvasItemList::drawUnique(QPainter& painter)
363{
364 Q3CanvasItem* prev=0;
365 for (Iterator it=fromLast(); it!=end(); --it) {
366 Q3CanvasItem *g=*it;
367 if (g!=prev) {
368 g->draw(painter);
369 prev=g;
370 }
371 }
372}
373
374/*!
375 Returns the concatenation of this list and list \a l.
376*/
377Q3CanvasItemList Q3CanvasItemList::operator+(const Q3CanvasItemList &l) const
378{
379 Q3CanvasItemList l2(*this);
380 for(const_iterator it = l.begin(); it != l.end(); ++it)
381 l2.append(*it);
382 return l2;
383}
384
385class Q3CanvasChunk {
386public:
387 Q3CanvasChunk() : changed(true) { }
388 // Other code assumes lists are not deleted. Assignment is also
389 // done on ChunkRecs. So don't add that sort of thing here.
390
391 void sort()
392 {
393 list.sort();
394 }
395
396 const Q3CanvasItemList* listPtr() const
397 {
398 return &list;
399 }
400
401 void add(Q3CanvasItem* item)
402 {
403 list.prepend(item);
404 changed = true;
405 }
406
407 void remove(Q3CanvasItem* item)
408 {
409 list.remove(item);
410 changed = true;
411 }
412
413 void change()
414 {
415 changed = true;
416 }
417
418 bool hasChanged() const
419 {
420 return changed;
421 }
422
423 bool takeChange()
424 {
425 bool y = changed;
426 changed = false;
427 return y;
428 }
429
430private:
431 Q3CanvasItemList list;
432 bool changed;
433};
434
435
436static int gcd(int a, int b)
437{
438 int r;
439 while ((r = a%b)) {
440 a=b;
441 b=r;
442 }
443 return b;
444}
445
446static int scm(int a, int b)
447{
448 int g = gcd(a,b);
449 return a/g*b;
450}
451
452
453
454/*!
455 \class Q3Canvas
456 \compat
457 \brief The Q3Canvas class provides a 2D area that can contain Q3CanvasItem objects.
458
459 The Q3Canvas class manages its 2D graphic area and all the canvas
460 items the area contains. The canvas has no visual appearance of
461 its own. Instead, it is displayed on screen using a Q3CanvasView.
462 Multiple Q3CanvasView widgets may be associated with a canvas to
463 provide multiple views of the same canvas.
464
465 The canvas is optimized for large numbers of items, particularly
466 where only a small percentage of the items change at any
467 one time. If the entire display changes very frequently, you should
468 consider using your own custom Q3ScrollView subclass.
469
470 Qt provides a rich
471 set of canvas item classes, e.g. Q3CanvasEllipse, Q3CanvasLine,
472 Q3CanvasPolygon, Q3CanvasPolygonalItem, Q3CanvasRectangle, Q3CanvasSpline,
473 Q3CanvasSprite and Q3CanvasText. You can subclass to create your own
474 canvas items; Q3CanvasPolygonalItem is the most common base class used
475 for this purpose.
476
477 Items appear on the canvas after their \link Q3CanvasItem::show()
478 show()\endlink function has been called (or \link
479 Q3CanvasItem::setVisible() setVisible(true)\endlink), and \e after
480 update() has been called. The canvas only shows items that are
481 \link Q3CanvasItem::setVisible() visible\endlink, and then only if
482 \l update() is called. (By default the canvas is white and so are
483 canvas items, so if nothing appears try changing colors.)
484
485 If you created the canvas without passing a width and height to
486 the constructor you must also call resize().
487
488 Although a canvas may appear to be similar to a widget with child
489 widgets, there are several notable differences:
490
491 \list
492 \i Canvas items are usually much faster to manipulate and redraw than
493 child widgets, with the speed advantage becoming especially great when
494 there are \e many canvas items and non-rectangular items. In most
495 situations canvas items are also a lot more memory efficient than child
496 widgets.
497
498 \i It's easy to detect overlapping items (collision detection).
499
500 \i The canvas can be larger than a widget. A million-by-million canvas
501 is perfectly possible. At such a size a widget might be very
502 inefficient, and some window systems might not support it at all,
503 whereas Q3Canvas scales well. Even with a billion pixels and a million
504 items, finding a particular canvas item, detecting collisions, etc.,
505 is still fast (though the memory consumption may be prohibitive
506 at such extremes).
507
508 \i Two or more Q3CanvasView objects can view the same canvas.
509
510 \i An arbitrary transformation matrix can be set on each Q3CanvasView
511 which makes it easy to zoom, rotate or shear the viewed canvas.
512
513 \i Widgets provide a lot more functionality, such as input (QKeyEvent,
514 QMouseEvent etc.) and layout management (QGridLayout etc.).
515
516 \endlist
517
518 A canvas consists of a background, a number of canvas items organized by
519 x, y and z coordinates, and a foreground. A canvas item's z coordinate
520 can be treated as a layer number -- canvas items with a higher z
521 coordinate appear in front of canvas items with a lower z coordinate.
522
523 The background is white by default, but can be set to a different color
524 using setBackgroundColor(), or to a repeated pixmap using
525 setBackgroundPixmap() or to a mosaic of smaller pixmaps using
526 setTiles(). Individual tiles can be set with setTile(). There
527 are corresponding get functions, e.g. backgroundColor() and
528 backgroundPixmap().
529
530 Note that Q3Canvas does not inherit from QWidget, even though it has some
531 functions which provide the same functionality as those in QWidget. One
532 of these is setBackgroundPixmap(); some others are resize(), size(),
533 width() and height(). \l Q3CanvasView is the widget used to display a
534 canvas on the screen.
535
536 Canvas items are added to a canvas by constructing them and passing the
537 canvas to the canvas item's constructor. An item can be moved to a
538 different canvas using Q3CanvasItem::setCanvas().
539
540 Canvas items are movable (and in the case of Q3CanvasSprites, animated)
541 objects that inherit Q3CanvasItem. Each canvas item has a position on the
542 canvas (x, y coordinates) and a height (z coordinate), all of which are
543 held as floating-point numbers. Moving canvas items also have x and y
544 velocities. It's possible for a canvas item to be outside the canvas
545 (for example Q3CanvasItem::x() is greater than width()). When a canvas
546 item is off the canvas, onCanvas() returns false and the canvas
547 disregards the item. (Canvas items off the canvas do not slow down any
548 of the common operations on the canvas.)
549
550 Canvas items can be moved with Q3CanvasItem::move(). The advance()
551 function moves all Q3CanvasItem::animated() canvas items and
552 setAdvancePeriod() makes Q3Canvas move them automatically on a periodic
553 basis. In the context of the Q3Canvas classes, to `animate' a canvas item
554 is to set it in motion, i.e. using Q3CanvasItem::setVelocity(). Animation
555 of a canvas item itself, i.e. items which change over time, is enabled
556 by calling Q3CanvasSprite::setFrameAnimation(), or more generally by
557 subclassing and reimplementing Q3CanvasItem::advance(). To detect collisions
558 use one of the Q3CanvasItem::collisions() functions.
559
560 The changed parts of the canvas are redrawn (if they are visible in a
561 canvas view) whenever update() is called. You can either call update()
562 manually after having changed the contents of the canvas, or force
563 periodic updates using setUpdatePeriod(). If you have moving objects on
564 the canvas, you must call advance() every time the objects should
565 move one step further. Periodic calls to advance() can be forced using
566 setAdvancePeriod(). The advance() function will call
567 Q3CanvasItem::advance() on every item that is \link
568 Q3CanvasItem::animated() animated\endlink and trigger an update of the
569 affected areas afterwards. (A canvas item that is `animated' is simply
570 a canvas item that is in motion.)
571
572 Q3Canvas organizes its canvas items into \e chunks; these are areas on
573 the canvas that are used to speed up most operations. Many operations
574 start by eliminating most chunks (i.e. those which haven't changed)
575 and then process only the canvas items that are in the few interesting
576 (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on
577 the canvas.
578
579 The chunk size is a key factor to Q3Canvas's speed: if there are too many
580 chunks, the speed benefit of grouping canvas items into chunks is
581 reduced. If the chunks are too large, it takes too long to process each
582 one. The Q3Canvas constructor tries to pick a suitable size, but you
583 can call retune() to change it at any time. The chunkSize() function
584 returns the current chunk size. The canvas items always make sure
585 they're in the right chunks; all you need to make sure of is that
586 the canvas uses the right chunk size. A good rule of thumb is that
587 the size should be a bit smaller than the average canvas item
588 size. If you have moving objects, the chunk size should be a bit
589 smaller than the average size of the moving items.
590
591 The foreground is normally nothing, but if you reimplement
592 drawForeground(), you can draw things in front of all the canvas
593 items.
594
595 Areas can be set as changed with setChanged() and set unchanged with
596 setUnchanged(). The entire canvas can be set as changed with
597 setAllChanged(). A list of all the items on the canvas is returned by
598 allItems().
599
600 An area can be copied (painted) to a QPainter with drawArea().
601
602 If the canvas is resized it emits the resized() signal.
603
604 The examples/canvas application and the 2D graphics page of the
605 examples/demo application demonstrate many of Q3Canvas's facilities.
606
607 \sa Q3CanvasView Q3CanvasItem, QtCanvas, {Porting to Graphics View}
608*/
609void Q3Canvas::init(int w, int h, int chunksze, int mxclusters)
610{
611 d = new Q3CanvasData;
612 awidth=w;
613 aheight=h;
614 chunksize=chunksze;
615 maxclusters=mxclusters;
616 chwidth=(w+chunksize-1)/chunksize;
617 chheight=(h+chunksize-1)/chunksize;
618 chunks=new Q3CanvasChunk[chwidth*chheight];
619 update_timer = 0;
620 bgcolor = white;
621 grid = 0;
622 htiles = 0;
623 vtiles = 0;
624 dblbuf = false;
625 debug_redraw_areas = false;
626}
627
628/*!
629 Create a Q3Canvas with no size. \a parent and \a name are passed to
630 the QObject superclass.
631
632 \warning You \e must call resize() at some time after creation to
633 be able to use the canvas.
634*/
635Q3Canvas::Q3Canvas(QObject* parent, const char* name)
636 : QObject(parent, name)
637{
638 init(0,0);
639}
640
641/*!
642 Constructs a Q3Canvas that is \a w pixels wide and \a h pixels high.
643*/
644Q3Canvas::Q3Canvas(int w, int h)
645{
646 init(w,h);
647}
648
649/*!
650 Constructs a Q3Canvas which will be composed of \a h tiles
651 horizontally and \a v tiles vertically. Each tile will be an image
652 \a tilewidth by \a tileheight pixels taken from pixmap \a p.
653
654 The pixmap \a p is a list of tiles, arranged left to right, (and
655 in the case of pixmaps that have multiple rows of tiles, top to
656 bottom), with tile 0 in the top-left corner, tile 1 next to the
657 right, and so on, e.g.
658
659 \table
660 \row \i 0 \i 1 \i 2 \i 3
661 \row \i 4 \i 5 \i 6 \i 7
662 \endtable
663
664 The Q3Canvas is initially sized to show exactly the given number of
665 tiles horizontally and vertically. If it is resized to be larger,
666 the entire matrix of tiles will be repeated as often as necessary
667 to cover the area. If it is smaller, tiles to the right and bottom
668 will not be visible.
669
670 \sa setTiles()
671*/
672Q3Canvas::Q3Canvas(QPixmap p,
673 int h, int v, int tilewidth, int tileheight)
674{
675 init(h*tilewidth, v*tileheight, scm(tilewidth,tileheight));
676 setTiles(p, h, v, tilewidth, tileheight);
677}
678
679void qt_unview(Q3Canvas* c)
680{
681 for (Q3CanvasView* view=c->d->viewList.first(); view != 0; view=c->d->viewList.next()) {
682 view->viewing = 0;
683 }
684}
685
686/*!
687 Destroys the canvas and all the canvas's canvas items.
688*/
689Q3Canvas::~Q3Canvas()
690{
691 qt_unview(this);
692 Q3CanvasItemList all = allItems();
693 for (Q3CanvasItemList::Iterator it=all.begin(); it!=all.end(); ++it)
694 delete *it;
695 delete [] chunks;
696 delete [] grid;
697 delete d;
698}
699
700/*!
701\internal
702Returns the chunk at a chunk position \a i, \a j.
703*/
704Q3CanvasChunk& Q3Canvas::chunk(int i, int j) const
705{
706 return chunks[i+chwidth*j];
707}
708
709/*!
710\internal
711Returns the chunk at a pixel position \a x, \a y.
712*/
713Q3CanvasChunk& Q3Canvas::chunkContaining(int x, int y) const
714{
715 return chunk(x/chunksize,y/chunksize);
716}
717
718/*!
719 Returns a list of all the items in the canvas.
720*/
721Q3CanvasItemList Q3Canvas::allItems()
722{
723 Q3CanvasItemList list;
724 for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
725 list.prepend((Q3CanvasItem*)it.currentKey());
726 }
727 return list;
728}
729
730
731/*!
732 Changes the size of the canvas to have a width of \a w and a
733 height of \a h. This is a slow operation.
734*/
735void Q3Canvas::resize(int w, int h)
736{
737 if (awidth==w && aheight==h)
738 return;
739
740 Q3CanvasItem* item;
741 Q3PtrList<Q3CanvasItem> hidden;
742 for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
743 if (((Q3CanvasItem*)it.currentKey())->isVisible()) {
744 ((Q3CanvasItem*)it.currentKey())->hide();
745 hidden.append(((Q3CanvasItem*)it.currentKey()));
746 }
747 }
748
749 int nchwidth=(w+chunksize-1)/chunksize;
750 int nchheight=(h+chunksize-1)/chunksize;
751
752 Q3CanvasChunk* newchunks = new Q3CanvasChunk[nchwidth*nchheight];
753
754 // Commit the new values.
755 //
756 awidth=w;
757 aheight=h;
758 chwidth=nchwidth;
759 chheight=nchheight;
760 delete [] chunks;
761 chunks=newchunks;
762
763 for (item=hidden.first(); item != 0; item=hidden.next()) {
764 item->show();
765 }
766
767 setAllChanged();
768
769 emit resized();
770}
771
772/*!
773 \fn void Q3Canvas::resized()
774
775 This signal is emitted whenever the canvas is resized. Each
776 Q3CanvasView connects to this signal to keep the scrollview's size
777 correct.
778*/
779
780/*!
781 Change the efficiency tuning parameters to \a mxclusters clusters,
782 each of size \a chunksze. This is a slow operation if there are
783 many objects on the canvas.
784
785 The canvas is divided into chunks which are rectangular areas \a
786 chunksze wide by \a chunksze high. Use a chunk size which is about
787 the average size of the canvas items. If you choose a chunk size
788 which is too small it will increase the amount of calculation
789 required when drawing since each change will affect many chunks.
790 If you choose a chunk size which is too large the amount of
791 drawing required will increase because for each change, a lot of
792 drawing will be required since there will be many (unchanged)
793 canvas items which are in the same chunk as the changed canvas
794 items.
795
796 Internally, a canvas uses a low-resolution "chunk matrix" to keep
797 track of all the items in the canvas. A 64x64 chunk matrix is the
798 default for a 1024x1024 pixel canvas, where each chunk collects
799 canvas items in a 16x16 pixel square. This default is also
800 affected by setTiles(). You can tune this default using this
801 function. For example if you have a very large canvas and want to
802 trade off speed for memory then you might set the chunk size to 32
803 or 64.
804
805 The \a mxclusters argument is the number of rectangular groups of
806 chunks that will be separately drawn. If the canvas has a large
807 number of small, dispersed items, this should be about that
808 number. Our testing suggests that a large number of clusters is
809 almost always best.
810
811*/
812void Q3Canvas::retune(int chunksze, int mxclusters)
813{
814 maxclusters=mxclusters;
815
816 if (chunksize!=chunksze) {
817 Q3PtrList<Q3CanvasItem> hidden;
818 for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
819 if (((Q3CanvasItem*)it.currentKey())->isVisible()) {
820 ((Q3CanvasItem*)it.currentKey())->hide();
821 hidden.append(((Q3CanvasItem*)it.currentKey()));
822 }
823 }
824
825 chunksize=chunksze;
826
827 int nchwidth=(awidth+chunksize-1)/chunksize;
828 int nchheight=(aheight+chunksize-1)/chunksize;
829
830 Q3CanvasChunk* newchunks = new Q3CanvasChunk[nchwidth*nchheight];
831
832 // Commit the new values.
833 //
834 chwidth=nchwidth;
835 chheight=nchheight;
836 delete [] chunks;
837 chunks=newchunks;
838
839 for (Q3CanvasItem* item=hidden.first(); item != 0; item=hidden.next()) {
840 item->show();
841 }
842 }
843}
844
845/*!
846 \fn int Q3Canvas::width() const
847
848 Returns the width of the canvas, in pixels.
849*/
850
851/*!
852 \fn int Q3Canvas::height() const
853
854 Returns the height of the canvas, in pixels.
855*/
856
857/*!
858 \fn QSize Q3Canvas::size() const
859
860 Returns the size of the canvas, in pixels.
861*/
862
863/*!
864 \fn QRect Q3Canvas::rect() const
865
866 Returns a rectangle the size of the canvas.
867*/
868
869
870/*!
871 \fn bool Q3Canvas::onCanvas(int x, int y) const
872
873 Returns true if the pixel position (\a x, \a y) is on the canvas;
874 otherwise returns false.
875
876 \sa validChunk()
877*/
878
879/*!
880 \fn bool Q3Canvas::onCanvas(const QPoint& p) const
881 \overload
882
883 Returns true if the pixel position \a p is on the canvas;
884 otherwise returns false.
885
886 \sa validChunk()
887*/
888
889/*!
890 \fn bool Q3Canvas::validChunk(int x, int y) const
891
892 Returns true if the chunk position (\a x, \a y) is on the canvas;
893 otherwise returns false.
894
895 \sa onCanvas()
896*/
897
898/*!
899 \fn bool Q3Canvas::validChunk(const QPoint& p) const
900 \overload
901
902 Returns true if the chunk position \a p is on the canvas; otherwise
903 returns false.
904
905 \sa onCanvas()
906*/
907
908/*!
909 \fn int Q3Canvas::chunkSize() const
910
911 Returns the chunk size of the canvas.
912
913 \sa retune()
914*/
915
916/*!
917\fn bool Q3Canvas::sameChunk(int x1, int y1, int x2, int y2) const
918\internal
919Tells if the points (\a x1, \a y1) and (\a x2, \a y2) are within the same chunk.
920*/
921
922/*!
923\internal
924This method adds an the item \a item to the list of Q3CanvasItem objects
925in the Q3Canvas. The Q3CanvasItem class calls this.
926*/
927void Q3Canvas::addItem(Q3CanvasItem* item)
928{
929 d->itemDict.insert((void*)item,(void*)1);
930}
931
932/*!
933\internal
934This method adds the item \a item to the list of Q3CanvasItem objects
935to be moved. The Q3CanvasItem class calls this.
936*/
937void Q3Canvas::addAnimation(Q3CanvasItem* item)
938{
939 d->animDict.insert((void*)item,(void*)1);
940}
941
942/*!
943\internal
944This method adds the item \a item to the list of Q3CanvasItem objects
945which are no longer to be moved. The Q3CanvasItem class calls this.
946*/
947void Q3Canvas::removeAnimation(Q3CanvasItem* item)
948{
949 d->animDict.remove((void*)item);
950}
951
952/*!
953\internal
954This method removes the item \a item from the list of Q3CanvasItem objects
955in this Q3Canvas. The Q3CanvasItem class calls this.
956*/
957void Q3Canvas::removeItem(Q3CanvasItem* item)
958{
959 d->itemDict.remove((void*)item);
960}
961
962/*!
963\internal
964This method adds the view \a view to the list of Q3CanvasView objects
965viewing this Q3Canvas. The Q3CanvasView class calls this.
966*/
967void Q3Canvas::addView(Q3CanvasView* view)
968{
969 d->viewList.append(view);
970 if (htiles>1 || vtiles>1 || pm.isNull())
971 view->viewport()->setBackgroundColor(backgroundColor());
972}
973
974/*!
975\internal
976This method removes the view \a view from the list of Q3CanvasView objects
977viewing this Q3Canvas. The Q3CanvasView class calls this.
978*/
979void Q3Canvas::removeView(Q3CanvasView* view)
980{
981 d->viewList.removeRef(view);
982}
983
984/*!
985 Sets the canvas to call advance() every \a ms milliseconds. Any
986 previous setting by setAdvancePeriod() or setUpdatePeriod() is
987 overridden.
988
989 If \a ms is less than 0 advancing will be stopped.
990*/
991void Q3Canvas::setAdvancePeriod(int ms)
992{
993 if (ms<0) {
994 if (update_timer)
995 update_timer->stop();
996 } else {
997 if (update_timer)
998 delete update_timer;
999 update_timer = new QTimer(this);
1000 connect(update_timer,SIGNAL(timeout()),this,SLOT(advance()));
1001 update_timer->start(ms);
1002 }
1003}
1004
1005/*!
1006 Sets the canvas to call update() every \a ms milliseconds. Any
1007 previous setting by setAdvancePeriod() or setUpdatePeriod() is
1008 overridden.
1009
1010 If \a ms is less than 0 automatic updating will be stopped.
1011*/
1012void Q3Canvas::setUpdatePeriod(int ms)
1013{
1014 if (ms<0) {
1015 if (update_timer)
1016 update_timer->stop();
1017 } else {
1018 if (update_timer)
1019 delete update_timer;
1020 update_timer = new QTimer(this);
1021 connect(update_timer,SIGNAL(timeout()),this,SLOT(update()));
1022 update_timer->start(ms);
1023 }
1024}
1025
1026/*!
1027 Moves all Q3CanvasItem::animated() canvas items on the canvas and
1028 refreshes all changes to all views of the canvas. (An `animated'
1029 item is an item that is in motion; see setVelocity().)
1030
1031 The advance takes place in two phases. In phase 0, the
1032 Q3CanvasItem::advance() function of each Q3CanvasItem::animated()
1033 canvas item is called with parameter 0. Then all these canvas
1034 items are called again, with parameter 1. In phase 0, the canvas
1035 items should not change position, merely examine other items on
1036 the canvas for which special processing is required, such as
1037 collisions between items. In phase 1, all canvas items should
1038 change positions, ignoring any other items on the canvas. This
1039 two-phase approach allows for considerations of "fairness",
1040 although no Q3CanvasItem subclasses supplied with Qt do anything
1041 interesting in phase 0.
1042
1043 The canvas can be configured to call this function periodically
1044 with setAdvancePeriod().
1045
1046 \sa update()
1047*/
1048void Q3Canvas::advance()
1049{
1050 Q3PtrDictIterator<void> it=d->animDict;
1051 while (it.current()) {
1052 Q3CanvasItem* i = (Q3CanvasItem*)(void*)it.currentKey();
1053 ++it;
1054 if (i)
1055 i->advance(0);
1056 }
1057 // we expect the dict contains the exact same items as in the
1058 // first pass.
1059 it.toFirst();
1060 while (it.current()) {
1061 Q3CanvasItem* i = (Q3CanvasItem*)(void*)it.currentKey();
1062 ++it;
1063 if (i)
1064 i->advance(1);
1065 }
1066 update();
1067}
1068
1069// Don't call this unless you know what you're doing.
1070// p is in the content's co-ordinate example.
1071/*!
1072 \internal
1073*/
1074void Q3Canvas::drawViewArea(Q3CanvasView* view, QPainter* p, const QRect& vr, bool)
1075{
1076 QPoint tl = view->contentsToViewport(QPoint(0,0));
1077
1078#ifndef QT_NO_TRANSFORMATIONS
1079 QMatrix wm = view->worldMatrix();
1080 QMatrix iwm = wm.invert();
1081 // ivr = covers all chunks in vr
1082 QRect ivr = iwm.map(vr);
1083 QMatrix twm;
1084 twm.translate(tl.x(),tl.y());
1085#else
1086 QRect ivr = vr;
1087#endif
1088
1089 QRect all(0,0,width(),height());
1090
1091 if (!all.contains(ivr)) {
1092 // Need to clip with edge of canvas.
1093
1094#ifndef QT_NO_TRANSFORMATIONS
1095 // For translation-only transformation, it is safe to include the right
1096 // and bottom edges, but otherwise, these must be excluded since they
1097 // are not precisely defined (different bresenham paths).
1098 Q3PointArray a;
1099 if (wm.m12()==0.0 && wm.m21()==0.0 && wm.m11() == 1.0 && wm.m22() == 1.0)
1100 a = Q3PointArray(QRect(all.x(),all.y(),all.width()+1,all.height()+1));
1101 else
1102 a = Q3PointArray(all);
1103
1104 a = (wm*twm).map(a);
1105#else
1106 Q3PointArray a(QRect(all.x(),all.y(),all.width()+1,all.height()+1));
1107#endif
1108 if (view->viewport()->backgroundMode() == NoBackground) {
1109 QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
1110 qt_setclipregion(p, QRegion(cvr)-QRegion(a));
1111 p->fillRect(vr,view->viewport()->palette()
1112 .brush(QPalette::Active,QPalette::Window));
1113 }
1114 qt_setclipregion(p, a);
1115 }
1116
1117 QRect r = vr; r.moveBy(tl.x(),tl.y()); // move to untransformed co-ords
1118 if (!all.contains(ivr)) {
1119 QRegion inside = p->clipRegion() & r;
1120 //QRegion outside = p->clipRegion() - r;
1121 //p->setClipRegion(outside);
1122 //p->fillRect(outside.boundingRect(),red);
1123 qt_setclipregion(p, inside);
1124 } else {
1125 qt_setcliprect(p, r);
1126 }
1127#ifndef QT_NO_TRANSFORMATIONS
1128 p->setWorldMatrix(wm*twm);
1129#else
1130#endif
1131 drawCanvasArea(ivr,p,false);
1132}
1133
1134/*!
1135 Repaints changed areas in all views of the canvas.
1136
1137 \sa advance()
1138*/
1139void Q3Canvas::update()
1140{
1141 // ##### fix QT_NO_TRANSFORMATIONS
1142#ifndef QT_NO_TRANSFORMATIONS
1143 Q3PtrList<QRect> doneareas;
1144 doneareas.setAutoDelete(true);
1145#endif
1146
1147 Q3PtrListIterator<Q3CanvasView> it(d->viewList);
1148 Q3CanvasView* view;
1149 while((view=it.current()) != 0) {
1150 ++it;
1151#ifndef QT_NO_TRANSFORMATIONS
1152 QMatrix wm = view->worldMatrix();
1153#endif
1154 QRect area(view->contentsX(),view->contentsY(),
1155 view->visibleWidth(),view->visibleHeight());
1156 if (area.width()>0 && area.height()>0) {
1157#ifndef QT_NO_TRANSFORMATIONS
1158 // r = Visible area of the canvas where there are changes
1159 QRect r = changeBounds(view->inverseWorldMatrix().map(area));
1160 if (!r.isEmpty()) {
1161 QRect tr = wm.map(r);
1162 tr.moveBy(-view->contentsX(), -view->contentsY());
1163 view->viewport()->update(tr);
1164 doneareas.append(new QRect(r));
1165 }
1166#endif
1167 }
1168 }
1169
1170#ifndef QT_NO_TRANSFORMATIONS
1171 for (QRect* r=doneareas.first(); r != 0; r=doneareas.next())
1172 setUnchanged(*r);
1173#endif
1174}
1175
1176
1177/*!
1178 Marks the whole canvas as changed.
1179 All views of the canvas will be entirely redrawn when
1180 update() is called next.
1181*/
1182void Q3Canvas::setAllChanged()
1183{
1184 setChanged(QRect(0,0,width(),height()));
1185}
1186
1187/*!
1188 Marks \a area as changed. This \a area will be redrawn in all
1189 views that are showing it when update() is called next.
1190*/
1191void Q3Canvas::setChanged(const QRect& area)
1192{
1193 QRect thearea = area.intersected(QRect(0, 0, width(), height()));
1194
1195 int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
1196 int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
1197 if (mx>chwidth)
1198 mx=chwidth;
1199 if (my>chheight)
1200 my=chheight;
1201
1202 int x=thearea.x()/chunksize;
1203 while(x<mx) {
1204 int y = thearea.y()/chunksize;
1205 while(y<my) {
1206 chunk(x,y).change();
1207 y++;
1208 }
1209 x++;
1210 }
1211}
1212
1213/*!
1214 Marks \a area as \e unchanged. The area will \e not be redrawn in
1215 the views for the next update(), unless it is marked or changed
1216 again before the next call to update().
1217*/
1218void Q3Canvas::setUnchanged(const QRect& area)
1219{
1220 QRect thearea = area.intersected(QRect(0, 0, width(), height()));
1221
1222 int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
1223 int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
1224 if (mx>chwidth)
1225 mx=chwidth;
1226 if (my>chheight)
1227 my=chheight;
1228
1229 int x=thearea.x()/chunksize;
1230 while(x<mx) {
1231 int y = thearea.y()/chunksize;
1232 while(y<my) {
1233 chunk(x,y).takeChange();
1234 y++;
1235 }
1236 x++;
1237 }
1238}
1239
1240
1241/*!
1242 \internal
1243*/
1244QRect Q3Canvas::changeBounds(const QRect& inarea)
1245{
1246 QRect area = inarea.intersected(QRect(0, 0, width(), height()));
1247
1248 int mx = (area.x()+area.width()+chunksize)/chunksize;
1249 int my = (area.y()+area.height()+chunksize)/chunksize;
1250 if (mx > chwidth)
1251 mx=chwidth;
1252 if (my > chheight)
1253 my=chheight;
1254
1255 QRect result;
1256
1257 int x=area.x()/chunksize;
1258 while(x<mx) {
1259 int y=area.y()/chunksize;
1260 while(y<my) {
1261 Q3CanvasChunk& ch=chunk(x,y);
1262 if (ch.hasChanged())
1263 result |= QRect(x,y,1,1);
1264 y++;
1265 }
1266 x++;
1267 }
1268
1269 if (!result.isEmpty()) {
1270 result.rLeft() *= chunksize;
1271 result.rTop() *= chunksize;
1272 result.rRight() *= chunksize;
1273 result.rBottom() *= chunksize;
1274 result.rRight() += chunksize;
1275 result.rBottom() += chunksize;
1276 }
1277
1278 return result;
1279}
1280
1281void Q3Canvas::ensureOffScrSize(int osw, int osh)
1282{
1283 if (osw > offscr.width() || osh > offscr.height())
1284 offscr.resize(QMAX(osw,offscr.width()),
1285 QMAX(osh,offscr.height()));
1286 else if (offscr.width() == 0 || offscr.height() == 0)
1287 offscr.resize(QMAX(offscr.width(), 1),
1288 QMAX(offscr.height(), 1));
1289}
1290
1291/*!
1292 Paints all canvas items that are in the area \a clip to \a
1293 painter, using double-buffering if \a dbuf is true.
1294
1295 e.g. to print the canvas to a printer:
1296 \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 0
1297*/
1298void Q3Canvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf)
1299{
1300 if (painter)
1301 drawCanvasArea(clip, painter, dbuf);
1302}
1303
1304QT_BEGIN_INCLUDE_NAMESPACE
1305#include <qdebug.h>
1306QT_END_INCLUDE_NAMESPACE
1307
1308/*!
1309 \internal
1310*/
1311void Q3Canvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool /*double_buffer*/)
1312{
1313 QRect area=inarea.intersected(QRect(0,0,width(),height()));
1314
1315 if (!p) return; // Nothing to do.
1316
1317 int lx=area.x()/chunksize;
1318 int ly=area.y()/chunksize;
1319 int mx=area.right()/chunksize;
1320 int my=area.bottom()/chunksize;
1321 if (mx>=chwidth)
1322 mx=chwidth-1;
1323 if (my>=chheight)
1324 my=chheight-1;
1325
1326 Q3CanvasItemList allvisible;
1327
1328 // Stores the region within area that need to be drawn. It is relative
1329 // to area.topLeft() (so as to keep within bounds of 16-bit XRegions)
1330 QRegion rgn;
1331
1332 for (int x=lx; x<=mx; x++) {
1333 for (int y=ly; y<=my; y++) {
1334 // Only reset change if all views updating, and
1335 // wholy within area. (conservative: ignore entire boundary)
1336 //
1337 // Disable this to help debugging.
1338 //
1339 if (!p) {
1340 if (chunk(x,y).takeChange()) {
1341 // ### should at least make bands
1342 rgn |= QRegion(x*chunksize-area.x(),y*chunksize-area.y(),
1343 chunksize,chunksize);
1344 allvisible += *chunk(x,y).listPtr();
1345 }
1346 } else {
1347 allvisible += *chunk(x,y).listPtr();
1348 }
1349 }
1350 }
1351 allvisible.sort();
1352
1353 drawBackground(*p,area);
1354 allvisible.drawUnique(*p);
1355 drawForeground(*p,area);
1356}
1357
1358/*!
1359\internal
1360This method to informs the Q3Canvas that a given chunk is
1361`dirty' and needs to be redrawn in the next Update.
1362
1363(\a x,\a y) is a chunk location.
1364
1365The sprite classes call this. Any new derived class of Q3CanvasItem
1366must do so too. SetChangedChunkContaining can be used instead.
1367*/
1368void Q3Canvas::setChangedChunk(int x, int y)
1369{
1370 if (validChunk(x,y)) {
1371 Q3CanvasChunk& ch=chunk(x,y);
1372 ch.change();
1373 }
1374}
1375
1376/*!
1377\internal
1378This method to informs the Q3Canvas that the chunk containing a given
1379pixel is `dirty' and needs to be redrawn in the next Update.
1380
1381(\a x,\a y) is a pixel location.
1382
1383The item classes call this. Any new derived class of Q3CanvasItem must
1384do so too. SetChangedChunk can be used instead.
1385*/
1386void Q3Canvas::setChangedChunkContaining(int x, int y)
1387{
1388 if (x>=0 && x<width() && y>=0 && y<height()) {
1389 Q3CanvasChunk& chunk=chunkContaining(x,y);
1390 chunk.change();
1391 }
1392}
1393
1394/*!
1395\internal
1396This method adds the Q3CanvasItem \a g to the list of those which need to be
1397drawn if the given chunk at location (\a x, \a y) is redrawn. Like
1398SetChangedChunk and SetChangedChunkContaining, this method marks the
1399chunk as `dirty'.
1400*/
1401void Q3Canvas::addItemToChunk(Q3CanvasItem* g, int x, int y)
1402{
1403 if (validChunk(x,y)) {
1404 chunk(x,y).add(g);
1405 }
1406}
1407
1408/*!
1409\internal
1410This method removes the Q3CanvasItem \a g from the list of those which need to
1411be drawn if the given chunk at location (\a x, \a y) is redrawn. Like
1412SetChangedChunk and SetChangedChunkContaining, this method marks the chunk
1413as `dirty'.
1414*/
1415void Q3Canvas::removeItemFromChunk(Q3CanvasItem* g, int x, int y)
1416{
1417 if (validChunk(x,y)) {
1418 chunk(x,y).remove(g);
1419 }
1420}
1421
1422
1423/*!
1424\internal
1425This method adds the Q3CanvasItem \a g to the list of those which need to be
1426drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. Like
1427SetChangedChunk and SetChangedChunkContaining, this method marks the
1428chunk as `dirty'.
1429*/
1430void Q3Canvas::addItemToChunkContaining(Q3CanvasItem* g, int x, int y)
1431{
1432 if (x>=0 && x<width() && y>=0 && y<height()) {
1433 chunkContaining(x,y).add(g);
1434 }
1435}
1436
1437/*!
1438\internal
1439This method removes the Q3CanvasItem \a g from the list of those which need to
1440be drawn if the chunk containing the given pixel (\a x, \a y) is redrawn.
1441Like SetChangedChunk and SetChangedChunkContaining, this method
1442marks the chunk as `dirty'.
1443*/
1444void Q3Canvas::removeItemFromChunkContaining(Q3CanvasItem* g, int x, int y)
1445{
1446 if (x>=0 && x<width() && y>=0 && y<height()) {
1447 chunkContaining(x,y).remove(g);
1448 }
1449}
1450
1451/*!
1452 Returns the color set by setBackgroundColor(). By default, this is
1453 white.
1454
1455 This function is not a reimplementation of
1456 QWidget::backgroundColor() (Q3Canvas is not a subclass of QWidget),
1457 but all Q3CanvasViews that are viewing the canvas will set their
1458 backgrounds to this color.
1459
1460 \sa setBackgroundColor(), backgroundPixmap()
1461*/
1462QColor Q3Canvas::backgroundColor() const
1463{
1464 return bgcolor;
1465}
1466
1467/*!
1468 Sets the solid background to be the color \a c.
1469
1470 \sa backgroundColor(), setBackgroundPixmap(), setTiles()
1471*/
1472void Q3Canvas::setBackgroundColor(const QColor& c)
1473{
1474 if (bgcolor != c) {
1475 bgcolor = c;
1476 Q3CanvasView* view=d->viewList.first();
1477 while (view != 0) {
1478 /* XXX this doesn't look right. Shouldn't this
1479 be more like setBackgroundPixmap? : Ian */
1480 view->viewport()->setEraseColor(bgcolor);
1481 view=d->viewList.next();
1482 }
1483 setAllChanged();
1484 }
1485}
1486
1487/*!
1488 Returns the pixmap set by setBackgroundPixmap(). By default,
1489 this is a null pixmap.
1490
1491 \sa setBackgroundPixmap(), backgroundColor()
1492*/
1493QPixmap Q3Canvas::backgroundPixmap() const
1494{
1495 return pm;
1496}
1497
1498/*!
1499 Sets the solid background to be the pixmap \a p repeated as
1500 necessary to cover the entire canvas.
1501
1502 \sa backgroundPixmap(), setBackgroundColor(), setTiles()
1503*/
1504void Q3Canvas::setBackgroundPixmap(const QPixmap& p)
1505{
1506 setTiles(p, 1, 1, p.width(), p.height());
1507 Q3CanvasView* view = d->viewList.first();
1508 while (view != 0) {
1509 view->updateContents();
1510 view = d->viewList.next();
1511 }
1512}
1513
1514/*!
1515 This virtual function is called for all updates of the canvas. It
1516 renders any background graphics using the painter \a painter, in
1517 the area \a clip. If the canvas has a background pixmap or a tiled
1518 background, that graphic is used, otherwise the canvas is cleared
1519 using the background color.
1520
1521 If the graphics for an area change, you must explicitly call
1522 setChanged(const QRect&) for the result to be visible when
1523 update() is next called.
1524
1525 \sa setBackgroundColor(), setBackgroundPixmap(), setTiles()
1526*/
1527void Q3Canvas::drawBackground(QPainter& painter, const QRect& clip)
1528{
1529 if (pm.isNull()) {
1530 painter.fillRect(clip,bgcolor);
1531 } else if (!grid) {
1532 for (int x=clip.x()/pm.width();
1533 x<(clip.x()+clip.width()+pm.width()-1)/pm.width(); x++)
1534 {
1535 for (int y=clip.y()/pm.height();
1536 y<(clip.y()+clip.height()+pm.height()-1)/pm.height(); y++)
1537 {
1538 painter.drawPixmap(x*pm.width(), y*pm.height(),pm);
1539 }
1540 }
1541 } else {
1542 const int x1 = clip.left()/tilew;
1543 int x2 = clip.right()/tilew;
1544 const int y1 = clip.top()/tileh;
1545 int y2 = clip.bottom()/tileh;
1546
1547 const int roww = pm.width()/tilew;
1548
1549 for (int j=y1; j<=y2; j++) {
1550 int jj = j%tilesVertically();
1551 for (int i=x1; i<=x2; i++) {
1552 int t = tile(i%tilesHorizontally(), jj);
1553 int tx = t % roww;
1554 int ty = t / roww;
1555 painter.drawPixmap(i*tilew, j*tileh, pm,
1556 tx*tilew, ty*tileh, tilew, tileh);
1557 }
1558 }
1559 }
1560}
1561
1562/*!
1563 This virtual function is called for all updates of the canvas. It
1564 renders any foreground graphics using the painter \a painter, in
1565 the area \a clip.
1566
1567 If the graphics for an area change, you must explicitly call
1568 setChanged(const QRect&) for the result to be visible when
1569 update() is next called.
1570
1571 The default is to draw nothing.
1572*/
1573void Q3Canvas::drawForeground(QPainter& painter, const QRect& clip)
1574{
1575 if (debug_redraw_areas) {
1576 painter.setPen(red);
1577 painter.setBrush(NoBrush);
1578 painter.drawRect(clip);
1579 }
1580}
1581
1582/*!
1583 If \a y is true (the default) double-buffering is switched on;
1584 otherwise double-buffering is switched off.
1585
1586 Turning off double-buffering causes the redrawn areas to flicker a
1587 little and also gives a (usually small) performance improvement.
1588*/
1589void Q3Canvas::setDoubleBuffering(bool y)
1590{
1591 dblbuf = y;
1592}
1593
1594
1595/*!
1596 Sets the Q3Canvas to be composed of \a h tiles horizontally and \a
1597 v tiles vertically. Each tile will be an image \a tilewidth by \a
1598 tileheight pixels from pixmap \a p.
1599
1600 The pixmap \a p is a list of tiles, arranged left to right, (and
1601 in the case of pixmaps that have multiple rows of tiles, top to
1602 bottom), with tile 0 in the top-left corner, tile 1 next to the
1603 right, and so on, e.g.
1604
1605 \table
1606 \row \i 0 \i 1 \i 2 \i 3
1607 \row \i 4 \i 5 \i 6 \i 7
1608 \endtable
1609
1610 If the canvas is larger than the matrix of tiles, the entire
1611 matrix is repeated as necessary to cover the whole canvas. If it
1612 is smaller, tiles to the right and bottom are not visible.
1613
1614 The width and height of \a p must be a multiple of \a tilewidth
1615 and \a tileheight. If they are not the function will do nothing.
1616
1617 If you want to unset any tiling set, then just pass in a null
1618 pixmap and 0 for \a h, \a v, \a tilewidth, and
1619 \a tileheight.
1620*/
1621void Q3Canvas::setTiles(QPixmap p,
1622 int h, int v, int tilewidth, int tileheight)
1623{
1624 if (!p.isNull() && (!tilewidth || !tileheight ||
1625 p.width() % tilewidth != 0 || p.height() % tileheight != 0))
1626 return;
1627
1628 htiles = h;
1629 vtiles = v;
1630 delete[] grid;
1631 pm = p;
1632 if (h && v && !p.isNull()) {
1633 grid = new ushort[h*v];
1634 memset(grid, 0, h*v*sizeof(ushort));
1635 tilew = tilewidth;
1636 tileh = tileheight;
1637 } else {
1638 grid = 0;
1639 }
1640 if (h + v > 10) {
1641 int s = scm(tilewidth,tileheight);
1642 retune(s < 128 ? s : QMAX(tilewidth,tileheight));
1643 }
1644 setAllChanged();
1645}
1646
1647/*!
1648 \fn int Q3Canvas::tile(int x, int y) const
1649
1650 Returns the tile at position (\a x, \a y). Initially, all tiles
1651 are 0.
1652
1653 The parameters must be within range, i.e.
1654 0 \< \a x \< tilesHorizontally() and
1655 0 \< \a y \< tilesVertically().
1656
1657 \sa setTile()
1658*/
1659
1660/*!
1661 \fn int Q3Canvas::tilesHorizontally() const
1662
1663 Returns the number of tiles horizontally.
1664*/
1665
1666/*!
1667 \fn int Q3Canvas::tilesVertically() const
1668
1669 Returns the number of tiles vertically.
1670*/
1671
1672/*!
1673 \fn int Q3Canvas::tileWidth() const
1674
1675 Returns the width of each tile.
1676*/
1677
1678/*!
1679 \fn int Q3Canvas::tileHeight() const
1680
1681 Returns the height of each tile.
1682*/
1683
1684
1685/*!
1686 Sets the tile at (\a x, \a y) to use tile number \a tilenum, which
1687 is an index into the tile pixmaps. The canvas will update
1688 appropriately when update() is next called.
1689
1690 The images are taken from the pixmap set by setTiles() and are
1691 arranged left to right, (and in the case of pixmaps that have
1692 multiple rows of tiles, top to bottom), with tile 0 in the
1693 top-left corner, tile 1 next to the right, and so on, e.g.
1694
1695 \table
1696 \row \i 0 \i 1 \i 2 \i 3
1697 \row \i 4 \i 5 \i 6 \i 7
1698 \endtable
1699
1700 \sa tile() setTiles()
1701*/
1702void Q3Canvas::setTile(int x, int y, int tilenum)
1703{
1704 ushort& t = grid[x+y*htiles];
1705 if (t != tilenum) {
1706 t = tilenum;
1707 if (tilew == tileh && tilew == chunksize)
1708 setChangedChunk(x, y); // common case
1709 else
1710 setChanged(QRect(x*tilew,y*tileh,tilew,tileh));
1711 }
1712}
1713
1714
1715// lesser-used data in canvas item, plus room for extension.
1716// Be careful adding to this - check all usages.
1717class Q3CanvasItemExtra {
1718 Q3CanvasItemExtra() : vx(0.0), vy(0.0) { }
1719 double vx,vy;
1720 friend class Q3CanvasItem;
1721};
1722
1723
1724/*!
1725 \class Q3CanvasItem
1726 \compat
1727 \brief The Q3CanvasItem class provides an abstract graphic object on a Q3Canvas.
1728
1729 A variety of Q3CanvasItem subclasses provide immediately usable
1730 behaviour. This class is a pure abstract superclass providing the
1731 behaviour that is shared among all the concrete canvas item classes.
1732 Q3CanvasItem is not intended for direct subclassing. It is much easier
1733 to subclass one of its subclasses, e.g. Q3CanvasPolygonalItem (the
1734 commonest base class), Q3CanvasRectangle, Q3CanvasSprite, Q3CanvasEllipse
1735 or Q3CanvasText.
1736
1737 Canvas items are added to a canvas by constructing them and passing the
1738 canvas to the canvas item's constructor. An item can be moved to a
1739 different canvas using setCanvas().
1740
1741 Items appear on the canvas after their \link show() show()\endlink
1742 function has been called (or \link setVisible()
1743 setVisible(true)\endlink), and \e after update() has been called. The
1744 canvas only shows items that are \link setVisible() visible\endlink,
1745 and then only if \l update() is called. If you created the canvas
1746 without passing a width and height to the constructor you'll also need
1747 to call \link Q3Canvas::resize() resize()\endlink. Since the canvas
1748 background defaults to white and canvas items default to white,
1749 you may need to change colors to see your items.
1750
1751 A Q3CanvasItem object can be moved in the x(), y() and z() dimensions
1752 using functions such as move(), moveBy(), setX(), setY() and setZ(). A
1753 canvas item can be set in motion, `animated', using setAnimated() and
1754 given a velocity in the x and y directions with setXVelocity() and
1755 setYVelocity() -- the same effect can be achieved by calling
1756 setVelocity(). Use the collidesWith() function to see if the canvas item
1757 will collide on the \e next advance(1) and use collisions() to see what
1758 collisions have occurred.
1759
1760 Use Q3CanvasSprite or your own subclass of Q3CanvasSprite to create canvas
1761 items which are animated, i.e. which change over time.
1762
1763 The size of a canvas item is given by boundingRect(). Use
1764 boundingRectAdvanced() to see what the size of the canvas item will be
1765 \e after the next advance(1) call.
1766
1767 The rtti() function is used for identifying subclasses of Q3CanvasItem.
1768 The canvas() function returns a pointer to the canvas which contains the
1769 canvas item.
1770
1771 Q3CanvasItem provides the show() and isVisible() functions like those in
1772 QWidget.
1773
1774 Q3CanvasItem also provides the setEnabled(), setActive() and
1775 setSelected() functions; these functions set the relevant boolean and
1776 cause a repaint but the boolean values they set are not used in
1777 Q3CanvasItem itself. You can make use of these booleans in your subclasses.
1778
1779 By default, canvas items have no velocity, no size, and are not in
1780 motion. The subclasses provided in Qt do not change these defaults
1781 except where noted.
1782
1783 \sa QtCanvas, {Porting to Graphics View}
1784*/
1785
1786/*!
1787 \enum Q3CanvasItem::RttiValues
1788
1789 This enum is used to name the different types of canvas item.
1790
1791 \value Rtti_Item Canvas item abstract base class
1792 \value Rtti_Ellipse
1793 \value Rtti_Line
1794 \value Rtti_Polygon
1795 \value Rtti_PolygonalItem
1796 \value Rtti_Rectangle
1797 \value Rtti_Spline
1798 \value Rtti_Sprite
1799 \value Rtti_Text
1800
1801*/
1802
1803/*!
1804 \fn void Q3CanvasItem::update()
1805
1806 Call this function to repaint the canvas's changed chunks.
1807*/
1808
1809/*!
1810 Constructs a Q3CanvasItem on canvas \a canvas.
1811
1812 \sa setCanvas()
1813*/
1814Q3CanvasItem::Q3CanvasItem(Q3Canvas* canvas) :
1815 cnv(canvas),
1816 myx(0),myy(0),myz(0)
1817{
1818 ani=0;
1819 vis=0;
1820 val=0;
1821 sel=0;
1822 ena=0;
1823 act=0;
1824
1825 ext = 0;
1826 if (cnv) cnv->addItem(this);
1827}
1828
1829/*!
1830 Destroys the Q3CanvasItem and removes it from its canvas.
1831*/
1832Q3CanvasItem::~Q3CanvasItem()
1833{
1834 if (cnv) {
1835 cnv->removeItem(this);
1836 cnv->removeAnimation(this);
1837 }
1838 delete ext;
1839}
1840
1841Q3CanvasItemExtra& Q3CanvasItem::extra()
1842{
1843 if (!ext)
1844 ext = new Q3CanvasItemExtra;
1845 return *ext;
1846}
1847
1848/*!
1849 \fn double Q3CanvasItem::x() const
1850
1851 Returns the horizontal position of the canvas item. Note that
1852 subclasses often have an origin other than the top-left corner.
1853*/
1854
1855/*!
1856 \fn double Q3CanvasItem::y() const
1857
1858 Returns the vertical position of the canvas item. Note that
1859 subclasses often have an origin other than the top-left corner.
1860*/
1861
1862/*!
1863 \fn double Q3CanvasItem::z() const
1864
1865 Returns the z index of the canvas item, which is used for visual
1866 order: higher-z items obscure (are in front of) lower-z items.
1867*/
1868
1869/*!
1870 \fn void Q3CanvasItem::setX(double x)
1871
1872 Moves the canvas item so that its x-position is \a x.
1873
1874 \sa x(), move()
1875*/
1876
1877/*!
1878 \fn void Q3CanvasItem::setY(double y)
1879
1880 Moves the canvas item so that its y-position is \a y.
1881
1882 \sa y(), move()
1883*/
1884
1885/*!
1886 \fn void Q3CanvasItem::setZ(double z)
1887
1888 Sets the z index of the canvas item to \a z. Higher-z items
1889 obscure (are in front of) lower-z items.
1890
1891 \sa z(), move()
1892*/
1893
1894
1895/*!
1896 Moves the canvas item relative to its current position by (\a dx,
1897 \a dy).
1898*/
1899void Q3CanvasItem::moveBy(double dx, double dy)
1900{
1901 if (dx || dy) {
1902 removeFromChunks();
1903 myx += dx;
1904 myy += dy;
1905 addToChunks();
1906 }
1907}
1908
1909
1910/*!
1911 Moves the canvas item to the absolute position (\a x, \a y).
1912*/
1913void Q3CanvasItem::move(double x, double y)
1914{
1915 moveBy(x-myx, y-myy);
1916}
1917
1918
1919/*!
1920 Returns true if the canvas item is in motion; otherwise returns
1921 false.
1922
1923 \sa setVelocity(), setAnimated()
1924*/
1925bool Q3CanvasItem::animated() const
1926{
1927 return (bool)ani;
1928}
1929
1930/*!
1931 Sets the canvas item to be in motion if \a y is true, or not if \a
1932 y is false. The speed and direction of the motion is set with
1933 setVelocity(), or with setXVelocity() and setYVelocity().
1934
1935 \sa advance(), Q3Canvas::advance()
1936*/
1937void Q3CanvasItem::setAnimated(bool y)
1938{
1939 if (y != (bool)ani) {
1940 ani = (uint)y;
1941 if (y) {
1942 cnv->addAnimation(this);
1943 } else {
1944 cnv->removeAnimation(this);
1945 }
1946 }
1947}
1948
1949/*!
1950 \fn void Q3CanvasItem::setXVelocity(double vx)
1951
1952 Sets the horizontal component of the canvas item's velocity to \a vx.
1953
1954 \sa setYVelocity() setVelocity()
1955*/
1956
1957/*!
1958 \fn void Q3CanvasItem::setYVelocity(double vy)
1959
1960 Sets the vertical component of the canvas item's velocity to \a vy.
1961
1962 \sa setXVelocity() setVelocity()
1963*/
1964
1965/*!
1966 Sets the canvas item to be in motion, moving by \a vx and \a vy
1967 pixels in the horizontal and vertical directions respectively.
1968
1969 \sa advance() setXVelocity() setYVelocity()
1970*/
1971void Q3CanvasItem::setVelocity(double vx, double vy)
1972{
1973 if (ext || vx!=0.0 || vy!=0.0) {
1974 if (!ani)
1975 setAnimated(true);
1976 extra().vx = vx;
1977 extra().vy = vy;
1978 }
1979}
1980
1981/*!
1982 Returns the horizontal velocity component of the canvas item.
1983*/
1984double Q3CanvasItem::xVelocity() const
1985{
1986 return ext ? ext->vx : 0;
1987}
1988
1989/*!
1990 Returns the vertical velocity component of the canvas item.
1991*/
1992double Q3CanvasItem::yVelocity() const
1993{
1994 return ext ? ext->vy : 0;
1995}
1996
1997/*!
1998 The default implementation moves the canvas item, if it is
1999 animated(), by the preset velocity if \a phase is 1, and does
2000 nothing if \a phase is 0.
2001
2002 Note that if you reimplement this function, the reimplementation
2003 must not change the canvas in any way, for example it must not add
2004 or remove items.
2005
2006 \sa Q3Canvas::advance() setVelocity()
2007*/
2008void Q3CanvasItem::advance(int phase)
2009{
2010 if (ext && phase==1)
2011 moveBy(ext->vx,ext->vy);
2012}
2013
2014/*!
2015 \fn void Q3CanvasItem::draw(QPainter& painter)
2016
2017 This abstract virtual function draws the canvas item using \a painter.
2018*/
2019
2020/*!
2021 Sets the Q3Canvas upon which the canvas item is to be drawn to \a c.
2022
2023 \sa canvas()
2024*/
2025void Q3CanvasItem::setCanvas(Q3Canvas* c)
2026{
2027 bool v=isVisible();
2028 setVisible(false);
2029 if (cnv) {
2030 if (ext)
2031 cnv->removeAnimation(this);
2032 cnv->removeItem(this);
2033 }
2034 cnv=c;
2035 if (cnv) {
2036 cnv->addItem(this);
2037 if (ext)
2038 cnv->addAnimation(this);
2039 }
2040 setVisible(v);
2041}
2042
2043/*!
2044 \fn Q3Canvas* Q3CanvasItem::canvas() const
2045
2046 Returns the canvas containing the canvas item.
2047*/
2048
2049/*! Shorthand for setVisible(true). */
2050void Q3CanvasItem::show()
2051{
2052 setVisible(true);
2053}
2054
2055/*! Shorthand for setVisible(false). */
2056void Q3CanvasItem::hide()
2057{
2058 setVisible(false);
2059}
2060
2061/*!
2062 Makes the canvas item visible if \a yes is true, or invisible if
2063 \a yes is false. The change takes effect when Q3Canvas::update() is
2064 next called.
2065*/
2066void Q3CanvasItem::setVisible(bool yes)
2067{
2068 if ((bool)vis!=yes) {
2069 if (yes) {
2070 vis=(uint)yes;
2071 addToChunks();
2072 } else {
2073 removeFromChunks();
2074 vis=(uint)yes;
2075 }
2076 }
2077}
2078/*!
2079 \obsolete
2080 \fn bool Q3CanvasItem::visible() const
2081 Use isVisible() instead.
2082*/
2083
2084/*!
2085 \fn bool Q3CanvasItem::isVisible() const
2086
2087 Returns true if the canvas item is visible; otherwise returns
2088 false.
2089
2090 Note that in this context true does \e not mean that the canvas
2091 item is currently in a view, merely that if a view is showing the
2092 area where the canvas item is positioned, and the item is not
2093 obscured by items with higher z values, and the view is not
2094 obscured by overlaying windows, it would be visible.
2095
2096 \sa setVisible(), z()
2097*/
2098
2099/*!
2100 \obsolete
2101 \fn bool Q3CanvasItem::selected() const
2102 Use isSelected() instead.
2103*/
2104
2105/*!
2106 \fn bool Q3CanvasItem::isSelected() const
2107
2108 Returns true if the canvas item is selected; otherwise returns false.
2109*/
2110
2111/*!
2112 Sets the selected flag of the item to \a yes. If this changes the
2113 item's selected state the item will be redrawn when
2114 Q3Canvas::update() is next called.
2115
2116 The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
2117 subclasses do not make use of this value. The setSelected()
2118 function is supplied because many applications need it, but it is
2119 up to you how you use the isSelected() value.
2120*/
2121void Q3CanvasItem::setSelected(bool yes)
2122{
2123 if ((bool)sel!=yes) {
2124 sel=(uint)yes;
2125 changeChunks();
2126 }
2127}
2128
2129/*!
2130 \obsolete
2131 \fn bool Q3CanvasItem::enabled() const
2132 Use isEnabled() instead.
2133*/
2134
2135/*!
2136 \fn bool Q3CanvasItem::isEnabled() const
2137
2138 Returns true if the Q3CanvasItem is enabled; otherwise returns false.
2139*/
2140
2141/*!
2142 Sets the enabled flag of the item to \a yes. If this changes the
2143 item's enabled state the item will be redrawn when
2144 Q3Canvas::update() is next called.
2145
2146 The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
2147 subclasses do not make use of this value. The setEnabled()
2148 function is supplied because many applications need it, but it is
2149 up to you how you use the isEnabled() value.
2150*/
2151void Q3CanvasItem::setEnabled(bool yes)
2152{
2153 if (ena!=(uint)yes) {
2154 ena=(uint)yes;
2155 changeChunks();
2156 }
2157}
2158
2159/*!
2160 \obsolete
2161 \fn bool Q3CanvasItem::active() const
2162 Use isActive() instead.
2163*/
2164
2165/*!
2166 \fn bool Q3CanvasItem::isActive() const
2167
2168 Returns true if the Q3CanvasItem is active; otherwise returns false.
2169*/
2170
2171/*!
2172 Sets the active flag of the item to \a yes. If this changes the
2173 item's active state the item will be redrawn when
2174 Q3Canvas::update() is next called.
2175
2176 The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
2177 subclasses do not make use of this value. The setActive() function
2178 is supplied because many applications need it, but it is up to you
2179 how you use the isActive() value.
2180*/
2181void Q3CanvasItem::setActive(bool yes)
2182{
2183 if (act!=(uint)yes) {
2184 act=(uint)yes;
2185 changeChunks();
2186 }
2187}
2188
2189bool qt_testCollision(const Q3CanvasSprite* s1, const Q3CanvasSprite* s2)
2190{
2191 const QImage* s2image = s2->imageAdvanced()->collision_mask;
2192 QRect s2area = s2->boundingRectAdvanced();
2193
2194 QRect cyourarea(s2area.x(),s2area.y(),
2195 s2area.width(),s2area.height());
2196
2197 QImage* s1image=s1->imageAdvanced()->collision_mask;
2198
2199 QRect s1area = s1->boundingRectAdvanced();
2200
2201 QRect ourarea = s1area.intersected(cyourarea);
2202
2203 if (ourarea.isEmpty())
2204 return false;
2205
2206 int x2=ourarea.x()-cyourarea.x();
2207 int y2=ourarea.y()-cyourarea.y();
2208 int x1=ourarea.x()-s1area.x();
2209 int y1=ourarea.y()-s1area.y();
2210 int w=ourarea.width();
2211 int h=ourarea.height();
2212
2213 if (!s2image) {
2214 if (!s1image)
2215 return w>0 && h>0;
2216 // swap everything around
2217 int t;
2218 t=x1; x1=x2; x2=t;
2219 t=y1; x1=y2; y2=t;
2220 s2image = s1image;
2221 s1image = 0;
2222 }
2223
2224 // s2image != 0
2225
2226 // A non-linear search may be more efficient.
2227 // Perhaps spiralling out from the center, or a simpler
2228 // vertical expansion from the centreline.
2229
2230 // We assume that sprite masks don't have
2231 // different bit orders.
2232 //
2233 // Q_ASSERT(s1image->bitOrder()==s2image->bitOrder());
2234
2235 if (s1image) {
2236 if (s1image->bitOrder() == QImage::LittleEndian) {
2237 for (int j=0; j<h; j++) {
2238 uchar* ml = s1image->scanLine(y1+j);
2239 const uchar* yl = s2image->scanLine(y2+j);
2240 for (int i=0; i<w; i++) {
2241 if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))
2242 && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7)))
2243 {
2244 return true;
2245 }
2246 }
2247 }
2248 } else {
2249 for (int j=0; j<h; j++) {
2250 uchar* ml = s1image->scanLine(y1+j);
2251 const uchar* yl = s2image->scanLine(y2+j);
2252 for (int i=0; i<w; i++) {
2253 if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))
2254 && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7))))
2255 {
2256 return true;
2257 }
2258 }
2259 }
2260 }
2261 } else {
2262 if (s2image->bitOrder() == QImage::LittleEndian) {
2263 for (int j=0; j<h; j++) {
2264 const uchar* yl = s2image->scanLine(y2+j);
2265 for (int i=0; i<w; i++) {
2266 if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)))
2267 {
2268 return true;
2269 }
2270 }
2271 }
2272 } else {
2273 for (int j=0; j<h; j++) {
2274 const uchar* yl = s2image->scanLine(y2+j);
2275 for (int i=0; i<w; i++) {
2276 if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))))
2277 {
2278 return true;
2279 }
2280 }
2281 }
2282 }
2283 }
2284
2285 return false;
2286}
2287
2288static bool collision_double_dispatch(const Q3CanvasSprite* s1,
2289 const Q3CanvasPolygonalItem* p1,
2290 const Q3CanvasRectangle* r1,
2291 const Q3CanvasEllipse* e1,
2292 const Q3CanvasText* t1,
2293 const Q3CanvasSprite* s2,
2294 const Q3CanvasPolygonalItem* p2,
2295 const Q3CanvasRectangle* r2,
2296 const Q3CanvasEllipse* e2,
2297 const Q3CanvasText* t2)
2298{
2299 const Q3CanvasItem* i1 = s1 ?
2300 (const Q3CanvasItem*)s1 : p1 ?
2301 (const Q3CanvasItem*)p1 : r1 ?
2302 (const Q3CanvasItem*)r1 : e1 ?
2303 (const Q3CanvasItem*)e1 : (const Q3CanvasItem*)t1;
2304 const Q3CanvasItem* i2 = s2 ?
2305 (const Q3CanvasItem*)s2 : p2 ?
2306 (const Q3CanvasItem*)p2 : r2 ?
2307 (const Q3CanvasItem*)r2 : e2 ?
2308 (const Q3CanvasItem*)e2 : (const Q3CanvasItem*)t2;
2309
2310 if (s1 && s2) {
2311 // a
2312 return qt_testCollision(s1,s2);
2313 } else if ((r1 || t1 || s1) && (r2 || t2 || s2)) {
2314 // b
2315 QRect rc1 = i1->boundingRectAdvanced();
2316 QRect rc2 = i2->boundingRectAdvanced();
2317 return rc1.intersects(rc2);
2318 } else if (e1 && e2
2319 && e1->angleLength()>=360*16 && e2->angleLength()>=360*16
2320 && e1->width()==e1->height()
2321 && e2->width()==e2->height()) {
2322 // c
2323 double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity());
2324 double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity());
2325 double rd = (e1->width()+e2->width())/2;
2326 return xd*xd+yd*yd <= rd*rd;
2327 } else if (p1 && (p2 || s2 || t2)) {
2328 // d
2329 Q3PointArray pa1 = p1->areaPointsAdvanced();
2330 Q3PointArray pa2 = p2 ? p2->areaPointsAdvanced()
2331 : Q3PointArray(i2->boundingRectAdvanced());
2332 bool col= !(QRegion(pa1) & QRegion(pa2,true)).isEmpty();
2333
2334 return col;
2335 } else {
2336 return collision_double_dispatch(s2,p2,r2,e2,t2,
2337 s1,p1,r1,e1,t1);
2338 }
2339}
2340
2341/*!
2342 \fn bool Q3CanvasItem::collidesWith(const Q3CanvasItem* other) const
2343
2344 Returns true if the canvas item will collide with the \a other
2345 item \e after they have moved by their current velocities;
2346 otherwise returns false.
2347
2348 \sa collisions()
2349*/
2350
2351
2352/*!
2353 \class Q3CanvasSprite
2354 \compat
2355 \brief The Q3CanvasSprite class provides an animated canvas item on a Q3Canvas.
2356
2357 A canvas sprite is an object which can contain any number of images
2358 (referred to as frames), only one of which is current, i.e.
2359 displayed, at any one time. The images can be passed in the
2360 constructor or set or changed later with setSequence(). If you
2361 subclass Q3CanvasSprite you can change the frame that is displayed
2362 periodically, e.g. whenever Q3CanvasItem::advance(1) is called to
2363 create the effect of animation.
2364
2365 The current frame can be set with setFrame() or with move(). The
2366 number of frames available is given by frameCount(). The bounding
2367 rectangle of the current frame is returned by boundingRect().
2368
2369 The current frame's image can be retrieved with image(); use
2370 imageAdvanced() to retrieve the image for the frame that will be
2371 shown after advance(1) is called. Use the image() overload passing
2372 it an integer index to retrieve a particular image from the list of
2373 frames.
2374
2375 Use width() and height() to retrieve the dimensions of the current
2376 frame.
2377
2378 Use leftEdge() and rightEdge() to retrieve the current frame's
2379 left-hand and right-hand x-coordinates respectively. Use
2380 bottomEdge() and topEdge() to retrieve the current frame's bottom
2381 and top y-coordinates respectively. These functions have an overload
2382 which will accept an integer frame number to retrieve the
2383 coordinates of a particular frame.
2384
2385 Q3CanvasSprite draws very quickly, at the expense of memory.
2386
2387 The current frame's image can be drawn on a painter with draw().
2388
2389 Like any other canvas item, canvas sprites can be moved with
2390 move() which sets the x and y coordinates and the frame number, as
2391 well as with Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by
2392 setting coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY()
2393 and Q3CanvasItem::setZ().
2394
2395 \sa QtCanvas, {Porting to Graphics View}
2396*/
2397
2398
2399/*!
2400 \reimp
2401*/
2402bool Q3CanvasSprite::collidesWith(const Q3CanvasItem* i) const
2403{
2404 return i->collidesWith(this,0,0,0,0);
2405}
2406
2407/*!
2408 Returns true if the canvas item collides with any of the given
2409 items; otherwise returns false. The parameters, \a s, \a p, \a r,
2410 \a e and \a t, are all the same object, this is just a type
2411 resolution trick.
2412*/
2413bool Q3CanvasSprite::collidesWith(const Q3CanvasSprite* s,
2414 const Q3CanvasPolygonalItem* p,
2415 const Q3CanvasRectangle* r,
2416 const Q3CanvasEllipse* e,
2417 const Q3CanvasText* t) const
2418{
2419 return collision_double_dispatch(s,p,r,e,t,this,0,0,0,0);
2420}
2421
2422/*!
2423 \reimp
2424*/
2425bool Q3CanvasPolygonalItem::collidesWith(const Q3CanvasItem* i) const
2426{
2427 return i->collidesWith(0,this,0,0,0);
2428}
2429
2430bool Q3CanvasPolygonalItem::collidesWith( const Q3CanvasSprite* s,
2431 const Q3CanvasPolygonalItem* p,
2432 const Q3CanvasRectangle* r,
2433 const Q3CanvasEllipse* e,
2434 const Q3CanvasText* t) const
2435{
2436 return collision_double_dispatch(s,p,r,e,t,0,this,0,0,0);
2437}
2438
2439/*!
2440 \reimp
2441*/
2442bool Q3CanvasRectangle::collidesWith(const Q3CanvasItem* i) const
2443{
2444 return i->collidesWith(0,this,this,0,0);
2445}
2446
2447bool Q3CanvasRectangle::collidesWith( const Q3CanvasSprite* s,
2448 const Q3CanvasPolygonalItem* p,
2449 const Q3CanvasRectangle* r,
2450 const Q3CanvasEllipse* e,
2451 const Q3CanvasText* t) const
2452{
2453 return collision_double_dispatch(s,p,r,e,t,0,this,this,0,0);
2454}
2455
2456
2457/*!
2458 \reimp
2459*/
2460bool Q3CanvasEllipse::collidesWith(const Q3CanvasItem* i) const
2461{
2462 return i->collidesWith(0,this,0,this,0);
2463}
2464
2465bool Q3CanvasEllipse::collidesWith( const Q3CanvasSprite* s,
2466 const Q3CanvasPolygonalItem* p,
2467 const Q3CanvasRectangle* r,
2468 const Q3CanvasEllipse* e,
2469 const Q3CanvasText* t) const
2470{
2471 return collision_double_dispatch(s,p,r,e,t,0,this,0,this,0);
2472}
2473
2474/*!
2475 \reimp
2476*/
2477bool Q3CanvasText::collidesWith(const Q3CanvasItem* i) const
2478{
2479 return i->collidesWith(0,0,0,0,this);
2480}
2481
2482bool Q3CanvasText::collidesWith( const Q3CanvasSprite* s,
2483 const Q3CanvasPolygonalItem* p,
2484 const Q3CanvasRectangle* r,
2485 const Q3CanvasEllipse* e,
2486 const Q3CanvasText* t) const
2487{
2488 return collision_double_dispatch(s,p,r,e,t,0,0,0,0,this);
2489}
2490
2491/*!
2492 Returns the list of canvas items that this canvas item has
2493 collided with.
2494
2495 A collision is generally defined as occurring when the pixels of
2496 one item draw on the pixels of another item, but not all
2497 subclasses are so precise. Also, since pixel-wise collision
2498 detection can be slow, this function works in either exact or
2499 inexact mode, according to the \a exact parameter.
2500
2501 If \a exact is true, the canvas items returned have been
2502 accurately tested for collision with the canvas item.
2503
2504 If \a exact is false, the canvas items returned are \e near the
2505 canvas item. You can test the canvas items returned using
2506 collidesWith() if any are interesting collision candidates. By
2507 using this approach, you can ignore some canvas items for which
2508 collisions are not relevant.
2509
2510 The returned list is a list of Q3CanvasItems, but often you will
2511 need to cast the items to their subclass types. The safe way to do
2512 this is to use rtti() before casting. This provides some of the
2513 functionality of the standard C++ dynamic cast operation even on
2514 compilers where dynamic casts are not available.
2515
2516 Note that a canvas item may be `on' a canvas, e.g. it was created
2517 with the canvas as parameter, even though its coordinates place it
2518 beyond the edge of the canvas's area. Collision detection only
2519 works for canvas items which are wholly or partly within the
2520 canvas's area.
2521
2522 Note that if items have a velocity (see \l setVelocity()), then
2523 collision testing is done based on where the item \e will be when
2524 it moves, not its current location. For example, a "ball" item
2525 doesn't need to actually embed into a "wall" item before a
2526 collision is detected. For items without velocity, plain
2527 intersection is used.
2528*/
2529Q3CanvasItemList Q3CanvasItem::collisions(bool exact) const
2530{
2531 return canvas()->collisions(chunks(),this,exact);
2532}
2533
2534/*!
2535 Returns a list of canvas items that collide with the point \a p.
2536 The list is ordered by z coordinates, from highest z coordinate
2537 (front-most item) to lowest z coordinate (rear-most item).
2538*/
2539Q3CanvasItemList Q3Canvas::collisions(const QPoint& p) const
2540{
2541 return collisions(QRect(p,QSize(1,1)));
2542}
2543
2544/*!
2545 \overload
2546
2547 Returns a list of items which collide with the rectangle \a r. The
2548 list is ordered by z coordinates, from highest z coordinate
2549 (front-most item) to lowest z coordinate (rear-most item).
2550*/
2551Q3CanvasItemList Q3Canvas::collisions(const QRect& r) const
2552{
2553 Q3CanvasRectangle i(r,(Q3Canvas*)this);
2554 i.setPen(NoPen);
2555 i.show(); // doesn't actually show, since we destroy it
2556 Q3CanvasItemList l = i.collisions(true);
2557 l.sort();
2558 return l;
2559}
2560
2561/*!
2562 \overload
2563
2564 Returns a list of canvas items which intersect with the chunks
2565 listed in \a chunklist, excluding \a item. If \a exact is true,
2566 only those which actually \link Q3CanvasItem::collidesWith()
2567 collide with\endlink \a item are returned; otherwise canvas items
2568 are included just for being in the chunks.
2569
2570 This is a utility function mainly used to implement the simpler
2571 Q3CanvasItem::collisions() function.
2572*/
2573Q3CanvasItemList Q3Canvas::collisions(const Q3PointArray& chunklist,
2574 const Q3CanvasItem* item, bool exact) const
2575{
2576 Q3PtrDict<void> seen;
2577 Q3CanvasItemList result;
2578 for (int i=0; i<(int)chunklist.count(); i++) {
2579 int x = chunklist[i].x();
2580 int y = chunklist[i].y();
2581 if (validChunk(x,y)) {
2582 const Q3CanvasItemList* l = chunk(x,y).listPtr();
2583 for (Q3CanvasItemList::ConstIterator it=l->begin(); it!=l->end(); ++it) {
2584 Q3CanvasItem *g=*it;
2585 if (g != item) {
2586 if (!seen.find(g)) {
2587 seen.replace(g,(void*)1);
2588 if (!exact || item->collidesWith(g))
2589 result.append(g);
2590 }
2591 }
2592 }
2593 }
2594 }
2595 return result;
2596}
2597
2598/*!
2599 \internal
2600 Adds the item to all the chunks it covers.
2601*/
2602void Q3CanvasItem::addToChunks()
2603{
2604 if (isVisible() && canvas()) {
2605 Q3PointArray pa = chunks();
2606 for (int i=0; i<(int)pa.count(); i++)
2607 canvas()->addItemToChunk(this,pa[i].x(),pa[i].y());
2608 val=(uint)true;
2609 }
2610}
2611
2612/*!
2613 \internal
2614 Removes the item from all the chunks it covers.
2615*/
2616void Q3CanvasItem::removeFromChunks()
2617{
2618 if (isVisible() && canvas()) {
2619 Q3PointArray pa = chunks();
2620 for (int i=0; i<(int)pa.count(); i++)
2621 canvas()->removeItemFromChunk(this,pa[i].x(),pa[i].y());
2622 }
2623}
2624
2625/*!
2626 \internal
2627 Sets all the chunks covered by the item to be refreshed with Q3Canvas::update()
2628 is next called.
2629*/
2630void Q3CanvasItem::changeChunks()
2631{
2632 if (isVisible() && canvas()) {
2633 if (!val)
2634 addToChunks();
2635 Q3PointArray pa = chunks();
2636 for (int i=0; i<(int)pa.count(); i++)
2637 canvas()->setChangedChunk(pa[i].x(),pa[i].y());
2638 }
2639}
2640
2641/*!
2642 \fn QRect Q3CanvasItem::boundingRect() const
2643
2644 Returns the bounding rectangle in pixels that the canvas item covers.
2645
2646 \sa boundingRectAdvanced()
2647*/
2648
2649/*!
2650 Returns the bounding rectangle of pixels that the canvas item \e
2651 will cover after advance(1) is called.
2652
2653 \sa boundingRect()
2654*/
2655QRect Q3CanvasItem::boundingRectAdvanced() const
2656{
2657 int dx = int(x()+xVelocity())-int(x());
2658 int dy = int(y()+yVelocity())-int(y());
2659 QRect r = boundingRect();
2660 r.moveBy(dx,dy);
2661 return r;
2662}
2663
2664/*!
2665 \class Q3CanvasPixmap
2666 \compat
2667 \brief The Q3CanvasPixmap class provides pixmaps for Q3CanvasSprites.
2668
2669 If you want to show a single pixmap on a Q3Canvas use a
2670 Q3CanvasSprite with just one pixmap.
2671
2672 When pixmaps are inserted into a Q3CanvasPixmapArray they are held
2673 as Q3CanvasPixmaps. \l{Q3CanvasSprite}s are used to show pixmaps on
2674 \l{Q3Canvas}es and hold their pixmaps in a Q3CanvasPixmapArray. If
2675 you retrieve a frame (pixmap) from a Q3CanvasSprite it will be
2676 returned as a Q3CanvasPixmap.
2677
2678 The pixmap is a QPixmap and can only be set in the constructor.
2679 There are three different constructors, one taking a QPixmap, one
2680 a QImage and one a file name that refers to a file in any
2681 supported file format (see QImageReader).
2682
2683 Q3CanvasPixmap can have a hotspot which is defined in terms of an (x,
2684 y) offset. When you create a Q3CanvasPixmap from a PNG file or from
2685 a QImage that has a QImage::offset(), the offset() is initialized
2686 appropriately, otherwise the constructor leaves it at (0, 0). You
2687 can set it later using setOffset(). When the Q3CanvasPixmap is used
2688 in a Q3CanvasSprite, the offset position is the point at
2689 Q3CanvasItem::x() and Q3CanvasItem::y(), not the top-left corner of
2690 the pixmap.
2691
2692 Note that for Q3CanvasPixmap objects created by a Q3CanvasSprite, the
2693 position of each Q3CanvasPixmap object is set so that the hotspot
2694 stays in the same position.
2695
2696 \sa Q3CanvasPixmapArray Q3CanvasItem Q3CanvasSprite, QtCanvas, {Porting to Graphics View}
2697*/
2698
2699#ifndef QT_NO_IMAGEIO
2700
2701/*!
2702 Constructs a Q3CanvasPixmap that uses the image stored in \a
2703 datafilename.
2704*/
2705Q3CanvasPixmap::Q3CanvasPixmap(const QString& datafilename)
2706{
2707 QImage image(datafilename);
2708 init(image);
2709}
2710
2711#endif
2712
2713/*!
2714 Constructs a Q3CanvasPixmap from the image \a image.
2715*/
2716Q3CanvasPixmap::Q3CanvasPixmap(const QImage& image)
2717{
2718 init(image);
2719}
2720/*!
2721 Constructs a Q3CanvasPixmap from the pixmap \a pm using the offset
2722 \a offset.
2723*/
2724Q3CanvasPixmap::Q3CanvasPixmap(const QPixmap& pm, const QPoint& offset)
2725{
2726 init(pm,offset.x(),offset.y());
2727}
2728
2729void Q3CanvasPixmap::init(const QImage& image)
2730{
2731 convertFromImage(image);
2732 hotx = image.offset().x();
2733 hoty = image.offset().y();
2734#ifndef QT_NO_IMAGE_DITHER_TO_1
2735 if(image.hasAlphaBuffer()) {
2736 QImage i = image.createAlphaMask();
2737 collision_mask = new QImage(i);
2738 } else
2739#endif
2740 collision_mask = 0;
2741}
2742
2743void Q3CanvasPixmap::init(const QPixmap& pixmap, int hx, int hy)
2744{
2745 (QPixmap&)*this = pixmap;
2746 hotx = hx;
2747 hoty = hy;
2748 if(pixmap.hasAlphaChannel()) {
2749 QImage i = mask().convertToImage();
2750 collision_mask = new QImage(i);
2751 } else
2752 collision_mask = 0;
2753}
2754
2755/*!
2756 Destroys the pixmap.
2757*/
2758Q3CanvasPixmap::~Q3CanvasPixmap()
2759{
2760 delete collision_mask;
2761}
2762
2763/*!
2764 \fn int Q3CanvasPixmap::offsetX() const
2765
2766 Returns the x-offset of the pixmap's hotspot.
2767
2768 \sa setOffset()
2769*/
2770
2771/*!
2772 \fn int Q3CanvasPixmap::offsetY() const
2773
2774 Returns the y-offset of the pixmap's hotspot.
2775
2776 \sa setOffset()
2777*/
2778
2779/*!
2780 \fn void Q3CanvasPixmap::setOffset(int x, int y)
2781
2782 Sets the offset of the pixmap's hotspot to (\a x, \a y).
2783
2784 \warning Do not call this function if any Q3CanvasSprites are
2785 currently showing this pixmap.
2786*/
2787
2788/*!
2789 \class Q3CanvasPixmapArray
2790 \compat
2791 \brief The Q3CanvasPixmapArray class provides an array of Q3CanvasPixmaps.
2792
2793 This class is used by Q3CanvasSprite to hold an array of pixmaps.
2794 It is used to implement animated sprites, i.e. images that change
2795 over time, with each pixmap in the array holding one frame.
2796
2797 Depending on the constructor you use you can load multiple pixmaps
2798 into the array either from a directory (specifying a wildcard
2799 pattern for the files), or from a list of QPixmaps. You can also
2800 read in a set of pixmaps after construction using readPixmaps().
2801
2802 Individual pixmaps can be set with setImage() and retrieved with
2803 image(). The number of pixmaps in the array is returned by
2804 count().
2805
2806 Q3CanvasSprite uses an image's mask for collision detection. You
2807 can change this by reading in a separate set of image masks using