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