You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeartwork/tdescreensaver/kdesavers/tdeasciiquarium/screen.cpp

223 lines
5.9 KiB

#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, TQ_SIGNAL(timeout()), TQ_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"