summaryrefslogtreecommitdiffstats
path: root/asciiquarium/src/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'asciiquarium/src/screen.cpp')
-rw-r--r--asciiquarium/src/screen.cpp251
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: