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

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

More QCanvas fixes: forced the transformation mode to be Areas in some sensitive places (for example, when drawing the background); reimplemented the "garbage on the edges" removal fix.

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