Index: /trunk/examples/canvas/blendshadow.cpp
===================================================================
--- /trunk/examples/canvas/blendshadow.cpp	(revision 69)
+++ /trunk/examples/canvas/blendshadow.cpp	(revision 69)
@@ -0,0 +1,53 @@
+#include <qimage.h>
+#include <qcolor.h>
+
+static inline int blendComponent( int v, int av, int s, int as )
+{
+    return as*s + av*v -(av*as*s)/255;  
+}
+    
+static inline QRgb blendShade( QRgb v, QRgb s )
+{
+    //shadow image is already reduced and blurred
+    int as = qAlpha(s); 
+    int av = qAlpha(v);
+    if ( as == 0 || av == 255 )
+	return v;
+
+    int a = as + av -(as*av)/255;
+    
+    int r = blendComponent( qRed(v),av, qRed(s), as)/a;
+    int g = blendComponent( qGreen(v),av, qGreen(s), as)/a;
+    int b = blendComponent( qBlue(v),av, qBlue(s), as)/a;
+
+    return qRgba(r,g,b,a);
+}
+
+ 
+
+int main( int*, char**)
+{
+    QImage image( "out.png" );
+    image.convertDepth( 32 );
+    QImage shade( "outshade.png" );
+    shade.convertDepth( 32 );
+    int dx = 10;
+    int dy = 5;
+	
+    int w = image.width();
+    int h = image.height();
+	
+    QImage img( w+dx, h+dy, 32 );
+    img.setAlphaBuffer( TRUE );
+
+    for ( int y = 0; y < h+dy; y++ ) {
+	for ( int x = 0; x < w+dx; x++ ) {
+	    QRgb sh =  (x<dx||y<dy) ? 0 : shade.pixel( x-dx, y-dy );
+	    QRgb pixel = (x<w&y<h) ? image.pixel( x, y ) : 0;
+	    img.setPixel( x, y, blendShade( pixel, sh ) ); 
+	}
+    }
+    img.save("blend.png", "PNG" );
+}
+
+
Index: /trunk/examples/canvas/canvas.cpp
===================================================================
--- /trunk/examples/canvas/canvas.cpp	(revision 69)
+++ /trunk/examples/canvas/canvas.cpp	(revision 69)
@@ -0,0 +1,747 @@
+#include <qdatetime.h>
+#include <qmainwindow.h>
+#include <qstatusbar.h>
+#include <qmessagebox.h>
+#include <qmenubar.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qprinter.h>
+#include <qlabel.h>
+#include <qimage.h>
+#include <qprogressdialog.h>
+#include "canvas.h"
+
+#include <stdlib.h>
+
+// We use a global variable to save memory - all the brushes and pens in
+// the mesh are shared.
+static QBrush *tb = 0;
+static QPen *tp = 0;
+
+class EdgeItem;
+class NodeItem;
+
+class EdgeItem: public QCanvasLine
+{
+public:
+    EdgeItem( NodeItem*, NodeItem*, QCanvas *canvas );
+    void setFromPoint( int x, int y ) ;
+    void setToPoint( int x, int y );
+    static int count() { return c; }
+    void moveBy(double dx, double dy);
+private:
+    static int c;
+};
+
+static const int imageRTTI = 984376;
+
+
+class ImageItem: public QCanvasRectangle
+{
+public:
+    ImageItem( QImage img, QCanvas *canvas );
+    int rtti () const { return imageRTTI; }
+    bool hit( const QPoint&) const;
+protected:
+    void drawShape( QPainter & );
+private:
+    QImage image;
+    QPixmap pixmap;
+};
+
+
+ImageItem::ImageItem( QImage img, QCanvas *canvas )
+    : QCanvasRectangle( canvas ), image(img)
+{
+    setSize( image.width(), image.height() );
+
+#if !defined(Q_WS_QWS)
+    pixmap.convertFromImage(image, OrderedAlphaDither);
+#endif
+}
+
+
+void ImageItem::drawShape( QPainter &p )
+{
+// On Qt/Embedded, we can paint a QImage as fast as a QPixmap,
+// but on other platforms, we need to use a QPixmap.
+#if defined(Q_WS_QWS)
+    p.drawImage( int(x()), int(y()), image, 0, 0, -1, -1, OrderedAlphaDither );
+#else
+    p.drawPixmap( int(x()), int(y()), pixmap );
+#endif
+}
+
+bool ImageItem::hit( const QPoint &p ) const
+{
+    int ix = p.x()-int(x());
+    int iy = p.y()-int(y());
+    if ( !image.valid( ix , iy ) )
+	return FALSE;
+    QRgb pixel = image.pixel( ix, iy );
+    return qAlpha( pixel ) != 0;
+}
+
+class NodeItem: public QCanvasEllipse
+{
+public:
+    NodeItem( QCanvas *canvas );
+    ~NodeItem() {}
+
+    void addInEdge( EdgeItem *edge ) { inList.append( edge ); }
+    void addOutEdge( EdgeItem *edge ) { outList.append( edge ); }
+
+    void moveBy(double dx, double dy);
+
+    //    QPoint center() { return boundingRect().center(); }
+private:
+    QPtrList<EdgeItem> inList;
+    QPtrList<EdgeItem> outList;
+};
+
+
+int EdgeItem::c = 0;
+
+
+void EdgeItem::moveBy(double, double)
+{
+    //nothing
+}
+
+EdgeItem::EdgeItem( NodeItem *from, NodeItem *to, QCanvas *canvas )
+    : QCanvasLine( canvas )
+{
+    c++;
+    setPen( *tp );
+    setBrush( *tb );
+    from->addOutEdge( this );
+    to->addInEdge( this );
+    setPoints( int(from->x()), int(from->y()), int(to->x()), int(to->y()) );
+    setZ( 127 );
+}
+
+void EdgeItem::setFromPoint( int x, int y )
+{
+    setPoints( x,y, endPoint().x(), endPoint().y() );
+}
+
+void EdgeItem::setToPoint( int x, int y )
+{
+    setPoints( startPoint().x(), startPoint().y(), x, y );
+}
+
+
+
+void NodeItem::moveBy(double dx, double dy)
+{
+    QCanvasEllipse::moveBy( dx, dy );
+
+    QPtrListIterator<EdgeItem> it1( inList );
+    EdgeItem *edge;
+    while (( edge = it1.current() )) {
+	++it1;
+	edge->setToPoint( int(x()), int(y()) );
+    }
+    QPtrListIterator<EdgeItem> it2( outList );
+    while (( edge = it2.current() )) {
+	++it2;
+	edge->setFromPoint( int(x()), int(y()) );
+    }
+}
+
+NodeItem::NodeItem( QCanvas *canvas )
+    : QCanvasEllipse( 6, 6, canvas )
+{
+    setPen( *tp );
+    setBrush( *tb );
+    setZ( 128 );
+}
+
+FigureEditor::FigureEditor(
+	QCanvas& c, QWidget* parent,
+	const char* name, WFlags f) :
+    QCanvasView(&c,parent,name,f)
+{
+}
+
+void FigureEditor::contentsMousePressEvent(QMouseEvent* e)
+{
+    QPoint p = inverseWorldMatrix().map(e->pos());
+    QCanvasItemList l=canvas()->collisions(p);
+    for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+	if ( (*it)->rtti() == imageRTTI ) {
+	    ImageItem *item= (ImageItem*)(*it);
+	    if ( !item->hit( p ) )
+		 continue;
+	}
+	moving = *it;
+	moving_start = p;
+	return;
+    }
+    moving = 0;
+}
+
+void FigureEditor::clear()
+{
+    QCanvasItemList list = canvas()->allItems();
+    QCanvasItemList::Iterator it = list.begin();
+    for (; it != list.end(); ++it) {
+	if ( *it )
+	    delete *it;
+    }
+}
+
+void FigureEditor::contentsMouseMoveEvent(QMouseEvent* e)
+{
+    if ( moving ) {
+	QPoint p = inverseWorldMatrix().map(e->pos());
+	moving->moveBy(p.x() - moving_start.x(),
+		       p.y() - moving_start.y());
+	moving_start = p;
+	canvas()->update();
+    }
+}
+
+
+
+BouncyLogo::BouncyLogo(QCanvas* canvas) :
+    QCanvasSprite(0,canvas)
+{
+    static QCanvasPixmapArray logo("qt-trans.xpm");
+    setSequence(&logo);
+    setAnimated(TRUE);
+    initPos();
+}
+
+
+const int logo_rtti = 1234;
+
+int BouncyLogo::rtti() const
+{
+    return logo_rtti;
+}
+
+void BouncyLogo::initPos()
+{
+    initSpeed();
+    int trial=1000;
+    do {
+	move(rand()%canvas()->width(),rand()%canvas()->height());
+	advance(0);
+    } while (trial-- && xVelocity()==0.0 && yVelocity()==0.0);
+}
+
+void BouncyLogo::initSpeed()
+{
+    const double speed = 4.0;
+    double d = (double)(rand()%1024) / 1024.0;
+    setVelocity( d*speed*2-speed, (1-d)*speed*2-speed );
+}
+
+void BouncyLogo::advance(int stage)
+{
+    switch ( stage ) {
+      case 0: {
+	double vx = xVelocity();
+	double vy = yVelocity();
+
+	if ( vx == 0.0 && vy == 0.0 ) {
+	    // stopped last turn
+	    initSpeed();
+	    vx = xVelocity();
+	    vy = yVelocity();
+	}
+
+	double nx = x() + vx;
+	double ny = y() + vy;
+
+	if ( nx < 0 || nx >= canvas()->width() )
+	    vx = -vx;
+	if ( ny < 0 || ny >= canvas()->height() )
+	    vy = -vy;
+
+	for (int bounce=0; bounce<4; bounce++) {
+	    QCanvasItemList l=collisions(FALSE);
+	    for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+		QCanvasItem *hit = *it;
+		if ( hit->rtti()==logo_rtti && hit->collidesWith(this) ) {
+		    switch ( bounce ) {
+		      case 0:
+			vx = -vx;
+			break;
+		      case 1:
+			vy = -vy;
+			vx = -vx;
+			break;
+		      case 2:
+			vx = -vx;
+			break;
+		      case 3:
+			// Stop for this turn
+			vx = 0;
+			vy = 0;
+			break;
+		    }
+		    setVelocity(vx,vy);
+		    break;
+		}
+	    }
+	}
+
+	if ( x()+vx < 0 || x()+vx >= canvas()->width() )
+	    vx = 0;
+	if ( y()+vy < 0 || y()+vy >= canvas()->height() )
+	    vy = 0;
+
+	setVelocity(vx,vy);
+      } break;
+      case 1:
+	QCanvasItem::advance(stage);
+	break;
+    }
+}
+
+static uint mainCount = 0;
+static QImage *butterflyimg;
+static QImage *logoimg;
+
+Main::Main(QCanvas& c, QWidget* parent, const char* name, WFlags f) :
+    QMainWindow(parent,name,f),
+    canvas(c)
+{
+    editor = new FigureEditor(canvas,this);
+    QMenuBar* menu = menuBar();
+
+    QPopupMenu* file = new QPopupMenu( menu );
+    file->insertItem("&Fill canvas", this, SLOT(init()), CTRL+Key_F);
+    file->insertItem("&Erase canvas", this, SLOT(clear()), CTRL+Key_E);
+    file->insertItem("&New view", this, SLOT(newView()), CTRL+Key_N);
+    file->insertSeparator();
+    file->insertItem("&Print...", this, SLOT(print()), CTRL+Key_P);
+    file->insertSeparator();
+    file->insertItem("E&xit", qApp, SLOT(quit()), CTRL+Key_Q);
+    menu->insertItem("&File", file);
+
+    QPopupMenu* edit = new QPopupMenu( menu );
+    edit->insertItem("Add &Circle", this, SLOT(addCircle()), ALT+Key_C);
+    edit->insertItem("Add &Hexagon", this, SLOT(addHexagon()), ALT+Key_H);
+    edit->insertItem("Add &Polygon", this, SLOT(addPolygon()), ALT+Key_P);
+    edit->insertItem("Add Spl&ine", this, SLOT(addSpline()), ALT+Key_I);
+    edit->insertItem("Add &Text", this, SLOT(addText()), ALT+Key_T);
+    edit->insertItem("Add &Line", this, SLOT(addLine()), ALT+Key_L);
+    edit->insertItem("Add &Rectangle", this, SLOT(addRectangle()), ALT+Key_R);
+    edit->insertItem("Add &Sprite", this, SLOT(addSprite()), ALT+Key_S);
+    edit->insertItem("Create &Mesh", this, SLOT(addMesh()), ALT+Key_M );
+    edit->insertItem("Add &Alpha-blended image", this, SLOT(addButterfly()), ALT+Key_A);
+    menu->insertItem("&Edit", edit);
+
+    QPopupMenu* view = new QPopupMenu( menu );
+    view->insertItem("&Enlarge", this, SLOT(enlarge()), SHIFT+CTRL+Key_Plus);
+    view->insertItem("Shr&ink", this, SLOT(shrink()), SHIFT+CTRL+Key_Minus);
+    view->insertSeparator();
+    view->insertItem("&Rotate clockwise", this, SLOT(rotateClockwise()), CTRL+Key_PageDown);
+    view->insertItem("Rotate &counterclockwise", this, SLOT(rotateCounterClockwise()), CTRL+Key_PageUp);
+    view->insertItem("&Zoom in", this, SLOT(zoomIn()), CTRL+Key_Plus);
+    view->insertItem("Zoom &out", this, SLOT(zoomOut()), CTRL+Key_Minus);
+    view->insertItem("Translate left", this, SLOT(moveL()), CTRL+Key_Left);
+    view->insertItem("Translate right", this, SLOT(moveR()), CTRL+Key_Right);
+    view->insertItem("Translate up", this, SLOT(moveU()), CTRL+Key_Up);
+    view->insertItem("Translate down", this, SLOT(moveD()), CTRL+Key_Down);
+    view->insertItem("&Mirror", this, SLOT(mirror()), CTRL+Key_Home);
+    menu->insertItem("&View", view);
+
+    options = new QPopupMenu( menu );
+    dbf_id = options->insertItem("Double buffer", this, SLOT(toggleDoubleBuffer()));
+    options->setItemChecked(dbf_id, TRUE);
+    menu->insertItem("&Options",options);
+
+    menu->insertSeparator();
+
+    QPopupMenu* help = new QPopupMenu( menu );
+    help->insertItem("&About", this, SLOT(help()), Key_F1);
+    help->setItemChecked(dbf_id, TRUE);
+    menu->insertItem("&Help",help);
+
+    statusBar();
+
+    setCentralWidget(editor);
+
+    printer = 0;
+
+    init();
+}
+
+void Main::init()
+{
+    clear();
+
+    static int r=24;
+    srand(++r);
+
+    mainCount++;
+    butterflyimg = 0;
+    logoimg = 0;
+
+    int i;
+    for ( i=0; i<canvas.width() / 56; i++) {
+	addButterfly();
+    }
+    for ( i=0; i<canvas.width() / 85; i++) {
+	addHexagon();
+    }
+    for ( i=0; i<canvas.width() / 128; i++) {
+	addLogo();
+    }
+}
+
+Main::~Main()
+{
+    delete printer;
+    if ( !--mainCount ) {
+	delete[] butterflyimg;
+	butterflyimg = 0;
+	delete[] logoimg;
+	logoimg = 0;
+    }
+}
+
+void Main::newView()
+{
+    // Open a new view... have it delete when closed.
+    Main *m = new Main(canvas, 0, 0, WDestructiveClose);
+    qApp->setMainWidget(m);
+    m->show();
+    qApp->setMainWidget(0);
+}
+
+void Main::clear()
+{
+    editor->clear();
+}
+
+void Main::help()
+{
+    static QMessageBox* about = new QMessageBox( "Qt Canvas Example",
+	    "<h3>The QCanvas classes example</h3>"
+	    "<ul>"
+		"<li> Press ALT-S for some sprites."
+		"<li> Press ALT-C for some circles."
+		"<li> Press ALT-L for some lines."
+		"<li> Drag the objects around."
+		"<li> Read the code!"
+	    "</ul>", QMessageBox::Information, 1, 0, 0, this, 0, FALSE );
+    about->setButtonText( 1, "Dismiss" );
+    about->show();
+}
+
+void Main::aboutQt()
+{
+    QMessageBox::aboutQt( this, "Qt Canvas Example" );
+}
+
+void Main::toggleDoubleBuffer()
+{
+    bool s = !options->isItemChecked(dbf_id);
+    options->setItemChecked(dbf_id,s);
+    canvas.setDoubleBuffering(s);
+}
+
+void Main::enlarge()
+{
+    canvas.resize(canvas.width()*4/3, canvas.height()*4/3);
+}
+
+void Main::shrink()
+{
+    canvas.resize(canvas.width()*3/4, canvas.height()*3/4);
+}
+
+void Main::rotateClockwise()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.rotate( 22.5 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::rotateCounterClockwise()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.rotate( -22.5 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::zoomIn()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.scale( 2.0, 2.0 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::zoomOut()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.scale( 0.5, 0.5 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::mirror()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.scale( -1, 1 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::moveL()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.translate( -16, 0 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::moveR()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.translate( +16, 0 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::moveU()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.translate( 0, -16 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::moveD()
+{
+    QWMatrix m = editor->worldMatrix();
+    m.translate( 0, +16 );
+    editor->setWorldMatrix( m );
+}
+
+void Main::print()
+{
+    if ( !printer ) printer = new QPrinter;
+    if ( printer->setup(this) ) {
+	QPainter pp(printer);
+	canvas.drawArea(QRect(0,0,canvas.width(),canvas.height()),&pp,FALSE);
+    }
+}
+
+
+void Main::addSprite()
+{
+    QCanvasItem* i = new BouncyLogo(&canvas);
+    i->setZ(rand()%256);
+    i->show();
+}
+
+QString butterfly_fn;
+QString logo_fn;
+
+
+void Main::addButterfly()
+{
+    if ( butterfly_fn.isEmpty() )
+	return;
+    if ( !butterflyimg ) {
+	butterflyimg = new QImage[4];
+	butterflyimg[0].load( butterfly_fn );
+	butterflyimg[1] = butterflyimg[0].smoothScale( int(butterflyimg[0].width()*0.75),
+		int(butterflyimg[0].height()*0.75) );
+	butterflyimg[2] = butterflyimg[0].smoothScale( int(butterflyimg[0].width()*0.5),
+		int(butterflyimg[0].height()*0.5) );
+	butterflyimg[3] = butterflyimg[0].smoothScale( int(butterflyimg[0].width()*0.25),
+		int(butterflyimg[0].height()*0.25) );
+    }
+    QCanvasPolygonalItem* i = new ImageItem(butterflyimg[rand()%4],&canvas);
+    i->move(rand()%(canvas.width()-butterflyimg->width()),
+	    rand()%(canvas.height()-butterflyimg->height()));
+    i->setZ(rand()%256+250);
+    i->show();
+}
+
+void Main::addLogo()
+{
+    if ( logo_fn.isEmpty() )
+	return;
+    if ( !logoimg ) {
+	logoimg = new QImage[4];
+	logoimg[0].load( logo_fn );
+	logoimg[1] = logoimg[0].smoothScale( int(logoimg[0].width()*0.75),
+		int(logoimg[0].height()*0.75) );
+	logoimg[2] = logoimg[0].smoothScale( int(logoimg[0].width()*0.5),
+		int(logoimg[0].height()*0.5) );
+	logoimg[3] = logoimg[0].smoothScale( int(logoimg[0].width()*0.25),
+		int(logoimg[0].height()*0.25) );
+    }
+    QCanvasPolygonalItem* i = new ImageItem(logoimg[rand()%4],&canvas);
+    i->move(rand()%(canvas.width()-logoimg->width()),
+	    rand()%(canvas.height()-logoimg->width()));
+    i->setZ(rand()%256+256);
+    i->show();
+}
+
+
+
+void Main::addCircle()
+{
+    QCanvasPolygonalItem* i = new QCanvasEllipse(50,50,&canvas);
+    i->setBrush( QColor(rand()%32*8,rand()%32*8,rand()%32*8) );
+    i->move(rand()%canvas.width(),rand()%canvas.height());
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addHexagon()
+{
+    QCanvasPolygon* i = new QCanvasPolygon(&canvas);
+    const int size = canvas.width() / 25;
+    QPointArray pa(6);
+    pa[0] = QPoint(2*size,0);
+    pa[1] = QPoint(size,-size*173/100);
+    pa[2] = QPoint(-size,-size*173/100);
+    pa[3] = QPoint(-2*size,0);
+    pa[4] = QPoint(-size,size*173/100);
+    pa[5] = QPoint(size,size*173/100);
+    i->setPoints(pa);
+    i->setBrush( QColor(rand()%32*8,rand()%32*8,rand()%32*8) );
+    i->move(rand()%canvas.width(),rand()%canvas.height());
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addPolygon()
+{
+    QCanvasPolygon* i = new QCanvasPolygon(&canvas);
+    const int size = canvas.width()/2;
+    QPointArray pa(6);
+    pa[0] = QPoint(0,0);
+    pa[1] = QPoint(size,size/5);
+    pa[2] = QPoint(size*4/5,size);
+    pa[3] = QPoint(size/6,size*5/4);
+    pa[4] = QPoint(size*3/4,size*3/4);
+    pa[5] = QPoint(size*3/4,size/4);
+    i->setPoints(pa);
+    i->setBrush( QColor(rand()%32*8,rand()%32*8,rand()%32*8) );
+    i->move(rand()%canvas.width(),rand()%canvas.height());
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addSpline()
+{
+    QCanvasSpline* i = new QCanvasSpline(&canvas);
+    const int size = canvas.width()/6;
+    QPointArray pa(12);
+    pa[0] = QPoint(0,0);
+    pa[1] = QPoint(size/2,0);
+    pa[2] = QPoint(size,size/2);
+    pa[3] = QPoint(size,size);
+    pa[4] = QPoint(size,size*3/2);
+    pa[5] = QPoint(size/2,size*2);
+    pa[6] = QPoint(0,size*2);
+    pa[7] = QPoint(-size/2,size*2);
+    pa[8] = QPoint(size/4,size*3/2);
+    pa[9] = QPoint(0,size);
+    pa[10]= QPoint(-size/4,size/2);
+    pa[11]= QPoint(-size/2,0);
+    i->setControlPoints(pa);
+    i->setBrush( QColor(rand()%32*8,rand()%32*8,rand()%32*8) );
+    i->move(rand()%canvas.width(),rand()%canvas.height());
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addText()
+{
+    QCanvasText* i = new QCanvasText(&canvas);
+    i->setText("QCanvasText");
+    i->move(rand()%canvas.width(),rand()%canvas.height());
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addLine()
+{
+    QCanvasLine* i = new QCanvasLine(&canvas);
+    i->setPoints( rand()%canvas.width(), rand()%canvas.height(),
+		  rand()%canvas.width(), rand()%canvas.height() );
+    i->setPen( QPen(QColor(rand()%32*8,rand()%32*8,rand()%32*8), 6) );
+    i->setZ(rand()%256);
+    i->show();
+}
+
+void Main::addMesh()
+{
+    int x0 = 0;
+    int y0 = 0;
+
+    if ( !tb ) tb = new QBrush( Qt::red );
+    if ( !tp ) tp = new QPen( Qt::black );
+
+    int nodecount = 0;
+
+    int w = canvas.width();
+    int h = canvas.height();
+
+    const int dist = 30;
+    int rows = h / dist;
+    int cols = w / dist;
+
+#ifndef QT_NO_PROGRESSDIALOG
+    QProgressDialog progress( "Creating mesh...", "Abort", rows,
+			      this, "progress", TRUE );
+#endif
+
+    QMemArray<NodeItem*> lastRow(cols);
+    for ( int j = 0; j < rows; j++ ) {
+	int n = j%2 ? cols-1 : cols;
+	NodeItem *prev = 0;
+	for ( int i = 0; i < n; i++ ) {
+	    NodeItem *el = new NodeItem( &canvas );
+	    nodecount++;
+	    int r = rand();
+	    int xrand = r %20;
+	    int yrand = (r/20) %20;
+	    el->move( xrand + x0 + i*dist + (j%2 ? dist/2 : 0 ),
+		      yrand + y0 + j*dist );
+
+	    if ( j > 0 ) {
+		if ( i < cols-1 )
+		    (new EdgeItem( lastRow[i], el, &canvas ))->show();
+		if ( j%2 )
+		    (new EdgeItem( lastRow[i+1], el, &canvas ))->show();
+		else if ( i > 0 )
+		    (new EdgeItem( lastRow[i-1], el, &canvas ))->show();
+	    }
+	    if ( prev ) {
+		(new EdgeItem( prev, el, &canvas ))->show();
+	    }
+	    if ( i > 0 ) lastRow[i-1] = prev;
+	    prev = el;
+	    el->show();
+	}
+	lastRow[n-1]=prev;
+#ifndef QT_NO_PROGRESSDIALOG
+	progress.setProgress( j );
+	if ( progress.wasCancelled() )
+	    break;
+#endif
+    }
+#ifndef QT_NO_PROGRESSDIALOG
+    progress.setProgress( rows );
+#endif
+    // qDebug( "%d nodes, %d edges", nodecount, EdgeItem::count() );
+}
+
+void Main::addRectangle()
+{
+    QCanvasPolygonalItem *i = new QCanvasRectangle( rand()%canvas.width(),rand()%canvas.height(),
+			    canvas.width()/5,canvas.width()/5,&canvas);
+    int z = rand()%256;
+    i->setBrush( QColor(z,z,z) );
+    i->setPen( QPen(QColor(rand()%32*8,rand()%32*8,rand()%32*8), 6) );
+    i->setZ(z);
+    i->show();
+}
Index: /trunk/examples/canvas/canvas.h
===================================================================
--- /trunk/examples/canvas/canvas.h	(revision 69)
+++ /trunk/examples/canvas/canvas.h	(revision 69)
@@ -0,0 +1,91 @@
+#ifndef EXAMPLE_H
+#define EXAMPLE_H
+
+#include <qpopupmenu.h>
+#include <qmainwindow.h>
+#include <qintdict.h>
+#include <qcanvas.h>
+
+class BouncyLogo : public QCanvasSprite {
+    void initPos();
+    void initSpeed();
+public:
+    BouncyLogo(QCanvas*);
+    void advance(int);
+    int rtti() const;
+};
+
+
+class FigureEditor : public QCanvasView {
+    Q_OBJECT
+
+public:
+    FigureEditor(QCanvas&, QWidget* parent=0, const char* name=0, WFlags f=0);
+    void clear();
+
+protected:
+    void contentsMousePressEvent(QMouseEvent*);
+    void contentsMouseMoveEvent(QMouseEvent*);
+
+signals:
+    void status(const QString&);
+
+private:
+    QCanvasItem* moving;
+    QPoint moving_start;
+};
+
+class Main : public QMainWindow {
+    Q_OBJECT
+
+public:
+    Main(QCanvas&, QWidget* parent=0, const char* name=0, WFlags f=0);
+    ~Main();
+
+public slots:
+    void help();
+
+private slots:
+    void aboutQt();
+    void newView();
+    void clear();
+    void init();
+
+    void addSprite();
+    void addCircle();
+    void addHexagon();
+    void addPolygon();
+    void addSpline();
+    void addText();
+    void addLine();
+    void addRectangle();
+    void addMesh();
+    void addLogo();
+    void addButterfly();
+
+    void enlarge();
+    void shrink();
+    void rotateClockwise();
+    void rotateCounterClockwise();
+    void zoomIn();
+    void zoomOut();
+    void mirror();
+    void moveL();
+    void moveR();
+    void moveU();
+    void moveD();
+
+    void print();
+
+    void toggleDoubleBuffer();
+
+private:
+    QCanvas& canvas;
+    FigureEditor *editor;
+
+    QPopupMenu* options;
+    QPrinter* printer;
+    int dbf_id;
+};
+
+#endif
Index: /trunk/examples/canvas/canvas.pro
===================================================================
--- /trunk/examples/canvas/canvas.pro	(revision 69)
+++ /trunk/examples/canvas/canvas.pro	(revision 69)
@@ -0,0 +1,10 @@
+TEMPLATE	= app
+TARGET		= canvas
+
+CONFIG		+= qt warn_on release
+DEPENDPATH	= ../../include
+
+REQUIRES	= canvas full-config
+
+HEADERS		= canvas.h
+SOURCES		= canvas.cpp main.cpp
Index: /trunk/examples/canvas/main.cpp
===================================================================
--- /trunk/examples/canvas/main.cpp	(revision 69)
+++ /trunk/examples/canvas/main.cpp	(revision 69)
@@ -0,0 +1,43 @@
+#include <qstatusbar.h>
+#include <qmessagebox.h>
+#include <qmenubar.h>
+#include <qapplication.h>
+#include <qimage.h>
+
+#include "canvas.h"
+
+#include <stdlib.h>
+
+extern QString butterfly_fn;
+extern QString logo_fn;
+
+int main(int argc, char** argv)
+{
+    QApplication app(argc,argv);
+    
+    if ( argc > 1 )
+	butterfly_fn = argv[1];
+    else
+	butterfly_fn = "butterfly.png";
+    
+    if ( argc > 2 )
+	logo_fn = argv[2];
+    else
+	logo_fn = "qtlogo.png";
+    
+    QCanvas canvas(800,600);
+    canvas.setAdvancePeriod(30);
+    Main m(canvas);
+    m.resize(m.sizeHint());
+    m.setCaption("Qt Example - Canvas");
+    if ( QApplication::desktop()->width() > m.width() + 10
+	&& QApplication::desktop()->height() > m.height() +30 )
+	m.show();
+    else
+	m.showMaximized();
+    
+    QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) );
+    
+    return app.exec();
+}
+
Index: /trunk/examples/canvas/makeimg.cpp
===================================================================
--- /trunk/examples/canvas/makeimg.cpp	(revision 69)
+++ /trunk/examples/canvas/makeimg.cpp	(revision 69)
@@ -0,0 +1,92 @@
+#include <qimage.h>
+#include <qcolor.h>
+
+static inline int blendComponent( int v, int av, int s, int as )
+{
+    //shadow gets a color inversely proportional to the
+    //alpha value
+    s = (s*(255-as))/255;
+    //then do standard blending
+    return as*s + av*v -(av*as*s)/255;  
+}
+    
+static inline QRgb blendShade( QRgb v, QRgb s )
+{
+    //pick a number: shadow is 1/3 of object
+    int as = qAlpha(s)/3; 
+    int av = qAlpha(v);
+    if ( as == 0 || av == 255 )
+	return v;
+
+    int a = as + av -(as*av)/255;
+
+    
+    int r = blendComponent( qRed(v),av, qRed(s), as)/a;
+    int g = blendComponent( qGreen(v),av, qGreen(s), as)/a;
+    int b = blendComponent( qBlue(v),av, qBlue(s), as)/a;
+
+    return qRgba(r,g,b,a);
+}
+
+int main( int*, char**)
+{
+    QImage *img;
+
+    img = new QImage( "in.png" );
+    int w,h;
+    int y;
+    img->setAlphaBuffer( TRUE );
+    *img = img->convertDepth( 32 );
+    w = img->width();
+    h = img->height();
+#if 0
+    for ( y = 0; y < h; y ++ ) {
+	uint *line = (uint*)img->scanLine( y );
+	for ( int x = 0; x < w; x++ ) {
+	    uint pixel = line[x];
+	    int r = qRed(pixel);
+	    int g = qGreen(pixel);
+	    int b = qBlue(pixel);
+	    int min = QMIN( r, QMIN( g, b ) );
+	    int max = QMAX( r, QMAX( g, b ) );
+	    r -= min;
+	    g -= min;
+	    b -= min;
+	    if ( max !=min ) {
+		r = (r*255)/(max-min);
+		g = (g*255)/(max-min);
+		b = (b*255)/(max-min);
+	    }
+	    int a = 255-min;
+	    a -=  (max-min)/3; //hack more transparency for colors.
+	    line[x] = qRgba( r, g, b, a );
+	}
+    }
+#endif    
+    *img = img->smoothScale( w/2, h/2 );
+
+    qDebug( "saving out.png");
+    img->save( "out.png", "PNG" );
+    
+    w = img->width();
+    h = img->height();
+    
+    QImage *img2 = new QImage( w, h, 32 );
+    img2->setAlphaBuffer( TRUE );
+    for ( y = 0; y < h; y++ ) {
+	for ( int x = 0; x < w; x++ ) {
+	    QRgb shader = img->pixel( x, y );
+
+	    int as = qAlpha(shader)/3;
+
+	    int r = (qRed(shader)*(255-as))/255;
+	    int g = (qGreen(shader)*(255-as))/255;
+	    int b = (qBlue(shader)*(255-as))/255;
+
+	    img2->setPixel( x, y, qRgba(r,g,b,as) ); 
+	}
+    }
+
+    img2->save( "outshade.png", "PNG" );
+
+}
Index: /trunk/examples/canvas/qt-trans.xpm
===================================================================
--- /trunk/examples/canvas/qt-trans.xpm	(revision 69)
+++ /trunk/examples/canvas/qt-trans.xpm	(revision 69)
@@ -0,0 +1,54 @@
+/* XPM */
+static char *qtlogo_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"54 34 13 1",
+/* colors */
+"  c #000000",
+". c #999999",
+"X c #333366",
+"o c #6666CC",
+"O c #333333",
+"@ c #666699",
+"# c #000066",
+"$ c #666666",
+"% c #3333CC",
+"& c #000033",
+"* c #9999CC",
+"= c #333399",
+"+ c None",
+/* pixels */
+"+++++++++++++++++++.$OOO$.++++++++++++++++++++++++++++",
+"+++++++++++++++++$         O.+++++++++++++++++++++++++",
+"+++++++++++++++.O            $++++++++++++++++++++++++",
+"++++++++++++++.    $.++.$     O+++++++++++++++++++++++",
+"+++++++++++++.   O.+++++++$    O++++++++++++++++++++++",
+"+++++++++++++O   ++++++++++$    $+++++++++++++++++++++",
+"++++++++++++$   .+++++++++++O    .++++++++++++++++++++",
+"+++++++++++.   O+++++++++++++    O++++++.+++++++++++++",
+"+++++++++++$   .+++++++++++++$    .+++.O +++++++++++++",
+"+++++++++++    +++++++++++++++    O+++.  +++++++++++++",
+"++++++++++.  &Xoooo*++++++++++$    +++.  +++++++++++++",
+"++++++++++@=%%%%%%%%%%*+++++++.    .++.  +++++++++++++",
+"+++++++**oooooo**++*o%%%%o+++++    $++O  +++++++++++++",
+"+++++*****$OOX@oooo*++*%%%%%*++O   $+.   OOO$++++++++*",
+"++..+.++....$O$+*ooooo*+*o%%%%%O   O+$   $$O.++++++**+",
+"***+*+++++$$....+++*oooo**+*o%%#   O++O  ++++++***o*++",
+"*+++++++++O  $.....++**oooo**+*X   &o*O  ++++*ooo*++++",
+"++++++++++$   O++.....++**oooo*X   &%%&  ..*o%%*++++++",
+"++++++++++$    ++++.....+++**ooO   $*o&  @oo*+++++++++",
+"++++++++++.    .++++++.....+++*O   Xo*O  .++++++++++++",
+"+++++++++++    O+++++++++......    .++O  +++++++++++++",
+"+++++++++++O    +++.$$$.++++++.   O+++O  +++++++++++++",
+"+++++++++++.    $$OO    O.++++O   .+++O  +++++++++++++",
+"++++++++++++O    .+++.O   $++.   O++++O  +++++++++++++",
+"++++++++++++.    O+++++O   $+O   +++++O  +++++++++++++",
+"+++++++++++++.    O+++++O   O   .+++++O  .++++++++++++",
+"++++++++++++++$     .++++O     .++++.+$  O+.$.++++++++",
+"+++++++++++++++.      O$$O    .+++++...      +++++++++",
+"+++++++++++++++++$            O+++++$$+.O O$.+++++++++",
+"+++++++++++++++++++$OO  O$.O   O.++. .++++++++++++++++",
+"+++++++++++++++++++++++++++.     OO  .++++++++++++++++",
+"++++++++++++++++++++++++++++.       O+++++++++++++++++",
+"+++++++++++++++++++++++++++++.      .+++++++++++++++++",
+"++++++++++++++++++++++++++++++.O  O.++++++++++++++++++"
+};
Index: /trunk/include/qcanvas.h
===================================================================
--- /trunk/include/qcanvas.h	(revision 69)
+++ /trunk/include/qcanvas.h	(revision 69)
@@ -0,0 +1,814 @@
+/**********************************************************************
+** $Id$
+**
+** Definition of QCanvas classes
+**
+** Created : 991211
+**
+** Copyright (C) 1999-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the canvas module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QCANVAS_H
+#define QCANVAS_H
+
+#ifndef QT_H
+#include "qscrollview.h"
+#include "qpixmap.h"
+#include "qptrlist.h"
+#include "qbrush.h"
+#include "qpen.h"
+#include "qvaluelist.h"
+#include "qpointarray.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_CANVAS ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_CANVAS )
+#define QM_EXPORT_CANVAS
+#define QM_TEMPLATE_EXTERN_CANVAS
+#else
+#define QM_EXPORT_CANVAS Q_EXPORT
+#define QM_TEMPLATE_EXTERN_CANVAS Q_TEMPLATE_EXTERN
+#endif
+
+#ifndef QT_NO_CANVAS
+
+
+class QCanvasSprite;
+class QCanvasPolygonalItem;
+class QCanvasRectangle;
+class QCanvasPolygon;
+class QCanvasEllipse;
+class QCanvasText;
+class QCanvasLine;
+class QCanvasChunk;
+class QCanvas;
+class QCanvasItem;
+class QCanvasView;
+class QCanvasPixmap;
+
+#if defined(Q_TEMPLATEDLL) && ( !defined(Q_CC_BOR) || !defined(QT_MAKEDLL) || defined(Q_EXPORT_TEMPLATES) )
+// MOC_SKIP_BEGIN
+QM_TEMPLATE_EXTERN_CANVAS template class QM_EXPORT_CANVAS QValueListIterator< QCanvasItem* >;
+QM_TEMPLATE_EXTERN_CANVAS template class QM_EXPORT_CANVAS QValueList< QCanvasItem* >;
+// MOC_SKIP_END
+#endif
+
+class QM_EXPORT_CANVAS QCanvasItemList : public QValueList<QCanvasItem*> {
+public:
+    void sort();
+    void drawUnique( QPainter& painter );
+    QCanvasItemList operator+(const QCanvasItemList &l) const;
+};
+
+
+class QCanvasItemExtra;
+
+class QM_EXPORT_CANVAS QCanvasItem : public Qt
+{
+public:
+    QCanvasItem(QCanvas* canvas);
+    virtual ~QCanvasItem();
+
+    double x() const
+	{ return myx; }
+    double y() const
+	{ return myy; }
+    double z() const
+	{ return myz; } // (depth)
+
+    virtual void moveBy(double dx, double dy);
+    void move(double x, double y);
+    void setX(double a) { move(a,y()); }
+    void setY(double a) { move(x(),a); }
+    void setZ(double a) { myz=a; changeChunks(); }
+
+    bool animated() const;
+    virtual void setAnimated(bool y);
+    virtual void setVelocity( double vx, double vy);
+    void setXVelocity( double vx ) { setVelocity(vx,yVelocity()); }
+    void setYVelocity( double vy ) { setVelocity(xVelocity(),vy); }
+    double xVelocity() const;
+    double yVelocity() const;
+    virtual void advance(int stage);
+
+    virtual bool collidesWith( const QCanvasItem* ) const=0;
+
+    QCanvasItemList collisions(bool exact /* NO DEFAULT */ ) const;
+
+    virtual void setCanvas(QCanvas*);
+
+    virtual void draw(QPainter&)=0;
+
+    void show();
+    void hide();
+
+    virtual void setVisible(bool yes);
+    bool isVisible() const
+	{ return (bool)vis; }
+    virtual void setSelected(bool yes);
+    bool isSelected() const
+	{ return (bool)sel; }
+    virtual void setEnabled(bool yes);
+    bool isEnabled() const
+	{ return (bool)ena; }
+    virtual void setActive(bool yes);
+    bool isActive() const
+	{ return (bool)act; }
+#ifndef QT_NO_COMPAT
+    bool visible() const
+	{ return (bool)vis; }
+    bool selected() const
+	{ return (bool)sel; }
+    bool enabled() const
+	{ return (bool)ena; }
+    bool active() const
+	{ return (bool)act; }
+#endif
+
+    enum RttiValues {
+	Rtti_Item = 0,
+	Rtti_Sprite = 1,
+	Rtti_PolygonalItem = 2,
+	Rtti_Text = 3,
+	Rtti_Polygon = 4,
+	Rtti_Rectangle = 5,
+	Rtti_Ellipse = 6,
+	Rtti_Line = 7,
+	Rtti_Spline = 8
+    };
+
+    virtual int rtti() const;
+    static int RTTI;
+
+    virtual QRect boundingRect() const=0;
+    virtual QRect boundingRectAdvanced() const;
+
+    QCanvas* canvas() const
+	{ return cnv; }
+
+protected:
+    void update() { changeChunks(); }
+
+private:
+    // For friendly subclasses...
+
+    friend class QCanvasPolygonalItem;
+    friend class QCanvasSprite;
+    friend class QCanvasRectangle;
+    friend class QCanvasPolygon;
+    friend class QCanvasEllipse;
+    friend class QCanvasText;
+    friend class QCanvasLine;
+
+    virtual QPointArray chunks() const;
+    virtual void addToChunks();
+    virtual void removeFromChunks();
+    virtual void changeChunks();
+    virtual bool collidesWith( const QCanvasSprite*,
+			       const QCanvasPolygonalItem*,
+			       const QCanvasRectangle*,
+			       const QCanvasEllipse*,
+			       const QCanvasText* ) const = 0;
+    // End of friend stuff
+
+    QCanvas* cnv;
+    static QCanvas* current_canvas;
+    double myx,myy,myz;
+    QCanvasItemExtra *ext;
+    QCanvasItemExtra& extra();
+    uint ani:1;
+    uint vis:1;
+    uint val:1;
+    uint sel:1;
+    uint ena:1;
+    uint act:1;
+};
+
+
+class QCanvasData;
+
+class QM_EXPORT_CANVAS QCanvas : public QObject
+{
+    Q_OBJECT
+public:
+    QCanvas( QObject* parent = 0, const char* name = 0 );
+    QCanvas(int w, int h);
+    QCanvas( QPixmap p, int h, int v, int tilewidth, int tileheight );
+
+    virtual ~QCanvas();
+
+    virtual void setTiles( QPixmap tiles, int h, int v,
+			   int tilewidth, int tileheight );
+    virtual void setBackgroundPixmap( const QPixmap& p );
+    QPixmap backgroundPixmap() const;
+
+    virtual void setBackgroundColor( const QColor& c );
+    QColor backgroundColor() const;
+
+    virtual void setTile( int x, int y, int tilenum );
+    int tile( int x, int y ) const
+	{ return grid[x+y*htiles]; }
+
+    int tilesHorizontally() const
+	{ return htiles; }
+    int tilesVertically() const
+	{ return vtiles; }
+
+    int tileWidth() const
+	{ return tilew; }
+    int tileHeight() const
+	{ return tileh; }
+
+    virtual void resize(int width, int height);
+    int width() const
+	{ return awidth; }
+    int height() const
+	{ return aheight; }
+    QSize size() const
+	{ return QSize(awidth,aheight); }
+    QRect rect() const
+	{ return QRect( 0, 0, awidth, aheight ); }
+    bool onCanvas( int x, int y ) const
+	{ return x>=0 && y>=0 && x<awidth && y<aheight; }
+    bool onCanvas( const QPoint& p ) const
+	{ return onCanvas(p.x(),p.y()); }
+    bool validChunk( int x, int y ) const
+	{ return x>=0 && y>=0 && x<chwidth && y<chheight; }
+    bool validChunk( const QPoint& p ) const
+	{ return validChunk(p.x(),p.y()); }
+
+    int chunkSize() const
+	{ return chunksize; }
+    virtual void retune(int chunksize, int maxclusters=100);
+
+    bool sameChunk(int x1, int y1, int x2, int y2) const
+	{ return x1/chunksize==x2/chunksize && y1/chunksize==y2/chunksize; }
+    virtual void setChangedChunk(int i, int j);
+    virtual void setChangedChunkContaining(int x, int y);
+    virtual void setAllChanged();
+    virtual void setChanged(const QRect& area);
+    virtual void setUnchanged(const QRect& area);
+
+    // These call setChangedChunk.
+    void addItemToChunk(QCanvasItem*, int i, int j);
+    void removeItemFromChunk(QCanvasItem*, int i, int j);
+    void addItemToChunkContaining(QCanvasItem*, int x, int y);
+    void removeItemFromChunkContaining(QCanvasItem*, int x, int y);
+
+    QCanvasItemList allItems();
+    QCanvasItemList collisions( const QPoint&) const;
+    QCanvasItemList collisions( const QRect&) const;
+    QCanvasItemList collisions( const QPointArray& pa, const QCanvasItem* item,
+				bool exact) const;
+
+    void drawArea(const QRect&, QPainter* p, bool double_buffer=FALSE);
+
+    // These are for QCanvasView to call
+    virtual void addView(QCanvasView*);
+    virtual void removeView(QCanvasView*);
+    void drawCanvasArea(const QRect&, QPainter* p=0, bool double_buffer=TRUE);
+    void drawViewArea( QCanvasView* view, QPainter* p, const QRect& r, bool dbuf );
+
+    // These are for QCanvasItem to call
+    virtual void addItem(QCanvasItem*);
+    virtual void addAnimation(QCanvasItem*);
+    virtual void removeItem(QCanvasItem*);
+    virtual void removeAnimation(QCanvasItem*);
+
+    virtual void setAdvancePeriod(int ms);
+    virtual void setUpdatePeriod(int ms);
+
+    virtual void setDoubleBuffering(bool y);
+
+signals:
+    void resized();
+
+public slots:
+    virtual void advance();
+    virtual void update();
+
+protected:
+    virtual void drawBackground(QPainter&, const QRect& area);
+    virtual void drawForeground(QPainter&, const QRect& area);
+
+private:
+    void init(int w, int h, int chunksze=16, int maxclust=100);
+
+    QCanvasChunk& chunk(int i, int j) const;
+    QCanvasChunk& chunkContaining(int x, int y) const;
+
+    QRect changeBounds(const QRect& inarea);
+    void drawChanges(const QRect& inarea);
+
+    void ensureOffScrSize( int osw, int osh );
+    QPixmap offscr;
+    int awidth,aheight;
+    int chunksize;
+    int maxclusters;
+    int chwidth,chheight;
+    QCanvasChunk* chunks;
+
+    QCanvasData* d;
+
+    void initTiles(QPixmap p, int h, int v, int tilewidth, int tileheight);
+    ushort *grid;
+    ushort htiles;
+    ushort vtiles;
+    ushort tilew;
+    ushort tileh;
+    bool oneone;
+    QPixmap pm;
+    QTimer* update_timer;
+    QColor bgcolor;
+    bool debug_redraw_areas;
+    bool dblbuf;
+
+    friend void qt_unview(QCanvas* c);
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+    QCanvas( const QCanvas & );
+    QCanvas &operator=( const QCanvas & );
+#endif
+};
+
+class QCanvasViewData;
+
+class QM_EXPORT_CANVAS QCanvasView : public QScrollView
+{
+    Q_OBJECT
+public:
+
+    QCanvasView(QWidget* parent=0, const char* name=0, WFlags f=0);
+    QCanvasView(QCanvas* viewing, QWidget* parent=0, const char* name=0, WFlags f=0);
+    ~QCanvasView();
+
+    QCanvas* canvas() const
+	{ return viewing; }
+    void setCanvas(QCanvas* v);
+
+    const QWMatrix &worldMatrix() const;
+    const QWMatrix &inverseWorldMatrix() const;
+    bool setWorldMatrix( const QWMatrix & );
+
+protected:
+    void drawContents( QPainter*, int cx, int cy, int cw, int ch );
+    QSize sizeHint() const;
+
+private:
+    void drawContents( QPainter* );
+    QCanvas* viewing;
+    QCanvasViewData* d;
+    friend void qt_unview(QCanvas* c);
+
+private slots:
+    void cMoving(int,int);
+    void updateContentsSize();
+
+private:
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+    QCanvasView( const QCanvasView & );
+    QCanvasView &operator=( const QCanvasView & );
+#endif
+};
+
+
+class QM_EXPORT_CANVAS QCanvasPixmap : public QPixmap
+{
+public:
+#ifndef QT_NO_IMAGEIO
+    QCanvasPixmap(const QString& datafilename);
+#endif
+    QCanvasPixmap(const QImage& image);
+    QCanvasPixmap(const QPixmap&, const QPoint& hotspot);
+    ~QCanvasPixmap();
+
+    int offsetX() const
+	{ return hotx; }
+    int offsetY() const
+	{ return hoty; }
+    void setOffset(int x, int y) { hotx = x; hoty = y; }
+
+private:
+#if defined(Q_DISABLE_COPY)
+    QCanvasPixmap( const QCanvasPixmap & );
+    QCanvasPixmap &operator=( const QCanvasPixmap & );
+#endif
+    void init(const QImage&);
+    void init(const QPixmap& pixmap, int hx, int hy);
+
+    friend class QCanvasSprite;
+    friend class QCanvasPixmapArray;
+    friend bool qt_testCollision(const QCanvasSprite* s1, const QCanvasSprite* s2);
+
+    int hotx,hoty;
+
+    QImage* collision_mask;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasPixmapArray
+{
+public:
+    QCanvasPixmapArray();
+#ifndef QT_NO_IMAGEIO
+    QCanvasPixmapArray(const QString& datafilenamepattern, int framecount=0);
+#endif
+    // this form is deprecated
+    QCanvasPixmapArray(QPtrList<QPixmap>, QPtrList<QPoint> hotspots);
+
+    QCanvasPixmapArray(QValueList<QPixmap>, QPointArray hotspots = QPointArray() );
+    ~QCanvasPixmapArray();
+
+#ifndef QT_NO_IMAGEIO
+    bool readPixmaps(const QString& datafilenamepattern, int framecount=0);
+    bool readCollisionMasks(const QString& filenamepattern);
+#endif
+
+    // deprecated
+    bool operator!(); // Failure check.
+    bool isValid() const;
+
+    QCanvasPixmap* image(int i) const
+	{ return img ? img[i] : 0; }
+    void setImage(int i, QCanvasPixmap* p);
+    uint count() const
+	{ return (uint)framecount; }
+
+private:
+#if defined(Q_DISABLE_COPY)
+    QCanvasPixmapArray( const QCanvasPixmapArray & );
+    QCanvasPixmapArray &operator=( const QCanvasPixmapArray & );
+#endif
+#ifndef QT_NO_IMAGEIO
+    bool readPixmaps(const QString& datafilenamepattern, int framecount, bool maskonly);
+#endif
+
+    void reset();
+    int framecount;
+    QCanvasPixmap** img;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasSprite : public QCanvasItem
+{
+public:
+    QCanvasSprite(QCanvasPixmapArray* array, QCanvas* canvas);
+
+    void setSequence(QCanvasPixmapArray* seq);
+
+    virtual ~QCanvasSprite();
+
+    void move(double x, double y);
+    virtual void move(double x, double y, int frame);
+    void setFrame(int);
+    enum FrameAnimationType { Cycle, Oscillate };
+    virtual void setFrameAnimation(FrameAnimationType=Cycle, int step=1, int state=0);
+    int frame() const
+	{ return frm; }
+    int frameCount() const
+	{ return images->count(); }
+
+    int rtti() const;
+    static int RTTI;
+
+    bool collidesWith( const QCanvasItem* ) const;
+
+    QRect boundingRect() const;
+
+    // is there a reason for these to be protected? Lars
+//protected:
+
+    int width() const;
+    int height() const;
+
+    int leftEdge() const;
+    int topEdge() const;
+    int rightEdge() const;
+    int bottomEdge() const;
+
+    int leftEdge(int nx) const;
+    int topEdge(int ny) const;
+    int rightEdge(int nx) const;
+    int bottomEdge(int ny) const;
+    QCanvasPixmap* image() const
+	{ return images->image(frm); }
+    virtual QCanvasPixmap* imageAdvanced() const;
+    QCanvasPixmap* image(int f) const
+	{ return images->image(f); }
+    virtual void advance(int stage);
+
+public:
+    void draw(QPainter& painter);
+
+private:
+#if defined(Q_DISABLE_COPY)
+    QCanvasSprite( const QCanvasSprite & );
+    QCanvasSprite &operator=( const QCanvasSprite & );
+#endif
+    void addToChunks();
+    void removeFromChunks();
+    void changeChunks();
+
+    int frm;
+    ushort anim_val;
+    uint anim_state:2;
+    uint anim_type:14;
+    bool collidesWith( const QCanvasSprite*,
+		       const QCanvasPolygonalItem*,
+		       const QCanvasRectangle*,
+		       const QCanvasEllipse*,
+		       const QCanvasText* ) const;
+
+    friend bool qt_testCollision( const QCanvasSprite* s1,
+				  const QCanvasSprite* s2 );
+
+    QCanvasPixmapArray* images;
+};
+
+class QPolygonalProcessor;
+
+class QM_EXPORT_CANVAS QCanvasPolygonalItem : public QCanvasItem
+{
+public:
+    QCanvasPolygonalItem(QCanvas* canvas);
+    virtual ~QCanvasPolygonalItem();
+
+    bool collidesWith( const QCanvasItem* ) const;
+
+    virtual void setPen(QPen p);
+    virtual void setBrush(QBrush b);
+
+    QPen pen() const
+	{ return pn; }
+    QBrush brush() const
+	{ return br; }
+
+    virtual QPointArray areaPoints() const=0;
+    virtual QPointArray areaPointsAdvanced() const;
+    QRect boundingRect() const;
+
+    int rtti() const;
+    static int RTTI;
+
+protected:
+    void draw(QPainter &);
+    virtual void drawShape(QPainter &) = 0;
+
+    bool winding() const;
+    void setWinding(bool);
+
+    void invalidate();
+    bool isValid() const
+	{ return (bool)val; }
+
+private:
+    void scanPolygon( const QPointArray& pa, int winding,
+		      QPolygonalProcessor& process ) const;
+    QPointArray chunks() const;
+
+    bool collidesWith( const QCanvasSprite*,
+		       const QCanvasPolygonalItem*,
+		       const QCanvasRectangle*,
+		       const QCanvasEllipse*,
+		       const QCanvasText* ) const;
+
+    QBrush br;
+    QPen pn;
+    uint wind:1;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasRectangle : public QCanvasPolygonalItem
+{
+public:
+    QCanvasRectangle(QCanvas* canvas);
+    QCanvasRectangle(const QRect&, QCanvas* canvas);
+    QCanvasRectangle(int x, int y, int width, int height, QCanvas* canvas);
+
+    ~QCanvasRectangle();
+
+    int width() const;
+    int height() const;
+    void setSize(int w, int h);
+    QSize size() const
+	{ return QSize(w,h); }
+    QPointArray areaPoints() const;
+    QRect rect() const
+	{ return QRect(int(x()),int(y()),w,h); }
+
+    bool collidesWith( const QCanvasItem* ) const;
+
+    int rtti() const;
+    static int RTTI;
+
+protected:
+    void drawShape(QPainter &);
+    QPointArray chunks() const;
+
+private:
+    bool collidesWith(   const QCanvasSprite*,
+			 const QCanvasPolygonalItem*,
+			 const QCanvasRectangle*,
+			 const QCanvasEllipse*,
+			 const QCanvasText* ) const;
+
+    int w, h;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasPolygon : public QCanvasPolygonalItem
+{
+public:
+    QCanvasPolygon(QCanvas* canvas);
+    ~QCanvasPolygon();
+    void setPoints(QPointArray);
+    QPointArray points() const;
+    void moveBy(double dx, double dy);
+
+    QPointArray areaPoints() const;
+
+    int rtti() const;
+    static int RTTI;
+
+protected:
+    void drawShape(QPainter &);
+    QPointArray poly;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasSpline : public QCanvasPolygon
+{
+public:
+    QCanvasSpline(QCanvas* canvas);
+    ~QCanvasSpline();
+
+    void setControlPoints(QPointArray, bool closed=TRUE);
+    QPointArray controlPoints() const;
+    bool closed() const;
+
+    int rtti() const;
+    static int RTTI;
+
+private:
+    void recalcPoly();
+    QPointArray bez;
+    bool cl;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasLine : public QCanvasPolygonalItem
+{
+public:
+    QCanvasLine(QCanvas* canvas);
+    ~QCanvasLine();
+    void setPoints(int x1, int y1, int x2, int y2);
+
+    QPoint startPoint() const
+	{ return QPoint(x1,y1); }
+    QPoint endPoint() const
+	{ return QPoint(x2,y2); }
+
+    int rtti() const;
+    static int RTTI;
+
+    void setPen(QPen p);
+    void moveBy(double dx, double dy);
+
+protected:
+    void drawShape(QPainter &);
+    QPointArray areaPoints() const;
+
+private:
+    int x1,y1,x2,y2;
+};
+
+
+class QM_EXPORT_CANVAS QCanvasEllipse : public QCanvasPolygonalItem
+{
+
+public:
+    QCanvasEllipse( QCanvas* canvas );
+    QCanvasEllipse( int width, int height, QCanvas* canvas );
+    QCanvasEllipse( int width, int height, int startangle, int angle,
+		    QCanvas* canvas );
+
+    ~QCanvasEllipse();
+
+    int width() const;
+    int height() const;
+    void setSize(int w, int h);
+    void setAngles(int start, int length);
+    int angleStart() const
+	{ return a1; }
+    int angleLength() const
+	{ return a2; }
+    QPointArray areaPoints() const;
+
+    bool collidesWith( const QCanvasItem* ) const;
+
+    int rtti() const;
+    static int RTTI;
+
+protected:
+    void drawShape(QPainter &);
+
+private:
+    bool collidesWith( const QCanvasSprite*,
+		       const QCanvasPolygonalItem*,
+		       const QCanvasRectangle*,
+		       const QCanvasEllipse*,
+		       const QCanvasText* ) const;
+    int w, h;
+    int a1, a2;
+};
+
+
+class QCanvasTextExtra;
+
+class QM_EXPORT_CANVAS QCanvasText : public QCanvasItem
+{
+public:
+    QCanvasText(QCanvas* canvas);
+    QCanvasText(const QString&, QCanvas* canvas);
+    QCanvasText(const QString&, QFont, QCanvas* canvas);
+
+    virtual ~QCanvasText();
+
+    void setText( const QString& );
+    void setFont( const QFont& );
+    void setColor( const QColor& );
+    QString text() const;
+    QFont font() const;
+    QColor color() const;
+
+    void moveBy(double dx, double dy);
+
+    int textFlags() const
+	{ return flags; }
+    void setTextFlags(int);
+
+    QRect boundingRect() const;
+
+    bool collidesWith( const QCanvasItem* ) const;
+
+    int rtti() const;
+    static int RTTI;
+
+protected:
+    virtual void draw(QPainter&);
+
+private:
+#if defined(Q_DISABLE_COPY)
+    QCanvasText( const QCanvasText & );
+    QCanvasText &operator=( const QCanvasText & );
+#endif
+    void addToChunks();
+    void removeFromChunks();
+    void changeChunks();
+
+    void setRect();
+    QRect brect;
+    QString txt;
+    int flags;
+    QFont fnt;
+    QColor col;
+    QCanvasTextExtra* extra;
+
+    bool collidesWith(   const QCanvasSprite*,
+			 const QCanvasPolygonalItem*,
+			 const QCanvasRectangle*,
+			 const QCanvasEllipse*,
+			 const QCanvasText* ) const;
+};
+
+#define Q_DEFINED_QCANVAS
+#include "qwinexport.h"
+#endif // QT_NO_CANVAS
+
+#endif // QCANVAS_H
Index: /trunk/include/qpolygonscanner.h
===================================================================
--- /trunk/include/qpolygonscanner.h	(revision 69)
+++ /trunk/include/qpolygonscanner.h	(revision 69)
@@ -0,0 +1,58 @@
+/****************************************************************************
+** $Id$
+**
+** Definition of QPolygonScanner class
+**
+** Created : 000120
+**
+** Copyright (C) 1999-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QPOLYGONSCANNER_H
+#define QPOLYGONSCANNER_H
+
+#ifndef QT_H
+#include "qglobal.h"
+#endif // QT_H
+
+class QPointArray;
+class QPoint;
+
+class Q_EXPORT QPolygonScanner {
+public:
+    // BIC: fix for 3.0
+    void scan( const QPointArray& pa, bool winding, int index=0, int npoints=-1 );
+    void scan( const QPointArray& pa, bool winding, int index, int npoints, bool stitchable );
+    enum Edge { Left=1, Right=2, Top=4, Bottom=8 };
+    void scan( const QPointArray& pa, bool winding, int index, int npoints, Edge edges );
+    virtual void processSpans( int n, QPoint* point, int* width )=0;
+};
+
+#endif // QPOLYGONSCANNER_H
Index: /trunk/include/qsession.h
===================================================================
--- /trunk/include/qsession.h	(revision 69)
+++ /trunk/include/qsession.h	(revision 69)
@@ -0,0 +1,44 @@
+/****************************************************************************
+** $Id$
+**
+** Definition of QSession class
+**
+** Created : 990510
+**
+** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QSESSION_H
+#define QSESSION_H
+
+#ifndef QT_H
+#endif // QT_H
+
+#endif
Index: /trunk/src/canvas/qcanvas.cpp
===================================================================
--- /trunk/src/canvas/qcanvas.cpp	(revision 69)
+++ /trunk/src/canvas/qcanvas.cpp	(revision 69)
@@ -0,0 +1,5374 @@
+/**********************************************************************
+** $Id$
+**
+** Implementation of QCanvas and associated classes
+**
+** Created : 991211
+**
+** Copyright (C) 1999-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the canvas module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qcanvas.h"
+#ifndef QT_NO_CANVAS
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qptrdict.h"
+#include "qpainter.h"
+#include "qpolygonscanner.h"
+#include "qtimer.h"
+#include "qtl.h"
+
+#include <stdlib.h>
+
+class QCanvasData {
+public:
+    QCanvasData() :
+	itemDict(1013), animDict(503)
+    {
+    }
+
+    QPtrList<QCanvasView> viewList;
+    QPtrDict<void> itemDict;
+    QPtrDict<void> animDict;
+};
+
+class QCanvasViewData {
+public:
+    QCanvasViewData() : repaint_from_moving( FALSE ) {}
+#ifndef QT_NO_TRANSFORMATIONS
+    QWMatrix xform;
+    QWMatrix ixform;
+#endif
+    bool repaint_from_moving;
+};
+
+// clusterizer
+
+class QCanvasClusterizer {
+public:
+    QCanvasClusterizer(int maxclusters);
+    ~QCanvasClusterizer();
+
+    void add(int x, int y); // 1x1 rectangle (point)
+    void add(int x, int y, int w, int h);
+    void add(const QRect& rect);
+
+    void clear();
+    int clusters() { return count; }
+    const QRect& operator[](int i);
+
+private:
+    QRect* cluster;
+    int count;
+    const int maxcl;
+};
+
+static
+void include(QRect& r, const QRect& rect)
+{
+    if (rect.left()<r.left()) {
+	    r.setLeft(rect.left());
+    }
+    if (rect.right()>r.right()) {
+	    r.setRight(rect.right());
+    }
+    if (rect.top()<r.top()) {
+	    r.setTop(rect.top());
+    }
+    if (rect.bottom()>r.bottom()) {
+	    r.setBottom(rect.bottom());
+    }
+}
+
+/*
+A QCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
+by a merging heuristic.
+*/
+QCanvasClusterizer::QCanvasClusterizer(int maxclusters) :
+    cluster(new QRect[maxclusters]),
+    count(0),
+    maxcl(maxclusters)
+{ }
+
+QCanvasClusterizer::~QCanvasClusterizer()
+{
+    delete [] cluster;
+}
+
+void QCanvasClusterizer::clear()
+{
+    count=0;
+}
+
+void QCanvasClusterizer::add(int x, int y)
+{
+    add(QRect(x,y,1,1));
+}
+
+void QCanvasClusterizer::add(int x, int y, int w, int h)
+{
+    add(QRect(x,y,w,h));
+}
+
+void QCanvasClusterizer::add(const QRect& rect)
+{
+    QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2);
+
+    //assert(rect.width()>0 && rect.height()>0);
+
+    int cursor;
+
+    for (cursor=0; cursor<count; cursor++) {
+	if (cluster[cursor].contains(rect)) {
+	    // Wholly contained already.
+	    return;
+	}
+    }
+
+    int lowestcost=9999999;
+    int cheapest=-1;
+    cursor = 0;
+    while( cursor<count ) {
+	if (cluster[cursor].intersects(biggerrect)) {
+	    QRect larger=cluster[cursor];
+	    include(larger,rect);
+	    int cost = larger.width()*larger.height() -
+		       cluster[cursor].width()*cluster[cursor].height();
+
+	    if (cost < lowestcost) {
+		bool bad=FALSE;
+		for (int c=0; c<count && !bad; c++) {
+		    bad=cluster[c].intersects(larger) && c!=cursor;
+		}
+		if (!bad) {
+		    cheapest=cursor;
+		    lowestcost=cost;
+		}
+	    }
+	}
+	cursor++;
+    }
+
+    if (cheapest>=0) {
+	include(cluster[cheapest],rect);
+	return;
+    }
+
+    if (count < maxcl) {
+	cluster[count++]=rect;
+	return;
+    }
+
+    // Do cheapest of:
+    //     add to closest cluster
+    //     do cheapest cluster merge, add to new cluster
+
+    lowestcost=9999999;
+    cheapest=-1;
+    cursor=0;
+    while( cursor<count ) {
+	QRect larger=cluster[cursor];
+	include(larger,rect);
+	int cost=larger.width()*larger.height()
+		- cluster[cursor].width()*cluster[cursor].height();
+	if (cost < lowestcost) {
+	    bool bad=FALSE;
+	    for (int c=0; c<count && !bad; c++) {
+		bad=cluster[c].intersects(larger) && c!=cursor;
+	    }
+	    if (!bad) {
+		cheapest=cursor;
+		lowestcost=cost;
+	    }
+	}
+	cursor++;
+    }
+
+    // ###
+    // could make an heuristic guess as to whether we need to bother
+    // looking for a cheap merge.
+
+    int cheapestmerge1 = -1;
+    int cheapestmerge2 = -1;
+
+    int merge1 = 0;
+    while( merge1 < count ) {
+	int merge2=0;
+	while( merge2 < count ) {
+	    if( merge1!=merge2) {
+		QRect larger=cluster[merge1];
+		include(larger,cluster[merge2]);
+		int cost=larger.width()*larger.height()
+		    - cluster[merge1].width()*cluster[merge1].height()
+		    - cluster[merge2].width()*cluster[merge2].height();
+		if (cost < lowestcost) {
+		    bool bad=FALSE;
+		    for (int c=0; c<count && !bad; c++) {
+			bad=cluster[c].intersects(larger) && c!=cursor;
+		    }
+		    if (!bad) {
+			cheapestmerge1=merge1;
+			cheapestmerge2=merge2;
+			lowestcost=cost;
+		    }
+		}
+	    }
+	    merge2++;
+	}
+	merge1++;
+    }
+
+    if (cheapestmerge1>=0) {
+	include(cluster[cheapestmerge1],cluster[cheapestmerge2]);
+	cluster[cheapestmerge2]=cluster[count--];
+    } else {
+	// if (!cheapest) debugRectangles(rect);
+	include(cluster[cheapest],rect);
+    }
+
+    // NB: clusters do not intersect (or intersection will
+    //     overwrite). This is a result of the above algorithm,
+    //     given the assumption that (x,y) are ordered topleft
+    //     to bottomright.
+
+    // ###
+    //
+    // add explicit x/y ordering to that comment, move it to the top
+    // and rephrase it as pre-/post-conditions.
+}
+
+const QRect& QCanvasClusterizer::operator[](int i)
+{
+    return cluster[i];
+}
+
+// end of clusterizer
+
+
+
+class QM_EXPORT_CANVAS QCanvasItemPtr {
+public:
+    QCanvasItemPtr() : ptr(0) { }
+    QCanvasItemPtr( QCanvasItem* p ) : ptr(p) { }
+
+    bool operator<=(const QCanvasItemPtr& that) const
+    {
+	// Order same-z objects by identity.
+	if (that.ptr->z()==ptr->z())
+	    return that.ptr <= ptr;
+	return that.ptr->z() <= ptr->z();
+    }
+    bool operator<(const QCanvasItemPtr& that) const
+    {
+	// Order same-z objects by identity.
+	if (that.ptr->z()==ptr->z())
+	    return that.ptr < ptr;
+	return that.ptr->z() < ptr->z();
+    }
+    bool operator>(const QCanvasItemPtr& that) const
+    {
+	// Order same-z objects by identity.
+	if (that.ptr->z()==ptr->z())
+	    return that.ptr > ptr;
+	return that.ptr->z() > ptr->z();
+    }
+    bool operator==(const QCanvasItemPtr& that) const
+    {
+	    return that.ptr == ptr;
+    }
+    operator QCanvasItem*() const { return ptr; }
+
+private:
+    QCanvasItem* ptr;
+};
+
+
+/*!
+    \class QCanvasItemList
+    \brief The QCanvasItemList class is a list of QCanvasItems.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    QCanvasItemList is a QValueList of pointers to \l{QCanvasItem}s.
+    This class is used by some methods in QCanvas that need to return
+    a list of canvas items.
+
+    The \l QValueList documentation describes how to use this list.
+*/
+
+/*!
+  \internal
+*/
+void QCanvasItemList::sort()
+{
+    qHeapSort(*((QValueList<QCanvasItemPtr>*)this));
+}
+
+/*!
+  \internal
+*/
+void QCanvasItemList::drawUnique( QPainter& painter )
+{
+    QCanvasItem* prev=0;
+    for (Iterator it=fromLast(); it!=end(); --it) {
+	QCanvasItem *g=*it;
+	if (g!=prev) {
+	    g->draw(painter);
+	    prev=g;
+	}
+    }
+}
+
+/*!
+    Returns the concatenation of this list and list \a l.
+*/
+QCanvasItemList QCanvasItemList::operator+(const QCanvasItemList &l) const
+{
+    QCanvasItemList l2(*this);
+    for(const_iterator it = l.begin(); it != l.end(); ++it)
+       l2.append(*it);
+    return l2;
+}
+
+class QCanvasChunk {
+public:
+    QCanvasChunk() : changed(TRUE) { }
+    // Other code assumes lists are not deleted. Assignment is also
+    // done on ChunkRecs. So don't add that sort of thing here.
+
+    void sort()
+    {
+	list.sort();
+    }
+
+    const QCanvasItemList* listPtr() const
+    {
+	return &list;
+    }
+
+    void add(QCanvasItem* item)
+    {
+	list.prepend(item);
+	changed = TRUE;
+    }
+
+    void remove(QCanvasItem* item)
+    {
+	list.remove(item);
+	changed = TRUE;
+    }
+
+    void change()
+    {
+	changed = TRUE;
+    }
+
+    bool hasChanged() const
+    {
+	return changed;
+    }
+
+    bool takeChange()
+    {
+	bool y = changed;
+	changed = FALSE;
+	return y;
+    }
+
+private:
+    QCanvasItemList list;
+    bool changed;
+};
+
+
+static int gcd(int a, int b)
+{
+    int r;
+    while ( (r = a%b) ) {
+	a=b;
+	b=r;
+    }
+    return b;
+}
+
+static int scm(int a, int b)
+{
+    int g = gcd(a,b);
+    return a/g*b;
+}
+
+
+
+/*!
+    \class QCanvas qcanvas.h
+    \brief The QCanvas class provides a 2D area that can contain QCanvasItem objects.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \ingroup abstractwidgets
+    \ingroup graphics
+    \ingroup images
+    \mainclass
+    \module canvas
+
+    The QCanvas class manages its 2D graphic area and all the canvas
+    items the area contains. The canvas has no visual appearance of
+    its own. Instead, it is displayed on screen using a QCanvasView.
+    Multiple QCanvasView widgets may be associated with a canvas to
+    provide multiple views of the same canvas.
+
+    The canvas is optimized for large numbers of items, particularly
+    where only a small percentage of the items change at any
+    one time. If the entire display changes very regularly, you should
+    consider using your own custom QScrollView subclass.
+
+    Qt provides a rich
+    set of canvas item classes, e.g. QCanvasEllipse, QCanvasLine,
+    QCanvasPolygon, QCanvasPolygonalItem, QCanvasRectangle, QCanvasSpline,
+    QCanvasSprite and QCanvasText. You can subclass to create your own
+    canvas items; QCanvasPolygonalItem is the most common base class used
+    for this purpose.
+
+    Items appear on the canvas after their \link QCanvasItem::show()
+    show()\endlink function has been called (or \link
+    QCanvasItem::setVisible() setVisible(TRUE)\endlink), and \e after
+    update() has been called. The canvas only shows items that are
+    \link QCanvasItem::setVisible() visible\endlink, and then only if
+    \l update() is called. (By default the canvas is white and so are
+    canvas items, so if nothing appears try changing colors.)
+
+    If you created the canvas without passing a width and height to
+    the constructor you must also call resize().
+
+    Although a canvas may appear to be similar to a widget with child
+    widgets, there are several notable differences:
+
+    \list
+    \i Canvas items are usually much faster to manipulate and redraw than
+    child widgets, with the speed advantage becoming especially great when
+    there are \e many canvas items and non-rectangular items. In most
+    situations canvas items are also a lot more memory efficient than child
+    widgets.
+
+    \i It's easy to detect overlapping items (collision detection).
+
+    \i The canvas can be larger than a widget. A million-by-million canvas
+    is perfectly possible. At such a size a widget might be very
+    inefficient, and some window systems might not support it at all,
+    whereas QCanvas scales well. Even with a billion pixels and a million
+    items, finding a particular canvas item, detecting collisions, etc.,
+    is still fast (though the memory consumption may be prohibitive
+    at such extremes).
+
+    \i Two or more QCanvasView objects can view the same canvas.
+
+    \i An arbitrary transformation matrix can be set on each QCanvasView
+    which makes it easy to zoom, rotate or shear the viewed canvas.
+
+    \i Widgets provide a lot more functionality, such as input (QKeyEvent,
+    QMouseEvent etc.) and layout management (QGridLayout etc.).
+
+    \endlist
+
+    A canvas consists of a background, a number of canvas items organized by
+    x, y and z coordinates, and a foreground. A canvas item's z coordinate
+    can be treated as a layer number -- canvas items with a higher z
+    coordinate appear in front of canvas items with a lower z coordinate.
+
+    The background is white by default, but can be set to a different color
+    using setBackgroundColor(), or to a repeated pixmap using
+    setBackgroundPixmap() or to a mosaic of smaller pixmaps using
+    setTiles(). Individual tiles can be set with setTile(). There
+    are corresponding get functions, e.g. backgroundColor() and
+    backgroundPixmap().
+
+    Note that QCanvas does not inherit from QWidget, even though it has some
+    functions which provide the same functionality as those in QWidget. One
+    of these is setBackgroundPixmap(); some others are resize(), size(),
+    width() and height(). \l QCanvasView is the widget used to display a
+    canvas on the screen.
+
+    Canvas items are added to a canvas by constructing them and passing the
+    canvas to the canvas item's constructor. An item can be moved to a
+    different canvas using QCanvasItem::setCanvas().
+
+    Canvas items are movable (and in the case of QCanvasSprites, animated)
+    objects that inherit QCanvasItem. Each canvas item has a position on the
+    canvas (x, y coordinates) and a height (z coordinate), all of which are
+    held as floating-point numbers. Moving canvas items also have x and y
+    velocities. It's possible for a canvas item to be outside the canvas
+    (for example QCanvasItem::x() is greater than width()). When a canvas
+    item is off the canvas, onCanvas() returns FALSE and the canvas
+    disregards the item. (Canvas items off the canvas do not slow down any
+    of the common operations on the canvas.)
+
+    Canvas items can be moved with QCanvasItem::move(). The advance()
+    function moves all QCanvasItem::animated() canvas items and
+    setAdvancePeriod() makes QCanvas move them automatically on a periodic
+    basis. In the context of the QCanvas classes, to `animate' a canvas item
+    is to set it in motion, i.e. using QCanvasItem::setVelocity(). Animation
+    of a canvas item itself, i.e. items which change over time, is enabled
+    by calling QCanvasSprite::setFrameAnimation(), or more generally by
+    subclassing and reimplementing QCanvasItem::advance(). To detect collisions
+    use one of the QCanvasItem::collisions() functions.
+
+    The changed parts of the canvas are redrawn (if they are visible in a
+    canvas view) whenever update() is called. You can either call update()
+    manually after having changed the contents of the canvas, or force
+    periodic updates using setUpdatePeriod(). If you have moving objects on
+    the canvas, you must call advance() every time the objects should
+    move one step further. Periodic calls to advance() can be forced using
+    setAdvancePeriod(). The advance() function will call
+    QCanvasItem::advance() on every item that is \link
+    QCanvasItem::animated() animated\endlink and trigger an update of the
+    affected areas afterwards. (A canvas item that is `animated' is simply
+    a canvas item that is in motion.)
+
+    QCanvas organizes its canvas items into \e chunks; these are areas on
+    the canvas that are used to speed up most operations. Many operations
+    start by eliminating most chunks (i.e. those which haven't changed)
+    and then process only the canvas items that are in the few interesting
+    (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on
+    the canvas.
+
+    The chunk size is a key factor to QCanvas's speed: if there are too many
+    chunks, the speed benefit of grouping canvas items into chunks is
+    reduced. If the chunks are too large, it takes too long to process each
+    one. The QCanvas constructor tries to pick a suitable size, but you
+    can call retune() to change it at any time. The chunkSize() function
+    returns the current chunk size. The canvas items always make sure
+    they're in the right chunks; all you need to make sure of is that
+    the canvas uses the right chunk size. A good rule of thumb is that
+    the size should be a bit smaller than the average canvas item
+    size. If you have moving objects, the chunk size should be a bit
+    smaller than the average size of the moving items.
+
+    The foreground is normally nothing, but if you reimplement
+    drawForeground(), you can draw things in front of all the canvas
+    items.
+
+    Areas can be set as changed with setChanged() and set unchanged with
+    setUnchanged(). The entire canvas can be set as changed with
+    setAllChanged(). A list of all the items on the canvas is returned by
+    allItems().
+
+    An area can be copied (painted) to a QPainter with drawArea().
+
+    If the canvas is resized it emits the resized() signal.
+
+    The examples/canvas application and the 2D graphics page of the
+    examples/demo application demonstrate many of QCanvas's facilities.
+
+    \sa QCanvasView QCanvasItem
+*/
+void QCanvas::init(int w, int h, int chunksze, int mxclusters)
+{
+    d = new QCanvasData;
+    awidth=w;
+    aheight=h;
+    chunksize=chunksze;
+    maxclusters=mxclusters;
+    chwidth=(w+chunksize-1)/chunksize;
+    chheight=(h+chunksize-1)/chunksize;
+    chunks=new QCanvasChunk[chwidth*chheight];
+    update_timer = 0;
+    bgcolor = white;
+    grid = 0;
+    htiles = 0;
+    vtiles = 0;
+    dblbuf = TRUE;
+    debug_redraw_areas = FALSE;
+}
+
+/*!
+    Create a QCanvas with no size. \a parent and \a name are passed to
+    the QObject superclass.
+
+    \warning You \e must call resize() at some time after creation to
+    be able to use the canvas.
+*/
+QCanvas::QCanvas( QObject* parent, const char* name )
+    : QObject( parent, name )
+{
+    init(0,0);
+}
+
+/*!
+    Constructs a QCanvas that is \a w pixels wide and \a h pixels high.
+*/
+QCanvas::QCanvas(int w, int h)
+{
+    init(w,h);
+}
+
+/*!
+    Constructs a QCanvas which will be composed of \a h tiles
+    horizontally and \a v tiles vertically. Each tile will be an image
+    \a tilewidth by \a tileheight pixels taken from pixmap \a p.
+
+    The pixmap \a p is a list of tiles, arranged left to right, (and
+    in the case of pixmaps that have multiple rows of tiles, top to
+    bottom), with tile 0 in the top-left corner, tile 1 next to the
+    right, and so on, e.g.
+
+    \table
+    \row \i 0 \i 1 \i 2 \i 3
+    \row \i 4 \i 5 \i 6 \i 7
+    \endtable
+
+    The QCanvas is initially sized to show exactly the given number of
+    tiles horizontally and vertically. If it is resized to be larger,
+    the entire matrix of tiles will be repeated as often as necessary
+    to cover the area. If it is smaller, tiles to the right and bottom
+    will not be visible.
+
+    \sa setTiles()
+*/
+QCanvas::QCanvas( QPixmap p,
+	int h, int v, int tilewidth, int tileheight )
+{
+    init(h*tilewidth, v*tileheight, scm(tilewidth,tileheight) );
+    setTiles( p, h, v, tilewidth, tileheight );
+}
+
+void qt_unview(QCanvas* c)
+{
+    for (QCanvasView* view=c->d->viewList.first(); view != 0; view=c->d->viewList.next()) {
+	view->viewing = 0;
+    }
+}
+
+/*!
+    Destroys the canvas and all the canvas's canvas items.
+*/
+QCanvas::~QCanvas()
+{
+    qt_unview(this);
+    QCanvasItemList all = allItems();
+    for (QCanvasItemList::Iterator it=all.begin(); it!=all.end(); ++it)
+	delete *it;
+    delete [] chunks;
+    delete [] grid;
+    delete d;
+}
+
+/*!
+\internal
+Returns the chunk at a chunk position \a i, \a j.
+*/
+QCanvasChunk& QCanvas::chunk(int i, int j) const
+{
+    return chunks[i+chwidth*j];
+}
+
+/*!
+\internal
+Returns the chunk at a pixel position \a x, \a y.
+*/
+QCanvasChunk& QCanvas::chunkContaining(int x, int y) const
+{
+    return chunk(x/chunksize,y/chunksize);
+}
+
+/*!
+    Returns a list of all the items in the canvas.
+*/
+QCanvasItemList QCanvas::allItems()
+{
+    QCanvasItemList list;
+    for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+	list.prepend((QCanvasItem*)it.currentKey());
+    }
+    return list;
+}
+
+
+/*!
+    Changes the size of the canvas to have a width of \a w and a
+    height of \a h. This is a slow operation.
+*/
+void QCanvas::resize(int w, int h)
+{
+    if (awidth==w && aheight==h)
+	return;
+
+    QCanvasItem* item;
+    QPtrList<QCanvasItem> hidden;
+    for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+	if (((QCanvasItem*)it.currentKey())->isVisible()) {
+	    ((QCanvasItem*)it.currentKey())->hide();
+	    hidden.append(((QCanvasItem*)it.currentKey()));
+	}
+    }
+
+    int nchwidth=(w+chunksize-1)/chunksize;
+    int nchheight=(h+chunksize-1)/chunksize;
+
+    QCanvasChunk* newchunks = new QCanvasChunk[nchwidth*nchheight];
+
+    // Commit the new values.
+    //
+    awidth=w;
+    aheight=h;
+    chwidth=nchwidth;
+    chheight=nchheight;
+    delete [] chunks;
+    chunks=newchunks;
+
+    for (item=hidden.first(); item != 0; item=hidden.next()) {
+	item->show();
+    }
+
+    setAllChanged();
+
+    emit resized();
+}
+
+/*!
+    \fn void QCanvas::resized()
+
+    This signal is emitted whenever the canvas is resized. Each
+    QCanvasView connects to this signal to keep the scrollview's size
+    correct.
+*/
+
+/*!
+    Change the efficiency tuning parameters to \a mxclusters clusters,
+    each of size \a chunksze. This is a slow operation if there are
+    many objects on the canvas.
+
+    The canvas is divided into chunks which are rectangular areas \a
+    chunksze wide by \a chunksze high. Use a chunk size which is about
+    the average size of the canvas items. If you choose a chunk size
+    which is too small it will increase the amount of calculation
+    required when drawing since each change will affect many chunks.
+    If you choose a chunk size which is too large the amount of
+    drawing required will increase because for each change, a lot of
+    drawing will be required since there will be many (unchanged)
+    canvas items which are in the same chunk as the changed canvas
+    items.
+
+    Internally, a canvas uses a low-resolution "chunk matrix" to keep
+    track of all the items in the canvas. A 64x64 chunk matrix is the
+    default for a 1024x1024 pixel canvas, where each chunk collects
+    canvas items in a 16x16 pixel square. This default is also
+    affected by setTiles(). You can tune this default using this
+    function. For example if you have a very large canvas and want to
+    trade off speed for memory then you might set the chunk size to 32
+    or 64.
+
+    The \a mxclusters argument is the number of rectangular groups of
+    chunks that will be separately drawn. If the canvas has a large
+    number of small, dispersed items, this should be about that
+    number. Our testing suggests that a large number of clusters is
+    almost always best.
+
+*/
+void QCanvas::retune(int chunksze, int mxclusters)
+{
+    maxclusters=mxclusters;
+
+    if ( chunksize!=chunksze ) {
+	QPtrList<QCanvasItem> hidden;
+	for (QPtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+	    if (((QCanvasItem*)it.currentKey())->isVisible()) {
+		((QCanvasItem*)it.currentKey())->hide();
+		hidden.append(((QCanvasItem*)it.currentKey()));
+	    }
+	}
+
+	chunksize=chunksze;
+
+	int nchwidth=(awidth+chunksize-1)/chunksize;
+	int nchheight=(aheight+chunksize-1)/chunksize;
+
+	QCanvasChunk* newchunks = new QCanvasChunk[nchwidth*nchheight];
+
+	// Commit the new values.
+	//
+	chwidth=nchwidth;
+	chheight=nchheight;
+	delete [] chunks;
+	chunks=newchunks;
+
+	for (QCanvasItem* item=hidden.first(); item != 0; item=hidden.next()) {
+	    item->show();
+	}
+    }
+}
+
+/*!
+    \fn int QCanvas::width() const
+
+    Returns the width of the canvas, in pixels.
+*/
+
+/*!
+    \fn int QCanvas::height() const
+
+    Returns the height of the canvas, in pixels.
+*/
+
+/*!
+    \fn QSize QCanvas::size() const
+
+    Returns the size of the canvas, in pixels.
+*/
+
+/*!
+    \fn QRect QCanvas::rect() const
+
+    Returns a rectangle the size of the canvas.
+*/
+
+
+/*!
+    \fn bool QCanvas::onCanvas( int x, int y ) const
+
+    Returns TRUE if the pixel position (\a x, \a y) is on the canvas;
+    otherwise returns FALSE.
+
+    \sa validChunk()
+*/
+
+/*!
+    \fn bool QCanvas::onCanvas( const QPoint& p ) const
+    \overload
+
+    Returns TRUE if the pixel position \a p is on the canvas;
+    otherwise returns FALSE.
+
+    \sa validChunk()
+*/
+
+/*!
+    \fn bool QCanvas::validChunk( int x, int y ) const
+
+    Returns TRUE if the chunk position (\a x, \a y) is on the canvas;
+    otherwise returns FALSE.
+
+    \sa onCanvas()
+*/
+
+/*!
+  \fn bool QCanvas::validChunk( const QPoint& p ) const
+  \overload
+
+  Returns TRUE if the chunk position \a p is on the canvas; otherwise
+  returns FALSE.
+
+  \sa onCanvas()
+*/
+
+/*!
+    \fn int QCanvas::chunkSize() const
+
+    Returns the chunk size of the canvas.
+
+    \sa retune()
+*/
+
+/*!
+\fn bool QCanvas::sameChunk(int x1, int y1, int x2, int y2) const
+\internal
+Tells if the points ( \a x1, \a y1 ) and ( \a x2, \a y2 ) are within the same chunk.
+*/
+
+/*!
+\internal
+This method adds an the item \a item to the list of QCanvasItem objects
+in the QCanvas. The QCanvasItem class calls this.
+*/
+void QCanvas::addItem(QCanvasItem* item)
+{
+    d->itemDict.insert((void*)item,(void*)1);
+}
+
+/*!
+\internal
+This method adds the item \a item to the list of QCanvasItem objects
+to be moved. The QCanvasItem class calls this.
+*/
+void QCanvas::addAnimation(QCanvasItem* item)
+{
+    d->animDict.insert((void*)item,(void*)1);
+}
+
+/*!
+\internal
+This method adds the item \a item  to the list of QCanvasItem objects
+which are no longer to be moved. The QCanvasItem class calls this.
+*/
+void QCanvas::removeAnimation(QCanvasItem* item)
+{
+    d->animDict.remove((void*)item);
+}
+
+/*!
+\internal
+This method removes the item \a item from the list of QCanvasItem objects
+in this QCanvas. The QCanvasItem class calls this.
+*/
+void QCanvas::removeItem(QCanvasItem* item)
+{
+    d->itemDict.remove((void*)item);
+}
+
+/*!
+\internal
+This method adds the view \a view to the list of QCanvasView objects
+viewing this QCanvas. The QCanvasView class calls this.
+*/
+void QCanvas::addView(QCanvasView* view)
+{
+    d->viewList.append(view);
+    if ( htiles>1 || vtiles>1 || pm.isNull() )
+	view->viewport()->setBackgroundColor(backgroundColor());
+}
+
+/*!
+\internal
+This method removes the view \a view from the list of QCanvasView objects
+viewing this QCanvas. The QCanvasView class calls this.
+*/
+void QCanvas::removeView(QCanvasView* view)
+{
+    d->viewList.removeRef(view);
+}
+
+/*!
+    Sets the canvas to call advance() every \a ms milliseconds. Any
+    previous setting by setAdvancePeriod() or setUpdatePeriod() is
+    overridden.
+
+    If \a ms is less than 0 advancing will be stopped.
+*/
+void QCanvas::setAdvancePeriod(int ms)
+{
+    if ( ms<0 ) {
+	if ( update_timer )
+	    update_timer->stop();
+    } else {
+	if ( update_timer )
+	    delete update_timer;
+	update_timer = new QTimer(this);
+	connect(update_timer,SIGNAL(timeout()),this,SLOT(advance()));
+	update_timer->start(ms);
+    }
+}
+
+/*!
+    Sets the canvas to call update() every \a ms milliseconds. Any
+    previous setting by setAdvancePeriod() or setUpdatePeriod() is
+    overridden.
+
+    If \a ms is less than 0 automatic updating will be stopped.
+*/
+void QCanvas::setUpdatePeriod(int ms)
+{
+    if ( ms<0 ) {
+	if ( update_timer )
+	    update_timer->stop();
+    } else {
+	if ( update_timer )
+	    delete update_timer;
+	update_timer = new QTimer(this);
+	connect(update_timer,SIGNAL(timeout()),this,SLOT(update()));
+	update_timer->start(ms);
+    }
+}
+
+/*!
+    Moves all QCanvasItem::animated() canvas items on the canvas and
+    refreshes all changes to all views of the canvas. (An `animated'
+    item is an item that is in motion; see setVelocity().)
+
+    The advance takes place in two phases. In phase 0, the
+    QCanvasItem::advance() function of each QCanvasItem::animated()
+    canvas item is called with paramater 0. Then all these canvas
+    items are called again, with parameter 1. In phase 0, the canvas
+    items should not change position, merely examine other items on
+    the canvas for which special processing is required, such as
+    collisions between items. In phase 1, all canvas items should
+    change positions, ignoring any other items on the canvas. This
+    two-phase approach allows for considerations of "fairness",
+    although no QCanvasItem subclasses supplied with Qt do anything
+    interesting in phase 0.
+
+    The canvas can be configured to call this function periodically
+    with setAdvancePeriod().
+
+    \sa update()
+*/
+void QCanvas::advance()
+{
+    QPtrDictIterator<void> it=d->animDict;
+    while ( it.current() ) {
+	QCanvasItem* i = (QCanvasItem*)(void*)it.currentKey();
+	++it;
+	if ( i )
+	    i->advance(0);
+    }
+    // we expect the dict contains the exact same items as in the
+    // first pass.
+    it.toFirst();
+    while ( it.current() ) {
+	QCanvasItem* i = (QCanvasItem*)(void*)it.currentKey();
+	++it;
+	if ( i )
+	    i->advance(1);
+    }
+    update();
+}
+
+// Don't call this unless you know what you're doing.
+// p is in the content's co-ordinate example.
+/*!
+  \internal
+*/
+void QCanvas::drawViewArea( QCanvasView* view, QPainter* p, const QRect& vr, bool dbuf )
+{
+    QPoint tl = view->contentsToViewport(QPoint(0,0));
+
+#ifndef QT_NO_TRANSFORMATIONS
+    QWMatrix wm = view->worldMatrix();
+    QWMatrix iwm = wm.invert();
+    // ivr = covers all chunks in vr
+    QRect ivr = iwm.map(vr);
+    QWMatrix twm;
+    twm.translate(tl.x(),tl.y());
+#else
+    QRect ivr = vr;
+#endif
+
+    QRect all(0,0,width(),height());
+
+    if ( !all.contains(ivr) ) {
+	// Need to clip with edge of canvas.
+
+#ifndef QT_NO_TRANSFORMATIONS
+	// For translation-only transformation, it is safe to include the right
+	// and bottom edges, but otherwise, these must be excluded since they
+	// are not precisely defined (different bresenham paths).
+	QPointArray a;
+	if ( wm.m12()==0.0 && wm.m21()==0.0 && wm.m11() == 1.0 && wm.m22() == 1.0 )
+	    a = QPointArray( QRect(all.x(),all.y(),all.width()+1,all.height()+1) );
+	else
+	    a = QPointArray( all );
+
+	a = (wm*twm).map(a);
+#else
+	QPointArray a( QRect(all.x(),all.y(),all.width()+1,all.height()+1) );
+#endif
+	if ( view->viewport()->backgroundMode() == NoBackground ) {
+	    QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
+	    p->setClipRegion(QRegion(cvr)-QRegion(a));
+	    p->fillRect(vr,view->viewport()->palette()
+		.brush(QPalette::Active,QColorGroup::Background));
+	}
+	p->setClipRegion(a);
+    }
+
+    if ( dbuf ) {
+	ensureOffScrSize( vr.width(), vr.height() );
+	QPainter dbp(&offscr);
+#ifndef QT_NO_TRANSFORMATIONS
+	twm.translate(-vr.x(),-vr.y());
+	twm.translate(-tl.x(),-tl.y());
+	dbp.setWorldMatrix( wm*twm, TRUE );
+#else
+	dbp.translate(-vr.x()-tl.x(),-vr.y()-tl.y());
+#endif
+	dbp.setClipRect(0,0,vr.width(), vr.height());
+	drawCanvasArea(ivr,&dbp,FALSE);
+	p->drawPixmap(vr.x(), vr.y(), offscr, 0, 0, vr.width(), vr.height());
+    } else {
+	QRect r = vr; r.moveBy(tl.x(),tl.y()); // move to untransformed co-ords
+	if ( !all.contains(ivr) ) {
+	    QRegion inside = p->clipRegion() & r;
+	    //QRegion outside = p->clipRegion() - r;
+	    //p->setClipRegion(outside);
+	    //p->fillRect(outside.boundingRect(),red);
+	    p->setClipRegion(inside);
+	} else {
+	    p->setClipRect(r);
+	}
+#ifndef QT_NO_TRANSFORMATIONS
+	p->setWorldMatrix( wm*twm );
+#else
+#endif
+	p->setBrushOrigin(tl.x(), tl.y());
+	drawCanvasArea(ivr,p,FALSE);
+    }
+}
+
+/*!
+    Repaints changed areas in all views of the canvas.
+
+    \sa advance()
+*/
+void QCanvas::update()
+{
+    QCanvasClusterizer clusterizer(d->viewList.count());
+#ifndef QT_NO_TRANSFORMATIONS
+    QPtrList<QRect> doneareas;
+    doneareas.setAutoDelete(TRUE);
+#endif
+
+    QPtrListIterator<QCanvasView> it(d->viewList);
+    QCanvasView* view;
+    while( (view=it.current()) != 0 ) {
+	++it;
+#ifndef QT_NO_TRANSFORMATIONS
+	QWMatrix wm = view->worldMatrix();
+#endif
+	QRect area(view->contentsX(),view->contentsY(),
+		   view->visibleWidth(),view->visibleHeight());
+	if (area.width()>0 && area.height()>0) {
+#ifndef QT_NO_TRANSFORMATIONS
+	    if ( !wm.isIdentity() ) {
+		// r = Visible area of the canvas where there are changes
+		QRect r = changeBounds(view->inverseWorldMatrix().map(area));
+		if ( !r.isEmpty() ) {
+		    QPainter p(view->viewport());
+		    // Translate to the coordinate system of drawViewArea().
+		    QPoint tl = view->contentsToViewport(QPoint(0,0));
+		    p.translate(tl.x(),tl.y());
+		    drawViewArea( view, &p, wm.map(r), dblbuf );
+		    doneareas.append(new QRect(r));
+		}
+	    } else
+#endif
+	    {
+		clusterizer.add(area);
+	    }
+	}
+    }
+
+    for (int i=0; i<clusterizer.clusters(); i++)
+	drawChanges(clusterizer[i]);
+
+#ifndef QT_NO_TRANSFORMATIONS
+    for ( QRect* r=doneareas.first(); r != 0; r=doneareas.next() )
+	setUnchanged(*r);
+#endif
+}
+
+
+// ### warwick - setAllChanged() is not a set function. please rename
+// it. ditto setChanged(). markChanged(), perhaps?
+// ### unfortunately this function is virtual, which makes renaming more difficult. Lars
+
+/*!
+    Marks the whole canvas as changed.
+    All views of the canvas will be entirely redrawn when
+    update() is called next.
+*/
+void QCanvas::setAllChanged()
+{
+    setChanged(QRect(0,0,width(),height()));
+}
+
+/*!
+    Marks \a area as changed. This \a area will be redrawn in all
+    views that are showing it when update() is called next.
+*/
+void QCanvas::setChanged(const QRect& area)
+{
+    QRect thearea = area.intersect(QRect(0,0,width(),height()));
+
+    int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
+    int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
+    if (mx>chwidth)
+	mx=chwidth;
+    if (my>chheight)
+	my=chheight;
+
+    int x=thearea.x()/chunksize;
+    while( x<mx) {
+	int y = thearea.y()/chunksize;
+	while( y<my ) {
+	    chunk(x,y).change();
+	    y++;
+	}
+	x++;
+    }
+}
+
+/*!
+    Marks \a area as \e unchanged. The area will \e not be redrawn in
+    the views for the next update(), unless it is marked or changed
+    again before the next call to update().
+*/
+void QCanvas::setUnchanged(const QRect& area)
+{
+    QRect thearea = area.intersect(QRect(0,0,width(),height()));
+
+    int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
+    int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
+    if (mx>chwidth)
+	mx=chwidth;
+    if (my>chheight)
+	my=chheight;
+
+    int x=thearea.x()/chunksize;
+    while( x<mx) {
+	int y = thearea.y()/chunksize;
+	while( y<my ) {
+	    chunk(x,y).takeChange();
+	    y++;
+	}
+	x++;
+    }
+}
+
+
+/*!
+  \internal
+*/
+QRect QCanvas::changeBounds(const QRect& inarea)
+{
+    QRect area=inarea.intersect(QRect(0,0,width(),height()));
+
+    int mx = (area.x()+area.width()+chunksize)/chunksize;
+    int my = (area.y()+area.height()+chunksize)/chunksize;
+    if (mx > chwidth)
+	mx=chwidth;
+    if (my > chheight)
+	my=chheight;
+
+    QRect result;
+
+    int x=area.x()/chunksize;
+    while( x<mx ) {
+	int y=area.y()/chunksize;
+	while( y<my ) {
+	    QCanvasChunk& ch=chunk(x,y);
+	    if ( ch.hasChanged() )
+		result |= QRect(x,y,1,1);
+	    y++;
+	}
+	x++;
+    }
+
+    if ( !result.isEmpty() ) {
+	result.rLeft() *= chunksize;
+	result.rTop() *= chunksize;
+	result.rRight() *= chunksize;
+	result.rBottom() *= chunksize;
+	result.rRight() += chunksize;
+	result.rBottom() += chunksize;
+    }
+
+    return result;
+}
+
+/*!
+\internal
+Redraws the area \a inarea of the QCanvas.
+*/
+void QCanvas::drawChanges(const QRect& inarea)
+{
+    QRect area=inarea.intersect(QRect(0,0,width(),height()));
+
+    QCanvasClusterizer clusters(maxclusters);
+
+    int mx = (area.x()+area.width()+chunksize)/chunksize;
+    int my = (area.y()+area.height()+chunksize)/chunksize;
+    if (mx > chwidth)
+	mx=chwidth;
+    if (my > chheight)
+	my=chheight;
+
+    int x=area.x()/chunksize;
+    while( x<mx ) {
+	int y=area.y()/chunksize;
+	while( y<my ) {
+	    QCanvasChunk& ch=chunk(x,y);
+	    if ( ch.hasChanged() )
+		clusters.add(x,y);
+	    y++;
+	}
+	x++;
+    }
+
+    for (int i=0; i<clusters.clusters(); i++) {
+	QRect elarea=clusters[i];
+	elarea.setRect(
+	    elarea.left()*chunksize,
+	    elarea.top()*chunksize,
+	    elarea.width()*chunksize,
+	    elarea.height()*chunksize
+	);
+	drawCanvasArea(elarea);
+    }
+}
+
+void QCanvas::ensureOffScrSize( int osw, int osh )
+{
+    if ( osw > offscr.width() || osh > offscr.height() )
+	offscr.resize(QMAX(osw,offscr.width()),
+		      QMAX(osh,offscr.height()));
+    else if ( offscr.width() == 0 || offscr.height() == 0 )
+	offscr.resize( QMAX( offscr.width(), 1),
+		       QMAX( offscr.height(), 1 ) );
+}
+
+/*!
+    Paints all canvas items that are in the area \a clip to \a
+    painter, using double-buffering if \a dbuf is TRUE.
+
+    e.g. to print the canvas to a printer:
+    \code
+    QPrinter pr;
+    if ( pr.setup() ) {
+	QPainter p(&pr);
+	canvas.drawArea( canvas.rect(), &p );
+    }
+    \endcode
+*/
+void QCanvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf)
+{
+    if ( painter )
+	drawCanvasArea( clip, painter, dbuf );
+}
+
+/*!
+  \internal
+*/
+void QCanvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool double_buffer)
+{
+    QRect area=inarea.intersect(QRect(0,0,width(),height()));
+
+    if ( !dblbuf )
+	double_buffer = FALSE;
+
+    if (!d->viewList.first() && !p) return; // Nothing to do.
+
+    int lx=area.x()/chunksize;
+    int ly=area.y()/chunksize;
+    int mx=area.right()/chunksize;
+    int my=area.bottom()/chunksize;
+    if (mx>=chwidth)
+	mx=chwidth-1;
+    if (my>=chheight)
+	my=chheight-1;
+
+    QCanvasItemList allvisible;
+
+    // Stores the region within area that need to be drawn. It is relative
+    // to area.topLeft()  (so as to keep within bounds of 16-bit XRegions)
+    QRegion rgn;
+
+    for (int x=lx; x<=mx; x++) {
+	for (int y=ly; y<=my; y++) {
+	    // Only reset change if all views updating, and
+	    // wholy within area. (conservative:  ignore entire boundary)
+	    //
+	    // Disable this to help debugging.
+	    //
+	    if (!p) {
+		if ( chunk(x,y).takeChange() ) {
+		    // ### should at least make bands
+		    rgn |= QRegion(x*chunksize-area.x(),y*chunksize-area.y(),
+				    chunksize,chunksize);
+		    allvisible += *chunk(x,y).listPtr();
+		}
+	    } else {
+		allvisible += *chunk(x,y).listPtr();
+	    }
+	}
+    }
+    allvisible.sort();
+
+    if ( double_buffer )
+	ensureOffScrSize( area.width(), area.height() );
+
+    if ( double_buffer && !offscr.isNull() ) {
+	QPainter painter;
+	painter.begin(&offscr);
+	painter.translate(-area.x(),-area.y());
+	if ( p ) {
+	    painter.setClipRect(QRect(0,0,area.width(),area.height()));
+	} else {
+	    painter.setClipRegion(rgn);
+	}
+	drawBackground(painter,area);
+	allvisible.drawUnique(painter);
+	drawForeground(painter,area);
+	painter.end();
+	if ( p ) {
+	    p->drawPixmap( area.x(), area.y(), offscr,
+		0, 0, area.width(), area.height() );
+	    return;
+	}
+    } else if ( p ) {
+	drawBackground(*p,area);
+	allvisible.drawUnique(*p);
+	drawForeground(*p,area);
+	return;
+    }
+
+    QPoint trtr; // keeps track of total translation of rgn
+
+    trtr -= area.topLeft();
+
+    for (QCanvasView* view=d->viewList.first(); view; view=d->viewList.next()) {
+#ifndef QT_NO_TRANSFORMATIONS
+	if ( !view->worldMatrix().isIdentity() )
+	    continue; // Cannot paint those here (see callers).
+#endif
+	QPainter painter(view->viewport());
+	QPoint tr = view->contentsToViewport(area.topLeft());
+	QPoint nrtr = view->contentsToViewport(QPoint(0,0)); // new translation
+	QPoint rtr = nrtr - trtr; // extra translation of rgn
+	trtr += rtr; // add to total
+	if (double_buffer) {
+	    rgn.translate(rtr.x(),rtr.y());
+	    painter.setClipRegion(rgn);
+	    painter.drawPixmap(tr,offscr, QRect(QPoint(0,0),area.size()));
+	} else {
+	    painter.translate(nrtr.x(),nrtr.y());
+	    rgn.translate(rtr.x(),rtr.y());
+	    painter.setClipRegion(rgn);
+	    drawBackground(painter,area);
+	    allvisible.drawUnique(painter);
+	    drawForeground(painter,area);
+	    painter.translate(-nrtr.x(),-nrtr.y());
+	}
+    }
+}
+
+/*!
+\internal
+This method to informs the QCanvas that a given chunk is
+`dirty' and needs to be redrawn in the next Update.
+
+(\a x,\a y) is a chunk location.
+
+The sprite classes call this. Any new derived class of QCanvasItem
+must do so too. SetChangedChunkContaining can be used instead.
+*/
+void QCanvas::setChangedChunk(int x, int y)
+{
+    if (validChunk(x,y)) {
+	QCanvasChunk& ch=chunk(x,y);
+	ch.change();
+    }
+}
+
+/*!
+\internal
+This method to informs the QCanvas that the chunk containing a given
+pixel is `dirty' and needs to be redrawn in the next Update.
+
+(\a x,\a y) is a pixel location.
+
+The item classes call this. Any new derived class of QCanvasItem must
+do so too. SetChangedChunk can be used instead.
+*/
+void QCanvas::setChangedChunkContaining(int x, int y)
+{
+    if (x>=0 && x<width() && y>=0 && y<height()) {
+	QCanvasChunk& chunk=chunkContaining(x,y);
+	chunk.change();
+    }
+}
+
+/*!
+\internal
+This method adds the QCanvasItem \a g to the list of those which need to be
+drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the
+chunk as `dirty'.
+*/
+void QCanvas::addItemToChunk(QCanvasItem* g, int x, int y)
+{
+    if (validChunk(x,y)) {
+	chunk(x,y).add(g);
+    }
+}
+
+/*!
+\internal
+This method removes the QCanvasItem \a g from the list of those which need to
+be drawn if the given chunk at location ( \a x, \a y ) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the chunk
+as `dirty'.
+*/
+void QCanvas::removeItemFromChunk(QCanvasItem* g, int x, int y)
+{
+    if (validChunk(x,y)) {
+	chunk(x,y).remove(g);
+    }
+}
+
+
+/*!
+\internal
+This method adds the QCanvasItem \a g to the list of those which need to be
+drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the
+chunk as `dirty'.
+*/
+void QCanvas::addItemToChunkContaining(QCanvasItem* g, int x, int y)
+{
+    if (x>=0 && x<width() && y>=0 && y<height()) {
+	chunkContaining(x,y).add(g);
+    }
+}
+
+/*!
+\internal
+This method removes the QCanvasItem \a g from the list of those which need to
+be drawn if the chunk containing the given pixel ( \a x, \a y ) is redrawn.
+Like SetChangedChunk and SetChangedChunkContaining, this method
+marks the chunk as `dirty'.
+*/
+void QCanvas::removeItemFromChunkContaining(QCanvasItem* g, int x, int y)
+{
+    if (x>=0 && x<width() && y>=0 && y<height()) {
+	chunkContaining(x,y).remove(g);
+    }
+}
+
+/*!
+    Returns the color set by setBackgroundColor(). By default, this is
+    white.
+
+    This function is not a reimplementation of
+    QWidget::backgroundColor() (QCanvas is not a subclass of QWidget),
+    but all QCanvasViews that are viewing the canvas will set their
+    backgrounds to this color.
+
+    \sa setBackgroundColor(), backgroundPixmap()
+*/
+QColor QCanvas::backgroundColor() const
+{
+    return bgcolor;
+}
+
+/*!
+    Sets the solid background to be the color \a c.
+
+    \sa backgroundColor(), setBackgroundPixmap(), setTiles()
+*/
+void QCanvas::setBackgroundColor( const QColor& c )
+{
+    if ( bgcolor != c ) {
+	bgcolor = c;
+	QCanvasView* view=d->viewList.first();
+	while ( view != 0 ) {
+	    /* XXX this doesn't look right. Shouldn't this
+	       be more like setBackgroundPixmap? : Ian */
+	    view->viewport()->setEraseColor( bgcolor );
+	    view=d->viewList.next();
+	}
+	setAllChanged();
+    }
+}
+
+/*!
+    Returns the pixmap set by setBackgroundPixmap(). By default,
+    this is a null pixmap.
+
+    \sa setBackgroundPixmap(), backgroundColor()
+*/
+QPixmap QCanvas::backgroundPixmap() const
+{
+    return pm;
+}
+
+/*!
+    Sets the solid background to be the pixmap \a p repeated as
+    necessary to cover the entire canvas.
+
+    \sa backgroundPixmap(), setBackgroundColor(), setTiles()
+*/
+void QCanvas::setBackgroundPixmap( const QPixmap& p )
+{
+    setTiles(p, 1, 1, p.width(), p.height());
+    QCanvasView* view = d->viewList.first();
+    while ( view != 0 ) {
+	view->updateContents();
+	view = d->viewList.next();
+    }
+}
+
+/*!
+    This virtual function is called for all updates of the canvas. It
+    renders any background graphics using the painter \a painter, in
+    the area \a clip. If the canvas has a background pixmap or a tiled
+    background, that graphic is used, otherwise the canvas is cleared
+    using the background color.
+
+    If the graphics for an area change, you must explicitly call
+    setChanged(const QRect&) for the result to be visible when
+    update() is next called.
+
+    \sa setBackgroundColor(), setBackgroundPixmap(), setTiles()
+*/
+void QCanvas::drawBackground(QPainter& painter, const QRect& clip)
+{
+    if ( pm.isNull() ) {
+	painter.fillRect(clip,bgcolor);
+    } else if ( !grid ) {
+	for (int x=clip.x()/pm.width();
+	    x<(clip.x()+clip.width()+pm.width()-1)/pm.width(); x++)
+	{
+	    for (int y=clip.y()/pm.height();
+		y<(clip.y()+clip.height()+pm.height()-1)/pm.height(); y++)
+	    {
+		painter.drawPixmap(x*pm.width(), y*pm.height(),pm);
+	    }
+	}
+    } else {
+	const int x1 = clip.left()/tilew;
+	int x2 = clip.right()/tilew;
+	const int y1 = clip.top()/tileh;
+	int y2 = clip.bottom()/tileh;
+
+	const int roww = pm.width()/tilew;
+
+	for (int j=y1; j<=y2; j++) {
+	    int jj = j%tilesVertically();
+	    for (int i=x1; i<=x2; i++) {
+		int t = tile(i%tilesHorizontally(), jj);
+		int tx = t % roww;
+		int ty = t / roww;
+		painter.drawPixmap( i*tilew, j*tileh, pm,
+				tx*tilew, ty*tileh, tilew, tileh );
+	    }
+	}
+    }
+}
+
+/*!
+    This virtual function is called for all updates of the canvas. It
+    renders any foreground graphics using the painter \a painter, in
+    the area \a clip.
+
+    If the graphics for an area change, you must explicitly call
+    setChanged(const QRect&) for the result to be visible when
+    update() is next called.
+
+    The default is to draw nothing.
+*/
+void QCanvas::drawForeground(QPainter& painter, const QRect& clip)
+{
+    if ( debug_redraw_areas ) {
+	painter.setPen(red);
+	painter.setBrush(NoBrush);
+	painter.drawRect(clip);
+    }
+}
+
+/*!
+    If \a y is TRUE (the default) double-buffering is switched on;
+    otherwise double-buffering is switched off.
+
+    Turning off double-buffering causes the redrawn areas to flicker a
+    little and also gives a (usually small) performance improvement.
+*/
+void QCanvas::setDoubleBuffering(bool y)
+{
+    dblbuf = y;
+}
+
+
+/*!
+    Sets the QCanvas to be composed of \a h tiles horizontally and \a
+    v tiles vertically. Each tile will be an image \a tilewidth by \a
+    tileheight pixels from pixmap \a p.
+
+    The pixmap \a p is a list of tiles, arranged left to right, (and
+    in the case of pixmaps that have multiple rows of tiles, top to
+    bottom), with tile 0 in the top-left corner, tile 1 next to the
+    right, and so on, e.g.
+
+    \table
+    \row \i 0 \i 1 \i 2 \i 3
+    \row \i 4 \i 5 \i 6 \i 7
+    \endtable
+
+    If the canvas is larger than the matrix of tiles, the entire
+    matrix is repeated as necessary to cover the whole canvas. If it
+    is smaller, tiles to the right and bottom are not visible.
+
+    The width and height of \a p must be a multiple of \a tilewidth
+    and \a tileheight. If they are not the function will do nothing.
+
+    If you want to unset any tiling set, then just pass in a null
+    pixmap and 0 for \a h, \a v, \a tilewidth, and
+    \a tileheight.
+*/
+void QCanvas::setTiles( QPixmap p,
+			int h, int v, int tilewidth, int tileheight )
+{
+    if ( !p.isNull() && (!tilewidth || !tileheight ||
+	 p.width() % tilewidth != 0 || p.height() % tileheight != 0 ) )
+    	return;
+
+    htiles = h;
+    vtiles = v;
+    delete[] grid;
+    pm = p;
+    if ( h && v && !p.isNull() ) {
+	grid = new ushort[h*v];
+	memset( grid, 0, h*v*sizeof(ushort) );
+	tilew = tilewidth;
+	tileh = tileheight;
+    } else {
+	grid = 0;
+    }
+    if ( h + v > 10 ) {
+	int s = scm(tilewidth,tileheight);
+	retune( s < 128 ? s : QMAX(tilewidth,tileheight) );
+    }
+    setAllChanged();
+}
+
+/*!
+    \fn int QCanvas::tile( int x, int y ) const
+
+    Returns the tile at position (\a x, \a y). Initially, all tiles
+    are 0.
+
+    The parameters must be within range, i.e.
+	0 \< \a x \< tilesHorizontally() and
+	0 \< \a y \< tilesVertically().
+
+    \sa setTile()
+*/
+
+/*!
+    \fn int QCanvas::tilesHorizontally() const
+
+    Returns the number of tiles horizontally.
+*/
+
+/*!
+    \fn int QCanvas::tilesVertically() const
+
+    Returns the number of tiles vertically.
+*/
+
+/*!
+    \fn int QCanvas::tileWidth() const
+
+    Returns the width of each tile.
+*/
+
+/*!
+    \fn int QCanvas::tileHeight() const
+
+    Returns the height of each tile.
+*/
+
+
+/*!
+    Sets the tile at (\a x, \a y) to use tile number \a tilenum, which
+    is an index into the tile pixmaps. The canvas will update
+    appropriately when update() is next called.
+
+    The images are taken from the pixmap set by setTiles() and are
+    arranged left to right, (and in the case of pixmaps that have
+    multiple rows of tiles, top to bottom), with tile 0 in the
+    top-left corner, tile 1 next to the right, and so on, e.g.
+
+    \table
+    \row \i 0 \i 1 \i 2 \i 3
+    \row \i 4 \i 5 \i 6 \i 7
+    \endtable
+
+    \sa tile() setTiles()
+*/
+void QCanvas::setTile( int x, int y, int tilenum )
+{
+    ushort& t = grid[x+y*htiles];
+    if ( t != tilenum ) {
+	t = tilenum;
+	if ( tilew == tileh && tilew == chunksize )
+	    setChangedChunk( x, y );	    // common case
+	else
+	    setChanged( QRect(x*tilew,y*tileh,tilew,tileh) );
+    }
+}
+
+
+// lesser-used data in canvas item, plus room for extension.
+// Be careful adding to this - check all usages.
+class QCanvasItemExtra {
+    QCanvasItemExtra() : vx(0.0), vy(0.0) { }
+    double vx,vy;
+    friend class QCanvasItem;
+};
+
+
+/*!
+    \class QCanvasItem qcanvas.h
+    \brief The QCanvasItem class provides an abstract graphic object on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A variety of QCanvasItem subclasses provide immediately usable
+    behaviour. This class is a pure abstract superclass providing the
+    behaviour that is shared among all the concrete canvas item classes.
+    QCanvasItem is not intended for direct subclassing. It is much easier
+    to subclass one of its subclasses, e.g. QCanvasPolygonalItem (the
+    commonest base class), QCanvasRectangle, QCanvasSprite, QCanvasEllipse
+    or QCanvasText.
+
+    Canvas items are added to a canvas by constructing them and passing the
+    canvas to the canvas item's constructor. An item can be moved to a
+    different canvas using setCanvas().
+
+    Items appear on the canvas after their \link show() show()\endlink
+    function has been called (or \link setVisible()
+    setVisible(TRUE)\endlink), and \e after update() has been called. The
+    canvas only shows items that are \link setVisible() visible\endlink,
+    and then only if \l update() is called. If you created the canvas
+    without passing a width and height to the constructor you'll also need
+    to call \link QCanvas::resize() resize()\endlink. Since the canvas
+    background defaults to white and canvas items default to white,
+    you may need to change colors to see your items.
+
+    A QCanvasItem object can be moved in the x(), y() and z() dimensions
+    using functions such as move(), moveBy(), setX(), setY() and setZ(). A
+    canvas item can be set in motion, `animated', using setAnimated() and
+    given a velocity in the x and y directions with setXVelocity() and
+    setYVelocity() -- the same effect can be achieved by calling
+    setVelocity(). Use the collidesWith() function to see if the canvas item
+    will collide on the \e next advance(1) and use collisions() to see what
+    collisions have occurred.
+
+    Use QCanvasSprite or your own subclass of QCanvasSprite to create canvas
+    items which are animated, i.e. which change over time.
+
+    The size of a canvas item is given by boundingRect(). Use
+    boundingRectAdvanced() to see what the size of the canvas item will be
+    \e after the next advance(1) call.
+
+    The rtti() function is used for identifying subclasses of QCanvasItem.
+    The canvas() function returns a pointer to the canvas which contains the
+    canvas item.
+
+    QCanvasItem provides the show() and isVisible() functions like those in
+    QWidget.
+
+    QCanvasItem also provides the setEnabled(), setActive() and
+    setSelected() functions; these functions set the relevant boolean and
+    cause a repaint but the boolean values they set are not used in
+    QCanvasItem itself. You can make use of these booleans in your subclasses.
+
+    By default, canvas items have no velocity, no size, and are not in
+    motion. The subclasses provided in Qt do not change these defaults
+    except where noted.
+
+*/
+
+/*!
+    \enum QCanvasItem::RttiValues
+
+    This enum is used to name the different types of canvas item.
+
+    \value Rtti_Item Canvas item abstract base class
+    \value Rtti_Ellipse
+    \value Rtti_Line
+    \value Rtti_Polygon
+    \value Rtti_PolygonalItem
+    \value Rtti_Rectangle
+    \value Rtti_Spline
+    \value Rtti_Sprite
+    \value Rtti_Text
+
+*/
+
+/*!
+    \fn void QCanvasItem::update()
+
+    Call this function to repaint the canvas's changed chunks.
+*/
+
+/*!
+    Constructs a QCanvasItem on canvas \a canvas.
+
+    \sa setCanvas()
+*/
+QCanvasItem::QCanvasItem(QCanvas* canvas) :
+    cnv(canvas),
+    myx(0),myy(0),myz(0)
+{
+    ani=0;
+    vis=0;
+    val=0;
+    sel=0;
+    ena=0;
+    act=0;
+
+    ext = 0;
+    if (cnv) cnv->addItem(this);
+}
+
+/*!
+    Destroys the QCanvasItem and removes it from its canvas.
+*/
+QCanvasItem::~QCanvasItem()
+{
+    if (cnv) {
+	cnv->removeItem(this);
+	cnv->removeAnimation(this);
+    }
+    delete ext;
+}
+
+QCanvasItemExtra& QCanvasItem::extra()
+{
+    if ( !ext )
+	ext = new QCanvasItemExtra;
+    return *ext;
+}
+
+/*!
+    \fn double QCanvasItem::x() const
+
+    Returns the horizontal position of the canvas item. Note that
+    subclasses often have an origin other than the top-left corner.
+*/
+
+/*!
+    \fn double QCanvasItem::y() const
+
+    Returns the vertical position of the canvas item. Note that
+    subclasses often have an origin other than the top-left corner.
+*/
+
+/*!
+    \fn double QCanvasItem::z() const
+
+    Returns the z index of the canvas item, which is used for visual
+    order: higher-z items obscure (are in front of) lower-z items.
+*/
+
+/*!
+    \fn void QCanvasItem::setX(double x)
+
+    Moves the canvas item so that its x-position is \a x.
+
+    \sa x(), move()
+*/
+
+/*!
+    \fn void QCanvasItem::setY(double y)
+
+    Moves the canvas item so that its y-position is \a y.
+
+    \sa y(), move()
+*/
+
+/*!
+    \fn void QCanvasItem::setZ(double z)
+
+    Sets the z index of the canvas item to \a z. Higher-z items
+    obscure (are in front of) lower-z items.
+
+    \sa z(), move()
+*/
+
+
+/*!
+    Moves the canvas item relative to its current position by (\a dx,
+    \a dy).
+*/
+void QCanvasItem::moveBy( double dx, double dy )
+{
+    if ( dx || dy ) {
+	removeFromChunks();
+	myx += dx;
+	myy += dy;
+	addToChunks();
+    }
+}
+
+
+/*!
+    Moves the canvas item to the absolute position (\a x, \a y).
+*/
+void QCanvasItem::move( double x, double y )
+{
+    moveBy( x-myx, y-myy );
+}
+
+
+/*!
+    Returns TRUE if the canvas item is in motion; otherwise returns
+    FALSE.
+
+    \sa setVelocity(), setAnimated()
+*/
+bool QCanvasItem::animated() const
+{
+    return (bool)ani;
+}
+
+/*!
+    Sets the canvas item to be in motion if \a y is TRUE, or not if \a
+    y is FALSE. The speed and direction of the motion is set with
+    setVelocity(), or with setXVelocity() and setYVelocity().
+
+    \sa advance(), QCanvas::advance()
+*/
+void QCanvasItem::setAnimated(bool y)
+{
+    if ( y != (bool)ani ) {
+	ani = (uint)y;
+	if ( y ) {
+	    cnv->addAnimation(this);
+	} else {
+	    cnv->removeAnimation(this);
+	}
+    }
+}
+
+/*!
+    \fn void QCanvasItem::setXVelocity( double vx )
+
+    Sets the horizontal component of the canvas item's velocity to \a vx.
+
+    \sa setYVelocity() setVelocity()
+*/
+
+/*!
+    \fn void QCanvasItem::setYVelocity( double vy )
+
+    Sets the vertical component of the canvas item's velocity to \a vy.
+
+    \sa setXVelocity() setVelocity()
+*/
+
+/*!
+    Sets the canvas item to be in motion, moving by \a vx and \a vy
+    pixels in the horizontal and vertical directions respectively.
+
+    \sa advance() setXVelocity() setYVelocity()
+*/
+void QCanvasItem::setVelocity( double vx, double vy)
+{
+    if ( ext || vx!=0.0 || vy!=0.0 ) {
+	if ( !ani )
+	    setAnimated(TRUE);
+	extra().vx = vx;
+	extra().vy = vy;
+    }
+}
+
+/*!
+    Returns the horizontal velocity component of the canvas item.
+*/
+double QCanvasItem::xVelocity() const
+{
+    return ext ? ext->vx : 0;
+}
+
+/*!
+    Returns the vertical velocity component of the canvas item.
+*/
+double QCanvasItem::yVelocity() const
+{
+    return ext ? ext->vy : 0;
+}
+
+/*!
+    The default implementation moves the canvas item, if it is
+    animated(), by the preset velocity if \a phase is 1, and does
+    nothing if \a phase is 0.
+
+    Note that if you reimplement this function, the reimplementation
+    must not change the canvas in any way, for example it must not add
+    or remove items.
+
+    \sa QCanvas::advance() setVelocity()
+*/
+void QCanvasItem::advance(int phase)
+{
+    if ( ext && phase==1 )
+	moveBy(ext->vx,ext->vy);
+}
+
+/*!
+    \fn void QCanvasItem::draw(QPainter& painter)
+
+    This abstract virtual function draws the canvas item using \a painter.
+*/
+
+/*!
+    Sets the QCanvas upon which the canvas item is to be drawn to \a c.
+
+    \sa canvas()
+*/
+void QCanvasItem::setCanvas(QCanvas* c)
+{
+    bool v=isVisible();
+    setVisible(FALSE);
+    if (cnv) {
+	cnv->removeItem(this);
+    }
+    cnv=c;
+    if (cnv) {
+	cnv->addItem(this);
+	if ( ext )
+	    cnv->addAnimation(this);
+    }
+    setVisible(v);
+}
+
+/*!
+    \fn QCanvas* QCanvasItem::canvas() const
+
+    Returns the canvas containing the canvas item.
+*/
+
+/*! Shorthand for setVisible(TRUE). */
+void QCanvasItem::show()
+{
+    setVisible(TRUE);
+}
+
+/*! Shorthand for setVisible(FALSE). */
+void QCanvasItem::hide()
+{
+    setVisible(FALSE);
+}
+
+/*!
+    Makes the canvas item visible if \a yes is TRUE, or invisible if
+    \a yes is FALSE. The change takes effect when QCanvas::update() is
+    next called.
+*/
+void QCanvasItem::setVisible(bool yes)
+{
+    if ((bool)vis!=yes) {
+	if (yes) {
+	    vis=(uint)yes;
+	    addToChunks();
+	} else {
+	    removeFromChunks();
+	    vis=(uint)yes;
+	}
+    }
+}
+/*!
+    \obsolete
+    \fn bool QCanvasItem::visible() const
+    Use isVisible() instead.
+*/
+
+/*!
+    \fn bool QCanvasItem::isVisible() const
+
+    Returns TRUE if the canvas item is visible; otherwise returns
+    FALSE.
+
+    Note that in this context TRUE does \e not mean that the canvas
+    item is currently in a view, merely that if a view is showing the
+    area where the canvas item is positioned, and the item is not
+    obscured by items with higher z values, and the view is not
+    obscured by overlaying windows, it would be visible.
+
+    \sa setVisible(), z()
+*/
+
+/*!
+    \obsolete
+    \fn bool QCanvasItem::selected() const
+    Use isSelected() instead.
+*/
+
+/*!
+    \fn bool QCanvasItem::isSelected() const
+
+    Returns TRUE if the canvas item is selected; otherwise returns FALSE.
+*/
+
+/*!
+    Sets the selected flag of the item to \a yes. If this changes the
+    item's selected state the item will be redrawn when
+    QCanvas::update() is next called.
+
+    The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
+    subclasses do not make use of this value. The setSelected()
+    function is supplied because many applications need it, but it is
+    up to you how you use the isSelected() value.
+*/
+void QCanvasItem::setSelected(bool yes)
+{
+    if ((bool)sel!=yes) {
+	sel=(uint)yes;
+	changeChunks();
+    }
+}
+
+/*!
+    \obsolete
+    \fn bool QCanvasItem::enabled() const
+    Use isEnabled() instead.
+*/
+
+/*!
+    \fn bool QCanvasItem::isEnabled() const
+
+    Returns TRUE if the QCanvasItem is enabled; otherwise returns FALSE.
+*/
+
+/*!
+    Sets the enabled flag of the item to \a yes. If this changes the
+    item's enabled state the item will be redrawn when
+    QCanvas::update() is next called.
+
+    The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
+    subclasses do not make use of this value. The setEnabled()
+    function is supplied because many applications need it, but it is
+    up to you how you use the isEnabled() value.
+*/
+void QCanvasItem::setEnabled(bool yes)
+{
+    if (ena!=(uint)yes) {
+	ena=(uint)yes;
+	changeChunks();
+    }
+}
+
+/*!
+    \obsolete
+    \fn bool QCanvasItem::active() const
+    Use isActive() instead.
+*/
+
+/*!
+    \fn bool QCanvasItem::isActive() const
+
+    Returns TRUE if the QCanvasItem is active; otherwise returns FALSE.
+*/
+
+/*!
+    Sets the active flag of the item to \a yes. If this changes the
+    item's active state the item will be redrawn when
+    QCanvas::update() is next called.
+
+    The QCanvas, QCanvasItem and the Qt-supplied QCanvasItem
+    subclasses do not make use of this value. The setActive() function
+    is supplied because many applications need it, but it is up to you
+    how you use the isActive() value.
+*/
+void QCanvasItem::setActive(bool yes)
+{
+    if (act!=(uint)yes) {
+	act=(uint)yes;
+	changeChunks();
+    }
+}
+
+bool qt_testCollision(const QCanvasSprite* s1, const QCanvasSprite* s2)
+{
+    const QImage* s2image = s2->imageAdvanced()->collision_mask;
+    QRect s2area = s2->boundingRectAdvanced();
+
+    QRect cyourarea(s2area.x(),s2area.y(),
+	    s2area.width(),s2area.height());
+
+    QImage* s1image=s1->imageAdvanced()->collision_mask;
+
+    QRect s1area = s1->boundingRectAdvanced();
+
+    QRect ourarea = s1area.intersect(cyourarea);
+
+    if ( ourarea.isEmpty() )
+	return FALSE;
+
+    int x2=ourarea.x()-cyourarea.x();
+    int y2=ourarea.y()-cyourarea.y();
+    int x1=ourarea.x()-s1area.x();
+    int y1=ourarea.y()-s1area.y();
+    int w=ourarea.width();
+    int h=ourarea.height();
+
+    if ( !s2image ) {
+	if ( !s1image )
+	    return w>0 && h>0;
+	// swap everything around
+	int t;
+	t=x1; x1=x2; x2=t;
+	t=y1; x1=y2; y2=t;
+	s2image = s1image;
+	s1image = 0;
+    }
+
+    // s2image != 0
+
+    // A non-linear search may be more efficient.
+    // Perhaps spiralling out from the center, or a simpler
+    // vertical expansion from the centreline.
+
+    // We assume that sprite masks don't have
+    // different bit orders.
+    //
+    // Q_ASSERT(s1image->bitOrder()==s2image->bitOrder());
+
+    if (s1image) {
+	if (s1image->bitOrder() == QImage::LittleEndian) {
+	    for (int j=0; j<h; j++) {
+		uchar* ml = s1image->scanLine(y1+j);
+		uchar* yl = s2image->scanLine(y2+j);
+		for (int i=0; i<w; i++) {
+		    if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))
+		    && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7)))
+		    {
+			return TRUE;
+		    }
+		}
+	    }
+	} else {
+	    for (int j=0; j<h; j++) {
+		uchar* ml = s1image->scanLine(y1+j);
+		uchar* yl = s2image->scanLine(y2+j);
+		for (int i=0; i<w; i++) {
+		    if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))
+		    && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7))))
+		    {
+			return TRUE;
+		    }
+		}
+	    }
+	}
+    } else {
+	if (s2image->bitOrder() == QImage::LittleEndian) {
+	    for (int j=0; j<h; j++) {
+		uchar* yl = s2image->scanLine(y2+j);
+		for (int i=0; i<w; i++) {
+		    if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)))
+		    {
+			return TRUE;
+		    }
+		}
+	    }
+	} else {
+	    for (int j=0; j<h; j++) {
+		uchar* yl = s2image->scanLine(y2+j);
+		for (int i=0; i<w; i++) {
+		    if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))))
+		    {
+			return TRUE;
+		    }
+		}
+	    }
+	}
+    }
+
+    return FALSE;
+}
+
+static bool collision_double_dispatch( const QCanvasSprite* s1,
+				       const QCanvasPolygonalItem* p1,
+				       const QCanvasRectangle* r1,
+				       const QCanvasEllipse* e1,
+				       const QCanvasText* t1,
+				       const QCanvasSprite* s2,
+				       const QCanvasPolygonalItem* p2,
+				       const QCanvasRectangle* r2,
+				       const QCanvasEllipse* e2,
+				       const QCanvasText* t2 )
+{
+    const QCanvasItem* i1 = s1 ?
+			    (const QCanvasItem*)s1 : p1 ?
+			    (const QCanvasItem*)p1 : r1 ?
+			    (const QCanvasItem*)r1 : e1 ?
+			    (const QCanvasItem*)e1 : (const QCanvasItem*)t1;
+    const QCanvasItem* i2 = s2 ?
+			    (const QCanvasItem*)s2 : p2 ?
+			    (const QCanvasItem*)p2 : r2 ?
+			    (const QCanvasItem*)r2 : e2 ?
+			    (const QCanvasItem*)e2 : (const QCanvasItem*)t2;
+
+    if ( s1 && s2 ) {
+	// a
+	return qt_testCollision(s1,s2);
+    } else if ( (r1 || t1 || s1) && (r2 || t2 || s2) ) {
+	// b
+	QRect rc1 = i1->boundingRectAdvanced();
+	QRect rc2 = i2->boundingRectAdvanced();
+	return rc1.intersects(rc2);
+    } else if ( e1 && e2
+		&& e1->angleLength()>=360*16 && e2->angleLength()>=360*16
+		&& e1->width()==e1->height()
+		&& e2->width()==e2->height() ) {
+	// c
+	double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity());
+	double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity());
+	double rd = (e1->width()+e2->width())/2;
+	return xd*xd+yd*yd <= rd*rd;
+    } else if ( p1 && (p2 || s2 || t2) ) {
+	// d
+	QPointArray pa1 = p1->areaPointsAdvanced();
+	QPointArray pa2 = p2 ? p2->areaPointsAdvanced()
+			  : QPointArray(i2->boundingRectAdvanced());
+	bool col= !(QRegion(pa1) & QRegion(pa2,TRUE)).isEmpty();
+
+	return col;
+    } else {
+	return collision_double_dispatch(s2,p2,r2,e2,t2,
+					 s1,p1,r1,e1,t1);
+    }
+}
+
+/*!
+    \fn bool QCanvasItem::collidesWith( const QCanvasItem* other ) const
+
+    Returns TRUE if the canvas item will collide with the \a other
+    item \e after they have moved by their current velocities;
+    otherwise returns FALSE.
+
+    \sa collisions()
+*/
+
+
+/*!
+    \class QCanvasSprite qcanvas.h
+    \brief The QCanvasSprite class provides an animated canvas item on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A canvas sprite is an object which can contain any number of images
+    (referred to as frames), only one of which is current, i.e.
+    displayed, at any one time. The images can be passed in the
+    constructor or set or changed later with setSequence(). If you
+    subclass QCanvasSprite you can change the frame that is displayed
+    periodically, e.g. whenever QCanvasItem::advance(1) is called to
+    create the effect of animation.
+
+    The current frame can be set with setFrame() or with move(). The
+    number of frames available is given by frameCount(). The bounding
+    rectangle of the current frame is returned by boundingRect().
+
+    The current frame's image can be retrieved with image(); use
+    imageAdvanced() to retrieve the image for the frame that will be
+    shown after advance(1) is called. Use the image() overload passing
+    it an integer index to retrieve a particular image from the list of
+    frames.
+
+    Use width() and height() to retrieve the dimensions of the current
+    frame.
+
+    Use leftEdge() and rightEdge() to retrieve the current frame's
+    left-hand and right-hand x-coordinates respectively. Use
+    bottomEdge() and topEdge() to retrieve the current frame's bottom
+    and top y-coordinates respectively. These functions have an overload
+    which will accept an integer frame number to retrieve the
+    coordinates of a particular frame.
+
+    QCanvasSprite draws very quickly, at the expense of memory.
+
+    The current frame's image can be drawn on a painter with draw().
+
+    Like any other canvas item, canvas sprites can be moved with
+    move() which sets the x and y coordinates and the frame number, as
+    well as with QCanvasItem::move() and QCanvasItem::moveBy(), or by
+    setting coordinates with QCanvasItem::setX(), QCanvasItem::setY()
+    and QCanvasItem::setZ().
+
+*/
+
+
+/*!
+  \reimp
+*/
+bool QCanvasSprite::collidesWith( const QCanvasItem* i ) const
+{
+    return i->collidesWith(this,0,0,0,0);
+}
+
+/*!
+    Returns TRUE if the canvas item collides with any of the given
+    items; otherwise returns FALSE. The parameters, \a s, \a p, \a r,
+    \a e and \a t, are all the same object, this is just a type
+    resolution trick.
+*/
+bool QCanvasSprite::collidesWith( const QCanvasSprite* s,
+				  const QCanvasPolygonalItem* p,
+				  const QCanvasRectangle* r,
+				  const QCanvasEllipse* e,
+				  const QCanvasText* t ) const
+{
+    return collision_double_dispatch(s,p,r,e,t,this,0,0,0,0);
+}
+
+/*!
+  \reimp
+*/
+bool QCanvasPolygonalItem::collidesWith( const QCanvasItem* i ) const
+{
+    return i->collidesWith(0,this,0,0,0);
+}
+
+bool QCanvasPolygonalItem::collidesWith(  const QCanvasSprite* s,
+				 const QCanvasPolygonalItem* p,
+				 const QCanvasRectangle* r,
+				 const QCanvasEllipse* e,
+				 const QCanvasText* t ) const
+{
+    return collision_double_dispatch(s,p,r,e,t,0,this,0,0,0);
+}
+
+/*!
+  \reimp
+*/
+bool QCanvasRectangle::collidesWith( const QCanvasItem* i ) const
+{
+    return i->collidesWith(0,this,this,0,0);
+}
+
+bool QCanvasRectangle::collidesWith(  const QCanvasSprite* s,
+				 const QCanvasPolygonalItem* p,
+				 const QCanvasRectangle* r,
+				 const QCanvasEllipse* e,
+				 const QCanvasText* t ) const
+{
+    return collision_double_dispatch(s,p,r,e,t,0,this,this,0,0);
+}
+
+
+/*!
+  \reimp
+*/
+bool QCanvasEllipse::collidesWith( const QCanvasItem* i ) const
+{
+    return i->collidesWith(0,this,0,this,0);
+}
+
+bool QCanvasEllipse::collidesWith(  const QCanvasSprite* s,
+				 const QCanvasPolygonalItem* p,
+				 const QCanvasRectangle* r,
+				 const QCanvasEllipse* e,
+				 const QCanvasText* t ) const
+{
+    return collision_double_dispatch(s,p,r,e,t,0,this,0,this,0);
+}
+
+/*!
+  \reimp
+*/
+bool QCanvasText::collidesWith( const QCanvasItem* i ) const
+{
+    return i->collidesWith(0,0,0,0,this);
+}
+
+bool QCanvasText::collidesWith(  const QCanvasSprite* s,
+				 const QCanvasPolygonalItem* p,
+				 const QCanvasRectangle* r,
+				 const QCanvasEllipse* e,
+				 const QCanvasText* t ) const
+{
+    return collision_double_dispatch(s,p,r,e,t,0,0,0,0,this);
+}
+
+/*!
+    Returns the list of canvas items that this canvas item has
+    collided with.
+
+    A collision is generally defined as occurring when the pixels of
+    one item draw on the pixels of another item, but not all
+    subclasses are so precise. Also, since pixel-wise collision
+    detection can be slow, this function works in either exact or
+    inexact mode, according to the \a exact parameter.
+
+    If \a exact is TRUE, the canvas items returned have been
+    accurately tested for collision with the canvas item.
+
+    If \a exact is FALSE, the canvas items returned are \e near the
+    canvas item. You can test the canvas items returned using
+    collidesWith() if any are interesting collision candidates. By
+    using this approach, you can ignore some canvas items for which
+    collisions are not relevant.
+
+    The returned list is a list of QCanvasItems, but often you will
+    need to cast the items to their subclass types. The safe way to do
+    this is to use rtti() before casting. This provides some of the
+    functionality of the standard C++ dynamic cast operation even on
+    compilers where dynamic casts are not available.
+
+    Note that a canvas item may be `on' a canvas, e.g. it was created
+    with the canvas as parameter, even though its coordinates place it
+    beyond the edge of the canvas's area. Collision detection only
+    works for canvas items which are wholly or partly within the
+    canvas's area.
+
+    Note that if items have a velocity (see \l setVelocity()), then
+    collision testing is done based on where the item \e will be when
+    it moves, not its current location. For example, a "ball" item
+    doesn't need to actually embed into a "wall" item before a
+    collision is detected. For items without velocity, plain
+    intersection is used.
+*/
+QCanvasItemList QCanvasItem::collisions(bool exact) const
+{
+    return canvas()->collisions(chunks(),this,exact);
+}
+
+/*!
+    Returns a list of canvas items that collide with the point \a p.
+    The list is ordered by z coordinates, from highest z coordinate
+    (front-most item) to lowest z coordinate (rear-most item).
+*/
+QCanvasItemList QCanvas::collisions(const QPoint& p) const
+{
+    return collisions(QRect(p,QSize(1,1)));
+}
+
+/*!
+    \overload
+
+    Returns a list of items which collide with the rectangle \a r. The
+    list is ordered by z coordinates, from highest z coordinate
+    (front-most item) to lowest z coordinate (rear-most item).
+*/
+QCanvasItemList QCanvas::collisions(const QRect& r) const
+{
+    QCanvasRectangle i(r,(QCanvas*)this);
+    i.setPen(NoPen);
+    i.show(); // doesn't actually show, since we destroy it
+    QCanvasItemList l = i.collisions(TRUE);
+    l.sort();
+    return l;
+}
+
+/*!
+    \overload
+
+    Returns a list of canvas items which intersect with the chunks
+    listed in \a chunklist, excluding \a item. If \a exact is TRUE,
+    only those which actually \link QCanvasItem::collidesWith()
+    collide with\endlink \a item are returned; otherwise canvas items
+    are included just for being in the chunks.
+
+    This is a utility function mainly used to implement the simpler
+    QCanvasItem::collisions() function.
+*/
+QCanvasItemList QCanvas::collisions(const QPointArray& chunklist,
+	    const QCanvasItem* item, bool exact) const
+{
+    QPtrDict<void> seen;
+    QCanvasItemList result;
+    for (int i=0; i<(int)chunklist.count(); i++) {
+	int x = chunklist[i].x();
+	int y = chunklist[i].y();
+	if ( validChunk(x,y) ) {
+	    const QCanvasItemList* l = chunk(x,y).listPtr();
+	    for (QCanvasItemList::ConstIterator it=l->begin(); it!=l->end(); ++it) {
+		QCanvasItem *g=*it;
+		if ( g != item ) {
+		    if ( !seen.find(g) ) {
+			seen.replace(g,(void*)1);
+			if ( !exact || item->collidesWith(g) )
+			    result.append(g);
+		    }
+		}
+	    }
+	}
+    }
+    return result;
+}
+
+/*!
+  \internal
+  Adds the item to all the chunks it covers.
+*/
+void QCanvasItem::addToChunks()
+{
+    if (isVisible() && canvas()) {
+	QPointArray pa = chunks();
+	for (int i=0; i<(int)pa.count(); i++)
+	    canvas()->addItemToChunk(this,pa[i].x(),pa[i].y());
+	val=(uint)TRUE;
+    }
+}
+
+/*!
+  \internal
+  Removes the item from all the chunks it covers.
+*/
+void QCanvasItem::removeFromChunks()
+{
+    if (isVisible() && canvas()) {
+	QPointArray pa = chunks();
+	for (int i=0; i<(int)pa.count(); i++)
+	    canvas()->removeItemFromChunk(this,pa[i].x(),pa[i].y());
+    }
+}
+
+/*!
+  \internal
+  Sets all the chunks covered by the item to be refreshed with QCanvas::update()
+  is next called.
+*/
+void QCanvasItem::changeChunks()
+{
+    if (isVisible() && canvas()) {
+	if (!val)
+	    addToChunks();
+	QPointArray pa = chunks();
+	for (int i=0; i<(int)pa.count(); i++)
+	    canvas()->setChangedChunk(pa[i].x(),pa[i].y());
+    }
+}
+
+/*!
+    \fn QRect QCanvasItem::boundingRect() const
+
+    Returns the bounding rectangle in pixels that the canvas item covers.
+
+    \sa boundingRectAdvanced()
+*/
+
+/*!
+    Returns the bounding rectangle of pixels that the canvas item \e
+    will cover after advance(1) is called.
+
+    \sa boundingRect()
+*/
+QRect QCanvasItem::boundingRectAdvanced() const
+{
+    int dx = int(x()+xVelocity())-int(x());
+    int dy = int(y()+yVelocity())-int(y());
+    QRect r = boundingRect();
+    r.moveBy(dx,dy);
+    return r;
+}
+
+/*!
+    \class QCanvasPixmap qcanvas.h
+    \brief The QCanvasPixmap class provides pixmaps for QCanvasSprites.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    If you want to show a single pixmap on a QCanvas use a
+    QCanvasSprite with just one pixmap.
+
+    When pixmaps are inserted into a QCanvasPixmapArray they are held
+    as QCanvasPixmaps. \l{QCanvasSprite}s are used to show pixmaps on
+    \l{QCanvas}es and hold their pixmaps in a QCanvasPixmapArray. If
+    you retrieve a frame (pixmap) from a QCanvasSprite it will be
+    returned as a QCanvasPixmap.
+
+    The pixmap is a QPixmap and can only be set in the constructor.
+    There are three different constructors, one taking a QPixmap, one
+    a QImage and one a file name that refers to a file in any
+    supported file format (see QImageIO).
+
+    QCanvasPixmap can have a hotspot which is defined in terms of an (x,
+    y) offset. When you create a QCanvasPixmap from a PNG file or from
+    a QImage that has a QImage::offset(), the offset() is initialized
+    appropriately, otherwise the constructor leaves it at (0, 0). You
+    can set it later using setOffset(). When the QCanvasPixmap is used
+    in a QCanvasSprite, the offset position is the point at
+    QCanvasItem::x() and QCanvasItem::y(), not the top-left corner of
+    the pixmap.
+
+    Note that for QCanvasPixmap objects created by a QCanvasSprite, the
+    position of each QCanvasPixmap object is set so that the hotspot
+    stays in the same position.
+
+    \sa QCanvasPixmapArray QCanvasItem QCanvasSprite
+*/
+
+#ifndef QT_NO_IMAGEIO
+
+/*!
+    Constructs a QCanvasPixmap that uses the image stored in \a
+    datafilename.
+*/
+QCanvasPixmap::QCanvasPixmap(const QString& datafilename)
+{
+    QImage image(datafilename);
+    init(image);
+}
+
+#endif
+
+/*!
+    Constructs a QCanvasPixmap from the image \a image.
+*/
+QCanvasPixmap::QCanvasPixmap(const QImage& image)
+{
+    init(image);
+}
+/*!
+    Constructs a QCanvasPixmap from the pixmap \a pm using the offset
+    \a offset.
+*/
+QCanvasPixmap::QCanvasPixmap(const QPixmap& pm, const QPoint& offset)
+{
+    init(pm,offset.x(),offset.y());
+}
+
+void QCanvasPixmap::init(const QImage& image)
+{
+    convertFromImage(image);
+    hotx = image.offset().x();
+    hoty = image.offset().y();
+#ifndef QT_NO_IMAGE_DITHER_TO_1
+    if( image.hasAlphaBuffer() ) {
+	QImage i = image.createAlphaMask();
+	collision_mask = new QImage(i);
+    } else
+#endif
+	collision_mask = 0;
+}
+
+void QCanvasPixmap::init(const QPixmap& pixmap, int hx, int hy)
+{
+    (QPixmap&)*this = pixmap;
+    hotx = hx;
+    hoty = hy;
+    if( pixmap.mask() )  {
+	QImage i = mask()->convertToImage();
+	collision_mask = new QImage(i);
+    } else
+	collision_mask = 0;
+}
+
+/*!
+    Destroys the pixmap.
+*/
+QCanvasPixmap::~QCanvasPixmap()
+{
+    delete collision_mask;
+}
+
+/*!
+    \fn int QCanvasPixmap::offsetX() const
+
+    Returns the x-offset of the pixmap's hotspot.
+
+    \sa setOffset()
+*/
+
+/*!
+    \fn int QCanvasPixmap::offsetY() const
+
+    Returns the y-offset of the pixmap's hotspot.
+
+    \sa setOffset()
+*/
+
+/*!
+    \fn void QCanvasPixmap::setOffset(int x, int y)
+
+    Sets the offset of the pixmap's hotspot to (\a x, \a y).
+
+    \warning Do not call this function if any QCanvasSprites are
+    currently showing this pixmap.
+*/
+
+/*!
+    \class QCanvasPixmapArray qcanvas.h
+    \brief The QCanvasPixmapArray class provides an array of QCanvasPixmaps.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+
+    This class is used by QCanvasSprite to hold an array of pixmaps.
+    It is used to implement animated sprites, i.e. images that change
+    over time, with each pixmap in the array holding one frame.
+
+    Depending on the constructor you use you can load multiple pixmaps
+    into the array either from a directory (specifying a wildcard
+    pattern for the files), or from a list of QPixmaps. You can also
+    read in a set of pixmaps after construction using readPixmaps().
+
+    Individual pixmaps can be set with setImage() and retrieved with
+    image(). The number of pixmaps in the array is returned by
+    count().
+
+    QCanvasSprite uses an image's mask for collision detection. You
+    can change this by reading in a separate set of image masks using
+    readCollisionMasks().
+
+*/
+
+/*!
+    Constructs an invalid array (i.e. isValid() will return FALSE).
+    You must call readPixmaps() before being able to use this
+    QCanvasPixmapArray.
+*/
+QCanvasPixmapArray::QCanvasPixmapArray()
+: framecount( 0 ), img( 0 )
+{
+}
+
+#ifndef QT_NO_IMAGEIO
+/*!
+    Constructs a QCanvasPixmapArray from files.
+
+    The \a fc parameter sets the number of frames to be loaded for
+    this image.
+
+    If \a fc is not 0, \a datafilenamepattern should contain "%1",
+    e.g. "foo%1.png". The actual filenames are formed by replacing the
+    %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
+    foo0001.png, foo0002.png, etc.
+
+    If \a fc is 0, \a datafilenamepattern is asssumed to be a
+    filename, and the image contained in this file will be loaded as
+    the first (and only) frame.
+
+    If \a datafilenamepattern does not exist, is not readable, isn't
+    an image, or some other error occurs, the array ends up empty and
+    isValid() returns FALSE.
+*/
+
+QCanvasPixmapArray::QCanvasPixmapArray( const QString& datafilenamepattern,
+					int fc )
+: framecount( 0 ), img( 0 )
+{
+    readPixmaps(datafilenamepattern,fc);
+}
+#endif
+
+/*!
+  \obsolete
+  Use QCanvasPixmapArray::QCanvasPixmapArray( QValueList<QPixmap>, QPointArray )
+  instead.
+
+  Constructs a QCanvasPixmapArray from the list of QPixmaps \a
+  list. The \a hotspots list has to be of the same size as \a list.
+*/
+QCanvasPixmapArray::QCanvasPixmapArray(QPtrList<QPixmap> list, QPtrList<QPoint> hotspots) :
+    framecount(list.count()),
+    img(new QCanvasPixmap*[list.count()])
+{
+    if (list.count() != hotspots.count()) {
+	qWarning("QCanvasPixmapArray: lists have different lengths");
+	reset();
+	img = 0;
+    } else {
+	list.first();
+	hotspots.first();
+	for (int i=0; i<framecount; i++) {
+	    img[i]=new QCanvasPixmap(*list.current(), *hotspots.current());
+	    list.next();
+	    hotspots.next();
+	}
+    }
+}
+
+/*!
+    Constructs a QCanvasPixmapArray from the list of QPixmaps in the
+    \a list. Each pixmap will get a hotspot according to the \a
+    hotspots array. If no hotspots are specified, each one is set to
+    be at position (0, 0).
+
+    If an error occurs, isValid() will return FALSE.
+*/
+QCanvasPixmapArray::QCanvasPixmapArray(QValueList<QPixmap> list, QPointArray hotspots) :
+    framecount((int)list.size()),
+    img(new QCanvasPixmap*[list.size()])
+{
+    bool have_hotspots = ( hotspots.size() != 0 );
+    if (have_hotspots && list.count() != hotspots.count()) {
+	qWarning("QCanvasPixmapArray: lists have different lengths");
+	reset();
+	img = 0;
+    } else {
+	QValueList<QPixmap>::iterator it;
+	it = list.begin();
+	for (int i=0; i<framecount; i++) {
+	    QPoint hs = have_hotspots ? hotspots[i] : QPoint( 0, 0 );
+	    img[i]=new QCanvasPixmap( *it, hs );
+	    ++it;
+	}
+    }
+}
+
+/*!
+    Destroys the pixmap array and all the pixmaps it contains.
+*/
+QCanvasPixmapArray::~QCanvasPixmapArray()
+{
+    reset();
+}
+
+void QCanvasPixmapArray::reset()
+{
+    for (int i=0; i<framecount; i++)
+	delete img[i];
+    delete [] img;
+    img = 0;
+    framecount = 0;
+}
+
+#ifndef QT_NO_IMAGEIO
+/*!
+    Reads one or more pixmaps into the pixmap array.
+
+    If \a fc is not 0, \a filenamepattern should contain "%1", e.g.
+    "foo%1.png". The actual filenames are formed by replacing the %1
+    with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
+    foo0001.png, foo0002.png, etc.
+
+    If \a fc is 0, \a filenamepattern is asssumed to be a filename,
+    and the image contained in this file will be loaded as the first
+    (and only) frame.
+
+    If \a filenamepattern does not exist, is not readable, isn't an
+    image, or some other error occurs, this function will return
+    FALSE, and isValid() will return FALSE; otherwise this function
+    will return TRUE.
+
+    \sa isValid()
+*/
+bool QCanvasPixmapArray::readPixmaps( const QString& filenamepattern,
+				      int fc)
+{
+    return readPixmaps(filenamepattern,fc,FALSE);
+}
+
+/*!
+    Reads new collision masks for the array.
+
+    By default, QCanvasSprite uses the image mask of a sprite to
+    detect collisions. Use this function to set your own collision
+    image masks.
+
+    If count() is 1 \a filename must specify a real filename to read
+    the mask from. If count() is greater than 1, the \a filename must
+    contain a "%1" that will get replaced by the number of the mask to
+    be loaded, just like QCanvasPixmapArray::readPixmaps().
+
+    All collision masks must be 1-bit images or this function call
+    will fail.
+
+    If the file isn't readable, contains the wrong number of images,
+    or there is some other error, this function will return FALSE, and
+    the array will be flagged as invalid; otherwise this function
+    returns TRUE.
+
+    \sa isValid()
+*/
+bool QCanvasPixmapArray::readCollisionMasks(const QString& filename)
+{
+    return readPixmaps(filename,framecount,TRUE);
+}
+
+
+bool QCanvasPixmapArray::readPixmaps( const QString& datafilenamepattern,
+				      int fc, bool maskonly)
+{
+    if ( !maskonly ) {
+	reset();
+	framecount = fc;
+	if ( !framecount )
+	    framecount=1;
+	img = new QCanvasPixmap*[framecount];
+    }
+    bool ok = TRUE;
+    bool arg = fc > 1;
+    if ( !arg )
+	framecount=1;
+    for (int i=0; i<framecount; i++) {
+	QString r;
+	r.sprintf("%04d",i);
+	if ( maskonly ) {
+	    img[i]->collision_mask->load(
+		arg ? datafilenamepattern.arg(r) : datafilenamepattern);
+	    ok = ok
+	       && !img[i]->collision_mask->isNull()
+	       && img[i]->collision_mask->depth()==1;
+	} else {
+	    img[i]=new QCanvasPixmap(
+		arg ? datafilenamepattern.arg(r) : datafilenamepattern);
+	    ok = ok && !img[i]->isNull();
+	}
+    }
+    if ( !ok ) {
+	reset();
+    }
+    return ok;
+}
+#endif
+
+/*!
+  \obsolete
+
+  Use isValid() instead.
+
+  This returns FALSE if the array is valid, and TRUE if it is not.
+*/
+bool QCanvasPixmapArray::operator!()
+{
+    return img==0;
+}
+
+/*!
+    Returns TRUE if the pixmap array is valid; otherwise returns
+    FALSE.
+*/
+bool QCanvasPixmapArray::isValid() const
+{
+    return (img != 0);
+}
+
+/*!
+    \fn QCanvasPixmap* QCanvasPixmapArray::image(int i) const
+
+    Returns pixmap \a i in the array, if \a i is non-negative and less
+    than than count(), and returns an unspecified value otherwise.
+*/
+
+// ### wouldn't it be better to put empty QCanvasPixmaps in there instead of
+// initializing the additional elements in the array to 0? Lars
+/*!
+    Replaces the pixmap at index \a i with pixmap \a p.
+
+    The array takes ownership of \a p and will delete \a p when the
+    array itself is deleted.
+
+    If \a i is beyond the end of the array the array is extended to at
+    least i+1 elements, with elements count() to i-1 being initialized
+    to 0.
+*/
+void QCanvasPixmapArray::setImage(int i, QCanvasPixmap* p)
+{
+    if ( i >= framecount ) {
+	QCanvasPixmap** newimg = new QCanvasPixmap*[i+1];
+	memcpy(newimg, img, sizeof( QCanvasPixmap * )*framecount);
+	memset(newimg + framecount, 0, sizeof( QCanvasPixmap * )*( i+1 - framecount ) );
+	framecount = i+1;
+	delete [] img;
+	img = newimg;
+    }
+    delete img[i]; img[i]=p;
+}
+
+/*!
+    \fn uint QCanvasPixmapArray::count() const
+
+    Returns the number of pixmaps in the array.
+*/
+
+/*!
+    Returns the x-coordinate of the current left edge of the sprite.
+    (This may change as the sprite animates since different frames may
+    have different left edges.)
+
+    \sa rightEdge() bottomEdge() topEdge()
+*/
+int QCanvasSprite::leftEdge() const
+{
+    return int(x()) - image()->hotx;
+}
+
+/*!
+    \overload
+
+    Returns what the x-coordinate of the left edge of the sprite would
+    be if the sprite (actually its hotspot) were moved to x-position
+    \a nx.
+
+    \sa rightEdge() bottomEdge() topEdge()
+*/
+int QCanvasSprite::leftEdge(int nx) const
+{
+    return nx - image()->hotx;
+}
+
+/*!
+    Returns the y-coordinate of the top edge of the sprite. (This may
+    change as the sprite animates since different frames may have
+    different top edges.)
+
+    \sa leftEdge() rightEdge() bottomEdge()
+*/
+int QCanvasSprite::topEdge() const
+{
+    return int(y()) - image()->hoty;
+}
+
+/*!
+    \overload
+
+    Returns what the y-coordinate of the top edge of the sprite would
+    be if the sprite (actually its hotspot) were moved to y-position
+    \a ny.
+
+    \sa leftEdge() rightEdge() bottomEdge()
+*/
+int QCanvasSprite::topEdge(int ny) const
+{
+    return ny - image()->hoty;
+}
+
+/*!
+    Returns the x-coordinate of the current right edge of the sprite.
+    (This may change as the sprite animates since different frames may
+    have different right edges.)
+
+    \sa leftEdge() bottomEdge() topEdge()
+*/
+int QCanvasSprite::rightEdge() const
+{
+    return leftEdge() + image()->width()-1;
+}
+
+/*!
+    \overload
+
+    Returns what the x-coordinate of the right edge of the sprite
+    would be if the sprite (actually its hotspot) were moved to
+    x-position \a nx.
+
+    \sa leftEdge() bottomEdge() topEdge()
+*/
+int QCanvasSprite::rightEdge(int nx) const
+{
+    return leftEdge(nx) + image()->width()-1;
+}
+
+/*!
+    Returns the y-coordinate of the current bottom edge of the sprite.
+    (This may change as the sprite animates since different frames may
+    have different bottom edges.)
+
+    \sa leftEdge() rightEdge() topEdge()
+*/
+int QCanvasSprite::bottomEdge() const
+{
+    return topEdge() + image()->height()-1;
+}
+
+/*!
+    \overload
+
+    Returns what the y-coordinate of the top edge of the sprite would
+    be if the sprite (actually its hotspot) were moved to y-position
+    \a ny.
+
+    \sa leftEdge() rightEdge() topEdge()
+*/
+int QCanvasSprite::bottomEdge(int ny) const
+{
+    return topEdge(ny) + image()->height()-1;
+}
+
+/*!
+    \fn QCanvasPixmap* QCanvasSprite::image() const
+
+    Returns the current frame's image.
+
+    \sa frame(), setFrame()
+*/
+
+/*!
+    \fn QCanvasPixmap* QCanvasSprite::image(int f) const
+    \overload
+
+    Returns the image for frame \a f. Does not do any bounds checking on \a f.
+*/
+
+/*!
+    Returns the image the sprite \e will have after advance(1) is
+    called. By default this is the same as image().
+*/
+QCanvasPixmap* QCanvasSprite::imageAdvanced() const
+{
+    return image();
+}
+
+/*!
+    Returns the bounding rectangle for the image in the sprite's
+    current frame. This assumes that the images are tightly cropped
+    (i.e. do not have transparent pixels all along a side).
+*/
+QRect QCanvasSprite::boundingRect() const
+{
+    return QRect(leftEdge(), topEdge(), width(), height());
+}
+
+
+/*!
+  \internal
+  Returns the chunks covered by the item.
+*/
+QPointArray QCanvasItem::chunks() const
+{
+    QPointArray r;
+    int n=0;
+    QRect br = boundingRect();
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	br &= QRect(0,0,canvas()->width(),canvas()->height());
+	if ( br.isValid() ) {
+	    r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2));
+	    for (int j=br.top()/chunksize; j<=br.bottom()/chunksize; j++) {
+		for (int i=br.left()/chunksize; i<=br.right()/chunksize; i++) {
+		    r[n++] = QPoint(i,j);
+		}
+	    }
+	}
+    }
+    r.resize(n);
+    return r;
+}
+
+
+/*!
+  \internal
+  Add the sprite to the chunks in its QCanvas which it overlaps.
+*/
+void QCanvasSprite::addToChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+	    for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+		canvas()->addItemToChunk(this,i,j);
+	    }
+	}
+    }
+}
+
+/*!
+  \internal
+  Remove the sprite from the chunks in its QCanvas which it overlaps.
+
+  \sa addToChunks()
+*/
+void QCanvasSprite::removeFromChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+	    for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+		canvas()->removeItemFromChunk(this,i,j);
+	    }
+	}
+    }
+}
+
+/*!
+    The width of the sprite for the current frame's image.
+
+    \sa frame()
+*/
+//### mark: Why don't we have width(int) and height(int) to be
+//consistent with leftEdge() and leftEdge(int)?
+int QCanvasSprite::width() const
+{
+    return image()->width();
+}
+
+/*!
+    The height of the sprite for the current frame's image.
+
+    \sa frame()
+*/
+int QCanvasSprite::height() const
+{
+    return image()->height();
+}
+
+
+/*!
+    Draws the current frame's image at the sprite's current position
+    on painter \a painter.
+*/
+void QCanvasSprite::draw(QPainter& painter)
+{
+    painter.drawPixmap(leftEdge(),topEdge(),*image());
+}
+
+/*!
+    \class QCanvasView qcanvas.h
+    \brief The QCanvasView class provides an on-screen view of a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A QCanvasView is widget which provides a view of a QCanvas.
+
+    If you want users to be able to interact with a canvas view,
+    subclass QCanvasView. You might then reimplement
+    QScrollView::contentsMousePressEvent(). For example:
+
+    \code
+    void MyCanvasView::contentsMousePressEvent( QMouseEvent* e )
+    {
+	QCanvasItemList l = canvas()->collisions(e->pos());
+	for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+	    if ( (*it)->rtti() == QCanvasRectangle::RTTI )
+		qDebug("A QCanvasRectangle lies somewhere at this point");
+	}
+    }
+    \endcode
+
+    The canvas view shows canvas canvas(); this can be changed using
+    setCanvas().
+
+    A transformation matrix can be used to transform the view of the
+    canvas in various ways, for example, zooming in or out or rotating.
+    For example:
+
+    \code
+    QWMatrix wm;
+    wm.scale( 2, 2 );   // Zooms in by 2 times
+    wm.rotate( 90 );    // Rotates 90 degrees counter clockwise
+			// around the origin.
+    wm.translate( 0, -canvas->height() );
+			// moves the canvas down so what was visible
+			// before is still visible.
+    myCanvasView->setWorldMatrix( wm );
+    \endcode
+
+    Use setWorldMatrix() to set the canvas view's world matrix: you must
+    ensure that the world matrix is invertible. The current world matrix
+    is retrievable with worldMatrix(), and its inversion is retrievable
+    with inverseWorldMatrix().
+
+    Example:
+
+    The following code finds the part of the canvas that is visible in
+    this view, i.e. the bounding rectangle of the view in canvas coordinates.
+
+    \code
+    QRect rc = QRect( myCanvasView->contentsX(), myCanvasView->contentsY(),
+			myCanvasView->visibleWidth(), myCanvasView->visibleHeight() );
+    QRect canvasRect = myCanvasView->inverseWorldMatrix().mapRect(rc);
+    \endcode
+
+    \sa QWMatrix QPainter::setWorldMatrix()
+
+*/
+
+/*!
+    Constructs a QCanvasView with parent \a parent, and name \a name,
+    using the widget flags \a f. The canvas view is not associated
+    with a canvas, so you must to call setCanvas() to view a
+    canvas.
+*/
+QCanvasView::QCanvasView(QWidget* parent, const char* name, WFlags f) :
+    QScrollView(parent,name,f|WResizeNoErase|WStaticContents)
+{
+    d = new QCanvasViewData;
+    viewing = 0;
+    setCanvas(0);
+    connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int)));
+}
+
+/*!
+    \overload
+
+    Constructs a QCanvasView which views canvas \a canvas, with parent
+    \a parent, and name \a name, using the widget flags \a f.
+*/
+QCanvasView::QCanvasView(QCanvas* canvas, QWidget* parent, const char* name, WFlags f) :
+    QScrollView(parent,name,f|WResizeNoErase|WStaticContents)
+{
+    d = new QCanvasViewData;
+    viewing = 0;
+    setCanvas(canvas);
+
+    connect(this,SIGNAL(contentsMoving(int,int)),this,SLOT(cMoving(int,int)));
+}
+
+/*!
+    Destroys the canvas view. The associated canvas is \e not deleted.
+*/
+QCanvasView::~QCanvasView()
+{
+    delete d;
+    d = 0;
+    setCanvas(0);
+}
+
+/*!
+    \fn QCanvas* QCanvasView::canvas() const
+
+    Returns a pointer to the canvas which the QCanvasView is currently
+    showing.
+*/
+
+
+/*!
+    Sets the canvas that the QCanvasView is showing to the canvas \a
+    canvas.
+*/
+void QCanvasView::setCanvas(QCanvas* canvas)
+{
+    if (viewing) {
+	disconnect(viewing);
+	viewing->removeView(this);
+    }
+    viewing=canvas;
+    if (viewing) {
+	connect(viewing,SIGNAL(resized()), this, SLOT(updateContentsSize()));
+	viewing->addView(this);
+    }
+    if ( d ) // called by d'tor
+        updateContentsSize();
+}
+
+#ifndef QT_NO_TRANSFORMATIONS
+/*!
+    Returns a reference to the canvas view's current transformation matrix.
+
+    \sa setWorldMatrix() inverseWorldMatrix()
+*/
+const QWMatrix &QCanvasView::worldMatrix() const
+{
+    return d->xform;
+}
+
+/*!
+    Returns a reference to the inverse of the canvas view's current
+    transformation matrix.
+
+    \sa setWorldMatrix() worldMatrix()
+*/
+const QWMatrix &QCanvasView::inverseWorldMatrix() const
+{
+    return d->ixform;
+}
+
+/*!
+    Sets the transformation matrix of the QCanvasView to \a wm. The
+    matrix must be invertible (i.e. if you create a world matrix that
+    zooms out by 2 times, then the inverse of this matrix is one that
+    will zoom in by 2 times).
+
+    When you use this, you should note that the performance of the
+    QCanvasView will decrease considerably.
+
+    Returns FALSE if \a wm is not invertable; otherwise returns TRUE.
+
+    \sa worldMatrix() inverseWorldMatrix() QWMatrix::isInvertible()
+*/
+bool QCanvasView::setWorldMatrix( const QWMatrix & wm )
+{
+    bool ok = wm.isInvertible();
+    if ( ok ) {
+	d->xform = wm;
+	d->ixform = wm.invert();
+	updateContentsSize();
+	viewport()->update();
+    }
+    return ok;
+}
+#endif
+
+void QCanvasView::updateContentsSize()
+{
+    if ( viewing ) {
+	QRect br;
+#ifndef QT_NO_TRANSFORMATIONS
+	br = d->xform.map(QRect(0,0,viewing->width(),viewing->height()));
+#else
+	br = QRect(0,0,viewing->width(),viewing->height());
+#endif
+
+	if ( br.width() < contentsWidth() ) {
+	    QRect r(contentsToViewport(QPoint(br.width(),0)),
+		    QSize(contentsWidth()-br.width(),contentsHeight()));
+	    viewport()->erase(r);
+	}
+	if ( br.height() < contentsHeight() ) {
+	    QRect r(contentsToViewport(QPoint(0,br.height())),
+		    QSize(contentsWidth(),contentsHeight()-br.height()));
+	    viewport()->erase(r);
+	}
+
+	resizeContents(br.width(),br.height());
+    } else {
+	viewport()->erase();
+	resizeContents(1,1);
+    }
+}
+
+void QCanvasView::cMoving(int x, int y)
+{
+    // A little kludge to smooth up repaints when scrolling
+    int dx = x - contentsX();
+    int dy = y - contentsY();
+    d->repaint_from_moving = QABS(dx) < width()/8 && QABS(dy) < height()/8;
+}
+
+/*!
+    Repaints part of the QCanvas that the canvas view is showing
+    starting at \a cx by \a cy, with a width of \a cw and a height of \a
+    ch using the painter \a p.
+*/
+void QCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+    QRect r(cx,cy,cw,ch);
+    if (viewing) {
+	//viewing->drawViewArea(this,p,r,TRUE);
+	viewing->drawViewArea(this,p,r,!d->repaint_from_moving);
+	d->repaint_from_moving = FALSE;
+    } else {
+	p->eraseRect(r);
+    }
+}
+
+/*!
+  \reimp
+  \internal
+
+  (Implemented to get rid of a compiler warning.)
+*/
+void QCanvasView::drawContents( QPainter * )
+{
+}
+
+/*!
+    Suggests a size sufficient to view the entire canvas.
+*/
+QSize QCanvasView::sizeHint() const
+{
+    if ( !canvas() )
+	return QScrollView::sizeHint();
+    // should maybe take transformations into account
+    return ( canvas()->size() + 2 * QSize(frameWidth(), frameWidth()) )
+	   .boundedTo( 3 * QApplication::desktop()->size() / 4 );
+}
+
+// ### Qt 4.0 customer request: operate on doubles rather than int.
+// ### I know, almost impossible due to the use of QRegion etc.
+/*!
+    \class QCanvasPolygonalItem qcanvas.h
+    \brief The QCanvasPolygonalItem class provides a polygonal canvas item
+    on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    The mostly rectangular classes, such as QCanvasSprite and
+    QCanvasText, use the object's bounding rectangle for movement,
+    repainting and collision calculations. For most other items, the
+    bounding rectangle can be far too large -- a diagonal line being
+    the worst case, and there are many other cases which are also bad.
+    QCanvasPolygonalItem provides polygon-based bounding rectangle
+    handling, etc., which is much faster for non-rectangular items.
+
+    Derived classes should try to define as small an area as possible
+    to maximize efficiency, but the polygon must \e definitely be
+    contained completely within the polygonal area. Calculating the
+    exact requirements is usually difficult, but if you allow a small
+    overestimate it can be easy and quick, while still getting almost
+    all of QCanvasPolygonalItem's speed.
+
+    Note that all subclasses \e must call hide() in their destructor
+    since hide() needs to be able to access areaPoints().
+
+    Normally, QCanvasPolygonalItem uses the odd-even algorithm for
+    determining whether an object intersects this object. You can
+    change this to the winding algorithm using setWinding().
+
+    The bounding rectangle is available using boundingRect(). The
+    points bounding the polygonal item are retrieved with
+    areaPoints(). Use areaPointsAdvanced() to retrieve the bounding
+    points the polygonal item \e will have after
+    QCanvasItem::advance(1) has been called.
+
+    If the shape of the polygonal item is about to change while the
+    item is visible, call invalidate() before updating with a
+    different result from \l areaPoints().
+
+    By default, QCanvasPolygonalItem objects have a black pen and no
+    brush (the default QPen and QBrush constructors). You can change
+    this with setPen() and setBrush(), but note that some
+    QCanvasPolygonalItem subclasses only use the brush, ignoring the
+    pen setting.
+
+    The polygonal item can be drawn on a painter with draw().
+    Subclasses must reimplement drawShape() to draw themselves.
+
+    Like any other canvas item polygonal items can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting coordinates
+    with QCanvasItem::setX(), QCanvasItem::setY() and QCanvasItem::setZ().
+
+*/
+
+
+/*
+  Since most polygonal items don't have a pen, the default is
+  NoPen and a black brush.
+*/
+static const QPen& defaultPolygonPen()
+{
+    static QPen* dp=0;
+    if ( !dp )
+	dp = new QPen;
+    return *dp;
+}
+
+static const QBrush& defaultPolygonBrush()
+{
+    static QBrush* db=0;
+    if ( !db )
+	db = new QBrush;
+    return *db;
+}
+
+/*!
+    Constructs a QCanvasPolygonalItem on the canvas \a canvas.
+*/
+QCanvasPolygonalItem::QCanvasPolygonalItem(QCanvas* canvas) :
+    QCanvasItem(canvas),
+    br(defaultPolygonBrush()),
+    pn(defaultPolygonPen())
+{
+    wind=0;
+}
+
+/*!
+    Note that all subclasses \e must call hide() in their destructor
+    since hide() needs to be able to access areaPoints().
+*/
+QCanvasPolygonalItem::~QCanvasPolygonalItem()
+{
+}
+
+/*!
+    Returns TRUE if the polygonal item uses the winding algorithm to
+    determine the "inside" of the polygon. Returns FALSE if it uses
+    the odd-even algorithm.
+
+    The default is to use the odd-even algorithm.
+
+    \sa setWinding()
+*/
+bool QCanvasPolygonalItem::winding() const
+{
+    return wind;
+}
+
+/*!
+    If \a enable is TRUE, the polygonal item will use the winding
+    algorithm to determine the "inside" of the polygon; otherwise the
+    odd-even algorithm will be used.
+
+    The default is to use the odd-even algorithm.
+
+    \sa winding()
+*/
+void QCanvasPolygonalItem::setWinding(bool enable)
+{
+    wind = enable;
+}
+
+/*!
+    Invalidates all information about the area covered by the canvas
+    item. The item will be updated automatically on the next call that
+    changes the item's status, for example, move() or update(). Call
+    this function if you are going to change the shape of the item (as
+    returned by areaPoints()) while the item is visible.
+*/
+void QCanvasPolygonalItem::invalidate()
+{
+    val = (uint)FALSE;
+    removeFromChunks();
+}
+
+/*!
+    \fn QCanvasPolygonalItem::isValid() const
+
+    Returns TRUE if the polygonal item's area information has been
+    invalidated; otherwise returns FALSE.
+
+    \sa invalidate()
+*/
+
+/*!
+    Returns the points the polygonal item \e will have after
+    QCanvasItem::advance(1) is called, i.e. what the points are when
+    advanced by the current xVelocity() and yVelocity().
+*/
+QPointArray QCanvasPolygonalItem::areaPointsAdvanced() const
+{
+    int dx = int(x()+xVelocity())-int(x());
+    int dy = int(y()+yVelocity())-int(y());
+    QPointArray r = areaPoints();
+    r.detach(); // Explicit sharing is stupid.
+    if ( dx || dy )
+	r.translate(dx,dy);
+    return r;
+}
+
+//#define QCANVAS_POLYGONS_DEBUG
+#ifdef QCANVAS_POLYGONS_DEBUG
+static QWidget* dbg_wid=0;
+static QPainter* dbg_ptr=0;
+#endif
+
+class QPolygonalProcessor {
+public:
+    QPolygonalProcessor(QCanvas* c, const QPointArray& pa) :
+	canvas(c)
+    {
+	QRect pixelbounds = pa.boundingRect();
+	int cs = canvas->chunkSize();
+	bounds.setLeft(pixelbounds.left()/cs);
+	bounds.setRight(pixelbounds.right()/cs);
+	bounds.setTop(pixelbounds.top()/cs);
+	bounds.setBottom(pixelbounds.bottom()/cs);
+	bitmap = QImage(bounds.width(),bounds.height(),1,2,QImage::LittleEndian);
+	pnt = 0;
+	bitmap.fill(0);
+#ifdef QCANVAS_POLYGONS_DEBUG
+	dbg_start();
+#endif
+    }
+
+    inline void add(int x, int y)
+    {
+	if ( pnt >= (int)result.size() ) {
+	    result.resize(pnt*2+10);
+	}
+	result[pnt++] = QPoint(x+bounds.x(),y+bounds.y());
+#ifdef QCANVAS_POLYGONS_DEBUG
+	if ( dbg_ptr ) {
+	    int cs = canvas->chunkSize();
+	    QRect r(x*cs+bounds.x()*cs,y*cs+bounds.y()*cs,cs-1,cs-1);
+	    dbg_ptr->setPen(Qt::blue);
+	    dbg_ptr->drawRect(r);
+	}
+#endif
+    }
+
+    inline void addBits(int x1, int x2, uchar newbits, int xo, int yo)
+    {
+	for (int i=x1; i<=x2; i++)
+	    if ( newbits & (1<<i) )
+		add(xo+i,yo);
+    }
+
+#ifdef QCANVAS_POLYGONS_DEBUG
+    void dbg_start()
+    {
+	if ( !dbg_wid ) {
+	    dbg_wid = new QWidget;
+	    dbg_wid->resize(800,600);
+	    dbg_wid->show();
+	    dbg_ptr = new QPainter(dbg_wid);
+	    dbg_ptr->setBrush(Qt::NoBrush);
+	}
+	dbg_ptr->fillRect(dbg_wid->rect(),Qt::white);
+    }
+#endif
+
+    void doSpans(int n, QPoint* pt, int* w)
+    {
+	int cs = canvas->chunkSize();
+	for (int j=0; j<n; j++) {
+	    int y = pt[j].y()/cs-bounds.y();
+	    uchar* l = bitmap.scanLine(y);
+	    int x = pt[j].x();
+	    int x1 = x/cs-bounds.x();
+	    int x2 = (x+w[j])/cs-bounds.x();
+	    int x1q = x1/8;
+	    int x1r = x1%8;
+	    int x2q = x2/8;
+	    int x2r = x2%8;
+#ifdef QCANVAS_POLYGONS_DEBUG
+	    if ( dbg_ptr ) dbg_ptr->setPen(Qt::yellow);
+#endif
+	    if ( x1q == x2q ) {
+		uchar newbits = (~l[x1q]) & (((2<<(x2r-x1r))-1)<<x1r);
+		if ( newbits ) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+		    if ( dbg_ptr ) dbg_ptr->setPen(Qt::darkGreen);
+#endif
+		    addBits(x1r,x2r,newbits,x1q*8,y);
+		    l[x1q] |= newbits;
+		}
+	    } else {
+#ifdef QCANVAS_POLYGONS_DEBUG
+		if ( dbg_ptr ) dbg_ptr->setPen(Qt::blue);
+#endif
+		uchar newbits1 = (~l[x1q]) & (0xff<<x1r);
+		if ( newbits1 ) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+		    if ( dbg_ptr ) dbg_ptr->setPen(Qt::green);
+#endif
+		    addBits(x1r,7,newbits1,x1q*8,y);
+		    l[x1q] |= newbits1;
+		}
+		for (int i=x1q+1; i<x2q; i++) {
+		    if ( l[i] != 0xff ) {
+			addBits(0,7,~l[i],i*8,y);
+			l[i]=0xff;
+		    }
+		}
+		uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r));
+		if ( newbits2 ) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+		    if ( dbg_ptr ) dbg_ptr->setPen(Qt::red);
+#endif
+		    addBits(0,x2r,newbits2,x2q*8,y);
+		    l[x2q] |= newbits2;
+		}
+	    }
+#ifdef QCANVAS_POLYGONS_DEBUG
+	    if ( dbg_ptr ) {
+		dbg_ptr->drawLine(pt[j],pt[j]+QPoint(w[j],0));
+	    }
+#endif
+	}
+	result.resize(pnt);
+    }
+
+    int pnt;
+    QPointArray result;
+    QCanvas* canvas;
+    QRect bounds;
+    QImage bitmap;
+};
+
+
+QPointArray QCanvasPolygonalItem::chunks() const
+{
+    QPointArray pa = areaPoints();
+
+    if ( !pa.size() ) {
+	pa.detach(); // Explicit sharing is stupid.
+	return pa;
+    }
+
+    QPolygonalProcessor processor(canvas(),pa);
+
+    scanPolygon(pa, wind, processor);
+
+    return processor.result;
+}
+/*!
+    Simply calls QCanvasItem::chunks().
+*/
+QPointArray QCanvasRectangle::chunks() const
+{
+    // No need to do a polygon scan!
+    return QCanvasItem::chunks();
+}
+
+/*!
+    Returns the bounding rectangle of the polygonal item, based on
+    areaPoints().
+*/
+QRect QCanvasPolygonalItem::boundingRect() const
+{
+    return areaPoints().boundingRect();
+}
+
+/*!
+    Reimplemented from QCanvasItem, this draws the polygonal item by
+    setting the pen and brush for the item on the painter \a p and
+    calling drawShape().
+*/
+void QCanvasPolygonalItem::draw(QPainter & p)
+{
+    p.setPen(pn);
+    p.setBrush(br);
+    drawShape(p);
+}
+
+/*!
+    \fn void QCanvasPolygonalItem::drawShape(QPainter & p)
+
+    Subclasses must reimplement this function to draw their shape. The
+    pen and brush of \a p are already set to pen() and brush() prior
+    to calling this function.
+
+    \sa draw()
+*/
+
+/*!
+    \fn QPen QCanvasPolygonalItem::pen() const
+
+    Returns the QPen used to draw the outline of the item, if any.
+
+    \sa setPen()
+*/
+
+/*!
+    \fn QBrush QCanvasPolygonalItem::brush() const
+
+    Returns the QBrush used to fill the item, if filled.
+
+    \sa setBrush()
+*/
+
+/*!
+    Sets the QPen used when drawing the item to the pen \a p.
+    Note that many QCanvasPolygonalItems do not use the pen value.
+
+    \sa setBrush(), pen(), drawShape()
+*/
+void QCanvasPolygonalItem::setPen(QPen p)
+{
+    if ( pn != p ) {
+	removeFromChunks();
+	pn = p;
+	addToChunks();
+    }
+}
+
+/*!
+    Sets the QBrush used when drawing the polygonal item to the brush \a b.
+
+    \sa setPen(), brush(), drawShape()
+*/
+void QCanvasPolygonalItem::setBrush(QBrush b)
+{
+    if ( br != b) {
+	br = b;
+	changeChunks();
+    }
+}
+
+
+/*!
+    \class QCanvasPolygon qcanvas.h
+    \brief The QCanvasPolygon class provides a polygon on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    Paints a polygon with a QBrush. The polygon's points can be set in
+    the constructor or set or changed later using setPoints(). Use
+    points() to retrieve the points, or areaPoints() to retrieve the
+    points relative to the canvas's origin.
+
+    The polygon can be drawn on a painter with drawShape().
+
+    Like any other canvas item polygons can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
+    coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
+    QCanvasItem::setZ().
+
+    Note: QCanvasPolygon does not use the pen.
+*/
+
+/*!
+    Constructs a point-less polygon on the canvas \a canvas. You
+    should call setPoints() before using it further.
+*/
+QCanvasPolygon::QCanvasPolygon(QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas)
+{
+}
+
+/*!
+    Destroys the polygon.
+*/
+QCanvasPolygon::~QCanvasPolygon()
+{
+    hide();
+}
+
+/*!
+    Draws the polygon using the painter \a p.
+
+    Note that QCanvasPolygon does not support an outline (the pen is
+    always NoPen).
+*/
+void QCanvasPolygon::drawShape(QPainter & p)
+{
+    // ### why can't we draw outlines? We could use drawPolyline for it. Lars
+    // ### see other message. Warwick
+
+    p.setPen(NoPen); // since QRegion(QPointArray) excludes outline :-(  )-:
+    p.drawPolygon(poly);
+}
+
+/*!
+    Sets the points of the polygon to be \a pa. These points will have
+    their x and y coordinates automatically translated by x(), y() as
+    the polygon is moved.
+*/
+void QCanvasPolygon::setPoints(QPointArray pa)
+{
+    removeFromChunks();
+    poly = pa;
+    poly.detach(); // Explicit sharing is stupid.
+    poly.translate((int)x(),(int)y());
+    addToChunks();
+}
+
+/*!
+  \reimp
+*/
+void QCanvasPolygon::moveBy(double dx, double dy)
+{
+    // Note: does NOT call QCanvasPolygonalItem::moveBy(), since that
+    // only does half this work.
+    //
+    int idx = int(x()+dx)-int(x());
+    int idy = int(y()+dy)-int(y());
+    if ( idx || idy ) {
+	removeFromChunks();
+	poly.translate(idx,idy);
+    }
+    myx+=dx;
+    myy+=dy;
+    if ( idx || idy ) {
+	addToChunks();
+    }
+}
+
+/*!
+    \class QCanvasSpline qcanvas.h
+    \brief The QCanvasSpline class provides multi-bezier splines on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A QCanvasSpline is a sequence of 4-point bezier curves joined
+    together to make a curved shape.
+
+    You set the control points of the spline with setControlPoints().
+
+    If the bezier is closed(), then the first control point will be
+    re-used as the last control point. Therefore, a closed bezier must
+    have a multiple of 3 control points and an open bezier must have
+    one extra point.
+
+    The beziers are not necessarily joined "smoothly". To ensure this,
+    set control points appropriately (general reference texts about
+    beziers will explain this in detail).
+
+    Like any other canvas item splines can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
+    coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
+    QCanvasItem::setZ().
+
+*/
+
+/*!
+    Create a spline with no control points on the canvas \a canvas.
+
+    \sa setControlPoints()
+*/
+QCanvasSpline::QCanvasSpline(QCanvas* canvas) :
+    QCanvasPolygon(canvas),
+    cl(TRUE)
+{
+}
+
+/*!
+    Destroy the spline.
+*/
+QCanvasSpline::~QCanvasSpline()
+{
+}
+
+// ### shouldn't we handle errors more gracefully than with an assert? Lars
+// ### no, since it's a programming error. Warwick
+/*!
+    Set the spline control points to \a ctrl.
+
+    If \a close is TRUE, then the first point in \a ctrl will be
+    re-used as the last point, and the number of control points must
+    be a multiple of 3. If \a close is FALSE, one additional control
+    point is required, and the number of control points must be one of
+    (4, 7, 10, 13, ...).
+
+    If the number of control points doesn't meet the above conditions,
+    the number of points will be truncated to the largest number of
+    points that do meet the requirement.
+*/
+void QCanvasSpline::setControlPoints(QPointArray ctrl, bool close)
+{
+    if ( (int)ctrl.count() % 3 != (close ? 0 : 1) ) {
+	qWarning( "QCanvasSpline::setControlPoints(): Number of points doesn't fit." );
+	int numCurves = (ctrl.count() - (close ? 0 : 1 ))/ 3;
+	ctrl.resize( numCurves*3 + ( close ? 0 : 1 ) );
+    }
+
+    cl = close;
+    bez = ctrl;
+    recalcPoly();
+}
+
+/*!
+    Returns the current set of control points.
+
+    \sa setControlPoints(), closed()
+*/
+QPointArray QCanvasSpline::controlPoints() const
+{
+    return bez;
+}
+
+/*!
+    Returns TRUE if the control points are a closed set; otherwise
+    returns FALSE.
+*/
+bool QCanvasSpline::closed() const
+{
+    return cl;
+}
+
+void QCanvasSpline::recalcPoly()
+{
+    QPtrList<QPointArray> segs;
+    segs.setAutoDelete(TRUE);
+    int n=0;
+    for (int i=0; i<(int)bez.count()-1; i+=3) {
+	QPointArray ctrl(4);
+	ctrl[0] = bez[i+0];
+	ctrl[1] = bez[i+1];
+	ctrl[2] = bez[i+2];
+	if ( cl )
+	    ctrl[3] = bez[(i+3)%(int)bez.count()];
+	else
+	    ctrl[3] = bez[i+3];
+	QPointArray *seg = new QPointArray(ctrl.cubicBezier());
+	n += seg->count()-1;
+	segs.append(seg);
+    }
+    QPointArray p(n+1);
+    n=0;
+    for (QPointArray* seg = segs.first(); seg; seg = segs.next()) {
+	for (int i=0; i<(int)seg->count()-1; i++)
+	    p[n++] = seg->point(i);
+	if ( n == (int)p.count()-1 )
+	    p[n] = seg->point(seg->count()-1);
+    }
+    QCanvasPolygon::setPoints(p);
+}
+
+/*!
+    \fn QPointArray QCanvasPolygonalItem::areaPoints() const
+
+    This function must be reimplemented by subclasses. It \e must
+    return the points bounding (i.e. outside and not touching) the
+    shape or drawing errors will occur.
+*/
+
+/*!
+    \fn QPointArray QCanvasPolygon::points() const
+
+    Returns the vertices of the polygon, not translated by the position.
+
+    \sa setPoints(), areaPoints()
+*/
+QPointArray QCanvasPolygon::points() const
+{
+    QPointArray pa = areaPoints();
+    pa.translate(int(-x()),int(-y()));
+    return pa;
+}
+
+/*!
+    Returns the vertices of the polygon translated by the polygon's
+    current x(), y() position, i.e. relative to the canvas's origin.
+
+    \sa setPoints(), points()
+*/
+QPointArray QCanvasPolygon::areaPoints() const
+{
+    return poly.copy();
+}
+
+// ### mark: Why don't we offer a constructor that lets the user set the
+// points -- that way for some uses just the constructor call would be
+// required?
+/*!
+    \class QCanvasLine qcanvas.h
+    \brief The QCanvasLine class provides a line on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    The line inherits functionality from QCanvasPolygonalItem, for
+    example the setPen() function. The start and end points of the
+    line are set with setPoints().
+
+    Like any other canvas item lines can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
+    coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
+    QCanvasItem::setZ().
+*/
+
+/*!
+    Constructs a line from (0,0) to (0,0) on \a canvas.
+
+    \sa setPoints().
+*/
+QCanvasLine::QCanvasLine(QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas)
+{
+    x1 = y1 = x2 = y2 = 0;
+}
+
+/*!
+    Destroys the line.
+*/
+QCanvasLine::~QCanvasLine()
+{
+    hide();
+}
+
+/*!
+  \reimp
+*/
+void QCanvasLine::setPen(QPen p)
+{
+    QCanvasPolygonalItem::setPen(p);
+}
+
+/*!
+    \fn QPoint QCanvasLine::startPoint () const
+
+    Returns the start point of the line.
+
+    \sa setPoints(), endPoint()
+*/
+
+/*!
+    \fn QPoint QCanvasLine::endPoint () const
+
+    Returns the end point of the line.
+
+    \sa setPoints(), startPoint()
+*/
+
+/*!
+    Sets the line's start point to (\a xa, \a ya) and its end point to
+    (\a xb, \a yb).
+*/
+void QCanvasLine::setPoints(int xa, int ya, int xb, int yb)
+{
+    if ( x1 != xa || x2 != xb || y1 != ya || y2 != yb ) {
+	removeFromChunks();
+	x1 = xa;
+	y1 = ya;
+	x2 = xb;
+	y2 = yb;
+	addToChunks();
+    }
+}
+
+/*!
+  \reimp
+*/
+void QCanvasLine::drawShape(QPainter &p)
+{
+    p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2));
+}
+
+/*!
+    \reimp
+
+    Note that the area defined by the line is somewhat thicker than
+    the line that is actually drawn.
+*/
+QPointArray QCanvasLine::areaPoints() const
+{
+    QPointArray p(4);
+    int xi = int(x());
+    int yi = int(y());
+    int pw = pen().width();
+    int dx = QABS(x1-x2);
+    int dy = QABS(y1-y2);
+    pw = pw*4/3+2; // approx pw*sqrt(2)
+    int px = x1<x2 ? -pw : pw ;
+    int py = y1<y2 ? -pw : pw ;
+    if ( dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2)) ) {
+	// steep
+	if ( px == py ) {
+	    p[0] = QPoint(x1+xi   ,y1+yi+py);
+	    p[1] = QPoint(x2+xi-px,y2+yi   );
+	    p[2] = QPoint(x2+xi   ,y2+yi-py);
+	    p[3] = QPoint(x1+xi+px,y1+yi   );
+	} else {
+	    p[0] = QPoint(x1+xi+px,y1+yi   );
+	    p[1] = QPoint(x2+xi   ,y2+yi-py);
+	    p[2] = QPoint(x2+xi-px,y2+yi   );
+	    p[3] = QPoint(x1+xi   ,y1+yi+py);
+	}
+    } else if ( dx > dy ) {
+	// horizontal
+	p[0] = QPoint(x1+xi+px,y1+yi+py);
+	p[1] = QPoint(x2+xi-px,y2+yi+py);
+	p[2] = QPoint(x2+xi-px,y2+yi-py);
+	p[3] = QPoint(x1+xi+px,y1+yi-py);
+    } else {
+	// vertical
+	p[0] = QPoint(x1+xi+px,y1+yi+py);
+	p[1] = QPoint(x2+xi+px,y2+yi-py);
+	p[2] = QPoint(x2+xi-px,y2+yi-py);
+	p[3] = QPoint(x1+xi-px,y1+yi+py);
+    }
+    return p;
+}
+
+/*! 
+    \reimp
+
+*/
+
+void QCanvasLine::moveBy(double dx, double dy)
+{
+    QCanvasPolygonalItem::moveBy(dx, dy);
+}
+
+/*!
+    \class QCanvasRectangle qcanvas.h
+    \brief The QCanvasRectangle class provides a rectangle on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    This item paints a single rectangle which may have any pen() and
+    brush(), but may not be tilted/rotated. For rotated rectangles,
+    use QCanvasPolygon.
+
+    The rectangle's size and initial position can be set in the
+    constructor. The size can be set or changed later using setSize().
+    Use height() and width() to retrieve the rectangle's dimensions.
+
+    The rectangle can be drawn on a painter with drawShape().
+
+    Like any other canvas item rectangles can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
+    coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
+    QCanvasItem::setZ().
+
+*/
+
+/*!
+    Constructs a rectangle at position (0,0) with both width and
+    height set to 32 pixels on \a canvas.
+*/
+QCanvasRectangle::QCanvasRectangle(QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(32), h(32)
+{
+}
+
+/*!
+    Constructs a rectangle positioned and sized by \a r on \a canvas.
+*/
+QCanvasRectangle::QCanvasRectangle(const QRect& r, QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(r.width()), h(r.height())
+{
+    move(r.x(),r.y());
+}
+
+/*!
+    Constructs a rectangle at position (\a x, \a y) and size \a width
+    by \a height, on \a canvas.
+*/
+QCanvasRectangle::QCanvasRectangle(int x, int y, int width, int height,
+	QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(width), h(height)
+{
+    move(x,y);
+}
+
+/*!
+    Destroys the rectangle.
+*/
+QCanvasRectangle::~QCanvasRectangle()
+{
+    hide();
+}
+
+
+/*!
+    Returns the width of the rectangle.
+*/
+int QCanvasRectangle::width() const
+{
+    return w;
+}
+
+/*!
+    Returns the height of the rectangle.
+*/
+int QCanvasRectangle::height() const
+{
+    return h;
+}
+
+/*!
+    Sets the \a width and \a height of the rectangle.
+*/
+void QCanvasRectangle::setSize(int width, int height)
+{
+    if ( w != width || h != height ) {
+	removeFromChunks();
+	w = width;
+	h = height;
+	addToChunks();
+    }
+}
+
+/*!
+    \fn QSize QCanvasRectangle::size() const
+
+    Returns the width() and height() of the rectangle.
+
+    \sa rect(), setSize()
+*/
+
+/*!
+    \fn QRect QCanvasRectangle::rect() const
+
+    Returns the integer-converted x(), y() position and size() of the
+    rectangle as a QRect.
+*/
+
+/*!
+  \reimp
+*/
+QPointArray QCanvasRectangle::areaPoints() const
+{
+    QPointArray pa(4);
+    int pw = (pen().width()+1)/2;
+    if ( pw < 1 ) pw = 1;
+    if ( pen() == NoPen ) pw = 0;
+    pa[0] = QPoint((int)x()-pw,(int)y()-pw);
+    pa[1] = pa[0] + QPoint(w+pw*2,0);
+    pa[2] = pa[1] + QPoint(0,h+pw*2);
+    pa[3] = pa[0] + QPoint(0,h+pw*2);
+    return pa;
+}
+
+/*!
+    Draws the rectangle on painter \a p.
+*/
+void QCanvasRectangle::drawShape(QPainter & p)
+{
+    p.drawRect((int)x(), (int)y(), w, h);
+}
+
+
+/*!
+    \class QCanvasEllipse qcanvas.h
+    \brief The QCanvasEllipse class provides an ellipse or ellipse segment on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A canvas item that paints an ellipse or ellipse segment with a QBrush.
+    The ellipse's height, width, start angle and angle length can be set
+    at construction time. The size can be changed at runtime with
+    setSize(), and the angles can be changed (if you're displaying an
+    ellipse segment rather than a whole ellipse) with setAngles().
+
+    Note that angles are specified in 16ths of a degree.
+
+    \target anglediagram
+    \img qcanvasellipse.png Ellipse
+
+    If a start angle and length angle are set then an ellipse segment
+    will be drawn. The start angle is the angle that goes from zero in a
+    counter-clockwise direction (shown in green in the diagram). The
+    length angle is the angle from the start angle in a
+    counter-clockwise direction (shown in blue in the diagram). The blue
+    segment is the segment of the ellipse that would be drawn. If no
+    start angle and length angle are specified the entire ellipse is
+    drawn.
+
+    The ellipse can be drawn on a painter with drawShape().
+
+    Like any other canvas item ellipses can be moved with move() and
+    moveBy(), or by setting coordinates with setX(), setY() and setZ().
+
+    Note: QCanvasEllipse does not use the pen.
+*/
+
+/*!
+    Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas.
+*/
+QCanvasEllipse::QCanvasEllipse(QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(32), h(32),
+    a1(0), a2(360*16)
+{
+}
+
+/*!
+    Constructs a \a width by \a height pixel ellipse, centered at
+    (0, 0) on \a canvas.
+*/
+QCanvasEllipse::QCanvasEllipse(int width, int height, QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(width),h(height),
+    a1(0),a2(360*16)
+{
+}
+
+// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars
+// ### it's how QPainter does it, so QCanvas does too for consistency. If it's
+// ###  a good idea, it should be added to QPainter, not just to QCanvas. Warwick
+/*!
+    Constructs a \a width by \a height pixel ellipse, centered at
+    (0, 0) on \a canvas. Only a segment of the ellipse is drawn,
+    starting at angle \a startangle, and extending for angle \a angle
+    (the angle length).
+
+    Note that angles are specified in
+    <small><sup>1</sup>/<sub>16</sub></small>ths of a degree.
+*/
+QCanvasEllipse::QCanvasEllipse(int width, int height,
+    int startangle, int angle, QCanvas* canvas) :
+    QCanvasPolygonalItem(canvas),
+    w(width),h(height),
+    a1(startangle),a2(angle)
+{
+}
+
+/*!
+    Destroys the ellipse.
+*/
+QCanvasEllipse::~QCanvasEllipse()
+{
+    hide();
+}
+
+/*!
+    Returns the width of the ellipse.
+*/
+int QCanvasEllipse::width() const
+{
+    return w;
+}
+
+/*!
+    Returns the height of the ellipse.
+*/
+int QCanvasEllipse::height() const
+{
+    return h;
+}
+
+/*!
+    Sets the \a width and \a height of the ellipse.
+*/
+void QCanvasEllipse::setSize(int width, int height)
+{
+    if ( w != width || h != height ) {
+	removeFromChunks();
+	w = width;
+	h = height;
+	addToChunks();
+    }
+}
+
+/*!
+    \fn int QCanvasEllipse::angleStart() const
+
+    Returns the start angle in 16ths of a degree. Initially
+    this will be 0.
+
+    \sa setAngles(), angleLength()
+*/
+
+/*!
+    \fn int QCanvasEllipse::angleLength() const
+
+    Returns the length angle (the extent of the ellipse segment) in
+    16ths of a degree. Initially this will be 360 * 16 (a complete
+    ellipse).
+
+    \sa setAngles(), angleStart()
+*/
+
+/*!
+    Sets the angles for the ellipse. The start angle is \a start and
+    the extent of the segment is \a length (the angle length) from the
+    \a start. The angles are specified in 16ths of a degree. By
+    default the ellipse will start at 0 and have an angle length of
+    360 * 16 (a complete ellipse).
+
+    \sa angleStart(), angleLength()
+*/
+void QCanvasEllipse::setAngles(int start, int length)
+{
+    if ( a1 != start || a2 != length ) {
+	removeFromChunks();
+	a1 = start;
+	a2 = length;
+	addToChunks();
+    }
+}
+
+/*!
+  \reimp
+*/
+QPointArray QCanvasEllipse::areaPoints() const
+{
+    QPointArray r;
+    // makeArc at 0,0, then translate so that fixed point math doesn't overflow
+    r.makeArc(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2);
+    r.resize(r.size()+1);
+    r.setPoint(r.size()-1,int(x()),int(y()));
+    return r;
+}
+
+// ### support outlines! Lars
+// ### QRegion doesn't, so we cannot (try it). Warwick
+/*!
+    Draws the ellipse, centered at x(), y() using the painter \a p.
+
+    Note that QCanvasEllipse does not support an outline (the pen is
+    always NoPen).
+*/
+void QCanvasEllipse::drawShape(QPainter & p)
+{
+    p.setPen(NoPen); // since QRegion(QPointArray) excludes outline :-(  )-:
+    if ( !a1 && a2 == 360*16 ) {
+	p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h);
+    } else {
+	p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2);
+    }
+}
+
+
+/*!
+    \class QCanvasText qcanvas.h
+    \brief The QCanvasText class provides a text object on a QCanvas.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module canvas
+    \ingroup graphics
+    \ingroup images
+
+    A canvas text item has text with font, color and alignment
+    attributes. The text and font can be set in the constructor or set
+    or changed later with setText() and setFont(). The color is set
+    with setColor() and the alignment with setTextFlags(). The text
+    item's bounding rectangle is retrieved with boundingRect().
+
+    The text can be drawn on a painter with draw().
+
+    Like any other canvas item text items can be moved with
+    QCanvasItem::move() and QCanvasItem::moveBy(), or by setting
+    coordinates with QCanvasItem::setX(), QCanvasItem::setY() and
+    QCanvasItem::setZ().
+*/
+
+/*!
+    Constructs a QCanvasText with the text "\<text\>", on \a canvas.
+*/
+QCanvasText::QCanvasText(QCanvas* canvas) :
+    QCanvasItem(canvas),
+    txt("<text>"), flags(0)
+{
+    setRect();
+}
+
+// ### add textflags to the constructor? Lars
+/*!
+    Constructs a QCanvasText with the text \a t, on canvas \a canvas.
+*/
+QCanvasText::QCanvasText(const QString& t, QCanvas* canvas) :
+    QCanvasItem(canvas),
+    txt(t), flags(0)
+{
+    setRect();
+}
+
+// ### see above
+/*!
+    Constructs a QCanvasText with the text \a t and font \a f, on the
+    canvas \a canvas.
+*/
+QCanvasText::QCanvasText(const QString& t, QFont f, QCanvas* canvas) :
+    QCanvasItem(canvas),
+    txt(t), flags(0),
+    fnt(f)
+{
+    setRect();
+}
+
+/*!
+    Destroys the canvas text item.
+*/
+QCanvasText::~QCanvasText()
+{
+    removeFromChunks();
+}
+
+/*!
+    Returns the bounding rectangle of the text.
+*/
+QRect QCanvasText::boundingRect() const { return brect; }
+
+void QCanvasText::setRect()
+{
+    brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt);
+}
+
+/*!
+    \fn int QCanvasText::textFlags() const
+
+    Returns the currently set alignment flags.
+
+    \sa setTextFlags() Qt::AlignmentFlags
+*/
+
+
+/*!
+    Sets the alignment flags to \a f. These are a bitwise OR of the
+    flags available to QPainter::drawText() -- see the
+    \l{Qt::AlignmentFlags}.
+
+    \sa setFont() setColor()
+*/
+void QCanvasText::setTextFlags(int f)
+{
+    if ( flags != f ) {
+	removeFromChunks();
+	flags = f;
+	setRect();
+	addToChunks();
+    }
+}
+
+/*!
+    Returns the text item's text.
+
+    \sa setText()
+*/
+QString QCanvasText::text() const
+{
+    return txt;
+}
+
+
+/*!
+    Sets the text item's text to \a t. The text may contain newlines.
+
+    \sa text(), setFont(), setColor() setTextFlags()
+*/
+void QCanvasText::setText( const QString& t )
+{
+    if ( txt != t ) {
+	removeFromChunks();
+	txt = t;
+	setRect();
+	addToChunks();
+    }
+}
+
+/*!
+    Returns the font in which the text is drawn.
+
+    \sa setFont()
+*/
+QFont QCanvasText::font() const
+{
+    return fnt;
+}
+
+/*!
+    Sets the font in which the text is drawn to font \a f.
+
+    \sa font()
+*/
+void QCanvasText::setFont( const QFont& f )
+{
+    if ( f != fnt ) {
+	removeFromChunks();
+	fnt = f;
+	setRect();
+	addToChunks();
+    }
+}
+
+/*!
+    Returns the color of the text.
+
+    \sa setColor()
+*/
+QColor QCanvasText::color() const
+{
+    return col;
+}
+
+/*!
+    Sets the color of the text to the color \a c.
+
+    \sa color(), setFont()
+*/
+void QCanvasText::setColor(const QColor& c)
+{
+    col=c;
+    changeChunks();
+}
+
+
+/*!
+  \reimp
+*/
+void QCanvasText::moveBy(double dx, double dy)
+{
+    int idx = int(x()+dx)-int(x());
+    int idy = int(y()+dy)-int(y());
+    if ( idx || idy ) {
+	removeFromChunks();
+    }
+    myx+=dx;
+    myy+=dy;
+    if ( idx || idy ) {
+	brect.moveBy(idx,idy);
+	addToChunks();
+    }
+}
+
+/*!
+    Draws the text using the painter \a painter.
+*/
+void QCanvasText::draw(QPainter& painter)
+{
+    painter.setFont(fnt);
+    painter.setPen(col);
+    painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt);
+}
+
+/*!
+  \reimp
+*/
+void QCanvasText::changeChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+	    for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+		canvas()->setChangedChunk(i,j);
+	    }
+	}
+    }
+}
+
+/*!
+    Adds the text item to the appropriate chunks.
+*/
+void QCanvasText::addToChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+	    for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+		canvas()->addItemToChunk(this,i,j);
+	    }
+	}
+    }
+}
+
+/*!
+    Removes the text item from the appropriate chunks.
+*/
+void QCanvasText::removeFromChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+	    for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+		canvas()->removeItemFromChunk(this,i,j);
+	    }
+	}
+    }
+}
+
+
+/*!
+    Returns 0 (QCanvasItem::Rtti_Item).
+
+    Make your derived classes return their own values for rtti(), so
+    that you can distinguish between objects returned by
+    QCanvas::at(). You should use values greater than 1000 to allow
+    for extensions to this class.
+
+    Overuse of this functionality can damage it's extensibility. For
+    example, once you have identified a base class of a QCanvasItem
+    found by QCanvas::at(), cast it to that type and call meaningful
+    methods rather than acting upon the object based on its rtti
+    value.
+
+    For example:
+
+    \code
+	QCanvasItem* item;
+	// Find an item, e.g. with QCanvasItem::collisions().
+	...
+	if (item->rtti() == MySprite::RTTI ) {
+	    MySprite* s = (MySprite*)item;
+	    if (s->isDamagable()) s->loseHitPoints(1000);
+	    if (s->isHot()) myself->loseHitPoints(1000);
+	    ...
+	}
+    \endcode
+*/
+int QCanvasItem::rtti() const { return RTTI; }
+int QCanvasItem::RTTI = Rtti_Item;
+
+/*!
+    Returns 1 (QCanvasItem::Rtti_Sprite).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasSprite::rtti() const { return RTTI; }
+int QCanvasSprite::RTTI = Rtti_Sprite;
+
+/*!
+    Returns 2 (QCanvasItem::Rtti_PolygonalItem).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasPolygonalItem::rtti() const { return RTTI; }
+int QCanvasPolygonalItem::RTTI = Rtti_PolygonalItem;
+
+/*!
+    Returns 3 (QCanvasItem::Rtti_Text).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasText::rtti() const { return Rtti_Text; }
+int QCanvasText::RTTI = Rtti_Text;
+
+/*!
+    Returns 4 (QCanvasItem::Rtti_Polygon).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasPolygon::rtti() const { return RTTI; }
+int QCanvasPolygon::RTTI = Rtti_Polygon;
+
+/*!
+    Returns 5 (QCanvasItem::Rtti_Rectangle).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasRectangle::rtti() const { return RTTI; }
+int QCanvasRectangle::RTTI = Rtti_Rectangle;
+
+/*!
+    Returns 6 (QCanvasItem::Rtti_Ellipse).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasEllipse::rtti() const { return RTTI; }
+int QCanvasEllipse::RTTI = Rtti_Ellipse;
+
+/*!
+    Returns 7 (QCanvasItem::Rtti_Line).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasLine::rtti() const { return RTTI; }
+int QCanvasLine::RTTI = Rtti_Line;
+
+/*!
+    Returns 8 (QCanvasItem::Rtti_Spline).
+
+    \sa QCanvasItem::rtti()
+*/
+int QCanvasSpline::rtti() const { return RTTI; }
+int QCanvasSpline::RTTI = Rtti_Spline;
+
+/*!
+    Constructs a QCanvasSprite which uses images from the
+    QCanvasPixmapArray \a a.
+
+    The sprite in initially positioned at (0, 0) on \a canvas, using
+    frame 0.
+*/
+QCanvasSprite::QCanvasSprite(QCanvasPixmapArray* a, QCanvas* canvas) :
+    QCanvasItem(canvas),
+    frm(0),
+    anim_val(0),
+    anim_state(0),
+    anim_type(0),
+    images(a)
+{
+}
+
+
+/*!
+    Set the array of images used for displaying the sprite to the
+    QCanvasPixmapArray \a a.
+
+    If the current frame() is larger than the number of images in \a
+    a, the current frame will be reset to 0.
+*/
+void QCanvasSprite::setSequence(QCanvasPixmapArray* a)
+{
+    bool isvisible = isVisible();
+    if ( isvisible && images )
+	hide();
+    images = a;
+    if ( frm >= (int)images->count() )
+	frm = 0;
+    if ( isvisible )
+	show();
+}
+
+/*!
+\internal
+
+Marks any chunks the sprite touches as changed.
+*/
+void QCanvasSprite::changeChunks()
+{
+    if (isVisible() && canvas()) {
+	int chunksize=canvas()->chunkSize();
+	for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+	    for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+		canvas()->setChangedChunk(i,j);
+	    }
+	}
+    }
+}
+
+/*!
+    Destroys the sprite and removes it from the canvas. Does \e not
+    delete the images.
+*/
+QCanvasSprite::~QCanvasSprite()
+{
+    removeFromChunks();
+}
+
+/*!
+    Sets the animation frame used for displaying the sprite to \a f,
+    an index into the QCanvasSprite's QCanvasPixmapArray. The call
+    will be ignored if \a f is larger than frameCount() or smaller
+    than 0.
+
+    \sa frame() move()
+*/
+void QCanvasSprite::setFrame(int f)
+{
+    move(x(),y(),f);
+}
+
+/*!
+    \enum QCanvasSprite::FrameAnimationType
+
+    This enum is used to identify the different types of frame
+    animation offered by QCanvasSprite.
+
+    \value Cycle at each advance the frame number will be incremented by
+    1 (modulo the frame count).
+    \value Oscillate at each advance the frame number will be
+    incremented by 1 up to the frame count then decremented to by 1 to
+    0, repeating this sequence forever.
+*/
+
+/*!
+    Sets the animation characteristics for the sprite.
+
+    For \a type == \c Cycle, the frames will increase by \a step
+    at each advance, modulo the frameCount().
+
+    For \a type == \c Oscillate, the frames will increase by \a step
+    at each advance, up to the frameCount(), then decrease by \a step
+    back to 0, repeating forever.
+
+    The \a state parameter is for internal use.
+*/
+void QCanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state)
+{
+    anim_val = step;
+    anim_type = type;
+    anim_state = state;
+    setAnimated(TRUE);
+}
+
+/*!
+    Extends the default QCanvasItem implementation to provide the
+    functionality of setFrameAnimation().
+
+    The \a phase is 0 or 1: see QCanvasItem::advance() for details.
+
+    \sa QCanvasItem::advance() setVelocity()
+*/
+void QCanvasSprite::advance(int phase)
+{
+    if ( phase==1 ) {
+	int nf = frame();
+	if ( anim_type == Oscillate ) {
+	    if ( anim_state )
+		nf += anim_val;
+	    else
+		nf -= anim_val;
+	    if ( nf < 0 ) {
+		nf = abs(anim_val);
+		anim_state = !anim_state;
+	    } else if ( nf >= frameCount() ) {
+		nf = frameCount()-1-abs(anim_val);
+		anim_state = !anim_state;
+	    }
+	} else {
+	    nf = (nf + anim_val + frameCount()) % frameCount();
+	}
+	move(x()+xVelocity(),y()+yVelocity(),nf);
+    }
+}
+
+
+/*!
+    \fn int QCanvasSprite::frame() const
+
+    Returns the index of the current animation frame in the
+    QCanvasSprite's QCanvasPixmapArray.
+
+    \sa setFrame(), move()
+*/
+
+/*!
+    \fn int QCanvasSprite::frameCount() const
+
+    Returns the number of frames in the QCanvasSprite's
+    QCanvasPixmapArray.
+*/
+
+
+/*!
+  \reimp
+  \internal
+  Moves the sprite to the position \a x, \a y.
+    Keep it visible.
+*/
+void QCanvasSprite::move(double x, double y) { QCanvasItem::move(x,y); }
+
+/*!
+    \fn void QCanvasSprite::move(double nx, double ny, int nf)
+
+    Set the position of the sprite to \a nx, \a ny and the current
+    frame to \a nf. \a nf will be ignored if it is larger than
+    frameCount() or smaller than 0.
+*/
+void QCanvasSprite::move(double nx, double ny, int nf)
+{
+    if (isVisible() && canvas()) {
+	hide();
+	QCanvasItem::move(nx,ny);
+	if ( nf >= 0 && nf < frameCount() )
+	    frm=nf;
+	show();
+    } else {
+	QCanvasItem::move(nx,ny);
+	if ( nf >= 0 && nf < frameCount() )
+	    frm=nf;
+    }
+}
+
+class QCanvasPolygonScanner : public QPolygonScanner {
+    QPolygonalProcessor& processor;
+public:
+    QCanvasPolygonScanner(QPolygonalProcessor& p) :
+	processor(p)
+    {
+    }
+    void processSpans( int n, QPoint* point, int* width )
+    {
+	processor.doSpans(n,point,width);
+    }
+};
+
+void QCanvasPolygonalItem::scanPolygon(const QPointArray& pa, int winding, QPolygonalProcessor& process) const
+{
+    QCanvasPolygonScanner scanner(process);
+    scanner.scan(pa,winding);
+}
+
+
+#endif // QT_NO_CANVAS
Index: /trunk/src/canvas/qt_canvas.pri
===================================================================
--- /trunk/src/canvas/qt_canvas.pri	(revision 69)
+++ /trunk/src/canvas/qt_canvas.pri	(revision 69)
@@ -0,0 +1,6 @@
+# Qt canvas module
+
+canvas {
+	HEADERS += $$CANVAS_H/qcanvas.h
+	SOURCES += $$CANVAS_CPP/qcanvas.cpp
+}
Index: /trunk/src/kernel/qpolygonscanner.cpp
===================================================================
--- /trunk/src/kernel/qpolygonscanner.cpp	(revision 69)
+++ /trunk/src/kernel/qpolygonscanner.cpp	(revision 69)
@@ -0,0 +1,940 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of QPolygonScanner class
+**
+** Created : 000120
+**
+** Copyright (C) 1999-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qpolygonscanner.h"
+#include "qpointarray.h"
+#include <stdlib.h>
+
+
+// Based on Xserver code miFillGeneralPoly...
+/*
+ *
+ *     Written by Brian Kelleher;  Oct. 1985
+ *
+ *     Routine to fill a polygon.  Two fill rules are
+ *     supported: frWINDING and frEVENODD.
+ *
+ *     See fillpoly.h for a complete description of the algorithm.
+ */
+
+/*
+ *     These are the data structures needed to scan
+ *     convert regions.  Two different scan conversion
+ *     methods are available -- the even-odd method, and
+ *     the winding number method.
+ *     The even-odd rule states that a point is inside
+ *     the polygon if a ray drawn from that point in any
+ *     direction will pass through an odd number of
+ *     path segments.
+ *     By the winding number rule, a point is decided
+ *     to be inside the polygon if a ray drawn from that
+ *     point in any direction passes through a different
+ *     number of clockwise and counterclockwise path
+ *     segments.
+ *
+ *     These data structures are adapted somewhat from
+ *     the algorithm in (Foley/Van Dam) for scan converting
+ *     polygons.
+ *     The basic algorithm is to start at the top (smallest y)
+ *     of the polygon, stepping down to the bottom of
+ *     the polygon by incrementing the y coordinate.  We
+ *     keep a list of edges which the current scanline crosses,
+ *     sorted by x.  This list is called the Active Edge Table (AET)
+ *     As we change the y-coordinate, we update each entry in
+ *     in the active edge table to reflect the edges new xcoord.
+ *     This list must be sorted at each scanline in case
+ *     two edges intersect.
+ *     We also keep a data structure known as the Edge Table (ET),
+ *     which keeps track of all the edges which the current
+ *     scanline has not yet reached.  The ET is basically a
+ *     list of ScanLineList structures containing a list of
+ *     edges which are entered at a given scanline.  There is one
+ *     ScanLineList per scanline at which an edge is entered.
+ *     When we enter a new edge, we move it from the ET to the AET.
+ *
+ *     From the AET, we can implement the even-odd rule as in
+ *     (Foley/Van Dam).
+ *     The winding number rule is a little trickier.  We also
+ *     keep the EdgeTableEntries in the AET linked by the
+ *     nextWETE (winding EdgeTableEntry) link.  This allows
+ *     the edges to be linked just as before for updating
+ *     purposes, but only uses the edges linked by the nextWETE
+ *     link as edges representing spans of the polygon to
+ *     drawn (as with the even-odd rule).
+ */
+
+/* $XConsortium: miscanfill.h,v 1.5 94/04/17 20:27:50 dpw Exp $ */
+/*
+
+Copyright (c) 1987  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+
+
+/*
+ *     scanfill.h
+ *
+ *     Written by Brian Kelleher; Jan 1985
+ *
+ *     This file contains a few macros to help track
+ *     the edge of a filled object.  The object is assumed
+ *     to be filled in scanline order, and thus the
+ *     algorithm used is an extension of Bresenham's line
+ *     drawing algorithm which assumes that y is always the
+ *     major axis.
+ *     Since these pieces of code are the same for any filled shape,
+ *     it is more convenient to gather the library in one
+ *     place, but since these pieces of code are also in
+ *     the inner loops of output primitives, procedure call
+ *     overhead is out of the question.
+ *     See the author for a derivation if needed.
+ */
+
+/*
+ *  In scan converting polygons, we want to choose those pixels
+ *  which are inside the polygon.  Thus, we add .5 to the starting
+ *  x coordinate for both left and right edges.  Now we choose the
+ *  first pixel which is inside the pgon for the left edge and the
+ *  first pixel which is outside the pgon for the right edge.
+ *  Draw the left pixel, but not the right.
+ *
+ *  How to add .5 to the starting x coordinate:
+ *      If the edge is moving to the right, then subtract dy from the
+ *  error term from the general form of the algorithm.
+ *      If the edge is moving to the left, then add dy to the error term.
+ *
+ *  The reason for the difference between edges moving to the left
+ *  and edges moving to the right is simple:  If an edge is moving
+ *  to the right, then we want the algorithm to flip immediately.
+ *  If it is moving to the left, then we don't want it to flip until
+ *  we traverse an entire pixel.
+ */
+#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
+    int dx;      /* local storage */ \
+\
+    /* \
+     *  if the edge is horizontal, then it is ignored \
+     *  and assumed not to be processed.  Otherwise, do this stuff. \
+     */ \
+    if ((dy) != 0) { \
+        xStart = (x1); \
+        dx = (x2) - xStart; \
+        if (dx < 0) { \
+            m = dx / (dy); \
+            m1 = m - 1; \
+            incr1 = -2 * dx + 2 * (dy) * m1; \
+            incr2 = -2 * dx + 2 * (dy) * m; \
+            d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
+        } else { \
+            m = dx / (dy); \
+            m1 = m + 1; \
+            incr1 = 2 * dx - 2 * (dy) * m1; \
+            incr2 = 2 * dx - 2 * (dy) * m; \
+            d = -2 * m * (dy) + 2 * dx; \
+        } \
+    } \
+}
+
+
+#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
+    if (m1 > 0) { \
+        if (d > 0) { \
+            minval += m1; \
+            d += incr1; \
+        } \
+        else { \
+            minval += m; \
+            d += incr2; \
+        } \
+    } else {\
+        if (d >= 0) { \
+            minval += m1; \
+            d += incr1; \
+        } \
+        else { \
+            minval += m; \
+            d += incr2; \
+        } \
+    } \
+}
+
+
+
+/*
+ *     This structure contains all of the information needed
+ *     to run the bresenham algorithm.
+ *     The variables may be hardcoded into the declarations
+ *     instead of using this structure to make use of
+ *     register declarations.
+ */
+typedef struct {
+    int minor;         /* minor axis        */
+    int d;           /* decision variable */
+    int m, m1;       /* slope and slope+1 */
+    int incr1, incr2; /* error increments */
+} BRESINFO;
+
+
+#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
+	BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \
+                     bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+        BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+typedef struct _EdgeTableEntry {
+     int ymax;             /* ycoord at which we exit this edge. */
+     BRESINFO bres;        /* Bresenham info to run the edge     */
+     struct _EdgeTableEntry *next;       /* next in the list     */
+     struct _EdgeTableEntry *back;       /* for insertion sort   */
+     struct _EdgeTableEntry *nextWETE;   /* for winding num rule */
+     int ClockWise;        /* flag for winding number rule       */
+} EdgeTableEntry;
+
+
+typedef struct _ScanLineList{
+     int scanline;              /* the scanline represented */
+     EdgeTableEntry *edgelist;  /* header node              */
+     struct _ScanLineList *next;  /* next in the list       */
+} ScanLineList;
+
+
+typedef struct {
+     int ymax;                 /* ymax for the polygon     */
+     int ymin;                 /* ymin for the polygon     */
+     ScanLineList scanlines;   /* header node              */
+} EdgeTable;
+
+
+/*
+ * Here is a struct to help with storage allocation
+ * so we can allocate a big chunk at a time, and then take
+ * pieces from this heap when we need to.
+ */
+#define SLLSPERBLOCK 25
+
+typedef struct _ScanLineListBlock {
+     ScanLineList SLLs[SLLSPERBLOCK];
+     struct _ScanLineListBlock *next;
+} ScanLineListBlock;
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines() :  Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ *
+ *     a few macros for the inner loops of the fill code where
+ *     performance considerations don't allow a procedure call.
+ *
+ *     Evaluate the given edge at the given scanline.
+ *     If the edge has expired, then we leave it and fix up
+ *     the active edge table; otherwise, we increment the
+ *     x value to be ready for the next scanline.
+ *     The winding number rule is in effect, so we must notify
+ *     the caller when the edge has been removed so he
+ *     can reorder the Winding Active Edge Table.
+ */
+#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
+   if (pAET->ymax == y) {          /* leaving this edge */ \
+      pPrevAET->next = pAET->next; \
+      pAET = pPrevAET->next; \
+      fixWAET = 1; \
+      if (pAET) \
+         pAET->back = pPrevAET; \
+   } \
+   else { \
+      BRESINCRPGONSTRUCT(pAET->bres); \
+      pPrevAET = pAET; \
+      pAET = pAET->next; \
+   } \
+}
+
+
+/*
+ *     Evaluate the given edge at the given scanline.
+ *     If the edge has expired, then we leave it and fix up
+ *     the active edge table; otherwise, we increment the
+ *     x value to be ready for the next scanline.
+ *     The even-odd rule is in effect.
+ */
+#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
+   if (pAET->ymax == y) {          /* leaving this edge */ \
+      pPrevAET->next = pAET->next; \
+      pAET = pPrevAET->next; \
+      if (pAET) \
+         pAET->back = pPrevAET; \
+   } \
+   else { \
+      BRESINCRPGONSTRUCT(pAET->bres) \
+      pPrevAET = pAET; \
+      pAET = pAET->next; \
+   } \
+}
+
+/***********************************************************
+
+Copyright (c) 1987  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define MAXINT 0x7fffffff
+#define MININT -MAXINT
+
+/*
+ *     fillUtils.c
+ *
+ *     Written by Brian Kelleher;  Oct. 1985
+ *
+ *     This module contains all of the utility functions
+ *     needed to scan convert a polygon.
+ *
+ */
+/*
+ *     InsertEdgeInET
+ *
+ *     Insert the given edge into the edge table.
+ *     First we must find the correct bucket in the
+ *     Edge table, then find the right slot in the
+ *     bucket.  Finally, we can insert it.
+ *
+ */
+bool
+miInsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
+	int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock)
+{
+    register EdgeTableEntry *start, *prev;
+    register ScanLineList *pSLL, *pPrevSLL;
+    ScanLineListBlock *tmpSLLBlock;
+
+    /*
+     * find the right bucket to put the edge into
+     */
+    pPrevSLL = &ET->scanlines;
+    pSLL = pPrevSLL->next;
+    while (pSLL && (pSLL->scanline < scanline))
+    {
+        pPrevSLL = pSLL;
+        pSLL = pSLL->next;
+    }
+
+    /*
+     * reassign pSLL (pointer to ScanLineList) if necessary
+     */
+    if ((!pSLL) || (pSLL->scanline > scanline))
+    {
+        if (*iSLLBlock > SLLSPERBLOCK-1)
+        {
+            tmpSLLBlock =
+		  (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock));
+	    if (!tmpSLLBlock)
+		return FALSE;
+            (*SLLBlock)->next = tmpSLLBlock;
+            tmpSLLBlock->next = 0;
+            *SLLBlock = tmpSLLBlock;
+            *iSLLBlock = 0;
+        }
+        pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+        pSLL->next = pPrevSLL->next;
+        pSLL->edgelist = 0;
+        pPrevSLL->next = pSLL;
+    }
+    pSLL->scanline = scanline;
+
+    /*
+     * now insert the edge in the right bucket
+     */
+    prev = 0;
+    start = pSLL->edgelist;
+    while (start && (start->bres.minor < ETE->bres.minor))
+    {
+        prev = start;
+        start = start->next;
+    }
+    ETE->next = start;
+
+    if (prev)
+        prev->next = ETE;
+    else
+        pSLL->edgelist = ETE;
+    return TRUE;
+}
+
+
+/*
+ *     CreateEdgeTable
+ *
+ *     This routine creates the edge table for
+ *     scan converting polygons.
+ *     The Edge Table (ET) looks like:
+ *
+ *    EdgeTable
+ *     --------
+ *    |  ymax  |        ScanLineLists
+ *    |scanline|-->------------>-------------->...
+ *     --------   |scanline|   |scanline|
+ *                |edgelist|   |edgelist|
+ *                ---------    ---------
+ *                    |             |
+ *                    |             |
+ *                    V             V
+ *              list of ETEs   list of ETEs
+ *
+ *     where ETE is an EdgeTableEntry data structure,
+ *     and there is one ScanLineList per scanline at
+ *     which an edge is initially entered.
+ *
+ */
+
+typedef struct {
+#if defined(Q_OS_MAC)
+    int y, x;
+#else
+    int x, y;
+#endif
+
+} DDXPointRec, *DDXPointPtr;
+
+/*
+ *     Clean up our act.
+ */
+void
+miFreeStorage(ScanLineListBlock   *pSLLBlock)
+{
+    register ScanLineListBlock   *tmpSLLBlock;
+
+    while (pSLLBlock)
+    {
+        tmpSLLBlock = pSLLBlock->next;
+        free(pSLLBlock);
+        pSLLBlock = tmpSLLBlock;
+    }
+}
+
+bool
+miCreateETandAET(int count, DDXPointPtr pts, EdgeTable *ET,
+	EdgeTableEntry *AET, EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
+{
+    register DDXPointPtr top, bottom;
+    register DDXPointPtr PrevPt, CurrPt;
+    int iSLLBlock = 0;
+
+    int dy;
+
+    if (count < 2)  return TRUE;
+
+    /*
+     *  initialize the Active Edge Table
+     */
+    AET->next = 0;
+    AET->back = 0;
+    AET->nextWETE = 0;
+    AET->bres.minor = MININT;
+
+    /*
+     *  initialize the Edge Table.
+     */
+    ET->scanlines.next = 0;
+    ET->ymax = MININT;
+    ET->ymin = MAXINT;
+    pSLLBlock->next = 0;
+
+    PrevPt = &pts[count-1];
+
+    /*
+     *  for each vertex in the array of points.
+     *  In this loop we are dealing with two vertices at
+     *  a time -- these make up one edge of the polygon.
+     */
+    while (count--)
+    {
+        CurrPt = pts++;
+
+        /*
+         *  find out which point is above and which is below.
+         */
+        if (PrevPt->y > CurrPt->y)
+        {
+            bottom = PrevPt, top = CurrPt;
+            pETEs->ClockWise = 0;
+        }
+        else
+        {
+            bottom = CurrPt, top = PrevPt;
+            pETEs->ClockWise = 1;
+        }
+
+        /*
+         * don't add horizontal edges to the Edge table.
+         */
+        if (bottom->y != top->y)
+        {
+            pETEs->ymax = bottom->y-1;  /* -1 so we don't get last scanline */
+
+            /*
+             *  initialize integer edge algorithm
+             */
+            dy = bottom->y - top->y;
+            BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres)
+
+            if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock))
+	    {
+		miFreeStorage(pSLLBlock->next);
+		return FALSE;
+	    }
+
+            ET->ymax = QMAX(ET->ymax, PrevPt->y);
+            ET->ymin = QMIN(ET->ymin, PrevPt->y);
+            pETEs++;
+        }
+
+        PrevPt = CurrPt;
+    }
+    return TRUE;
+}
+
+
+/*
+ *     loadAET
+ *
+ *     This routine moves EdgeTableEntries from the
+ *     EdgeTable into the Active Edge Table,
+ *     leaving them sorted by smaller x coordinate.
+ *
+ */
+
+void
+miloadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
+{
+    register EdgeTableEntry *pPrevAET;
+    register EdgeTableEntry *tmp;
+
+    pPrevAET = AET;
+    AET = AET->next;
+    while (ETEs)
+    {
+        while (AET && (AET->bres.minor < ETEs->bres.minor))
+        {
+            pPrevAET = AET;
+            AET = AET->next;
+        }
+        tmp = ETEs->next;
+        ETEs->next = AET;
+        if (AET)
+            AET->back = ETEs;
+        ETEs->back = pPrevAET;
+        pPrevAET->next = ETEs;
+        pPrevAET = ETEs;
+
+        ETEs = tmp;
+    }
+}
+
+
+/*
+ *     computeWAET
+ *
+ *     This routine links the AET by the
+ *     nextWETE (winding EdgeTableEntry) link for
+ *     use by the winding number rule.  The final
+ *     Active Edge Table (AET) might look something
+ *     like:
+ *
+ *     AET
+ *     ----------  ---------   ---------
+ *     |ymax    |  |ymax    |  |ymax    |
+ *     | ...    |  |...     |  |...     |
+ *     |next    |->|next    |->|next    |->...
+ *     |nextWETE|  |nextWETE|  |nextWETE|
+ *     ---------   ---------   ^--------
+ *         |                   |       |
+ *         V------------------->       V---> ...
+ *
+ */
+void
+micomputeWAET(EdgeTableEntry *AET)
+{
+    register EdgeTableEntry *pWETE;
+    register int inside = 1;
+    register int isInside = 0;
+
+    AET->nextWETE = 0;
+    pWETE = AET;
+    AET = AET->next;
+    while (AET)
+    {
+        if (AET->ClockWise)
+            isInside++;
+        else
+            isInside--;
+
+        if ((!inside && !isInside) ||
+            ( inside &&  isInside))
+        {
+            pWETE->nextWETE = AET;
+            pWETE = AET;
+            inside = !inside;
+        }
+        AET = AET->next;
+    }
+    pWETE->nextWETE = 0;
+}
+
+
+/*
+ *     InsertionSort
+ *
+ *     Just a simple insertion sort using
+ *     pointers and back pointers to sort the Active
+ *     Edge Table.
+ *
+ */
+
+int
+miInsertionSort(EdgeTableEntry *AET)
+{
+    register EdgeTableEntry *pETEchase;
+    register EdgeTableEntry *pETEinsert;
+    register EdgeTableEntry *pETEchaseBackTMP;
+    register int changed = 0;
+
+    AET = AET->next;
+    while (AET)
+    {
+        pETEinsert = AET;
+        pETEchase = AET;
+        while (pETEchase->back->bres.minor > AET->bres.minor)
+            pETEchase = pETEchase->back;
+
+        AET = AET->next;
+        if (pETEchase != pETEinsert)
+        {
+            pETEchaseBackTMP = pETEchase->back;
+            pETEinsert->back->next = AET;
+            if (AET)
+                AET->back = pETEinsert->back;
+            pETEinsert->next = pETEchase;
+            pETEchase->back->next = pETEinsert;
+            pETEchase->back = pETEinsert;
+            pETEinsert->back = pETEchaseBackTMP;
+            changed = 1;
+        }
+    }
+    return(changed);
+}
+
+/*!
+    \overload
+*/
+void QPolygonScanner::scan(const QPointArray& pa, bool winding, int index, int npoints)
+{
+    scan( pa, winding, index, npoints, TRUE );
+}
+
+/*!
+    \overload
+
+    If \a stitchable is FALSE, the right and bottom edges of the
+    polygon are included. This causes adjacent polygons to overlap.
+*/
+void QPolygonScanner::scan(const QPointArray& pa, bool winding, int index, int npoints, bool stitchable)
+{
+    scan( pa, winding, index, npoints,
+	stitchable ? Edge(Left+Top) : Edge(Left+Right+Top+Bottom) );
+}
+
+/*!
+    Calls processSpans() for all scanlines of the polygon defined by
+    \a npoints starting at \a index in \a pa.
+
+    If \a winding is TRUE, the Winding algorithm rather than the
+    Odd-Even rule is used.
+
+    The \a edges is any bitwise combination of:
+    \list
+    \i \c QPolygonScanner::Left
+    \i \c QPolygonScanner::Right
+    \i \c QPolygonScanner::Top
+    \i \c QPolygonScanner::Bottom
+    \endlist
+    \a edges determines which edges are included.
+
+    \warning The edges feature does not work properly.
+
+*/
+void QPolygonScanner::scan( const QPointArray& pa, bool winding, int index, int npoints, Edge edges )
+{
+
+
+    DDXPointPtr ptsIn = (DDXPointPtr)pa.data();
+    ptsIn += index;
+    register EdgeTableEntry *pAET;  /* the Active Edge Table   */
+    register int y;                 /* the current scanline    */
+    register int nPts = 0;          /* number of pts in buffer */
+    register EdgeTableEntry *pWETE; /* Winding Edge Table      */
+    register ScanLineList *pSLL;    /* Current ScanLineList    */
+    register DDXPointPtr ptsOut;      /* ptr to output buffers   */
+    int *width;
+    DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */
+    int FirstWidth[NUMPTSTOBUFFER];
+    EdgeTableEntry *pPrevAET;       /* previous AET entry      */
+    EdgeTable ET;                   /* Edge Table header node  */
+    EdgeTableEntry AET;             /* Active ET header node   */
+    EdgeTableEntry *pETEs;          /* Edge Table Entries buff */
+    ScanLineListBlock SLLBlock;     /* header for ScanLineList */
+    int fixWAET = 0;
+    int edge_l = (edges & Left) ? 1 : 0;
+    int edge_r = (edges & Right) ? 1 : 0;
+    int edge_t = 1; //#### (edges & Top) ? 1 : 0;
+    int edge_b = (edges & Bottom) ? 1 : 0;
+
+    if (npoints == -1)
+	npoints = pa.size();
+
+    if (npoints < 3)
+	return;
+
+    if(!(pETEs = (EdgeTableEntry *)
+        malloc(sizeof(EdgeTableEntry) * npoints)))
+	return;
+    ptsOut = FirstPoint;
+    width = FirstWidth;
+    if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock))
+    {
+	free(pETEs);
+	return;
+    }
+    pSLL = ET.scanlines.next;
+
+    if (!winding)
+    {
+        /*
+         *  for each scanline
+         */
+        for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++)
+        {
+            /*
+             *  Add a new edge to the active edge table when we
+             *  get to the next edge.
+             */
+            if (pSLL && y == pSLL->scanline)
+            {
+                miloadAET(&AET, pSLL->edgelist);
+                pSLL = pSLL->next;
+            }
+            pPrevAET = &AET;
+            pAET = AET.next;
+
+            /*
+             *  for each active edge
+             */
+            while (pAET)
+            {
+                ptsOut->x = pAET->bres.minor + 1 - edge_l;
+		ptsOut++->y = y;
+                *width++ = pAET->next->bres.minor - pAET->bres.minor
+		    - 1 + edge_l + edge_r;
+                nPts++;
+
+                /*
+                 *  send out the buffer when its full
+                 */
+                if (nPts == NUMPTSTOBUFFER)
+		{
+		    processSpans( nPts, (QPoint*)FirstPoint, FirstWidth );
+                    ptsOut = FirstPoint;
+                    width = FirstWidth;
+                    nPts = 0;
+                }
+                EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+                EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+            }
+            miInsertionSort(&AET);
+        }
+    }
+    else      /* default to WindingNumber */
+    {
+        /*
+         *  for each scanline
+         */
+        for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++)
+        {
+            /*
+             *  Add a new edge to the active edge table when we
+             *  get to the next edge.
+             */
+            if (pSLL && y == pSLL->scanline)
+            {
+                miloadAET(&AET, pSLL->edgelist);
+                micomputeWAET(&AET);
+                pSLL = pSLL->next;
+            }
+            pPrevAET = &AET;
+            pAET = AET.next;
+            pWETE = pAET;
+
+            /*
+             *  for each active edge
+             */
+            while (pAET)
+            {
+                /*
+                 *  if the next edge in the active edge table is
+                 *  also the next edge in the winding active edge
+                 *  table.
+                 */
+                if (pWETE == pAET)
+                {
+                    ptsOut->x = pAET->bres.minor + 1 - edge_l;
+		    ptsOut++->y = y;
+                    *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r;
+                    nPts++;
+
+                    /*
+                     *  send out the buffer
+                     */
+                    if (nPts == NUMPTSTOBUFFER)
+                    {
+			processSpans( nPts, (QPoint*)FirstPoint, FirstWidth );
+                        ptsOut = FirstPoint;
+                        width  = FirstWidth;
+                        nPts = 0;
+                    }
+
+                    pWETE = pWETE->nextWETE;
+                    while (pWETE != pAET) {
+                        EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+		    }
+                    pWETE = pWETE->nextWETE;
+                }
+                EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+            }
+
+            /*
+             *  reevaluate the Winding active edge table if we
+             *  just had to resort it or if we just exited an edge.
+             */
+            if (miInsertionSort(&AET) || fixWAET)
+            {
+                micomputeWAET(&AET);
+                fixWAET = 0;
+            }
+        }
+    }
+
+    /*
+     *     Get any spans that we missed by buffering
+     */
+
+
+    processSpans( nPts, (QPoint*)FirstPoint, FirstWidth );
+    free(pETEs);
+    miFreeStorage(SLLBlock.next);
+}
+/***** END OF X11-based CODE *****/
+
+
