diff options
Diffstat (limited to 'asciiquarium/src/screen.cpp')
-rw-r--r-- | asciiquarium/src/screen.cpp | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/asciiquarium/src/screen.cpp b/asciiquarium/src/screen.cpp new file mode 100644 index 00000000..240502ff --- /dev/null +++ b/asciiquarium/src/screen.cpp @@ -0,0 +1,251 @@ +/* + * Asciiquarium - Native KDE Screensaver based on the Asciiquarium program + * (c) Kirk Baucom <kbaucom@schizoid.com>, which you can find at + * http://www.robobunny.com/projects/asciiquarium/ + * + * Ported to KDE by Maksim Orlovich <maksim@kde.org> and + * Michael Pyne <michael.pyne@kdemail.net>. + * + * Copyright (c) 2003 Kirk Baucom <kbaucom@schizoid.com> + * Copyright (c) 2005 Maksim Orlovich <maksim@kde.org> + * Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qcolor.h> +#include <qfontmetrics.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qtimer.h> +#include <qwidget.h> + +#include <kglobalsettings.h> + +#include "screen.h" +#include "sprite.h" +#include "aasaver.h" + +Screen::Screen(AASaver* widget): m_widget(widget) +{ + QFontMetrics fm(KGlobalSettings::fixedFont()); + + // Compute cell geometries. + m_cellW = fm.maxWidth(); + m_cellH = fm.lineSpacing(); + + // Computer number of full cells that will fit. + m_width = widget->width() / m_cellW; + m_height = widget->height() / m_cellH; + + // Calculate offset needed to evenly distribute excess screen space. + m_offX = (widget->width() - m_width * m_cellW) / 2; + m_offY = (widget->height() - m_height * m_cellH) / 2; + + // Create double buffer. + m_backBuffer = QPixmap(m_widget->size()); + m_backBuffer.fill(black); + + // FIXME: handle resizing! + + // Setup animation timer. + QTimer* timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(doAnimate())); + + timer->start(msPerTick()); +} + +int Screen::msPerTick() const +{ + return 50; +} + +Screen::~Screen() +{ +} + +void Screen::updateSpan(int x, int y, const QPixmap &updatePixmap) +{ + if (y < 0 || y >= m_height) return; + + QPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH); + bitBlt(&m_backBuffer, upperLeft, &updatePixmap, updatePixmap.rect(), Qt::CopyROP); + m_widget->update(QRect(upperLeft, updatePixmap.size())); +} + +void Screen::clearSpan(int x, int y, const QPixmap &clearPixmap) +{ + if (y < 0 || y >= m_height) return; + + QPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH); + bitBlt(&m_backBuffer, upperLeft, &clearPixmap, clearPixmap.rect(), Qt::CopyROP); + m_widget->update(QRect(upperLeft, clearPixmap.size())); +} + +//Actually paints the region on the widget. +void Screen::paint(QRegion r) +{ + QPainter p(m_widget); + QMemArray<QRect> rects = r.rects(); + + for (int r = 0; r < rects.size(); ++r) + { + //Determine the grid locations described by the rect + QRect bound = rects[r]; + + bitBlt(m_widget, bound.topLeft(), &m_backBuffer, bound, Qt::CopyROP); + } //for rect in region +}; + +/** + * Utility type used to faciliate sorting of the Sprite list in order to + * implement the Painter's Algorithm when painting the back buffer. + */ +struct ZKey +{ + /** + * Logical depth of sprite. Now 0 is farthest away from the eyes, unlike + * with Sprite::depth(). + */ + int z; + + Sprite* addr; + + ZKey(): z(0), addr(0) + {} + + ZKey(Sprite* spr): z(1000 - spr->depth()), addr(spr) + {} + + bool operator<(const ZKey& other) const + { + if (z < other.z) return true; + if (z > other.z) return false; + + return addr < other.addr; + } +}; + +void Screen::doAnimate() +{ + //First, rebuild a new list of sprites, and build a dirty region + QRegion dirtyRegion; + + QValueVector<Sprite*> sprites; + QValueVector<Sprite*> colliders; + + // Look for sprites that can suffer a collision. + for (unsigned pos = 0; pos < m_sprites.size(); ++pos) + { + if(m_sprites[pos]->canCollide()) + colliders.append(m_sprites[pos]); + } + + // Find collisions. + // FIXME: Use transparent regions for accuracy. + for (unsigned pos = 0; pos < colliders.size(); ++pos) + for (unsigned sprite = 0; sprite < m_sprites.size(); ++sprite) + { + if(m_sprites[sprite] == colliders[pos]) + continue; + + if(colliders[pos]->geom().intersects(m_sprites[sprite]->geom())) + colliders[pos]->collision(m_sprites[sprite]); + } + + //Retain all live existing sprites + for (int pos = 0; pos < m_sprites.size(); ++pos) + { + Sprite* sprite = m_sprites[pos]; + QRect oldRect = sprite->geom(); + if (!sprite->isKilled()) { + bool dirty = sprite->tickUpdate(); + + if (dirty) + dirtyRegion |= oldRect | sprite->geom(); + + if (!sprite->isKilled()) + sprites.append(sprite); + } + + if (sprite->isKilled()) //note:may be made true by updateTick! + { + dirtyRegion |= oldRect; + delete sprite; + } + } + + //Add new sprites. + for (int pos = 0; pos < m_addedSprites.size(); ++pos) + { + dirtyRegion |= m_addedSprites[pos]->geom(); + sprites.append(m_addedSprites[pos]); + } + + m_addedSprites.clear(); + m_sprites = sprites; + + //Compute the list of sprites affected. Note that this is + //done iteratively until fixed point. + QValueVector<Sprite*> paintSprites; + QValueVector<Sprite*> remSprites; + + bool changed; + do + { + changed = false; + remSprites.clear(); + + for (int c = 0; c < sprites.size(); ++c) + { + Sprite* sprite = sprites[c]; + + if (dirtyRegion.intersect(sprite->geom()).isEmpty()) + remSprites.append(sprite); //not to be painted thus far + else + { + //This sprite is to be painted + paintSprites.append(sprite); + + //make sure we repaint everything overlapping it + dirtyRegion |= sprite->geom(); + changed = true; + } + } + sprites = remSprites; + } + while (changed); + + //Z-sort the items. + QMap<ZKey, Sprite* > sorted; + for (int pos = 0; pos < paintSprites.size(); ++pos) + sorted[ZKey(paintSprites[pos])] = paintSprites[pos]; + + //Paint, in Z-order + for (QMapIterator<ZKey, Sprite*> i = sorted.begin(); + i != sorted.end(); ++i) + i.data()->paint(); + + // Make sure black strip at edge is still present. + if(!paintSprites.isEmpty()) + { + QPainter p(&m_backBuffer); + p.fillRect(m_backBuffer.width() - m_offX, 0, m_offX, m_backBuffer.height(), Qt::black); + } +} + +#include "screen.moc" + +// vim: set et ts=8 sw=4: |