KHTML
SVGSMILElement.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 1. Redistributions of source code must retain the above copyright 00008 * notice, this list of conditions and the following disclaimer. 00009 * 2. Redistributions in binary form must reproduce the above copyright 00010 * notice, this list of conditions and the following disclaimer in the 00011 * documentation and/or other materials provided with the distribution. 00012 * 00013 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 00014 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00015 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00016 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 00017 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00018 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00019 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00020 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 00021 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00022 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00023 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00024 */ 00025 00026 #include "config.h" 00027 #if ENABLE(SVG_ANIMATION) 00028 #include "SVGSMILElement.h" 00029 00030 #include "CSSPropertyNames.h" 00031 #include "Document.h" 00032 #include "Event.h" 00033 #include "EventListener.h" 00034 #include "FloatConversion.h" 00035 #include "FrameView.h" 00036 #include "HTMLNames.h" 00037 #include "SVGNames.h" 00038 #include "SVGParserUtilities.h" 00039 #include "SVGSVGElement.h" 00040 #include "SVGURIReference.h" 00041 #include "SMILTimeContainer.h" 00042 #include "XLinkNames.h" 00043 #include <math.h> 00044 #include <wtf/MathExtras.h> 00045 #include <wtf/Vector.h> 00046 00047 using namespace std; 00048 00049 namespace WebCore { 00050 00051 // This is used for duration type time values that can't be negative. 00052 static const double invalidCachedTime = -1.; 00053 00054 class ConditionEventListener : public EventListener { 00055 public: 00056 ConditionEventListener(SVGSMILElement* animation, Element* eventBase, SVGSMILElement::Condition* condition) 00057 : m_animation(animation) 00058 , m_condition(condition) 00059 , m_eventBase(eventBase) 00060 { 00061 m_eventBase->addEventListener(m_condition->m_name, this, false); 00062 } 00063 00064 void unregister() 00065 { 00066 // If this has only one ref then the event base is dead already and we don't need to remove ourself. 00067 if (!hasOneRef()) 00068 m_eventBase->removeEventListener(m_condition->m_name, this, false); 00069 } 00070 00071 virtual void handleEvent(Event* event, bool isWindowEvent) 00072 { 00073 m_animation->handleConditionEvent(event, m_condition); 00074 } 00075 private: 00076 SVGSMILElement* m_animation; 00077 SVGSMILElement::Condition* m_condition; 00078 Element* m_eventBase; 00079 }; 00080 00081 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats) 00082 : m_type(type) 00083 , m_beginOrEnd(beginOrEnd) 00084 , m_baseID(baseID) 00085 , m_name(name) 00086 , m_offset(offset) 00087 , m_repeats(repeats) 00088 { 00089 } 00090 00091 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc) 00092 : SVGElement(tagName, doc) 00093 , m_conditionsConnected(false) 00094 , m_hasEndEventConditions(false) 00095 , m_intervalBegin(SMILTime::unresolved()) 00096 , m_intervalEnd(SMILTime::unresolved()) 00097 , m_previousIntervalBegin(SMILTime::unresolved()) 00098 , m_isWaitingForFirstInterval(true) 00099 , m_activeState(Inactive) 00100 , m_lastPercent(0) 00101 , m_lastRepeat(0) 00102 , m_nextProgressTime(0) 00103 , m_documentOrderIndex(0) 00104 , m_cachedDur(invalidCachedTime) 00105 , m_cachedRepeatDur(invalidCachedTime) 00106 , m_cachedRepeatCount(invalidCachedTime) 00107 , m_cachedMin(invalidCachedTime) 00108 , m_cachedMax(invalidCachedTime) 00109 { 00110 } 00111 00112 SVGSMILElement::~SVGSMILElement() 00113 { 00114 disconnectConditions(); 00115 if (m_timeContainer) 00116 m_timeContainer->unschedule(this); 00117 } 00118 00119 void SVGSMILElement::insertedIntoDocument() 00120 { 00121 SVGElement::insertedIntoDocument(); 00122 #ifndef NDEBUG 00123 // Verify we are not in <use> instance tree. 00124 for (Node* n = this; n; n = n->parent()) 00125 ASSERT(!n->isShadowNode()); 00126 #endif 00127 SVGSVGElement* owner = ownerSVGElement(); 00128 if (!owner) 00129 return; 00130 m_timeContainer = owner->timeContainer(); 00131 ASSERT(m_timeContainer); 00132 m_timeContainer->setDocumentOrderIndexesDirty(); 00133 reschedule(); 00134 } 00135 00136 void SVGSMILElement::removedFromDocument() 00137 { 00138 if (m_timeContainer) { 00139 m_timeContainer->unschedule(this); 00140 m_timeContainer = 0; 00141 } 00142 // Calling disconnectConditions() may kill us if there are syncbase conditions. 00143 // OK, but we don't want to die inside the call. 00144 RefPtr<SVGSMILElement> keepAlive(this); 00145 disconnectConditions(); 00146 SVGElement::removedFromDocument(); 00147 } 00148 00149 void SVGSMILElement::finishParsingChildren() 00150 { 00151 SVGElement::finishParsingChildren(); 00152 00153 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated." 00154 if (!hasAttribute(SVGNames::beginAttr)) 00155 m_beginTimes.append(0); 00156 00157 if (m_isWaitingForFirstInterval) { 00158 resolveFirstInterval(); 00159 reschedule(); 00160 } 00161 } 00162 00163 SMILTime SVGSMILElement::parseOffsetValue(const String& data) 00164 { 00165 bool ok; 00166 double result = 0; 00167 String parse = data.stripWhiteSpace(); 00168 if (parse.endsWith("h")) 00169 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60; 00170 else if (parse.endsWith("min")) 00171 result = parse.left(parse.length() - 3).toDouble(&ok) * 60; 00172 else if (parse.endsWith("ms")) 00173 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000; 00174 else if (parse.endsWith("s")) 00175 result = parse.left(parse.length() - 1).toDouble(&ok); 00176 else 00177 result = parse.toDouble(&ok); 00178 if (!ok) 00179 return SMILTime::unresolved(); 00180 return result; 00181 } 00182 00183 SMILTime SVGSMILElement::parseClockValue(const String& data) 00184 { 00185 if (data.isNull()) 00186 return SMILTime::unresolved(); 00187 00188 String parse = data.stripWhiteSpace(); 00189 00190 static const AtomicString indefiniteValue("indefinite"); 00191 if (parse == indefiniteValue) 00192 return SMILTime::indefinite(); 00193 00194 double result = 0; 00195 bool ok; 00196 int doublePointOne = parse.find(':'); 00197 int doublePointTwo = parse.find(':', doublePointOne + 1); 00198 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) { 00199 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60; 00200 if (!ok) 00201 return SMILTime::unresolved(); 00202 result += parse.substring(3, 2).toUIntStrict(&ok) * 60; 00203 if (!ok) 00204 return SMILTime::unresolved(); 00205 result += parse.substring(6).toDouble(&ok); 00206 } else if (doublePointOne == 2 && doublePointTwo == -1 && parse.length() >= 5) { 00207 result += parse.substring(0, 2).toUIntStrict(&ok) * 60; 00208 if (!ok) 00209 return SMILTime::unresolved(); 00210 result += parse.substring(3).toDouble(&ok); 00211 } else 00212 return parseOffsetValue(parse); 00213 00214 if (!ok) 00215 return SMILTime::unresolved(); 00216 return result; 00217 } 00218 00219 static void sortTimeList(Vector<SMILTime>& timeList) 00220 { 00221 std::sort(timeList.begin(), timeList.end()); 00222 } 00223 00224 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd) 00225 { 00226 String parseString = value.stripWhiteSpace(); 00227 00228 double sign = 1.; 00229 bool ok; 00230 int pos = parseString.find('+'); 00231 if (pos == -1) { 00232 pos = parseString.find('-'); 00233 if (pos != -1) 00234 sign = -1.; 00235 } 00236 String conditionString; 00237 SMILTime offset = 0; 00238 if (pos == -1) 00239 conditionString = parseString; 00240 else { 00241 conditionString = parseString.left(pos).stripWhiteSpace(); 00242 String offsetString = parseString.substring(pos + 1).stripWhiteSpace(); 00243 offset = parseOffsetValue(offsetString); 00244 if (offset.isUnresolved()) 00245 return false; 00246 offset = offset * sign; 00247 } 00248 if (conditionString.isEmpty()) 00249 return false; 00250 pos = conditionString.find('.'); 00251 00252 String baseID; 00253 String nameString; 00254 if (pos == -1) 00255 nameString = conditionString; 00256 else { 00257 baseID = conditionString.left(pos); 00258 nameString = conditionString.substring(pos + 1); 00259 } 00260 if (nameString.isEmpty()) 00261 return false; 00262 00263 Condition::Type type; 00264 int repeats = -1; 00265 if (nameString.startsWith("repeat(") && nameString.endsWith(")")) { 00266 // FIXME: For repeat events we just need to add the data carrying TimeEvent class and 00267 // fire the events at appropriate times. 00268 repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok); 00269 if (!ok) 00270 return false; 00271 nameString = "repeat"; 00272 type = Condition::EventBase; 00273 } else if (nameString == "begin" || nameString == "end") { 00274 if (baseID.isEmpty()) 00275 return false; 00276 type = Condition::Syncbase; 00277 } else if (nameString.startsWith("accesskey(")) { 00278 // FIXME: accesskey() support. 00279 type = Condition::AccessKey; 00280 } else 00281 type = Condition::EventBase; 00282 00283 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats)); 00284 00285 if (type == Condition::EventBase && beginOrEnd == End) 00286 m_hasEndEventConditions = true; 00287 00288 return true; 00289 } 00290 00291 bool SVGSMILElement::isSMILElement(Node* node) 00292 { 00293 if (!node) 00294 return false; 00295 return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag) 00296 || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag); 00297 } 00298 00299 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd) 00300 { 00301 Vector<SMILTime>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes; 00302 if (beginOrEnd == End) 00303 m_hasEndEventConditions = false; 00304 HashSet<double> existing; 00305 for (unsigned n = 0; n < timeList.size(); ++n) 00306 existing.add(timeList[n].value()); 00307 Vector<String> splitString; 00308 parseString.split(';', splitString); 00309 for (unsigned n = 0; n < splitString.size(); ++n) { 00310 SMILTime value = parseClockValue(splitString[n]); 00311 if (value.isUnresolved()) 00312 parseCondition(splitString[n], beginOrEnd); 00313 else if (!existing.contains(value.value())) 00314 timeList.append(value); 00315 } 00316 sortTimeList(timeList); 00317 } 00318 00319 void SVGSMILElement::parseMappedAttribute(MappedAttribute* attr) 00320 { 00321 if (attr->name() == SVGNames::beginAttr) { 00322 if (!m_conditions.isEmpty()) { 00323 disconnectConditions(); 00324 m_conditions.clear(); 00325 parseBeginOrEnd(getAttribute(SVGNames::endAttr), End); 00326 } 00327 parseBeginOrEnd(attr->value().string(), Begin); 00328 if (inDocument()) 00329 connectConditions(); 00330 } else if (attr->name() == SVGNames::endAttr) { 00331 if (!m_conditions.isEmpty()) { 00332 disconnectConditions(); 00333 m_conditions.clear(); 00334 parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin); 00335 } 00336 parseBeginOrEnd(attr->value().string(), End); 00337 if (inDocument()) 00338 connectConditions(); 00339 } else 00340 SVGElement::parseMappedAttribute(attr); 00341 } 00342 00343 void SVGSMILElement::attributeChanged(Attribute* attr, bool preserveDecls) 00344 { 00345 SVGElement::attributeChanged(attr, preserveDecls); 00346 00347 const QualifiedName& attrName = attr->name(); 00348 if (attrName == SVGNames::durAttr) 00349 m_cachedDur = invalidCachedTime; 00350 else if (attrName == SVGNames::repeatDurAttr) 00351 m_cachedRepeatDur = invalidCachedTime; 00352 else if (attrName == SVGNames::repeatCountAttr) 00353 m_cachedRepeatCount = invalidCachedTime; 00354 else if (attrName == SVGNames::minAttr) 00355 m_cachedMin = invalidCachedTime; 00356 else if (attrName == SVGNames::maxAttr) 00357 m_cachedMax = invalidCachedTime; 00358 00359 if (inDocument()) { 00360 if (attrName == SVGNames::beginAttr) 00361 beginListChanged(); 00362 else if (attrName == SVGNames::endAttr) 00363 endListChanged(); 00364 } 00365 } 00366 00367 void SVGSMILElement::connectConditions() 00368 { 00369 if (m_conditionsConnected) 00370 disconnectConditions(); 00371 m_conditionsConnected = true; 00372 for (unsigned n = 0; n < m_conditions.size(); ++n) { 00373 Condition& condition = m_conditions[n]; 00374 if (condition.m_type == Condition::EventBase) { 00375 ASSERT(!condition.m_syncbase); 00376 Element* eventBase = condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID); 00377 if (!eventBase) 00378 continue; 00379 ASSERT(!condition.m_eventListener); 00380 condition.m_eventListener = new ConditionEventListener(this, eventBase, &condition); 00381 } else if (condition.m_type == Condition::Syncbase) { 00382 ASSERT(!condition.m_baseID.isEmpty()); 00383 condition.m_syncbase = document()->getElementById(condition.m_baseID); 00384 if (!isSMILElement(condition.m_syncbase.get())) { 00385 condition.m_syncbase = 0; 00386 continue; 00387 } 00388 SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get()); 00389 syncbase->addTimeDependent(this); 00390 } 00391 } 00392 } 00393 00394 void SVGSMILElement::disconnectConditions() 00395 { 00396 if (!m_conditionsConnected) 00397 return; 00398 m_conditionsConnected = false; 00399 for (unsigned n = 0; n < m_conditions.size(); ++n) { 00400 Condition& condition = m_conditions[n]; 00401 if (condition.m_type == Condition::EventBase) { 00402 ASSERT(!condition.m_syncbase); 00403 if (condition.m_eventListener) { 00404 condition.m_eventListener->unregister(); 00405 condition.m_eventListener = 0; 00406 } 00407 } else if (condition.m_type == Condition::Syncbase) { 00408 if (condition.m_syncbase) { 00409 ASSERT(isSMILElement(condition.m_syncbase.get())); 00410 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this); 00411 } 00412 } 00413 condition.m_syncbase = 0; 00414 } 00415 } 00416 00417 void SVGSMILElement::reschedule() 00418 { 00419 if (m_timeContainer) 00420 m_timeContainer->schedule(this); 00421 } 00422 00423 SVGElement* SVGSMILElement::targetElement() const 00424 { 00425 String href = xlinkHref(); 00426 Node* target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href)); 00427 if (target && target->isSVGElement()) 00428 return static_cast<SVGElement*>(target); 00429 return 0; 00430 } 00431 00432 String SVGSMILElement::attributeName() const 00433 { 00434 return getAttribute(SVGNames::attributeNameAttr).string().stripWhiteSpace(); 00435 } 00436 00437 SMILTime SVGSMILElement::elapsed() const 00438 { 00439 return m_timeContainer ? m_timeContainer->elapsed() : 0; 00440 } 00441 00442 bool SVGSMILElement::isInactive() const 00443 { 00444 return m_activeState == Inactive; 00445 } 00446 00447 bool SVGSMILElement::isFrozen() const 00448 { 00449 return m_activeState == Frozen; 00450 } 00451 00452 SVGSMILElement::Restart SVGSMILElement::restart() const 00453 { 00454 static const AtomicString never("never"); 00455 static const AtomicString whenNotActive("whenNotActive"); 00456 const AtomicString& value = getAttribute(SVGNames::restartAttr); 00457 if (value == never) 00458 return RestartNever; 00459 if (value == whenNotActive) 00460 return RestartWhenNotActive; 00461 return RestartAlways; 00462 } 00463 00464 SVGSMILElement::FillMode SVGSMILElement::fill() const 00465 { 00466 static const AtomicString freeze("freeze"); 00467 const AtomicString& value = getAttribute(SVGNames::fillAttr); 00468 return value == freeze ? FillFreeze : FillRemove; 00469 } 00470 00471 String SVGSMILElement::xlinkHref() const 00472 { 00473 return getAttribute(XLinkNames::hrefAttr); 00474 } 00475 00476 SMILTime SVGSMILElement::dur() const 00477 { 00478 if (m_cachedDur != invalidCachedTime) 00479 return m_cachedDur; 00480 const AtomicString& value = getAttribute(SVGNames::durAttr); 00481 SMILTime clockValue = parseClockValue(value); 00482 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue; 00483 } 00484 00485 SMILTime SVGSMILElement::repeatDur() const 00486 { 00487 if (m_cachedRepeatDur != invalidCachedTime) 00488 return m_cachedRepeatDur; 00489 const AtomicString& value = getAttribute(SVGNames::repeatDurAttr); 00490 SMILTime clockValue = parseClockValue(value); 00491 return m_cachedRepeatDur = clockValue < 0 ? SMILTime::unresolved() : clockValue; 00492 } 00493 00494 // So a count is not really a time but let just all pretend we did not notice. 00495 SMILTime SVGSMILElement::repeatCount() const 00496 { 00497 if (m_cachedRepeatCount != invalidCachedTime) 00498 return m_cachedRepeatCount; 00499 const AtomicString& value = getAttribute(SVGNames::repeatCountAttr); 00500 if (value.isNull()) 00501 return SMILTime::unresolved(); 00502 00503 static const AtomicString indefiniteValue("indefinite"); 00504 if (value == indefiniteValue) 00505 return SMILTime::indefinite(); 00506 bool ok; 00507 double result = value.string().toDouble(&ok); 00508 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved(); 00509 } 00510 00511 SMILTime SVGSMILElement::maxValue() const 00512 { 00513 if (m_cachedMax != invalidCachedTime) 00514 return m_cachedMax; 00515 const AtomicString& value = getAttribute(SVGNames::maxAttr); 00516 SMILTime result = parseClockValue(value); 00517 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result; 00518 } 00519 00520 SMILTime SVGSMILElement::minValue() const 00521 { 00522 if (m_cachedMin != invalidCachedTime) 00523 return m_cachedMin; 00524 const AtomicString& value = getAttribute(SVGNames::minAttr); 00525 SMILTime result = parseClockValue(value); 00526 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result; 00527 } 00528 00529 SMILTime SVGSMILElement::simpleDuration() const 00530 { 00531 return min(dur(), SMILTime::indefinite()); 00532 } 00533 00534 void SVGSMILElement::addBeginTime(SMILTime time) 00535 { 00536 m_beginTimes.append(time); 00537 sortTimeList(m_beginTimes); 00538 beginListChanged(); 00539 } 00540 00541 void SVGSMILElement::addEndTime(SMILTime time) 00542 { 00543 m_endTimes.append(time); 00544 sortTimeList(m_endTimes); 00545 endListChanged(); 00546 } 00547 00548 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const 00549 { 00550 // FIXME: This searches from the beginning which is inefficient. The list is usually not long 00551 // (one entry in common cases) but you can construct a case where it does grow. 00552 const Vector<SMILTime>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes; 00553 for (unsigned n = 0; n < list.size(); ++n) { 00554 SMILTime time = list[n]; 00555 ASSERT(!time.isUnresolved()); 00556 if (time.isIndefinite() && beginOrEnd == Begin) { 00557 // "The special value "indefinite" does not yield an instance time in the begin list." 00558 continue; 00559 } 00560 if (equalsMinimumOK) { 00561 if (time >= minimumTime) 00562 return time; 00563 } else if (time > minimumTime) 00564 return time; 00565 } 00566 return SMILTime::unresolved(); 00567 } 00568 00569 SMILTime SVGSMILElement::repeatingDuration() const 00570 { 00571 // Computing the active duration 00572 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur 00573 SMILTime repeatCount = this->repeatCount(); 00574 SMILTime repeatDur = this->repeatDur(); 00575 SMILTime simpleDuration = this->simpleDuration(); 00576 if (simpleDuration == 0 || (repeatDur.isUnresolved() && repeatCount.isUnresolved())) 00577 return simpleDuration; 00578 SMILTime repeatCountDuration = simpleDuration * repeatCount; 00579 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite())); 00580 } 00581 00582 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const 00583 { 00584 // Computing the active duration 00585 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur 00586 SMILTime preliminaryActiveDuration; 00587 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved()) 00588 preliminaryActiveDuration = resolvedEnd - resolvedBegin; 00589 else if (!resolvedEnd.isFinite()) 00590 preliminaryActiveDuration = repeatingDuration(); 00591 else 00592 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin); 00593 00594 SMILTime minValue = this->minValue(); 00595 SMILTime maxValue = this->maxValue(); 00596 if (minValue > maxValue) { 00597 // Ignore both. 00598 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax 00599 minValue = 0; 00600 maxValue = SMILTime::indefinite(); 00601 } 00602 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration)); 00603 } 00604 00605 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const 00606 { 00607 // See the pseudocode in 00608 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle 00609 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd; 00610 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity(); 00611 while (true) { 00612 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true); 00613 if (tempBegin.isUnresolved()) 00614 break; 00615 SMILTime tempEnd; 00616 if (m_endTimes.isEmpty()) 00617 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite()); 00618 else { 00619 tempEnd = findInstanceTime(End, tempBegin, true); 00620 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd)) 00621 tempEnd = findInstanceTime(End, tempBegin, false); 00622 if (tempEnd.isUnresolved()) { 00623 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions) 00624 break; 00625 } 00626 tempEnd = resolveActiveEnd(tempBegin, tempEnd); 00627 } 00628 if (tempEnd > 0 || !first) { 00629 beginResult = tempBegin; 00630 endResult = tempEnd; 00631 return; 00632 } else if (restart() == RestartNever) 00633 break; 00634 else 00635 beginAfter = tempEnd; 00636 lastIntervalTempEnd = tempEnd; 00637 } 00638 beginResult = SMILTime::unresolved(); 00639 endResult = SMILTime::unresolved(); 00640 } 00641 00642 void SVGSMILElement::resolveFirstInterval() 00643 { 00644 SMILTime begin; 00645 SMILTime end; 00646 resolveInterval(true, begin, end); 00647 ASSERT(!begin.isIndefinite()); 00648 00649 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) { 00650 bool wasUnresolved = m_intervalBegin.isUnresolved(); 00651 m_intervalBegin = begin; 00652 m_intervalEnd = end; 00653 notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval); 00654 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin); 00655 reschedule(); 00656 } 00657 } 00658 00659 void SVGSMILElement::resolveNextInterval() 00660 { 00661 SMILTime begin; 00662 SMILTime end; 00663 resolveInterval(false, begin, end); 00664 ASSERT(!begin.isIndefinite()); 00665 00666 if (!begin.isUnresolved() && begin != m_intervalBegin) { 00667 m_intervalBegin = begin; 00668 m_intervalEnd = end; 00669 notifyDependentsIntervalChanged(NewInterval); 00670 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin); 00671 } 00672 } 00673 00674 SMILTime SVGSMILElement::nextProgressTime() const 00675 { 00676 return m_nextProgressTime; 00677 } 00678 00679 void SVGSMILElement::beginListChanged() 00680 { 00681 SMILTime elapsed = this->elapsed(); 00682 if (m_isWaitingForFirstInterval) 00683 resolveFirstInterval(); 00684 else if (elapsed < m_intervalBegin) { 00685 SMILTime newBegin = findInstanceTime(Begin, elapsed, false); 00686 if (newBegin < m_intervalBegin) { 00687 // Begin time changed, re-resolve the interval. 00688 SMILTime oldBegin = m_intervalBegin; 00689 m_intervalBegin = elapsed; 00690 resolveInterval(false, m_intervalBegin, m_intervalEnd); 00691 ASSERT(!m_intervalBegin.isUnresolved()); 00692 if (m_intervalBegin != oldBegin) 00693 notifyDependentsIntervalChanged(ExistingInterval); 00694 } 00695 } 00696 m_nextProgressTime = elapsed; 00697 reschedule(); 00698 } 00699 00700 void SVGSMILElement::endListChanged() 00701 { 00702 SMILTime elapsed = this->elapsed(); 00703 if (m_isWaitingForFirstInterval) 00704 resolveFirstInterval(); 00705 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) { 00706 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false); 00707 if (newEnd < m_intervalEnd) { 00708 newEnd = resolveActiveEnd(m_intervalBegin, newEnd); 00709 if (newEnd != m_intervalEnd) { 00710 m_intervalEnd = newEnd; 00711 notifyDependentsIntervalChanged(ExistingInterval); 00712 } 00713 } 00714 } 00715 m_nextProgressTime = elapsed; 00716 reschedule(); 00717 } 00718 00719 void SVGSMILElement::checkRestart(SMILTime elapsed) 00720 { 00721 ASSERT(!m_isWaitingForFirstInterval); 00722 ASSERT(elapsed >= m_intervalBegin); 00723 00724 Restart restart = this->restart(); 00725 if (restart == RestartNever) 00726 return; 00727 00728 if (elapsed < m_intervalEnd) { 00729 if (restart != RestartAlways) 00730 return; 00731 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false); 00732 if (nextBegin < m_intervalEnd) { 00733 m_intervalEnd = nextBegin; 00734 notifyDependentsIntervalChanged(ExistingInterval); 00735 } 00736 } 00737 if (elapsed >= m_intervalEnd) 00738 resolveNextInterval(); 00739 } 00740 00741 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const 00742 { 00743 SMILTime simpleDuration = this->simpleDuration(); 00744 repeat = 0; 00745 if (simpleDuration.isIndefinite()) { 00746 repeat = 0; 00747 return 0.f; 00748 } 00749 if (simpleDuration == 0) { 00750 repeat = 0; 00751 return 1.f; 00752 } 00753 ASSERT(m_intervalBegin.isFinite()); 00754 ASSERT(simpleDuration.isFinite()); 00755 SMILTime activeTime = elapsed - m_intervalBegin; 00756 SMILTime repeatingDuration = this->repeatingDuration(); 00757 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) { 00758 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value()); 00759 if (fmod(repeatingDuration.value(), simpleDuration.value() == 0.)) 00760 repeat--; 00761 return 1.f; 00762 } 00763 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value()); 00764 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value()); 00765 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value()); 00766 } 00767 00768 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const 00769 { 00770 if (m_activeState == Active) { 00771 // If duration is indefinite the value does not actually change over time. Same is true for <set>. 00772 SMILTime simpleDuration = this->simpleDuration(); 00773 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) { 00774 SMILTime repeatCount = this->repeatCount(); 00775 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration(); 00776 // We are supposed to do freeze semantics when repeating ends, even if the element is still active. 00777 // Take care that we get a timer callback at that point. 00778 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite()) 00779 return repeatingDurationEnd; 00780 return m_intervalEnd; 00781 } 00782 return elapsed + 0.025; 00783 } 00784 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved(); 00785 } 00786 00787 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const 00788 { 00789 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd) 00790 return Active; 00791 00792 if (m_activeState == Active) 00793 return fill() == FillFreeze ? Frozen : Inactive; 00794 00795 return m_activeState; 00796 } 00797 00798 bool SVGSMILElement::isContributing(SMILTime elapsed) const 00799 { 00800 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove. 00801 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen; 00802 } 00803 00804 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement) 00805 { 00806 ASSERT(m_timeContainer); 00807 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite()); 00808 00809 if (!m_conditionsConnected) 00810 connectConditions(); 00811 00812 if (!m_intervalBegin.isFinite()) { 00813 ASSERT(m_activeState == Inactive); 00814 m_nextProgressTime = SMILTime::unresolved(); 00815 return; 00816 } 00817 00818 if (elapsed < m_intervalBegin) { 00819 ASSERT(m_activeState != Active); 00820 if (m_activeState == Frozen && resultElement) 00821 updateAnimation(m_lastPercent, m_lastRepeat, resultElement); 00822 m_nextProgressTime = m_intervalBegin; 00823 return; 00824 } 00825 00826 m_previousIntervalBegin = m_intervalBegin; 00827 00828 if (m_activeState == Inactive) { 00829 m_isWaitingForFirstInterval = false; 00830 m_activeState = Active; 00831 startedActiveInterval(); 00832 } 00833 00834 unsigned repeat; 00835 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat); 00836 00837 checkRestart(elapsed); 00838 00839 ActiveState oldActiveState = m_activeState; 00840 m_activeState = determineActiveState(elapsed); 00841 00842 if (isContributing(elapsed)) { 00843 if (resultElement) 00844 updateAnimation(percent, repeat, resultElement); 00845 m_lastPercent = percent; 00846 m_lastRepeat = repeat; 00847 } 00848 00849 if (oldActiveState == Active && m_activeState != Active) 00850 endedActiveInterval(); 00851 00852 m_nextProgressTime = calculateNextProgressTime(elapsed); 00853 } 00854 00855 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting) 00856 { 00857 ASSERT(m_intervalBegin.isFinite()); 00858 static HashSet<SVGSMILElement*> loopBreaker; 00859 if (loopBreaker.contains(this)) 00860 return; 00861 loopBreaker.add(this); 00862 00863 TimeDependentSet::iterator end = m_timeDependents.end(); 00864 for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) { 00865 SVGSMILElement* dependent = *it; 00866 dependent->createInstanceTimesFromSyncbase(this, newOrExisting); 00867 } 00868 00869 loopBreaker.remove(this); 00870 } 00871 00872 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval newOrExisting) 00873 { 00874 // FIXME: To be really correct, this should handle updating exising interval by changing 00875 // the associated times instead of creating new ones. 00876 for (unsigned n = 0; n < m_conditions.size(); ++n) { 00877 Condition& condition = m_conditions[n]; 00878 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) { 00879 ASSERT(condition.m_name == "begin" || condition.m_name == "end"); 00880 // No nested time containers in SVG, no need for crazy time space conversions. Phew! 00881 SMILTime time = 0; 00882 if (condition.m_name == "begin") 00883 time = syncbase->m_intervalBegin + condition.m_offset; 00884 else 00885 time = syncbase->m_intervalEnd + condition.m_offset; 00886 ASSERT(time.isFinite()); 00887 if (condition.m_beginOrEnd == Begin) 00888 addBeginTime(time); 00889 else 00890 addEndTime(time); 00891 } 00892 } 00893 } 00894 00895 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation) 00896 { 00897 m_timeDependents.add(animation); 00898 if (m_intervalBegin.isFinite()) 00899 animation->createInstanceTimesFromSyncbase(this, NewInterval); 00900 } 00901 00902 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation) 00903 { 00904 m_timeDependents.remove(animation); 00905 } 00906 00907 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition) 00908 { 00909 if (condition->m_beginOrEnd == Begin) 00910 addBeginTime(elapsed() + condition->m_offset); 00911 else 00912 addEndTime(elapsed() + condition->m_offset); 00913 } 00914 00915 void SVGSMILElement::beginByLinkActivation() 00916 { 00917 addBeginTime(elapsed()); 00918 } 00919 00920 } 00921 00922 #endif 00923
KDE 4.6 API Reference