KSVGTimeScheduler.cpp [plain text]
#include "config.h"
#if SVG_SUPPORT
#include "KSVGTimeScheduler.h"
#include "Document.h"
#include "SVGAnimateColorElement.h"
#include "SVGAnimateTransformElement.h"
#include "SVGAnimatedTransformList.h"
#include "SVGDOMImplementation.h"
#include "SVGMatrix.h"
#include "SVGNames.h"
#include "SVGStyledElement.h"
#include "SVGStyledTransformableElement.h"
#include "SystemTime.h"
#include "Timer.h"
namespace WebCore {
const double staticTimerInterval = 0.050;
typedef HashSet<SVGAnimationElement*> SVGNotifySet;
class SVGTimer : private Timer<TimeScheduler>
{
public:
SVGTimer(TimeScheduler*, double interval, bool singleShot);
void start();
using Timer<TimeScheduler>::stop;
using Timer<TimeScheduler>::isActive;
void notifyAll();
void addNotify(SVGAnimationElement*, bool enabled = false);
void removeNotify(SVGAnimationElement*);
static SVGTimer* downcast(Timer<TimeScheduler>* t) { return static_cast<SVGTimer*>(t); }
private:
double calculateTimePercentage(double elapsed, double start, double end, double duration, double repetitions);
TimeScheduler* m_scheduler;
double m_interval;
bool m_singleShot;
SVGNotifySet m_notifySet;
SVGNotifySet m_enabledNotifySet;
};
SVGTimer::SVGTimer(TimeScheduler* scheduler, double interval, bool singleShot)
: Timer<TimeScheduler>(scheduler, &TimeScheduler::timerFired)
, m_scheduler(scheduler), m_interval(interval), m_singleShot(singleShot)
{
}
void SVGTimer::start()
{
if (m_singleShot)
startOneShot(m_interval);
else
startRepeating(m_interval);
}
double SVGTimer::calculateTimePercentage(double elapsed, double start, double end, double duration, double repetitions)
{
double percentage = 0.0;
double useElapsed = elapsed - (duration * repetitions);
if (duration > 0.0 && end == 0.0)
percentage = 1.0 - (((start + duration) - useElapsed) / duration);
else if (duration > 0.0 && end != 0.0) {
if (duration > end)
percentage = 1.0 - (((start + end) - useElapsed) / end);
else
percentage = 1.0 - (((start + duration) - useElapsed) / duration);
} else if(duration == 0.0 && end != 0.0)
percentage = 1.0 - (((start + end) - useElapsed) / end);
return percentage;
}
void SVGTimer::notifyAll()
{
if (m_enabledNotifySet.isEmpty())
return;
double elapsed = m_scheduler->elapsed() * 1000.0;
typedef HashMap<SVGElement*, Vector<SVGAnimationElement*> > TargetAnimationMap;
TargetAnimationMap targetMap;
SVGNotifySet::const_iterator end = m_notifySet.end();
for (SVGNotifySet::const_iterator it = m_notifySet.begin(); it != end; ++it) {
SVGAnimationElement* animation = *it;
if (!m_enabledNotifySet.contains(animation)) {
if (!animation->isFrozen())
continue;
if (elapsed <= (animation->getStartTime() + animation->getSimpleDuration()))
continue;
}
SVGElement* target = const_cast<SVGElement *>(animation->targetElement());
TargetAnimationMap::iterator i = targetMap.find(target);
if (i != targetMap.end())
i->second.append(animation);
else {
Vector<SVGAnimationElement*> list;
list.append(animation);
targetMap.set(target, list);
}
}
TargetAnimationMap::iterator targetIterator = targetMap.begin();
TargetAnimationMap::iterator tend = targetMap.end();
for (; targetIterator != tend; ++targetIterator) {
HashMap<String, Color> targetColor; RefPtr<SVGTransformList> targetTransforms;
unsigned count = targetIterator->second.size();
for (unsigned i = 0; i < count; ++i) {
SVGAnimationElement* animation = targetIterator->second[i];
double end = animation->getEndTime();
double start = animation->getStartTime();
double duration = animation->getSimpleDuration();
double repetitions = animation->repeations();
if((duration <= 0.0 && end <= 0.0) ||
(animation->isIndefinite(duration) && end <= 0.0)) continue;
float percentage = calculateTimePercentage(elapsed, start, end, duration, repetitions);
if(percentage <= 1.0 || animation->connected())
animation->handleTimerEvent(percentage);
#if 0
if(animation->hasTagName(SVGNames::animateTransformTag))
{
SVGAnimateTransformElement *animTransform = static_cast<SVGAnimateTransformElement *>(animation);
if(!animTransform)
continue;
RefPtr<SVGMatrix> transformMatrix = animTransform->transformMatrix();
if(!transformMatrix)
continue;
RefPtr<SVGMatrix> initialMatrix = animTransform->initialMatrix();
RefPtr<SVGTransform> data = new SVGTransform();
if(!targetTransforms) {
targetTransforms = new SVGTransformList();
if(animation->isAdditive() && initialMatrix)
{
RefPtr<SVGMatrix> matrix = new SVGMatrix(initialMatrix->matrix());
data->setMatrix(matrix.get());
targetTransforms->appendItem(data.get());
data = new SVGTransform();
}
}
if(targetTransforms->numberOfItems() <= 1)
data->setMatrix(transformMatrix.get());
else
{
if(!animation->isAdditive())
targetTransforms->clear();
data->setMatrix(transformMatrix.get());
}
targetTransforms->appendItem(data.get());
}
else
#endif
if(animation->hasTagName(SVGNames::animateColorTag))
{
SVGAnimateColorElement *animColor = static_cast<SVGAnimateColorElement *>(animation);
if(!animColor)
continue;
DeprecatedString name = animColor->attributeName();
Color color = animColor->color();
if(!targetColor.contains(name))
{
if(animation->isAdditive())
{
int r = animColor->initialColor().red() + color.red();
int g = animColor->initialColor().green() + color.green();
int b = animColor->initialColor().blue() + color.blue();
targetColor.set(name, animColor->clampColor(r, g, b));
}
else
targetColor.set(name, color);
}
else
{
if(!animation->isAdditive())
targetColor.set(name, color);
else
{
Color baseColor = targetColor.get(name);
int r = baseColor.red() + color.red();
int g = baseColor.green() + color.green();
int b = baseColor.blue() + color.blue();
targetColor.set(name, animColor->clampColor(r, g, b));
}
}
}
}
if (targetTransforms) {
SVGElement* key = targetIterator->first;
if (key && key->isStyled() && key->isStyledTransformable()) {
SVGStyledTransformableElement *transform = static_cast<SVGStyledTransformableElement *>(key);
transform->transform()->setAnimVal(targetTransforms.get());
transform->updateLocalTransform(transform->transform()->animVal());
}
}
HashMap<String, Color>::iterator cend = targetColor.end();
for(HashMap<String, Color>::iterator cit = targetColor.begin(); cit != cend; ++cit)
{
if(cit->second.isValid())
{
SVGAnimationElement::setTargetAttribute(targetIterator->first,
cit->first.impl(),
String(cit->second.name()).impl());
}
}
}
for (targetIterator = targetMap.begin(); targetIterator != tend; ++targetIterator) {
SVGElement *key = targetIterator->first;
if (key && key->isStyled())
static_cast<SVGStyledElement *>(key)->setChanged(true);
}
}
void SVGTimer::addNotify(SVGAnimationElement* element, bool enabled)
{
m_notifySet.add(element);
if (enabled)
m_enabledNotifySet.add(element);
else
m_enabledNotifySet.remove(element);
}
void SVGTimer::removeNotify(SVGAnimationElement *element)
{
m_enabledNotifySet.remove(element);
if (m_enabledNotifySet.isEmpty())
stop();
}
TimeScheduler::TimeScheduler(Document *document)
: m_creationTime(currentTime()), m_savedTime(0), m_document(document)
{
m_intervalTimer = new SVGTimer(this, staticTimerInterval, false);
}
TimeScheduler::~TimeScheduler()
{
deleteAllValues(m_timerSet);
delete m_intervalTimer;
}
void TimeScheduler::addTimer(SVGAnimationElement* element, unsigned ms)
{
SVGTimer* svgTimer = new SVGTimer(this, ms * 0.001, true);
svgTimer->addNotify(element, true);
m_timerSet.add(svgTimer);
m_intervalTimer->addNotify(element, false);
}
void TimeScheduler::connectIntervalTimer(SVGAnimationElement* element)
{
m_intervalTimer->addNotify(element, true);
}
void TimeScheduler::disconnectIntervalTimer(SVGAnimationElement* element)
{
m_intervalTimer->removeNotify(element);
}
void TimeScheduler::startAnimations()
{
m_creationTime = currentTime();
SVGTimerSet::iterator end = m_timerSet.end();
for (SVGTimerSet::iterator it = m_timerSet.begin(); it != end; ++it) {
SVGTimer* svgTimer = *it;
if (svgTimer && !svgTimer->isActive())
svgTimer->start();
}
}
void TimeScheduler::toggleAnimations()
{
if (m_intervalTimer->isActive()) {
m_intervalTimer->stop();
m_savedTime = currentTime();
} else {
if (m_savedTime != 0) {
m_creationTime += currentTime() - m_savedTime;
m_savedTime = 0;
}
m_intervalTimer->start();
}
}
bool TimeScheduler::animationsPaused() const
{
return !m_intervalTimer->isActive();
}
void TimeScheduler::timerFired(Timer<TimeScheduler>* baseTimer)
{
RefPtr<Document> doc = m_document;
SVGTimer* timer = SVGTimer::downcast(baseTimer);
timer->notifyAll();
if (timer != m_intervalTimer) {
ASSERT(!timer->isActive());
ASSERT(m_timerSet.contains(timer));
m_timerSet.remove(timer);
delete timer;
if (!m_intervalTimer->isActive())
m_intervalTimer->start();
}
doc->updateRendering();
}
double TimeScheduler::elapsed() const
{
return currentTime() - m_creationTime;
}
}
#endif // SVG_SUPPORT