summaryrefslogtreecommitdiffstats
path: root/tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp')
-rw-r--r--tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp b/tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp
new file mode 100644
index 00000000..ade5abc1
--- /dev/null
+++ b/tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp
@@ -0,0 +1,222 @@
+#include <tqcolor.h>
+#include <tqfontmetrics.h>
+#include <tqpainter.h>
+#include <tqpixmap.h>
+#include <tqtimer.h>
+#include <tqwidget.h>
+
+#include <tdeglobalsettings.h>
+
+#include "screen.h"
+#include "sprite.h"
+#include "aasaver.h"
+
+Screen::Screen(AASaver* widget): m_widget(widget)
+{
+ TQFontMetrics fm(TDEGlobalSettings::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 = TQPixmap(m_widget->size());
+ m_backBuffer.fill(black);
+
+ // FIXME: handle resizing!
+
+ // Setup animation timer.
+ TQTimer* timer = new TQTimer(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 TQPixmap &updatePixmap)
+{
+ if (y < 0 || y >= m_height) return;
+
+ TQPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
+ bitBlt(&m_backBuffer, upperLeft, &updatePixmap, updatePixmap.rect(), TQt::CopyROP);
+ m_widget->update(TQRect(upperLeft, updatePixmap.size()));
+}
+
+void Screen::clearSpan(int x, int y, const TQPixmap &clearPixmap)
+{
+ if (y < 0 || y >= m_height) return;
+
+ TQPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
+ bitBlt(&m_backBuffer, upperLeft, &clearPixmap, clearPixmap.rect(), TQt::CopyROP);
+ m_widget->update(TQRect(upperLeft, clearPixmap.size()));
+}
+
+//Actually paints the region on the widget.
+void Screen::paint(TQRegion r)
+{
+ TQPainter p(m_widget);
+ TQMemArray<TQRect> rects = r.rects();
+
+ for (int r = 0; r < rects.size(); ++r)
+ {
+ //Determine the grid locations described by the rect
+ TQRect bound = rects[r];
+
+ bitBlt(m_widget, bound.topLeft(), &m_backBuffer, bound, TQt::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
+ TQRegion dirtyRegion;
+
+ TQValueVector<Sprite*> sprites;
+ TQValueVector<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];
+ TQRect 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.
+ TQValueVector<Sprite*> paintSprites;
+ TQValueVector<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.
+ TQMap<ZKey, Sprite* > sorted;
+ for (int pos = 0; pos < paintSprites.size(); ++pos)
+ sorted[ZKey(paintSprites[pos])] = paintSprites[pos];
+
+ //Paint, in Z-order
+ for (TQMapIterator<ZKey, Sprite*> i = sorted.begin();
+ i != sorted.end(); ++i)
+ i.data()->paint();
+
+ // Make sure black strip at edge is still present.
+ if(!paintSprites.isEmpty())
+ {
+ TQPainter p(&m_backBuffer);
+ p.fillRect(m_backBuffer.width() - m_offX, 0, m_offX, m_backBuffer.height(), TQt::black);
+ }
+}
+
+#include "screen.moc"