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

Last change on this file since 352 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 131.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 paramater 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
2808 readCollisionMasks().
2809
2810 \sa QtCanvas, {Porting to Graphics View}
2811*/
2812
2813/*!
2814 Constructs an invalid array (i.e. isValid() will return false).
2815 You must call readPixmaps() before being able to use this
2816 Q3CanvasPixmapArray.
2817*/
2818Q3CanvasPixmapArray::Q3CanvasPixmapArray()
2819: framecount(0), img(0)
2820{
2821}
2822
2823#ifndef QT_NO_IMAGEIO
2824/*!
2825 Constructs a Q3CanvasPixmapArray from files.
2826
2827 The \a fc parameter sets the number of frames to be loaded for
2828 this image.
2829
2830 If \a fc is not 0, \a datafilenamepattern should contain "%1",
2831 e.g. "foo%1.png". The actual filenames are formed by replacing the
2832 %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
2833 foo0001.png, foo0002.png, etc.
2834
2835 If \a fc is 0, \a datafilenamepattern is asssumed to be a
2836 filename, and the image contained in this file will be loaded as
2837 the first (and only) frame.
2838
2839 If \a datafilenamepattern does not exist, is not readable, isn't
2840 an image, or some other error occurs, the array ends up empty and
2841 isValid() returns false.
2842*/
2843
2844Q3CanvasPixmapArray::Q3CanvasPixmapArray(const QString& datafilenamepattern,
2845 int fc)
2846: framecount(0), img(0)
2847{
2848 readPixmaps(datafilenamepattern,fc);
2849}
2850#endif
2851
2852/*!
2853 \obsolete
2854 Use Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3ValueList<QPixmap>, Q3PointArray)
2855 instead.
2856
2857 Constructs a Q3CanvasPixmapArray from the list of QPixmaps \a
2858 list. The \a hotspots list has to be of the same size as \a list.
2859*/
2860Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3PtrList<QPixmap> list, Q3PtrList<QPoint> hotspots) :
2861 framecount(list.count()),
2862 img(new Q3CanvasPixmap*[list.count()])
2863{
2864 if (list.count() != hotspots.count()) {
2865 qWarning("Q3CanvasPixmapArray: lists have different lengths");
2866 reset();
2867 img = 0;
2868 } else {
2869 list.first();
2870 hotspots.first();
2871 for (int i=0; i<framecount; i++) {
2872 img[i]=new Q3CanvasPixmap(*list.current(), *hotspots.current());
2873 list.next();
2874 hotspots.next();
2875 }
2876 }
2877}
2878
2879/*!
2880 Constructs a Q3CanvasPixmapArray from the list of QPixmaps in the
2881 \a list. Each pixmap will get a hotspot according to the \a
2882 hotspots array. If no hotspots are specified, each one is set to
2883 be at position (0, 0).
2884
2885 If an error occurs, isValid() will return false.
2886*/
2887Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3ValueList<QPixmap> list, Q3PointArray hotspots) :
2888 framecount((int)list.size()),
2889 img(new Q3CanvasPixmap*[list.size()])
2890{
2891 bool have_hotspots = (hotspots.size() != 0);
2892 if (have_hotspots && list.count() != hotspots.count()) {
2893 qWarning("Q3CanvasPixmapArray: lists have different lengths");
2894 reset();
2895 img = 0;
2896 } else {
2897 Q3ValueList<QPixmap>::iterator it;
2898 it = list.begin();
2899 for (int i=0; i<framecount; i++) {
2900 QPoint hs = have_hotspots ? hotspots[i] : QPoint(0, 0);
2901 img[i]=new Q3CanvasPixmap(*it, hs);
2902 ++it;
2903 }
2904 }
2905}
2906
2907/*!
2908 Destroys the pixmap array and all the pixmaps it contains.
2909*/
2910Q3CanvasPixmapArray::~Q3CanvasPixmapArray()
2911{
2912 reset();
2913}
2914
2915void Q3CanvasPixmapArray::reset()
2916{
2917 for (int i=0; i<framecount; i++)
2918 delete img[i];
2919 delete [] img;
2920 img = 0;
2921 framecount = 0;
2922}
2923
2924#ifndef QT_NO_IMAGEIO
2925/*!
2926 Reads one or more pixmaps into the pixmap array.
2927
2928 If \a fc is not 0, \a filenamepattern should contain "%1", e.g.
2929 "foo%1.png". The actual filenames are formed by replacing the %1
2930 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
2931 foo0001.png, foo0002.png, etc.
2932
2933 If \a fc is 0, \a filenamepattern is asssumed to be a filename,
2934 and the image contained in this file will be loaded as the first
2935 (and only) frame.
2936
2937 If \a filenamepattern does not exist, is not readable, isn't an
2938 image, or some other error occurs, this function will return
2939 false, and isValid() will return false; otherwise this function
2940 will return true.
2941
2942 \sa isValid()
2943*/
2944bool Q3CanvasPixmapArray::readPixmaps(const QString& filenamepattern,
2945 int fc)
2946{
2947 return readPixmaps(filenamepattern,fc,false);
2948}
2949
2950/*!
2951 Reads new collision masks for the array.
2952
2953 By default, Q3CanvasSprite uses the image mask of a sprite to
2954 detect collisions. Use this function to set your own collision
2955 image masks.
2956
2957 If count() is 1 \a filename must specify a real filename to read
2958 the mask from. If count() is greater than 1, the \a filename must
2959 contain a "%1" that will get replaced by the number of the mask to
2960 be loaded, just like Q3CanvasPixmapArray::readPixmaps().
2961
2962 All collision masks must be 1-bit images or this function call
2963 will fail.
2964
2965 If the file isn't readable, contains the wrong number of images,
2966 or there is some other error, this function will return false, and
2967 the array will be flagged as invalid; otherwise this function
2968 returns true.
2969
2970 \sa isValid()
2971*/
2972bool Q3CanvasPixmapArray::readCollisionMasks(const QString& filename)
2973{
2974 return readPixmaps(filename,framecount,true);
2975}
2976
2977
2978bool Q3CanvasPixmapArray::readPixmaps(const QString& datafilenamepattern,
2979 int fc, bool maskonly)
2980{
2981 if (!maskonly) {
2982 reset();
2983 framecount = fc;
2984 if (!framecount)
2985 framecount=1;
2986 img = new Q3CanvasPixmap*[framecount];
2987 }
2988 if (!img)
2989 return false;
2990
2991 bool ok = true;
2992 bool arg = fc > 1;
2993 if (!arg)
2994 framecount=1;
2995 for (int i=0; i<framecount; i++) {
2996 QString r;
2997 r.sprintf("%04d",i);
2998 if (maskonly) {
2999 if (!img[i]->collision_mask)
3000 img[i]->collision_mask = new QImage();
3001 img[i]->collision_mask->load(
3002 arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3003 ok = ok
3004 && !img[i]->collision_mask->isNull()
3005 && img[i]->collision_mask->depth()==1;
3006 } else {
3007 img[i]=new Q3CanvasPixmap(
3008 arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3009 ok = ok && !img[i]->isNull();
3010 }
3011 }
3012 if (!ok) {
3013 reset();
3014 }
3015 return ok;
3016}
3017#endif
3018
3019/*!
3020 \obsolete
3021
3022 Use isValid() instead.
3023
3024 This returns false if the array is valid, and true if it is not.
3025*/
3026bool Q3CanvasPixmapArray::operator!()
3027{
3028 return img==0;
3029}
3030
3031/*!
3032 Returns true if the pixmap array is valid; otherwise returns
3033 false.
3034*/
3035bool Q3CanvasPixmapArray::isValid() const
3036{
3037 return (img != 0);
3038}
3039
3040/*!
3041 \fn Q3CanvasPixmap* Q3CanvasPixmapArray::image(int i) const
3042
3043 Returns pixmap \a i in the array, if \a i is non-negative and less
3044 than than count(), and returns an unspecified value otherwise.
3045*/
3046
3047// ### wouldn't it be better to put empty Q3CanvasPixmaps in there instead of
3048// initializing the additional elements in the array to 0? Lars
3049/*!
3050 Replaces the pixmap at index \a i with pixmap \a p.
3051
3052 The array takes ownership of \a p and will delete \a p when the
3053 array itself is deleted.
3054
3055 If \a i is beyond the end of the array the array is extended to at
3056 least i+1 elements, with elements count() to i-1 being initialized
3057 to 0.
3058*/
3059void Q3CanvasPixmapArray::setImage(int i, Q3CanvasPixmap* p)
3060{
3061 if (i >= framecount) {
3062 Q3CanvasPixmap** newimg = new Q3CanvasPixmap*[i+1];
3063 memcpy(newimg, img, sizeof(Q3CanvasPixmap *)*framecount);
3064 memset(newimg + framecount, 0, sizeof(Q3CanvasPixmap *)*(i+1 - framecount));
3065 framecount = i+1;
3066 delete [] img;
3067 img = newimg;
3068 }
3069 delete img[i]; img[i]=p;
3070}
3071
3072/*!
3073 \fn uint Q3CanvasPixmapArray::count() const
3074
3075 Returns the number of pixmaps in the array.
3076*/
3077
3078/*!
3079 Returns the x-coordinate of the current left edge of the sprite.
3080 (This may change as the sprite animates since different frames may
3081 have different left edges.)
3082
3083 \sa rightEdge() bottomEdge() topEdge()
3084*/
3085int Q3CanvasSprite::leftEdge() const
3086{
3087 return int(x()) - image()->hotx;
3088}
3089
3090/*!
3091 \overload
3092
3093 Returns what the x-coordinate of the left edge of the sprite would
3094 be if the sprite (actually its hotspot) were moved to x-position
3095 \a nx.
3096
3097 \sa rightEdge() bottomEdge() topEdge()
3098*/
3099int Q3CanvasSprite::leftEdge(int nx) const
3100{
3101 return nx - image()->hotx;
3102}
3103
3104/*!
3105 Returns the y-coordinate of the top edge of the sprite. (This may
3106 change as the sprite animates since different frames may have
3107 different top edges.)
3108
3109 \sa leftEdge() rightEdge() bottomEdge()
3110*/
3111int Q3CanvasSprite::topEdge() const
3112{
3113 return int(y()) - image()->hoty;
3114}
3115
3116/*!
3117 \overload
3118
3119 Returns what the y-coordinate of the top edge of the sprite would
3120 be if the sprite (actually its hotspot) were moved to y-position
3121 \a ny.
3122
3123 \sa leftEdge() rightEdge() bottomEdge()
3124*/
3125int Q3CanvasSprite::topEdge(int ny) const
3126{
3127 return ny - image()->hoty;
3128}
3129
3130/*!
3131 Returns the x-coordinate of the current right edge of the sprite.
3132 (This may change as the sprite animates since different frames may
3133 have different right edges.)
3134
3135 \sa leftEdge() bottomEdge() topEdge()
3136*/
3137int Q3CanvasSprite::rightEdge() const
3138{
3139 return leftEdge() + image()->width()-1;
3140}
3141
3142/*!
3143 \overload
3144
3145 Returns what the x-coordinate of the right edge of the sprite
3146 would be if the sprite (actually its hotspot) were moved to
3147 x-position \a nx.
3148
3149 \sa leftEdge() bottomEdge() topEdge()
3150*/
3151int Q3CanvasSprite::rightEdge(int nx) const
3152{
3153 return leftEdge(nx) + image()->width()-1;
3154}
3155
3156/*!
3157 Returns the y-coordinate of the current bottom edge of the sprite.
3158 (This may change as the sprite animates since different frames may
3159 have different bottom edges.)
3160
3161 \sa leftEdge() rightEdge() topEdge()
3162*/
3163int Q3CanvasSprite::bottomEdge() const
3164{
3165 return topEdge() + image()->height()-1;
3166}
3167
3168/*!
3169 \overload
3170
3171 Returns what the y-coordinate of the top edge of the sprite would
3172 be if the sprite (actually its hotspot) were moved to y-position
3173 \a ny.
3174
3175 \sa leftEdge() rightEdge() topEdge()
3176*/
3177int Q3CanvasSprite::bottomEdge(int ny) const
3178{
3179 return topEdge(ny) + image()->height()-1;
3180}
3181
3182/*!
3183 \fn Q3CanvasPixmap* Q3CanvasSprite::image() const
3184
3185 Returns the current frame's image.
3186
3187 \sa frame(), setFrame()
3188*/
3189
3190/*!
3191 \fn Q3CanvasPixmap* Q3CanvasSprite::image(int f) const
3192 \overload
3193
3194 Returns the image for frame \a f. Does not do any bounds checking on \a f.
3195*/
3196
3197/*!
3198 Returns the image the sprite \e will have after advance(1) is
3199 called. By default this is the same as image().
3200*/
3201Q3CanvasPixmap* Q3CanvasSprite::imageAdvanced() const
3202{
3203 return image();
3204}
3205
3206/*!
3207 Returns the bounding rectangle for the image in the sprite's
3208 current frame. This assumes that the images are tightly cropped
3209 (i.e. do not have transparent pixels all along a side).
3210*/
3211QRect Q3CanvasSprite::boundingRect() const
3212{
3213 return QRect(leftEdge(), topEdge(), width(), height());
3214}
3215
3216
3217/*!
3218 \internal
3219 Returns the chunks covered by the item.
3220*/
3221Q3PointArray Q3CanvasItem::chunks() const
3222{
3223 Q3PointArray r;
3224 int n=0;
3225 QRect br = boundingRect();
3226 if (isVisible() && canvas()) {
3227 int chunksize=canvas()->chunkSize();
3228 br &= QRect(0,0,canvas()->width(),canvas()->height());
3229 if (br.isValid()) {
3230 r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2));
3231 for (int j=br.top()/chunksize; j<=br.bottom()/chunksize; j++) {
3232 for (int i=br.left()/chunksize; i<=br.right()/chunksize; i++) {
3233 r[n++] = QPoint(i,j);
3234 }
3235 }
3236 }
3237 }
3238 r.resize(n);
3239 return r;
3240}
3241
3242
3243/*!
3244 \internal
3245 Add the sprite to the chunks in its Q3Canvas which it overlaps.
3246*/
3247void Q3CanvasSprite::addToChunks()
3248{
3249 if (isVisible() && canvas()) {
3250 int chunksize=canvas()->chunkSize();
3251 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
3252 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
3253 canvas()->addItemToChunk(this,i,j);
3254 }
3255 }
3256 }
3257}
3258
3259/*!
3260 \internal
3261 Remove the sprite from the chunks in its Q3Canvas which it overlaps.
3262
3263 \sa addToChunks()
3264*/
3265void Q3CanvasSprite::removeFromChunks()
3266{
3267 if (isVisible() && canvas()) {
3268 int chunksize=canvas()->chunkSize();
3269 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
3270 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
3271 canvas()->removeItemFromChunk(this,i,j);
3272 }
3273 }
3274 }
3275}
3276
3277/*!
3278 The width of the sprite for the current frame's image.
3279
3280 \sa frame()
3281*/
3282//### mark: Why don't we have width(int) and height(int) to be
3283//consistent with leftEdge() and leftEdge(int)?
3284int Q3CanvasSprite::width() const
3285{
3286 return image()->width();
3287}
3288
3289/*!
3290 The height of the sprite for the current frame's image.
3291
3292 \sa frame()
3293*/
3294int Q3CanvasSprite::height() const
3295{
3296 return image()->height();
3297}
3298
3299
3300/*!
3301 Draws the current frame's image at the sprite's current position
3302 on painter \a painter.
3303*/
3304void Q3CanvasSprite::draw(QPainter& painter)
3305{
3306 painter.drawPixmap(leftEdge(),topEdge(),*image());
3307}
3308
3309/*!
3310 \class Q3CanvasView
3311 \compat
3312 \brief The Q3CanvasView class provides an on-screen view of a Q3Canvas.
3313
3314 A Q3CanvasView is widget which provides a view of a Q3Canvas.
3315
3316 If you want users to be able to interact with a canvas view,
3317 subclass Q3CanvasView. You might then reimplement
3318 Q3ScrollView::contentsMousePressEvent(). For example:
3319
3320 \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 1
3321
3322 The canvas view shows canvas canvas(); this can be changed using
3323 setCanvas().
3324
3325 A transformation matrix can be used to transform the view of the
3326 canvas in various ways, for example, zooming in or out or rotating.
3327 For example:
3328
3329 \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 2
3330
3331 Use setWorldMatrix() to set the canvas view's world matrix: you must
3332 ensure that the world matrix is invertible. The current world matrix
3333 is retrievable with worldMatrix(), and its inversion is retrievable
3334 with inverseWorldMatrix().
3335
3336 Example:
3337
3338 The following code finds the part of the canvas that is visible in
3339 this view, i.e. the bounding rectangle of the view in canvas coordinates.
3340
3341 \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 3
3342
3343 \sa QMatrix QPainter::setWorldMatrix(), QtCanvas, {Porting to Graphics View}
3344*/
3345
3346/*!
3347 Constructs a Q3CanvasView with parent \a parent, and name \a name,
3348 using the widget flags \a f. The canvas view is not associated
3349 with a canvas, so you must to call setCanvas() to view a
3350 canvas.
3351*/
3352Q3CanvasView::Q3CanvasView(QWidget* parent, const char* name, Qt::WindowFlags f)
3353 : Q3ScrollView(parent,name,f|WResizeNoErase|WStaticContents)
3354{
3355 d = new Q3CanvasViewData;
3356 viewing = 0;
3357 setCanvas(0);
3358}
3359
3360/*!
3361 \overload
3362
3363 Constructs a Q3CanvasView which views canvas \a canvas, with parent
3364 \a parent, and name \a name, using the widget flags \a f.
3365*/
3366Q3CanvasView::Q3CanvasView(Q3Canvas* canvas, QWidget* parent, const char* name, Qt::WindowFlags f)
3367 : Q3ScrollView(parent,name,f|WResizeNoErase|WStaticContents)
3368{
3369 d = new Q3CanvasViewData;
3370 viewing = 0;
3371 setCanvas(canvas);
3372}
3373
3374/*!
3375 Destroys the canvas view. The associated canvas is \e not deleted.
3376*/
3377Q3CanvasView::~Q3CanvasView()
3378{
3379 delete d;
3380 d = 0;
3381 setCanvas(0);
3382}
3383
3384/*!
3385 \fn Q3Canvas* Q3CanvasView::canvas() const
3386
3387 Returns a pointer to the canvas which the Q3CanvasView is currently
3388 showing.
3389*/
3390
3391
3392/*!
3393 Sets the canvas that the Q3CanvasView is showing to the canvas \a
3394 canvas.
3395*/
3396void Q3CanvasView::setCanvas(Q3Canvas* canvas)
3397{
3398 if (viewing == canvas)
3399 return;
3400
3401 if (viewing) {
3402 disconnect(viewing);
3403 viewing->removeView(this);
3404 }
3405 viewing=canvas;
3406 if (viewing) {
3407 connect(viewing,SIGNAL(resized()), this, SLOT(updateContentsSize()));
3408 viewing->addView(this);
3409 viewing->setAllChanged();
3410 }
3411 if (d) // called by d'tor
3412 updateContentsSize();
3413 update();
3414}
3415
3416#ifndef QT_NO_TRANSFORMATIONS
3417/*!
3418 Returns a reference to the canvas view's current transformation matrix.
3419
3420 \sa setWorldMatrix() inverseWorldMatrix()
3421*/
3422const QMatrix &Q3CanvasView::worldMatrix() const
3423{
3424 return d->xform;
3425}
3426
3427/*!
3428 Returns a reference to the inverse of the canvas view's current
3429 transformation matrix.
3430
3431 \sa setWorldMatrix() worldMatrix()
3432*/
3433const QMatrix &Q3CanvasView::inverseWorldMatrix() const
3434{
3435 return d->ixform;
3436}
3437
3438/*!
3439 Sets the transformation matrix of the Q3CanvasView to \a wm. The
3440 matrix must be invertible (i.e. if you create a world matrix that
3441 zooms out by 2 times, then the inverse of this matrix is one that
3442 will zoom in by 2 times).
3443
3444 When you use this, you should note that the performance of the
3445 Q3CanvasView will decrease considerably.
3446
3447 Returns false if \a wm is not invertable; otherwise returns true.
3448
3449 \sa worldMatrix() inverseWorldMatrix() QMatrix::isInvertible()
3450*/
3451bool Q3CanvasView::setWorldMatrix(const QMatrix & wm)
3452{
3453 bool ok = wm.isInvertible();
3454 if (ok) {
3455 d->xform = wm;
3456 d->ixform = wm.invert();
3457 updateContentsSize();
3458 viewport()->update();
3459 }
3460 return ok;
3461}
3462#endif
3463
3464void Q3CanvasView::updateContentsSize()
3465{
3466 if (viewing) {
3467 QRect br;
3468#ifndef QT_NO_TRANSFORMATIONS
3469 br = d->xform.map(QRect(0,0,viewing->width(),viewing->height()));
3470#else
3471 br = QRect(0,0,viewing->width(),viewing->height());
3472#endif
3473
3474 if (br.width() < contentsWidth()) {
3475 QRect r(contentsToViewport(QPoint(br.width(),0)),
3476 QSize(contentsWidth()-br.width(),contentsHeight()));
3477 d->eraseRegion = r;
3478 }
3479 if (br.height() < contentsHeight()) {
3480 QRect r(contentsToViewport(QPoint(0,br.height())),
3481 QSize(contentsWidth(),contentsHeight()-br.height()));
3482 d->eraseRegion |= r;
3483 }
3484
3485 resizeContents(br.width(),br.height());
3486 } else {
3487 d->eraseRegion = rect();
3488 resizeContents(1,1);
3489 }
3490}
3491
3492/*!
3493 Repaints part of the Q3Canvas that the canvas view is showing
3494 starting at \a cx by \a cy, with a width of \a cw and a height of \a
3495 ch using the painter \a p.
3496*/
3497void Q3CanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
3498{
3499 QRect r(cx,cy,cw,ch);
3500 if (!d->eraseRegion.isEmpty()) {
3501 const QVector<QRect> rects = d->eraseRegion.rects();
3502 for (int i = 0; i < rects.size(); ++i)
3503 p->eraseRect(rects.at(i));
3504
3505 d->eraseRegion = QRegion();
3506 }
3507
3508 if (viewing) {
3509 viewing->drawViewArea(this,p,r,false);
3510 } else {
3511 p->eraseRect(r);
3512 }
3513}
3514
3515/*!
3516 \reimp
3517 \internal
3518
3519 (Implemented to get rid of a compiler warning.)
3520*/
3521void Q3CanvasView::drawContents(QPainter *)
3522{
3523}
3524
3525/*!
3526 Suggests a size sufficient to view the entire canvas.
3527*/
3528QSize Q3CanvasView::sizeHint() const
3529{
3530 if (!canvas())
3531 return Q3ScrollView::sizeHint();
3532 // should maybe take transformations into account
3533 return (canvas()->size() + 2 * QSize(frameWidth(), frameWidth()))
3534 .boundedTo(3 * QApplication::desktop()->size() / 4);
3535}
3536
3537/*!
3538 \class Q3CanvasPolygonalItem
3539 \compat
3540 \brief The Q3CanvasPolygonalItem class provides a polygonal canvas item
3541 on a Q3Canvas.
3542
3543 The mostly rectangular classes, such as Q3CanvasSprite and
3544 Q3CanvasText, use the object's bounding rectangle for movement,
3545 repainting and collision calculations. For most other items, the
3546 bounding rectangle can be far too large -- a diagonal line being
3547 the worst case, and there are many other cases which are also bad.
3548 Q3CanvasPolygonalItem provides polygon-based bounding rectangle
3549 handling, etc., which is much faster for non-rectangular items.
3550
3551 Derived classes should try to define as small an area as possible
3552 to maximize efficiency, but the polygon must \e definitely be
3553 contained completely within the polygonal area. Calculating the
3554 exact requirements is usually difficult, but if you allow a small
3555 overestimate it can be easy and quick, while still getting almost
3556 all of Q3CanvasPolygonalItem's speed.
3557
3558 Note that all subclasses \e must call hide() in their destructor
3559 since hide() needs to be able to access areaPoints().
3560
3561 Normally, Q3CanvasPolygonalItem uses the odd-even algorithm for
3562 determining whether an object intersects this object. You can
3563 change this to the winding algorithm using setWinding().
3564
3565 The bounding rectangle is available using boundingRect(). The
3566 points bounding the polygonal item are retrieved with
3567 areaPoints(). Use areaPointsAdvanced() to retrieve the bounding
3568 points the polygonal item \e will have after
3569 Q3CanvasItem::advance(1) has been called.
3570
3571 If the shape of the polygonal item is about to change while the
3572 item is visible, call invalidate() before updating with a
3573 different result from \l areaPoints().
3574
3575 By default, Q3CanvasPolygonalItem objects have a black pen and no
3576 brush (the default QPen and QBrush constructors). You can change
3577 this with setPen() and setBrush(), but note that some
3578 Q3CanvasPolygonalItem subclasses only use the brush, ignoring the
3579 pen setting.
3580
3581 The polygonal item can be drawn on a painter with draw().
3582 Subclasses must reimplement drawShape() to draw themselves.
3583
3584 Like any other canvas item polygonal items can be moved with
3585 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting coordinates
3586 with Q3CanvasItem::setX(), Q3CanvasItem::setY() and Q3CanvasItem::setZ().
3587
3588 \sa QtCanvas, {Porting to Graphics View}
3589*/
3590
3591
3592/*
3593 Since most polygonal items don't have a pen, the default is
3594 NoPen and a black brush.
3595*/
3596static const QPen& defaultPolygonPen()
3597{
3598 static QPen* dp=0;
3599 if (!dp)
3600 dp = new QPen;
3601 return *dp;
3602}
3603
3604static const QBrush& defaultPolygonBrush()
3605{
3606 static QBrush* db=0;
3607 if (!db)
3608 db = new QBrush;
3609 return *db;
3610}
3611
3612/*!
3613 Constructs a Q3CanvasPolygonalItem on the canvas \a canvas.
3614*/
3615Q3CanvasPolygonalItem::Q3CanvasPolygonalItem(Q3Canvas* canvas) :
3616 Q3CanvasItem(canvas),
3617 br(defaultPolygonBrush()),
3618 pn(defaultPolygonPen())
3619{
3620 wind=0;
3621}
3622
3623/*!
3624 Note that all subclasses \e must call hide() in their destructor
3625 since hide() needs to be able to access areaPoints().
3626*/
3627Q3CanvasPolygonalItem::~Q3CanvasPolygonalItem()
3628{
3629}
3630
3631/*!
3632 Returns true if the polygonal item uses the winding algorithm to
3633 determine the "inside" of the polygon. Returns false if it uses
3634 the odd-even algorithm.
3635
3636 The default is to use the odd-even algorithm.
3637
3638 \sa setWinding()
3639*/
3640bool Q3CanvasPolygonalItem::winding() const
3641{
3642 return wind;
3643}
3644
3645/*!
3646 If \a enable is true, the polygonal item will use the winding
3647 algorithm to determine the "inside" of the polygon; otherwise the
3648 odd-even algorithm will be used.
3649
3650 The default is to use the odd-even algorithm.
3651
3652 \sa winding()
3653*/
3654void Q3CanvasPolygonalItem::setWinding(bool enable)
3655{
3656 wind = enable;
3657}
3658
3659/*!
3660 Invalidates all information about the area covered by the canvas
3661 item. The item will be updated automatically on the next call that
3662 changes the item's status, for example, move() or update(). Call
3663 this function if you are going to change the shape of the item (as
3664 returned by areaPoints()) while the item is visible.
3665*/
3666void Q3CanvasPolygonalItem::invalidate()
3667{
3668 val = (uint)false;
3669 removeFromChunks();
3670}
3671
3672/*!
3673 \fn Q3CanvasPolygonalItem::isValid() const
3674
3675 Returns true if the polygonal item's area information has not been
3676 invalidated; otherwise returns false.
3677
3678 \sa invalidate()
3679*/
3680
3681/*!
3682 Returns the points the polygonal item \e will have after
3683 Q3CanvasItem::advance(1) is called, i.e. what the points are when
3684 advanced by the current xVelocity() and yVelocity().
3685*/
3686Q3PointArray Q3CanvasPolygonalItem::areaPointsAdvanced() const
3687{
3688 int dx = int(x()+xVelocity())-int(x());
3689 int dy = int(y()+yVelocity())-int(y());
3690 Q3PointArray r = areaPoints();
3691 r.detach(); // Explicit sharing is stupid.
3692 if (dx || dy)
3693 r.translate(dx,dy);
3694 return r;
3695}
3696
3697//#define QCANVAS_POLYGONS_DEBUG
3698#ifdef QCANVAS_POLYGONS_DEBUG
3699static QWidget* dbg_wid=0;
3700static QPainter* dbg_ptr=0;
3701#endif
3702
3703class QPolygonalProcessor {
3704public:
3705 QPolygonalProcessor(Q3Canvas* c, const Q3PointArray& pa) :
3706 canvas(c)
3707 {
3708 QRect pixelbounds = pa.boundingRect();
3709 int cs = canvas->chunkSize();
3710 QRect canvasbounds = pixelbounds.intersected(canvas->rect());
3711 bounds.setLeft(canvasbounds.left()/cs);
3712 bounds.setRight(canvasbounds.right()/cs);
3713 bounds.setTop(canvasbounds.top()/cs);
3714 bounds.setBottom(canvasbounds.bottom()/cs);
3715 bitmap = QImage(bounds.width() + 1, bounds.height(), 1, 2, QImage::LittleEndian);
3716 pnt = 0;
3717 bitmap.fill(0);
3718#ifdef QCANVAS_POLYGONS_DEBUG
3719 dbg_start();
3720#endif
3721 }
3722
3723 inline void add(int x, int y)
3724 {
3725 if (pnt >= (int)result.size()) {
3726 result.resize(pnt*2+10);
3727 }
3728 result[pnt++] = QPoint(x+bounds.x(),y+bounds.y());
3729#ifdef QCANVAS_POLYGONS_DEBUG
3730 if (dbg_ptr) {
3731 int cs = canvas->chunkSize();
3732 QRect r(x*cs+bounds.x()*cs,y*cs+bounds.y()*cs,cs-1,cs-1);
3733 dbg_ptr->setPen(Qt::blue);
3734 dbg_ptr->drawRect(r);
3735 }
3736#endif
3737 }
3738
3739 inline void addBits(int x1, int x2, uchar newbits, int xo, int yo)
3740 {
3741 for (int i=x1; i<=x2; i++)
3742 if (newbits & (1<<i))
3743 add(xo+i,yo);
3744 }
3745
3746#ifdef QCANVAS_POLYGONS_DEBUG
3747 void dbg_start()
3748 {
3749 if (!dbg_wid) {
3750 dbg_wid = new QWidget;
3751 dbg_wid->resize(800,600);
3752 dbg_wid->show();
3753 dbg_ptr = new QPainter(dbg_wid);
3754 dbg_ptr->setBrush(Qt::NoBrush);
3755 }
3756 dbg_ptr->fillRect(dbg_wid->rect(),Qt::white);
3757 }
3758#endif
3759
3760 void doSpans(int n, QPoint* pt, int* w)
3761 {
3762 int cs = canvas->chunkSize();
3763 for (int j=0; j<n; j++) {
3764 int y = pt[j].y()/cs-bounds.y();
3765 if (y >= bitmap.height() || y < 0) continue;
3766 uchar* l = bitmap.scanLine(y);
3767 int x = pt[j].x();
3768 int x1 = x/cs-bounds.x();
3769 if (x1 > bounds.width()) continue;
3770 x1 = QMAX(0,x1);
3771 int x2 = (x+w[j])/cs-bounds.x();
3772 if (x2 < 0) continue;
3773 x2 = QMIN(bounds.width(), x2);
3774 int x1q = x1/8;
3775 int x1r = x1%8;
3776 int x2q = x2/8;
3777 int x2r = x2%8;
3778#ifdef QCANVAS_POLYGONS_DEBUG
3779 if (dbg_ptr) dbg_ptr->setPen(Qt::yellow);
3780#endif
3781 if (x1q == x2q) {
3782 uchar newbits = (~l[x1q]) & (((2<<(x2r-x1r))-1)<<x1r);
3783 if (newbits) {
3784#ifdef QCANVAS_POLYGONS_DEBUG
3785 if (dbg_ptr) dbg_ptr->setPen(Qt::darkGreen);
3786#endif
3787 addBits(x1r,x2r,newbits,x1q*8,y);
3788 l[x1q] |= newbits;
3789 }
3790 } else {
3791#ifdef QCANVAS_POLYGONS_DEBUG
3792 if (dbg_ptr) dbg_ptr->setPen(Qt::blue);
3793#endif
3794 uchar newbits1 = (~l[x1q]) & (0xff<<x1r);
3795 if (newbits1) {
3796#ifdef QCANVAS_POLYGONS_DEBUG
3797 if (dbg_ptr) dbg_ptr->setPen(Qt::green);
3798#endif
3799 addBits(x1r,7,newbits1,x1q*8,y);
3800 l[x1q] |= newbits1;
3801 }
3802 for (int i=x1q+1; i<x2q; i++) {
3803 if (l[i] != 0xff) {
3804 addBits(0,7,~l[i],i*8,y);
3805 l[i]=0xff;
3806 }
3807 }
3808 uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r));
3809 if (newbits2) {
3810#ifdef QCANVAS_POLYGONS_DEBUG
3811 if (dbg_ptr) dbg_ptr->setPen(Qt::red);
3812#endif
3813 addBits(0,x2r,newbits2,x2q*8,y);
3814 l[x2q] |= newbits2;
3815 }
3816 }
3817#ifdef QCANVAS_POLYGONS_DEBUG
3818 if (dbg_ptr) {
3819 dbg_ptr->drawLine(pt[j],pt[j]+QPoint(w[j],0));
3820 }
3821#endif
3822 }
3823 result.resize(pnt);
3824 }
3825
3826 int pnt;
3827 Q3PointArray result;
3828 Q3Canvas* canvas;
3829 QRect bounds;
3830 QImage bitmap;
3831};
3832
3833
3834Q3PointArray Q3CanvasPolygonalItem::chunks() const
3835{
3836 Q3PointArray pa = areaPoints();
3837
3838 if (!pa.size()) {
3839 pa.detach(); // Explicit sharing is stupid.
3840 return pa;
3841 }
3842
3843 QPolygonalProcessor processor(canvas(),pa);
3844
3845 scanPolygon(pa, wind, processor);
3846
3847 return processor.result;
3848}
3849/*!
3850 Simply calls Q3CanvasItem::chunks().
3851*/
3852Q3PointArray Q3CanvasRectangle::chunks() const
3853{
3854 // No need to do a polygon scan!
3855 return Q3CanvasItem::chunks();
3856}
3857
3858/*!
3859 Returns the bounding rectangle of the polygonal item, based on
3860 areaPoints().
3861*/
3862QRect Q3CanvasPolygonalItem::boundingRect() const
3863{
3864 return areaPoints().boundingRect();
3865}
3866
3867/*!
3868 Reimplemented from Q3CanvasItem, this draws the polygonal item by
3869 setting the pen and brush for the item on the painter \a p and
3870 calling drawShape().
3871*/
3872void Q3CanvasPolygonalItem::draw(QPainter & p)
3873{
3874 p.setPen(pn);
3875 p.setBrush(br);
3876 drawShape(p);
3877}
3878
3879/*!
3880 \fn void Q3CanvasPolygonalItem::drawShape(QPainter & p)
3881
3882 Subclasses must reimplement this function to draw their shape. The
3883 pen and brush of \a p are already set to pen() and brush() prior
3884 to calling this function.
3885
3886 \sa draw()
3887*/
3888
3889/*!
3890 \fn QPen Q3CanvasPolygonalItem::pen() const
3891
3892 Returns the QPen used to draw the outline of the item, if any.
3893
3894 \sa setPen()
3895*/
3896
3897/*!
3898 \fn QBrush Q3CanvasPolygonalItem::brush() const
3899
3900 Returns the QBrush used to fill the item, if filled.
3901
3902 \sa setBrush()
3903*/
3904
3905/*!
3906 Sets the QPen used when drawing the item to the pen \a p.
3907 Note that many Q3CanvasPolygonalItems do not use the pen value.
3908
3909 \sa setBrush(), pen(), drawShape()
3910*/
3911void Q3CanvasPolygonalItem::setPen(QPen p)
3912{
3913 if (pn != p) {
3914 removeFromChunks();
3915 pn = p;
3916 addToChunks();
3917 }
3918}
3919
3920/*!
3921 Sets the QBrush used when drawing the polygonal item to the brush \a b.
3922
3923 \sa setPen(), brush(), drawShape()
3924*/
3925void Q3CanvasPolygonalItem::setBrush(QBrush b)
3926{
3927 if (br != b) {
3928 br = b;
3929 changeChunks();
3930 }
3931}
3932
3933
3934/*!
3935 \class Q3CanvasPolygon
3936 \compat
3937 \brief The Q3CanvasPolygon class provides a polygon on a Q3Canvas.
3938
3939 Paints a polygon with a QBrush. The polygon's points can be set in
3940 the constructor or set or changed later using setPoints(). Use
3941 points() to retrieve the points, or areaPoints() to retrieve the
3942 points relative to the canvas's origin.
3943
3944 The polygon can be drawn on a painter with drawShape().
3945
3946 Like any other canvas item polygons can be moved with
3947 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
3948 coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
3949 Q3CanvasItem::setZ().
3950
3951 Note: Q3CanvasPolygon does not use the pen.
3952
3953 \sa QtCanvas, {Porting to Graphics View}
3954*/
3955
3956/*!
3957 Constructs a point-less polygon on the canvas \a canvas. You
3958 should call setPoints() before using it further.
3959*/
3960Q3CanvasPolygon::Q3CanvasPolygon(Q3Canvas* canvas) :
3961 Q3CanvasPolygonalItem(canvas)
3962{
3963}
3964
3965/*!
3966 Destroys the polygon.
3967*/
3968Q3CanvasPolygon::~Q3CanvasPolygon()
3969{
3970 hide();
3971}
3972
3973/*!
3974 Draws the polygon using the painter \a p.
3975
3976 Note that Q3CanvasPolygon does not support an outline (the pen is
3977 always NoPen).
3978*/
3979void Q3CanvasPolygon::drawShape(QPainter & p)
3980{
3981 // ### why can't we draw outlines? We could use drawPolyline for it. Lars
3982 // ### see other message. Warwick
3983
3984 p.setPen(NoPen); // since QRegion(Q3PointArray) excludes outline :-()-:
3985 p.drawPolygon(poly);
3986}
3987
3988/*!
3989 Sets the points of the polygon to be \a pa. These points will have
3990 their x and y coordinates automatically translated by x(), y() as
3991 the polygon is moved.
3992*/
3993void Q3CanvasPolygon::setPoints(Q3PointArray pa)
3994{
3995 removeFromChunks();
3996 poly = pa;
3997 poly.detach(); // Explicit sharing is stupid.
3998 poly.translate((int)x(),(int)y());
3999 addToChunks();
4000}
4001
4002/*!
4003 \reimp
4004*/
4005void Q3CanvasPolygon::moveBy(double dx, double dy)
4006{
4007 // Note: does NOT call Q3CanvasPolygonalItem::moveBy(), since that
4008 // only does half this work.
4009 //
4010 int idx = int(x()+dx)-int(x());
4011 int idy = int(y()+dy)-int(y());
4012 if (idx || idy) {
4013 removeFromChunks();
4014 poly.translate(idx,idy);
4015 }
4016 myx+=dx;
4017 myy+=dy;
4018 if (idx || idy) {
4019 addToChunks();
4020 }
4021}
4022
4023/*!
4024 \class Q3CanvasSpline
4025 \compat
4026 \brief The Q3CanvasSpline class provides multi-bezier splines on a Q3Canvas.
4027
4028 A Q3CanvasSpline is a sequence of 4-point bezier curves joined
4029 together to make a curved shape.
4030
4031 You set the control points of the spline with setControlPoints().
4032
4033 If the bezier is closed(), then the first control point will be
4034 re-used as the last control point. Therefore, a closed bezier must
4035 have a multiple of 3 control points and an open bezier must have
4036 one extra point.
4037
4038 The beziers are not necessarily joined "smoothly". To ensure this,
4039 set control points appropriately (general reference texts about
4040 beziers will explain this in detail).
4041
4042 Like any other canvas item splines can be moved with
4043 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
4044 coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
4045 Q3CanvasItem::setZ().
4046
4047 \sa QtCanvas, {Porting to Graphics View}
4048*/
4049
4050/*!
4051 Create a spline with no control points on the canvas \a canvas.
4052
4053 \sa setControlPoints()
4054*/
4055Q3CanvasSpline::Q3CanvasSpline(Q3Canvas* canvas) :
4056 Q3CanvasPolygon(canvas),
4057 cl(true)
4058{
4059}
4060
4061/*!
4062 Destroy the spline.
4063*/
4064Q3CanvasSpline::~Q3CanvasSpline()
4065{
4066}
4067
4068/*!
4069 Set the spline control points to \a ctrl.
4070
4071 If \a close is true, then the first point in \a ctrl will be
4072 re-used as the last point, and the number of control points must
4073 be a multiple of 3. If \a close is false, one additional control
4074 point is required, and the number of control points must be one of
4075 (4, 7, 10, 13, ...).
4076
4077 If the number of control points doesn't meet the above conditions,
4078 the number of points will be truncated to the largest number of
4079 points that do meet the requirement.
4080*/
4081void Q3CanvasSpline::setControlPoints(Q3PointArray ctrl, bool close)
4082{
4083 if ((int)ctrl.count() % 3 != (close ? 0 : 1)) {
4084 qWarning("Q3CanvasSpline::setControlPoints(): Number of points doesn't fit.");
4085 int numCurves = (ctrl.count() - (close ? 0 : 1))/ 3;
4086 ctrl.resize(numCurves*3 + (close ? 0 : 1));
4087 }
4088
4089 cl = close;
4090 bez = ctrl;
4091 recalcPoly();
4092}
4093
4094/*!
4095 Returns the current set of control points.
4096
4097 \sa setControlPoints(), closed()
4098*/
4099Q3PointArray Q3CanvasSpline::controlPoints() const
4100{
4101 return bez;
4102}
4103
4104/*!
4105 Returns true if the control points are a closed set; otherwise
4106 returns false.
4107*/
4108bool Q3CanvasSpline::closed() const
4109{
4110 return cl;
4111}
4112
4113void Q3CanvasSpline::recalcPoly()
4114{
4115 Q3PtrList<Q3PointArray> segs;
4116 segs.setAutoDelete(true);
4117 int n=0;
4118 for (int i=0; i<(int)bez.count()-1; i+=3) {
4119 Q3PointArray ctrl(4);
4120 ctrl[0] = bez[i+0];
4121 ctrl[1] = bez[i+1];
4122 ctrl[2] = bez[i+2];
4123 if (cl)
4124 ctrl[3] = bez[(i+3)%(int)bez.count()];
4125 else
4126 ctrl[3] = bez[i+3];
4127 Q3PointArray *seg = new Q3PointArray(ctrl.cubicBezier());
4128 n += seg->count()-1;
4129 segs.append(seg);
4130 }
4131 Q3PointArray p(n+1);
4132 n=0;
4133 for (Q3PointArray* seg = segs.first(); seg; seg = segs.next()) {
4134 for (int i=0; i<(int)seg->count()-1; i++)
4135 p[n++] = seg->point(i);
4136 if (n == (int)p.count()-1)
4137 p[n] = seg->point(seg->count()-1);
4138 }
4139 Q3CanvasPolygon::setPoints(p);
4140}
4141
4142/*!
4143 \fn Q3PointArray Q3CanvasPolygonalItem::areaPoints() const
4144
4145 This function must be reimplemented by subclasses. It \e must
4146 return the points bounding (i.e. outside and not touching) the
4147 shape or drawing errors will occur.
4148*/
4149
4150/*!
4151 \fn Q3PointArray Q3CanvasPolygon::points() const
4152
4153 Returns the vertices of the polygon, not translated by the position.
4154
4155 \sa setPoints(), areaPoints()
4156*/
4157Q3PointArray Q3CanvasPolygon::points() const
4158{
4159 Q3PointArray pa = areaPoints();
4160 pa.translate(int(-x()),int(-y()));
4161 return pa;
4162}
4163
4164/*!
4165 Returns the vertices of the polygon translated by the polygon's
4166 current x(), y() position, i.e. relative to the canvas's origin.
4167
4168 \sa setPoints(), points()
4169*/
4170Q3PointArray Q3CanvasPolygon::areaPoints() const
4171{
4172 return poly.copy();
4173}
4174
4175/*!
4176 \class Q3CanvasLine
4177 \compat
4178 \brief The Q3CanvasLine class provides a line on a Q3Canvas.
4179
4180 The line inherits functionality from Q3CanvasPolygonalItem, for
4181 example the setPen() function. The start and end points of the
4182 line are set with setPoints().
4183
4184 Like any other canvas item lines can be moved with
4185 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
4186 coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
4187 Q3CanvasItem::setZ().
4188
4189 \sa QtCanvas, {Porting to Graphics View}
4190*/
4191
4192/*!
4193 Constructs a line from (0,0) to (0,0) on \a canvas.
4194
4195 \sa setPoints()
4196*/
4197Q3CanvasLine::Q3CanvasLine(Q3Canvas* canvas) :
4198 Q3CanvasPolygonalItem(canvas)
4199{
4200 x1 = y1 = x2 = y2 = 0;
4201}
4202
4203/*!
4204 Destroys the line.
4205*/
4206Q3CanvasLine::~Q3CanvasLine()
4207{
4208 hide();
4209}
4210
4211/*!
4212 \reimp
4213*/
4214void Q3CanvasLine::setPen(QPen p)
4215{
4216 Q3CanvasPolygonalItem::setPen(p);
4217}
4218
4219/*!
4220 \fn QPoint Q3CanvasLine::startPoint () const
4221
4222 Returns the start point of the line.
4223
4224 \sa setPoints(), endPoint()
4225*/
4226
4227/*!
4228 \fn QPoint Q3CanvasLine::endPoint () const
4229
4230 Returns the end point of the line.
4231
4232 \sa setPoints(), startPoint()
4233*/
4234
4235/*!
4236 Sets the line's start point to (\a xa, \a ya) and its end point to
4237 (\a xb, \a yb).
4238*/
4239void Q3CanvasLine::setPoints(int xa, int ya, int xb, int yb)
4240{
4241 if (x1 != xa || x2 != xb || y1 != ya || y2 != yb) {
4242 removeFromChunks();
4243 x1 = xa;
4244 y1 = ya;
4245 x2 = xb;
4246 y2 = yb;
4247 addToChunks();
4248 }
4249}
4250
4251/*!
4252 \reimp
4253*/
4254void Q3CanvasLine::drawShape(QPainter &p)
4255{
4256 p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2));
4257}
4258
4259/*!
4260 \reimp
4261
4262 Note that the area defined by the line is somewhat thicker than
4263 the line that is actually drawn.
4264*/
4265Q3PointArray Q3CanvasLine::areaPoints() const
4266{
4267 Q3PointArray p(4);
4268 int xi = int(x());
4269 int yi = int(y());
4270 int pw = pen().width();
4271 int dx = QABS(x1-x2);
4272 int dy = QABS(y1-y2);
4273 pw = pw*4/3+2; // approx pw*sqrt(2)
4274 int px = x1<x2 ? -pw : pw ;
4275 int py = y1<y2 ? -pw : pw ;
4276 if (dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2))) {
4277 // steep
4278 if (px == py) {
4279 p[0] = QPoint(x1+xi ,y1+yi+py);
4280 p[1] = QPoint(x2+xi-px,y2+yi );
4281 p[2] = QPoint(x2+xi ,y2+yi-py);
4282 p[3] = QPoint(x1+xi+px,y1+yi );
4283 } else {
4284 p[0] = QPoint(x1+xi+px,y1+yi );
4285 p[1] = QPoint(x2+xi ,y2+yi-py);
4286 p[2] = QPoint(x2+xi-px,y2+yi );
4287 p[3] = QPoint(x1+xi ,y1+yi+py);
4288 }
4289 } else if (dx > dy) {
4290 // horizontal
4291 p[0] = QPoint(x1+xi+px,y1+yi+py);
4292 p[1] = QPoint(x2+xi-px,y2+yi+py);
4293 p[2] = QPoint(x2+xi-px,y2+yi-py);
4294 p[3] = QPoint(x1+xi+px,y1+yi-py);
4295 } else {
4296 // vertical
4297 p[0] = QPoint(x1+xi+px,y1+yi+py);
4298 p[1] = QPoint(x2+xi+px,y2+yi-py);
4299 p[2] = QPoint(x2+xi-px,y2+yi-py);
4300 p[3] = QPoint(x1+xi-px,y1+yi+py);
4301 }
4302 return p;
4303}
4304
4305/*!
4306 \reimp
4307
4308*/
4309
4310void Q3CanvasLine::moveBy(double dx, double dy)
4311{
4312 Q3CanvasPolygonalItem::moveBy(dx, dy);
4313}
4314
4315/*!
4316 \class Q3CanvasRectangle
4317 \compat
4318 \brief The Q3CanvasRectangle class provides a rectangle on a Q3Canvas.
4319
4320 This item paints a single rectangle which may have any pen() and
4321 brush(), but may not be tilted/rotated. For rotated rectangles,
4322 use Q3CanvasPolygon.
4323
4324 The rectangle's size and initial position can be set in the
4325 constructor. The size can be set or changed later using setSize().
4326 Use height() and width() to retrieve the rectangle's dimensions.
4327
4328 The rectangle can be drawn on a painter with drawShape().
4329
4330 Like any other canvas item rectangles can be moved with
4331 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
4332 coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
4333 Q3CanvasItem::setZ().
4334
4335 \sa QtCanvas, {Porting to Graphics View}
4336*/
4337
4338/*!
4339 Constructs a rectangle at position (0,0) with both width and
4340 height set to 32 pixels on \a canvas.
4341*/
4342Q3CanvasRectangle::Q3CanvasRectangle(Q3Canvas* canvas) :
4343 Q3CanvasPolygonalItem(canvas),
4344 w(32), h(32)
4345{
4346}
4347
4348/*!
4349 Constructs a rectangle positioned and sized by \a r on \a canvas.
4350*/
4351Q3CanvasRectangle::Q3CanvasRectangle(const QRect& r, Q3Canvas* canvas) :
4352 Q3CanvasPolygonalItem(canvas),
4353 w(r.width()), h(r.height())
4354{
4355 move(r.x(),r.y());
4356}
4357
4358/*!
4359 Constructs a rectangle at position (\a x, \a y) and size \a width
4360 by \a height, on \a canvas.
4361*/
4362Q3CanvasRectangle::Q3CanvasRectangle(int x, int y, int width, int height,
4363 Q3Canvas* canvas) :
4364 Q3CanvasPolygonalItem(canvas),
4365 w(width), h(height)
4366{
4367 move(x,y);
4368}
4369
4370/*!
4371 Destroys the rectangle.
4372*/
4373Q3CanvasRectangle::~Q3CanvasRectangle()
4374{
4375 hide();
4376}
4377
4378
4379/*!
4380 Returns the width of the rectangle.
4381*/
4382int Q3CanvasRectangle::width() const
4383{
4384 return w;
4385}
4386
4387/*!
4388 Returns the height of the rectangle.
4389*/
4390int Q3CanvasRectangle::height() const
4391{
4392 return h;
4393}
4394
4395/*!
4396 Sets the \a width and \a height of the rectangle.
4397*/
4398void Q3CanvasRectangle::setSize(int width, int height)
4399{
4400 if (w != width || h != height) {
4401 removeFromChunks();
4402 w = width;
4403 h = height;
4404 addToChunks();
4405 }
4406}
4407
4408/*!
4409 \fn QSize Q3CanvasRectangle::size() const
4410
4411 Returns the width() and height() of the rectangle.
4412
4413 \sa rect(), setSize()
4414*/
4415
4416/*!
4417 \fn QRect Q3CanvasRectangle::rect() const
4418
4419 Returns the integer-converted x(), y() position and size() of the
4420 rectangle as a QRect.
4421*/
4422
4423/*!
4424 \reimp
4425*/
4426Q3PointArray Q3CanvasRectangle::areaPoints() const
4427{
4428 Q3PointArray pa(4);
4429 int pw = (pen().width()+1)/2;
4430 if (pw < 1) pw = 1;
4431 if (pen() == NoPen) pw = 0;
4432 pa[0] = QPoint((int)x()-pw,(int)y()-pw);
4433 pa[1] = pa[0] + QPoint(w+pw*2,0);
4434 pa[2] = pa[1] + QPoint(0,h+pw*2);
4435 pa[3] = pa[0] + QPoint(0,h+pw*2);
4436 return pa;
4437}
4438
4439/*!
4440 Draws the rectangle on painter \a p.
4441*/
4442void Q3CanvasRectangle::drawShape(QPainter & p)
4443{
4444 p.drawRect((int)x(), (int)y(), w, h);
4445}
4446
4447
4448/*!
4449 \class Q3CanvasEllipse
4450 \compat
4451 \brief The Q3CanvasEllipse class provides an ellipse or ellipse segment on a Q3Canvas.
4452
4453 A canvas item that paints an ellipse or ellipse segment with a QBrush.
4454 The ellipse's height, width, start angle and angle length can be set
4455 at construction time. The size can be changed at runtime with
4456 setSize(), and the angles can be changed (if you're displaying an
4457 ellipse segment rather than a whole ellipse) with setAngles().
4458
4459 Note that angles are specified in 16ths of a degree.
4460
4461 \target anglediagram
4462 \img qcanvasellipse.png Ellipse
4463
4464 If a start angle and length angle are set then an ellipse segment
4465 will be drawn. The start angle is the angle that goes from zero in a
4466 counter-clockwise direction (shown in green in the diagram). The
4467 length angle is the angle from the start angle in a
4468 counter-clockwise direction (shown in blue in the diagram). The blue
4469 segment is the segment of the ellipse that would be drawn. If no
4470 start angle and length angle are specified the entire ellipse is
4471 drawn.
4472
4473 The ellipse can be drawn on a painter with drawShape().
4474
4475 Like any other canvas item ellipses can be moved with move() and
4476 moveBy(), or by setting coordinates with setX(), setY() and setZ().
4477
4478 Note: Q3CanvasEllipse does not use the pen.
4479
4480 \sa QtCanvas, {Porting to Graphics View}
4481*/
4482
4483/*!
4484 Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas.
4485*/
4486Q3CanvasEllipse::Q3CanvasEllipse(Q3Canvas* canvas) :
4487 Q3CanvasPolygonalItem(canvas),
4488 w(32), h(32),
4489 a1(0), a2(360*16)
4490{
4491}
4492
4493/*!
4494 Constructs a \a width by \a height pixel ellipse, centered at
4495 (0, 0) on \a canvas.
4496*/
4497Q3CanvasEllipse::Q3CanvasEllipse(int width, int height, Q3Canvas* canvas) :
4498 Q3CanvasPolygonalItem(canvas),
4499 w(width),h(height),
4500 a1(0),a2(360*16)
4501{
4502}
4503
4504// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars
4505// ### it's how QPainter does it, so Q3Canvas does too for consistency. If it's
4506// ### a good idea, it should be added to QPainter, not just to Q3Canvas. Warwick
4507/*!
4508 Constructs a \a width by \a height pixel ellipse, centered at
4509 (0, 0) on \a canvas. Only a segment of the ellipse is drawn,
4510 starting at angle \a startangle, and extending for angle \a angle
4511 (the angle length).
4512
4513 Note that angles are specified in sixteenths of a degree.
4514*/
4515Q3CanvasEllipse::Q3CanvasEllipse(int width, int height,
4516 int startangle, int angle, Q3Canvas* canvas) :
4517 Q3CanvasPolygonalItem(canvas),
4518 w(width),h(height),
4519 a1(startangle),a2(angle)
4520{
4521}
4522
4523/*!
4524 Destroys the ellipse.
4525*/
4526Q3CanvasEllipse::~Q3CanvasEllipse()
4527{
4528 hide();
4529}
4530
4531/*!
4532 Returns the width of the ellipse.
4533*/
4534int Q3CanvasEllipse::width() const
4535{
4536 return w;
4537}
4538
4539/*!
4540 Returns the height of the ellipse.
4541*/
4542int Q3CanvasEllipse::height() const
4543{
4544 return h;
4545}
4546
4547/*!
4548 Sets the \a width and \a height of the ellipse.
4549*/
4550void Q3CanvasEllipse::setSize(int width, int height)
4551{
4552 if (w != width || h != height) {
4553 removeFromChunks();
4554 w = width;
4555 h = height;
4556 addToChunks();
4557 }
4558}
4559
4560/*!
4561 \fn int Q3CanvasEllipse::angleStart() const
4562
4563 Returns the start angle in 16ths of a degree. Initially
4564 this will be 0.
4565
4566 \sa setAngles(), angleLength()
4567*/
4568
4569/*!
4570 \fn int Q3CanvasEllipse::angleLength() const
4571
4572 Returns the length angle (the extent of the ellipse segment) in
4573 16ths of a degree. Initially this will be 360 * 16 (a complete
4574 ellipse).
4575
4576 \sa setAngles(), angleStart()
4577*/
4578
4579/*!
4580 Sets the angles for the ellipse. The start angle is \a start and
4581 the extent of the segment is \a length (the angle length) from the
4582 \a start. The angles are specified in 16ths of a degree. By
4583 default the ellipse will start at 0 and have an angle length of
4584 360 * 16 (a complete ellipse).
4585
4586 \sa angleStart(), angleLength()
4587*/
4588void Q3CanvasEllipse::setAngles(int start, int length)
4589{
4590 if (a1 != start || a2 != length) {
4591 removeFromChunks();
4592 a1 = start;
4593 a2 = length;
4594 addToChunks();
4595 }
4596}
4597
4598/*!
4599 \reimp
4600*/
4601Q3PointArray Q3CanvasEllipse::areaPoints() const
4602{
4603 Q3PointArray r;
4604 // makeArc at 0,0, then translate so that fixed point math doesn't overflow
4605 r.makeArc(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2);
4606 r.resize(r.size()+1);
4607 r.setPoint(r.size()-1,int(x()),int(y()));
4608 return r;
4609}
4610
4611// ### support outlines! Lars
4612// ### QRegion doesn't, so we cannot (try it). Warwick
4613/*!
4614 Draws the ellipse, centered at x(), y() using the painter \a p.
4615
4616 Note that Q3CanvasEllipse does not support an outline (the pen is
4617 always NoPen).
4618*/
4619void Q3CanvasEllipse::drawShape(QPainter & p)
4620{
4621 p.setPen(NoPen); // since QRegion(Q3PointArray) excludes outline :-()-:
4622 if (!a1 && a2 == 360*16) {
4623 p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h);
4624 } else {
4625 p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2);
4626 }
4627}
4628
4629
4630/*!
4631 \class Q3CanvasText
4632 \compat
4633 \brief The Q3CanvasText class provides a text object on a Q3Canvas.
4634
4635 A canvas text item has text with font, color and alignment
4636 attributes. The text and font can be set in the constructor or set
4637 or changed later with setText() and setFont(). The color is set
4638 with setColor() and the alignment with setTextFlags(). The text
4639 item's bounding rectangle is retrieved with boundingRect().
4640
4641 The text can be drawn on a painter with draw().
4642
4643 Like any other canvas item text items can be moved with
4644 Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
4645 coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
4646 Q3CanvasItem::setZ().
4647
4648 \sa QtCanvas, {Porting to Graphics View}
4649*/
4650
4651/*!
4652 Constructs a Q3CanvasText with the text "\<text\>", on \a canvas.
4653*/
4654Q3CanvasText::Q3CanvasText(Q3Canvas* canvas) :
4655 Q3CanvasItem(canvas),
4656 txt(QLatin1String("<text>")), flags(0)
4657{
4658 setRect();
4659}
4660
4661// ### add textflags to the constructor? Lars
4662/*!
4663 Constructs a Q3CanvasText with the text \a t, on canvas \a canvas.
4664*/
4665Q3CanvasText::Q3CanvasText(const QString& t, Q3Canvas* canvas) :
4666 Q3CanvasItem(canvas),
4667 txt(t), flags(0)
4668{
4669 setRect();
4670}
4671
4672// ### see above
4673/*!
4674 Constructs a Q3CanvasText with the text \a t and font \a f, on the
4675 canvas \a canvas.
4676*/
4677Q3CanvasText::Q3CanvasText(const QString& t, QFont f, Q3Canvas* canvas) :
4678 Q3CanvasItem(canvas),
4679 txt(t), flags(0),
4680 fnt(f)
4681{
4682 setRect();
4683}
4684
4685/*!
4686 Destroys the canvas text item.
4687*/
4688Q3CanvasText::~Q3CanvasText()
4689{
4690 removeFromChunks();
4691}
4692
4693/*!
4694 Returns the bounding rectangle of the text.
4695*/
4696QRect Q3CanvasText::boundingRect() const { return brect; }
4697
4698void Q3CanvasText::setRect()
4699{
4700 brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt);
4701}
4702
4703/*!
4704 \fn int Q3CanvasText::textFlags() const
4705
4706 Returns the currently set alignment flags.
4707
4708 \sa setTextFlags() Qt::AlignmentFlag
4709*/
4710
4711
4712/*!
4713 Sets the alignment flags to \a f. These are a bitwise OR of the
4714 flags available to QPainter::drawText() -- see the
4715 \l{Qt::AlignmentFlag}s.
4716
4717 \sa setFont() setColor()
4718*/
4719void Q3CanvasText::setTextFlags(int f)
4720{
4721 if (flags != f) {
4722 removeFromChunks();
4723 flags = f;
4724 setRect();
4725 addToChunks();
4726 }
4727}
4728
4729/*!
4730 Returns the text item's text.
4731
4732 \sa setText()
4733*/
4734QString Q3CanvasText::text() const
4735{
4736 return txt;
4737}
4738
4739
4740/*!
4741 Sets the text item's text to \a t. The text may contain newlines.
4742
4743 \sa text(), setFont(), setColor() setTextFlags()
4744*/
4745void Q3CanvasText::setText(const QString& t)
4746{
4747 if (txt != t) {
4748 removeFromChunks();
4749 txt = t;
4750 setRect();
4751 addToChunks();
4752 }
4753}
4754
4755/*!
4756 Returns the font in which the text is drawn.
4757
4758 \sa setFont()
4759*/
4760QFont Q3CanvasText::font() const
4761{
4762 return fnt;
4763}
4764
4765/*!
4766 Sets the font in which the text is drawn to font \a f.
4767
4768 \sa font()
4769*/
4770void Q3CanvasText::setFont(const QFont& f)
4771{
4772 if (f != fnt) {
4773 removeFromChunks();
4774 fnt = f;
4775 setRect();
4776 addToChunks();
4777 }
4778}
4779
4780/*!
4781 Returns the color of the text.
4782
4783 \sa setColor()
4784*/
4785QColor Q3CanvasText::color() const
4786{
4787 return col;
4788}
4789
4790/*!
4791 Sets the color of the text to the color \a c.
4792
4793 \sa color(), setFont()
4794*/
4795void Q3CanvasText::setColor(const QColor& c)
4796{
4797 col=c;
4798 changeChunks();
4799}
4800
4801
4802/*!
4803 \reimp
4804*/
4805void Q3CanvasText::moveBy(double dx, double dy)
4806{
4807 int idx = int(x()+dx)-int(x());
4808 int idy = int(y()+dy)-int(y());
4809 if (idx || idy) {
4810 removeFromChunks();
4811 }
4812 myx+=dx;
4813 myy+=dy;
4814 if (idx || idy) {
4815 brect.moveBy(idx,idy);
4816 addToChunks();
4817 }
4818}
4819
4820/*!
4821 Draws the text using the painter \a painter.
4822*/
4823void Q3CanvasText::draw(QPainter& painter)
4824{
4825 painter.setFont(fnt);
4826 painter.setPen(col);
4827 painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt);
4828}
4829
4830/*!
4831 \reimp
4832*/
4833void Q3CanvasText::changeChunks()
4834{
4835 if (isVisible() && canvas()) {
4836 int chunksize=canvas()->chunkSize();
4837 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
4838 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
4839 canvas()->setChangedChunk(i,j);
4840 }
4841 }
4842 }
4843}
4844
4845/*!
4846 Adds the text item to the appropriate chunks.
4847*/
4848void Q3CanvasText::addToChunks()
4849{
4850 if (isVisible() && canvas()) {
4851 int chunksize=canvas()->chunkSize();
4852 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
4853 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
4854 canvas()->addItemToChunk(this,i,j);
4855 }
4856 }
4857 }
4858}
4859
4860/*!
4861 Removes the text item from the appropriate chunks.
4862*/
4863void Q3CanvasText::removeFromChunks()
4864{
4865 if (isVisible() && canvas()) {
4866 int chunksize=canvas()->chunkSize();
4867 for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
4868 for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
4869 canvas()->removeItemFromChunk(this,i,j);
4870 }
4871 }
4872 }
4873}
4874
4875
4876/*!
4877 Returns 0 (Q3CanvasItem::Rtti_Item).
4878
4879 Make your derived classes return their own values for rtti(), so
4880 that you can distinguish between objects returned by
4881 Q3Canvas::at(). You should use values greater than 1000 to allow
4882 for extensions to this class.
4883
4884 Overuse of this functionality can damage its extensibility. For
4885 example, once you have identified a base class of a Q3CanvasItem
4886 found by Q3Canvas::at(), cast it to that type and call meaningful
4887 methods rather than acting upon the object based on its rtti
4888 value.
4889
4890 For example:
4891
4892 \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 4
4893*/
4894int Q3CanvasItem::rtti() const { return RTTI; }
4895int Q3CanvasItem::RTTI = Rtti_Item;
4896
4897/*!
4898 Returns 1 (Q3CanvasItem::Rtti_Sprite).
4899
4900 \sa Q3CanvasItem::rtti()
4901*/
4902int Q3CanvasSprite::rtti() const { return RTTI; }
4903int Q3CanvasSprite::RTTI = Rtti_Sprite;
4904
4905/*!
4906 Returns 2 (Q3CanvasItem::Rtti_PolygonalItem).
4907
4908 \sa Q3CanvasItem::rtti()
4909*/
4910int Q3CanvasPolygonalItem::rtti() const { return RTTI; }
4911int Q3CanvasPolygonalItem::RTTI = Rtti_PolygonalItem;
4912
4913/*!
4914 Returns 3 (Q3CanvasItem::Rtti_Text).
4915
4916 \sa Q3CanvasItem::rtti()
4917*/
4918int Q3CanvasText::rtti() const { return RTTI; }
4919int Q3CanvasText::RTTI = Rtti_Text;
4920
4921/*!
4922 Returns 4 (Q3CanvasItem::Rtti_Polygon).
4923
4924 \sa Q3CanvasItem::rtti()
4925*/
4926int Q3CanvasPolygon::rtti() const { return RTTI; }
4927int Q3CanvasPolygon::RTTI = Rtti_Polygon;
4928
4929/*!
4930 Returns 5 (Q3CanvasItem::Rtti_Rectangle).
4931
4932 \sa Q3CanvasItem::rtti()
4933*/
4934int Q3CanvasRectangle::rtti() const { return RTTI; }
4935int Q3CanvasRectangle::RTTI = Rtti_Rectangle;
4936
4937/*!
4938 Returns 6 (Q3CanvasItem::Rtti_Ellipse).
4939
4940 \sa Q3CanvasItem::rtti()
4941*/
4942int Q3CanvasEllipse::rtti() const { return RTTI; }
4943int Q3CanvasEllipse::RTTI = Rtti_Ellipse;
4944
4945/*!
4946 Returns 7 (Q3CanvasItem::Rtti_Line).
4947
4948 \sa Q3CanvasItem::rtti()
4949*/
4950int Q3CanvasLine::rtti() const { return RTTI; }
4951int Q3CanvasLine::RTTI = Rtti_Line;
4952
4953/*!
4954 Returns 8 (Q3CanvasItem::Rtti_Spline).
4955
4956 \sa Q3CanvasItem::rtti()
4957*/
4958int Q3CanvasSpline::rtti() const { return RTTI; }
4959int Q3CanvasSpline::RTTI = Rtti_Spline;
4960
4961/*!
4962 Constructs a Q3CanvasSprite which uses images from the
4963 Q3CanvasPixmapArray \a a.
4964
4965 The sprite in initially positioned at (0, 0) on \a canvas, using
4966 frame 0.
4967*/
4968Q3CanvasSprite::Q3CanvasSprite(Q3CanvasPixmapArray* a, Q3Canvas* canvas) :
4969 Q3CanvasItem(canvas),
4970 frm(0),
4971 anim_val(0),
4972 anim_state(0),
4973 anim_type(0),
4974 images(a)
4975{
4976}
4977
4978
4979/*!
4980 Set the array of images used for displaying the sprite to the
4981 Q3CanvasPixmapArray \a a.
4982
4983 If the current frame() is larger than the number of images in \a
4984 a, the current frame will be reset to 0.
4985*/
4986void Q3CanvasSprite::setSequence(Q3CanvasPixmapArray* a)
4987{
4988 bool isvisible = isVisible();
4989 if (isvisible && images)
4990 hide();
4991 images = a;
4992 if (frm >= (int)images->count())
4993 frm = 0;
4994 if (isvisible)
4995 show();
4996}
4997
4998/*!
4999\internal
5000
5001Marks any chunks the sprite touches as changed.
5002*/
5003void Q3CanvasSprite::changeChunks()
5004{
5005 if (isVisible() && canvas()) {
5006 int chunksize=canvas()->chunkSize();
5007 for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
5008 for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
5009 canvas()->setChangedChunk(i,j);
5010 }
5011 }
5012 }
5013}
5014
5015/*!
5016 Destroys the sprite and removes it from the canvas. Does \e not
5017 delete the images.
5018*/
5019Q3CanvasSprite::~Q3CanvasSprite()
5020{
5021 removeFromChunks();
5022}
5023
5024/*!
5025 Sets the animation frame used for displaying the sprite to \a f,
5026 an index into the Q3CanvasSprite's Q3CanvasPixmapArray. The call
5027 will be ignored if \a f is larger than frameCount() or smaller
5028 than 0.
5029
5030 \sa frame() move()
5031*/
5032void Q3CanvasSprite::setFrame(int f)
5033{
5034 move(x(),y(),f);
5035}
5036
5037/*!
5038 \enum Q3CanvasSprite::FrameAnimationType
5039
5040 This enum is used to identify the different types of frame
5041 animation offered by Q3CanvasSprite.
5042
5043 \value Cycle at each advance the frame number will be incremented by
5044 1 (modulo the frame count).
5045 \value Oscillate at each advance the frame number will be
5046 incremented by 1 up to the frame count then decremented to by 1 to
5047 0, repeating this sequence forever.
5048*/
5049
5050/*!
5051 Sets the animation characteristics for the sprite.
5052
5053 For \a type == \c Cycle, the frames will increase by \a step
5054 at each advance, modulo the frameCount().
5055
5056 For \a type == \c Oscillate, the frames will increase by \a step
5057 at each advance, up to the frameCount(), then decrease by \a step
5058 back to 0, repeating forever.
5059
5060 The \a state parameter is for internal use.
5061*/
5062void Q3CanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state)
5063{
5064 anim_val = step;
5065 anim_type = type;
5066 anim_state = state;
5067 setAnimated(true);
5068}
5069
5070/*!
5071 Extends the default Q3CanvasItem implementation to provide the
5072 functionality of setFrameAnimation().
5073
5074 The \a phase is 0 or 1: see Q3CanvasItem::advance() for details.
5075
5076 \sa Q3CanvasItem::advance() setVelocity()
5077*/
5078void Q3CanvasSprite::advance(int phase)
5079{
5080 if (phase==1) {
5081 int nf = frame();
5082 if (anim_type == Oscillate) {
5083 if (anim_state)
5084 nf += anim_val;
5085 else
5086 nf -= anim_val;
5087 if (nf < 0) {
5088 nf = abs(anim_val);
5089 anim_state = !anim_state;
5090 } else if (nf >= frameCount()) {
5091 nf = frameCount()-1-abs(anim_val);
5092 anim_state = !anim_state;
5093 }
5094 } else {
5095 nf = (nf + anim_val + frameCount()) % frameCount();
5096 }
5097 move(x()+xVelocity(),y()+yVelocity(),nf);
5098 }
5099}
5100
5101
5102/*!
5103 \fn int Q3CanvasSprite::frame() const
5104
5105 Returns the index of the current animation frame in the
5106 Q3CanvasSprite's Q3CanvasPixmapArray.
5107
5108 \sa setFrame(), move()
5109*/
5110
5111/*!
5112 \fn int Q3CanvasSprite::frameCount() const
5113
5114 Returns the number of frames in the Q3CanvasSprite's
5115 Q3CanvasPixmapArray.
5116*/
5117
5118
5119/*!
5120 Moves the sprite to (\a x, \a y).
5121*/
5122void Q3CanvasSprite::move(double x, double y) { Q3CanvasItem::move(x,y); }
5123
5124/*!
5125 \fn void Q3CanvasSprite::move(double nx, double ny, int nf)
5126
5127 Moves the sprite to (\a nx, \a ny) and sets the current
5128 frame to \a nf. \a nf will be ignored if it is larger than
5129 frameCount() or smaller than 0.
5130*/
5131void Q3CanvasSprite::move(double nx, double ny, int nf)
5132{
5133 if (isVisible() && canvas()) {
5134 hide();
5135 Q3CanvasItem::move(nx,ny);
5136 if (nf >= 0 && nf < frameCount())
5137 frm=nf;
5138 show();
5139 } else {
5140 Q3CanvasItem::move(nx,ny);
5141 if (nf >= 0 && nf < frameCount())
5142 frm=nf;
5143 }
5144}
5145
5146class Q3CanvasPolygonScanner : public Q3PolygonScanner {
5147 QPolygonalProcessor& processor;
5148public:
5149 Q3CanvasPolygonScanner(QPolygonalProcessor& p) :
5150 processor(p)
5151 {
5152 }
5153 void processSpans(int n, QPoint* point, int* width)
5154 {
5155 processor.doSpans(n,point,width);
5156 }
5157};
5158
5159void Q3CanvasPolygonalItem::scanPolygon(const Q3PointArray& pa, int winding, QPolygonalProcessor& process) const
5160{
5161 Q3CanvasPolygonScanner scanner(process);
5162 scanner.scan(pa,winding);
5163}
5164
5165QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.