Back to index

kdeartwork  4.3.2
screen.cpp
Go to the documentation of this file.
00001 /*
00002  * Asciiquarium - Native KDE Screensaver based on the Asciiquarium program
00003  *    (c) Kirk Baucom <kbaucom@schizoid.com>, which you can find at
00004  *    http://www.robobunny.com/projects/asciiquarium/
00005  *
00006  * Ported to KDE by Maksim Orlovich <maksim@kde.org> and
00007  * Michael Pyne <michael.pyne@kdemail.net>.
00008  *
00009  * Copyright (c) 2003 Kirk Baucom     <kbaucom@schizoid.com>
00010  * Copyright (c) 2005 Maksim Orlovich <maksim@kde.org>
00011  * Copyright (c) 2005, 2008 Michael Pyne <michael.pyne@kdemail.net>
00012  *
00013  * This program is free software; you can redistribute it and/or
00014  * modify it under the terms of the GNU General Public License
00015  * as published by the Free Software Foundation; either version 2
00016  * of the License, or (at your option) any later version.
00017  *
00018  * This program is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License along with
00024  * this program; if not, write to the Free Software Foundation, Inc., 51
00025  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
00026  */
00027 
00028 #include "screen.h"
00029 
00030 #include <QtCore/QTimer>
00031 #include <QtCore/QList>
00032 #include <QtGui/QColor>
00033 #include <QtGui/QFontMetrics>
00034 #include <QtGui/QPainter>
00035 #include <QtGui/QPixmap>
00036 #include <QtGui/QWidget>
00037 
00038 #include <kglobalsettings.h>
00039 
00040 #include "sprite.h"
00041 #include "aasaver.h"
00042 
00043 Screen::Screen(AASaver* widget): m_widget(widget), m_curPainter(0)
00044 {
00045     QFontMetrics fm(KGlobalSettings::fixedFont());
00046 
00047     // Compute cell geometries.
00048     m_cellW  = fm.maxWidth();
00049     m_cellH  = fm.lineSpacing();
00050 
00051     // Computer number of full cells that will fit.
00052     m_width  = widget->width()  / m_cellW;
00053     m_height = widget->height() / m_cellH;
00054 
00055     // Calculate offset needed to evenly distribute excess screen space.
00056     m_offX = (widget->width() - m_width * m_cellW) / 2;
00057     m_offY = (widget->height() - m_height * m_cellH) / 2;
00058 
00059     // Setup animation timer.
00060     QTimer* timer = new QTimer(this);
00061     connect(timer, SIGNAL(timeout()), m_widget, SLOT(update()));
00062 
00063     timer->start(msPerTick());
00064 }
00065 
00066 int Screen::msPerTick() const
00067 {
00068     return 50;
00069 }
00070 
00071 Screen::~Screen()
00072 {
00073 }
00074 
00075 void Screen::updateSpan(int x, int y, const QPixmap &updatePixmap)
00076 {
00077     if (y < 0 || y >= m_height)
00078         return;
00079 
00080     QPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
00081     m_curPainter->drawPixmap(upperLeft, updatePixmap);
00082 }
00083 
00084 void Screen::clearSpan(int x, int y, const QPixmap &clearPixmap)
00085 {
00086     if (y < 0 || y >= m_height)
00087         return;
00088 
00089     QPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
00090     m_curPainter->fillRect(QRect(upperLeft, clearPixmap.size()), Qt::black);
00091 }
00092 
00093 //Actually paints the region on the widget.
00094 void Screen::paint(QPaintEvent *)
00095 {
00096     if(m_backBuffer.isNull()) {
00097         m_backBuffer = QPixmap(m_widget->size());
00098         m_backBuffer.fill(Qt::black);
00099     }
00100 
00101     { // Artificial scoping for the QPainter
00102         QPainter p(&m_backBuffer);
00103 
00104         // XXX: This is a hack to allow updateSpan and clearSpan access to the
00105         // current QPainter.  Could use some re-architecting.  This sequence
00106         // draws onto the back buffer.
00107         m_curPainter = &p;
00108         doAnimate();
00109         m_curPainter = 0;
00110     }
00111 
00112     // Draw onto the main widget now.
00113     QPainter p(m_widget);
00114     p.setCompositionMode(QPainter::CompositionMode_Source); // bitBlt ftw
00115     p.drawPixmap(0, 0, m_backBuffer);
00116 }
00117 
00122 struct ZKey
00123 {
00128     int      z;
00129 
00130     Sprite*  addr;
00131 
00132     ZKey(): z(0), addr(0)
00133     {}
00134 
00135     ZKey(Sprite* spr): z(1000 - spr->depth()), addr(spr)
00136     {}
00137 
00138     bool operator<(const ZKey& other) const
00139     {
00140         if (z < other.z) return true;
00141         if (z > other.z) return false;
00142 
00143         return addr < other.addr;
00144     }
00145 };
00146 
00147 void Screen::doAnimate()
00148 {
00149     //First, rebuild a new list of sprites, and build a dirty region
00150     QRegion dirtyRegion;
00151 
00152     QList<Sprite*> sprites;
00153     QList<Sprite*> colliders;
00154 
00155     // Look for sprites that can suffer a collision.
00156     foreach(Sprite *sprite, m_sprites) {
00157         if(sprite->canCollide())
00158             colliders.append(sprite);
00159     }
00160 
00161     // Find collisions.
00162     // FIXME: Use transparent regions for accuracy.
00163     foreach(Sprite *collider, colliders) {
00164         foreach(Sprite *sprite, m_sprites) {
00165             // Can't collide with yourself...
00166             if(sprite == collider)
00167                 continue;
00168 
00169             if(collider->geom().intersects(sprite->geom()))
00170                 collider->collision(sprite);
00171         }
00172     }
00173 
00174     //Retain all live existing sprites
00175     foreach(Sprite *sprite, m_sprites) {
00176         QRect oldRect = sprite->geom();
00177 
00178         if (!sprite->isKilled()) {
00179             bool dirty = sprite->tickUpdate();
00180 
00181             if (dirty)
00182                 dirtyRegion |= oldRect | sprite->geom();
00183 
00184             if (!sprite->isKilled())
00185                 sprites.append(sprite);
00186         }
00187 
00188         if (sprite->isKilled()) //note:may be made true by updateTick!
00189         {
00190             dirtyRegion |= oldRect;
00191             delete sprite;
00192         }
00193     }
00194 
00195     //Add new sprites.
00196     foreach(Sprite *sprite, m_addedSprites) {
00197         dirtyRegion |= sprite->geom();
00198         sprites.append(sprite);
00199     }
00200 
00201     m_addedSprites.clear();
00202     m_sprites = sprites;
00203 
00204     //Compute the list of sprites affected. Note that this is
00205     //done iteratively until fixed point.
00206     QList<Sprite*> paintSprites;
00207     QList<Sprite*> remSprites;
00208 
00209     bool changed;
00210     do
00211     {
00212         changed = false;
00213         remSprites.clear();
00214 
00215         foreach(Sprite *sprite, sprites) {
00216             if (dirtyRegion.intersect(sprite->geom()).isEmpty())
00217                 remSprites.append(sprite); //not to be painted thus far
00218             else
00219             {
00220                 //This sprite is to be painted
00221                 paintSprites.append(sprite);
00222 
00223                 //make sure we repaint everything overlapping it
00224                 dirtyRegion |= sprite->geom();
00225                 changed = true;
00226             }
00227         }
00228 
00229         sprites = remSprites;
00230     }
00231     while (changed);
00232 
00233     //Z-sort the items.
00234     QMap<ZKey, Sprite* > sorted;
00235     foreach(Sprite *sprite, paintSprites)
00236         sorted[ZKey(sprite)] = sprite;
00237 
00238     //Paint, in Z-order
00239     foreach(Sprite *sprite, sorted)
00240         sprite->paint();
00241 }
00242 
00243 #include "screen.moc"
00244 
00245 // vim: set et ts=8 sw=4: