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