grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
new file mode 100644
index 0000000..b9e65f7
--- /dev/null
+++ b/src/animator/SkDisplayApply.cpp
@@ -0,0 +1,814 @@
+/* libs/graphics/animator/SkDisplayApply.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkDisplayApply.h"
+#include "SkAnimateActive.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayType.h"
+#include "SkDrawGroup.h"
+#include "SkParse.h"
+#include "SkScript.h"
+#include "SkSystemEventTypes.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+#include <ctype.h>
+
+enum SkApply_Properties {
+    SK_PROPERTY(animator),
+    SK_PROPERTY(step),
+    SK_PROPERTY(steps),
+    SK_PROPERTY(time)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+// if no attibutes, enclosed displayable is both scope & target
+// only if both scope & target are specified, or if target and enclosed displayable, are scope and target different
+const SkMemberInfo SkApply::fInfo[] = {
+    SK_MEMBER_PROPERTY(animator, Animate),
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER(dontDraw, Boolean),
+    SK_MEMBER(dynamicScope, String),
+    SK_MEMBER(interval, MSec),  // recommended redraw interval
+    SK_MEMBER(mode, ApplyMode),
+#if 0
+    SK_MEMBER(pickup, Boolean),
+#endif
+    SK_MEMBER(restore, Boolean),
+    SK_MEMBER(scope, Drawable), // thing that scopes animation (unnamed enclosed displayable goes here)
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER_PROPERTY(steps, Int),
+    SK_MEMBER_PROPERTY(time, MSec),
+    SK_MEMBER(transition, ApplyTransition)
+};
+
+#endif 
+
+DEFINE_GET_MEMBER(SkApply);
+
+SkApply::SkApply() : begin(0), dontDraw(false), interval((SkMSec) -1), mode((Mode) -1), /*pickup(false), */
+    restore(false), scope(NULL), steps(-1), transition((Transition) -1), fActive(NULL), /*fCurrentScope(NULL),*/
+    fLastTime(0), fAppended(false), fContainsScope(false), fDeleteScope(false), fEmbedded(false), 
+    fEnabled(false), fEnabling(false) {
+}
+
+SkApply::~SkApply() {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++)
+        delete *curPtr;
+    if (fDeleteScope)
+        delete scope;
+    // !!! caller must call maker.removeActive(fActive)
+    delete fActive;
+}
+
+void SkApply::activate(SkAnimateMaker& maker) {
+    if (fActive != NULL) {
+        if (fActive->fDrawIndex == 0 && fActive->fDrawMax == 0)
+            return; // if only one use, nothing more to do
+        if (restore == false)
+            return; // all share same state, regardless of instance number
+        bool save = fActive->initializeSave();
+        fActive->fixInterpolator(save);
+    } else {
+        fActive = new SkActive(*this, maker);
+        fActive->init();
+        maker.appendActive(fActive);
+        if (restore) {
+            fActive->initializeSave();
+            int animators = fAnimators.count();
+            for (int index = 0; index < animators; index++)
+                fActive->saveInterpolatorValues(index);
+        }
+    }
+}
+
+void SkApply::append(SkApply* apply) {
+    if (fActive == NULL)
+        return;
+    int oldCount = fActive->fAnimators.count();
+    fActive->append(apply);
+    if (restore) {
+        fActive->appendSave(oldCount);
+        int newCount = fActive->fAnimators.count();
+        for (int index = oldCount; index < newCount; index++)
+            fActive->saveInterpolatorValues(index);
+    }
+}
+
+void SkApply::applyValues(int animatorIndex, SkOperand* values, int count,
+     SkDisplayTypes valuesType, SkMSec time) 
+{
+    SkAnimateBase* animator = fActive->fAnimators[animatorIndex];
+    const SkMemberInfo * info = animator->fFieldInfo;
+    SkASSERT(animator);
+    SkASSERT(info != NULL);
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    SkDisplayable* target = getTarget(animator);
+    if (animator->hasExecute() || type == SkType_MemberFunction || type == SkType_MemberProperty) {
+        SkDisplayable* executor = animator->hasExecute() ? animator : target;
+        if (type != SkType_MemberProperty) {
+            SkTDArray<SkScriptValue> typedValues;
+            for (int index = 0; index < count; index++) {
+                SkScriptValue temp;
+                temp.fType = valuesType;
+                temp.fOperand = values[index];
+                *typedValues.append() = temp;
+            }
+            executor->executeFunction(target, info->functionIndex(), typedValues, info->getType(), NULL);
+        } else {
+            SkScriptValue scriptValue;
+            scriptValue.fOperand = values[0];
+            scriptValue.fType = info->getType();
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    } else {
+        SkTypedArray converted;
+        if (type == SkType_ARGB) {
+            if (count == 4) {
+                // !!! assert that it is SkType_Float ?
+                animator->packARGB(&values->fScalar, count, &converted);
+                values = converted.begin();
+                count = converted.count();
+            } else
+                SkASSERT(count == 1);
+        }
+//      SkASSERT(type == SkType_ARGB || type == SkType_String ||info->isSettable());
+        if (type == SkType_String || type == SkType_DynamicString)
+            info->setString(target, values->fString);
+        else if (type == SkType_Drawable || type == SkType_Displayable)
+            target->setReference(info, values->fDisplayable);
+        else
+            info->setValue(target, values, count);
+    } 
+}
+
+bool SkApply::contains(SkDisplayable* child) {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++) {
+        if (*curPtr == child || (*curPtr)->contains(child))
+            return true;
+    }
+    return fDeleteScope && scope == child;
+}
+
+SkDisplayable* SkApply::deepCopy(SkAnimateMaker* maker) {
+    SkDrawable* saveScope = scope;
+    scope = NULL;
+    SkApply* result = (SkApply*) INHERITED::deepCopy(maker);
+    result->scope = scope = saveScope;
+    SkAnimateBase** end = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < end; animPtr++) {
+        SkAnimateBase* anim = (SkAnimateBase*) (*animPtr)->deepCopy(maker);
+        *result->fAnimators.append() = anim;
+        maker->helperAdd(anim);
+    }
+    return result;
+}
+
+void SkApply::disable() {
+    //!!! this is the right thing to do, but has bad side effects because of other problems
+    // currently, if an apply is in a g and scopes a statement in another g, it ends up as members
+    // of both containers. The disabling here incorrectly disables both instances
+    // maybe the fEnabled flag needs to be moved to the fActive data so that both 
+    // instances are not affected.
+//  fEnabled = false;
+}
+
+bool SkApply::draw(SkAnimateMaker& maker) {
+    if (scope ==NULL)
+        return false;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return false;
+    if (fEnabled == false)
+        enable(maker);
+    SkASSERT(scope);
+    activate(maker);
+    if (mode == kMode_immediate)
+        return fActive->draw();
+    bool result = interpolate(maker, maker.getInTime());
+    if (dontDraw == false) {
+//      if (scope->isDrawable())
+            result |= scope->draw(maker);
+    }
+    if (restore) {
+        for (int index = 0; index < fActive->fAnimators.count(); index++)
+            endSave(index);
+        fActive->advance();
+    }
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkApply::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (dynamicScope.isEmpty() == false)
+        SkDebugf("dynamicScope=\"%s\" ", dynamicScope.c_str());
+    if (dontDraw)
+        SkDebugf("dontDraw=\"true\" ");
+    if (begin != 0) //perhaps we want this no matter what?
+        SkDebugf("begin=\"%g\" ", (float) begin/1000.0f);   //is this correct?
+    if (interval != (SkMSec) -1)
+        SkDebugf("interval=\"%g\" ", (float) interval/1000.0f);
+    if (steps != -1)
+        SkDebugf("steps=\"%d\" ", steps);
+    if (restore)
+        SkDebugf("restore=\"true\" ");
+    if (transition == kTransition_reverse)
+        SkDebugf("transition=\"reverse\" ");
+    if (mode == kMode_immediate) {
+        SkDebugf("mode=\"immediate\" ");
+    }
+    else if (mode == kMode_create) {
+        SkDebugf("mode=\"create\" ");
+    }
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    if (scope) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        scope->dump(maker);
+    }
+    int index;
+//  if (fActive) {
+        for (index = 0; index < fAnimators.count(); index++) {
+            if (closedYet == false) {
+                SkDebugf(">\n");
+                closedYet = true;
+            }
+            SkAnimateBase* animator = fAnimators[index];
+            animator->dump(maker);
+//      }
+    }
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+#endif
+
+bool SkApply::enable(SkAnimateMaker& maker) {
+    fEnabled = true;
+    bool initialized = fActive != NULL;
+    if (dynamicScope.size() > 0)
+        enableDynamic(maker);
+    if (maker.fError.hasError()) 
+        return false;
+    int animators = fAnimators.count();
+    int index;
+    for (index = 0; index < animators; index++) {
+        SkAnimateBase* animator = fAnimators[index];
+        animator->fStart = maker.fEnableTime;
+        animator->fResetPending = animator->fReset;
+    }
+    if (scope && scope->isApply())
+        ((SkApply*) scope)->setEmbedded();
+/*  if (mode == kMode_once) {
+        if (scope) {
+            activate(maker);
+            interpolate(maker, maker.fEnableTime);
+            inactivate(maker);
+        }
+        return true;
+    }*/
+    if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL)
+        return false;   // !!! error?
+    bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false);
+    if (mode == kMode_immediate && enableMe || mode == kMode_create)
+        activate(maker);    // for non-drawables like post, prime them here
+    if (mode == kMode_immediate && enableMe)
+        fActive->enable();
+    if (mode == kMode_create && scope != NULL) {
+        enableCreate(maker);
+        return true;
+    }
+    if (mode == kMode_immediate) {
+        return scope->isApply() || scope->isDrawable() == false;
+    }
+    refresh(maker);
+    SkDisplayList& displayList = maker.fDisplayList;
+    SkDrawable* drawable;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = maker.getAppTime();
+    debugOut.appendS32(time - maker.fDebugTimeBase);
+    debugOut.append(" apply enable id=");
+    debugOut.append(_id);
+    debugOut.append("; start=");
+    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    if (scope == NULL || scope->isApply() || scope->getType() == SkType_Movie || scope->isDrawable() == false) {
+        activate(maker);    // for non-drawables like post, prime them here
+        if (initialized) {
+            append(this);
+        }
+        fEnabling = true;
+        interpolate(maker, maker.fEnableTime);
+        fEnabling = false;
+        if (scope != NULL && dontDraw == false)
+            scope->enable(maker);
+        return true;
+    } else if (initialized && restore == false)
+        append(this);
+#if 0
+    bool wasActive = inactivate(maker); // start fresh
+    if (wasActive) {
+        activate(maker);
+        interpolate(maker, maker.fEnableTime);
+        return true;
+    }
+#endif
+//  start here;
+    // now that one apply might embed another, only the parent apply should replace the scope 
+    // or get appended to the display list
+    // similarly, an apply added by an add immediate has already been located in the display list
+    // and should not get moved or added again here
+    if (fEmbedded) {
+        return false;   // already added to display list by embedder
+    }
+    drawable = (SkDrawable*) scope;
+    SkTDDrawableArray* parentList;
+    SkTDDrawableArray* grandList;
+    SkGroup* parentGroup;
+    SkGroup* thisGroup;
+    int old = displayList.findGroup(drawable, &parentList, &parentGroup, &thisGroup, &grandList);
+    if (old < 0)
+        goto append;
+    else if (fContainsScope) {
+        if ((*parentList)[old] != this || restore == true) {
+append:
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            if (parentList->count() < 10000) {
+                fAppended = true;
+                *parentList->append() = this;
+            } else
+                maker.setErrorCode(SkDisplayXMLParserError::kDisplayTreeTooDeep);
+            old = -1;
+        } else
+            reset();
+    } else {
+        SkASSERT(old < parentList->count());
+        if ((*parentList)[old]->isApply()) {
+            SkApply* apply = (SkApply*) (*parentList)[old];
+            if (apply != this && apply->fActive == NULL)
+                apply->activate(maker);
+            apply->append(this);
+            parentGroup = NULL;
+        } else {
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            SkDrawable** newApplyLocation = &(*parentList)[old];
+            SkGroup* pGroup;
+            int oldApply = displayList.findGroup(this, &parentList, &pGroup, &thisGroup, &grandList);
+            if (oldApply >= 0) {
+                (*parentList)[oldApply] = (SkDrawable*) SkDisplayType::CreateInstance(&maker, SkType_Apply);
+                parentGroup = NULL;
+                fDeleteScope = true;
+            }
+            *newApplyLocation = this;
+        }
+    }
+    if (parentGroup) {
+        parentGroup->markCopySet(old);
+        fDeleteScope = dynamicScope.size() == 0;
+    }
+    return true;
+}
+
+void SkApply::enableCreate(SkAnimateMaker& maker) {
+    SkString newID;
+    for (int step = 0; step <= steps; step++) {
+        fLastTime = step * SK_MSec1;
+        bool success = maker.computeID(scope, this, &newID);
+        if (success == false)
+            return;
+        if (maker.find(newID.c_str(), NULL))
+            continue;
+        SkApply* copy = (SkApply*) deepCopy(&maker); // work on copy of animator state
+        if (mode == kMode_create)
+            copy->mode = (Mode) -1;
+        SkDrawable* copyScope = copy->scope = (SkDrawable*) scope->deepCopy(&maker);
+        *fScopes.append() = copyScope;
+        if (copyScope->resolveIDs(maker, scope, this)) {
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        if (newID.size() > 0) 
+            maker.setID(copyScope, newID);
+        if (copy->resolveIDs(maker, this, this)) { // fix up all fields, including target
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        copy->activate(maker);
+        copy->interpolate(maker, step * SK_MSec1);
+        maker.removeActive(copy->fActive);
+    next:
+        delete copy;
+    }
+}
+
+void SkApply::enableDynamic(SkAnimateMaker& maker) {
+    SkASSERT(mode != kMode_create); // create + dynamic are not currently compatible
+    SkDisplayable* newScope;
+    bool success = SkAnimatorScript::EvaluateDisplayable(maker, this, dynamicScope.c_str(),
+        &newScope);
+    if (success && scope != newScope) {
+        SkTDDrawableArray* pList, * gList;
+        SkGroup* pGroup = NULL, * found = NULL;
+        int old = maker.fDisplayList.findGroup(scope, &pList, &pGroup, &found, &gList);
+        if (pList && old >= 0 && (*pList)[old]->isApply() && (*pList)[old] != this) {
+            if (fAppended == false) {
+                if (found != NULL) {
+                    SkDisplayable* oldChild = (*pList)[old];
+                    if (oldChild->isApply() && found->copySet(old)) {
+                        found->markCopyClear(old);
+    //                  delete oldChild;
+                    }
+                }
+                (*pList)[old] = scope;
+            } else
+                pList->remove(old);
+        }
+        scope = (SkDrawable*) newScope;
+        onEndElement(maker);
+    }
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+}
+
+void SkApply::endSave(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo* info = animate->fFieldInfo;
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return;
+    SkDisplayable* target = getTarget(animate);
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    int activeIndex = fActive->fDrawIndex + index;
+    SkOperand* last = new SkOperand[count];
+    SkAutoTDelete<SkOperand> autoLast(last);
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, last, count);
+        SkOperand* saveOperand = fActive->fSaveRestore[activeIndex];
+        if (saveOperand)
+            info->setValue(target, fActive->fSaveRestore[activeIndex], count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success = true);
+        last[0] = scriptValue.fOperand;
+        scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
+        target->setProperty(info->propertyIndex(), scriptValue);
+    }
+    SkOperand* save = fActive->fSaveRestore[activeIndex];
+    if (save)
+        memcpy(save, last, count * sizeof(SkOperand));
+}
+
+bool SkApply::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(step):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = fLastTime / SK_MSec1;
+            break;
+        case SK_PROPERTY(steps):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = steps;
+            break;
+        case SK_PROPERTY(time):
+            value->fType = SkType_MSec;
+            value->fOperand.fS32 = fLastTime;
+            break;
+        default:
+    //      SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkApply::getStep(SkScriptValue* value) {
+    getProperty(SK_PROPERTY(step), value);
+}
+
+SkDrawable* SkApply::getTarget(SkAnimateBase* animate) {
+    if (animate->fTargetIsScope == false || mode != kMode_create)
+        return animate->fTarget;
+    return scope;
+}
+
+bool SkApply::hasDelayedAnimator() const {
+    SkAnimateBase** animEnd = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < animEnd; animPtr++) {
+        SkAnimateBase* animator = *animPtr;
+        if (animator->fDelayed) 
+            return true;
+    }
+    return false;
+}
+
+bool SkApply::hasEnable() const {
+    return true;
+}
+
+bool SkApply::inactivate(SkAnimateMaker& maker) {
+    if (fActive == NULL)
+        return false;
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+    return true;
+}
+
+#ifdef SK_DEBUG
+SkMSec lastTime = (SkMSec) -1;
+#endif
+
+bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) {
+    if (fActive == NULL)
+        return false;
+    bool result = false;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkMSec time = maker.getAppTime();
+    if (lastTime == (SkMSec) -1)
+        lastTime = rawTime - 1;
+    if (fActive != NULL && 
+        strcmp(id, "a3") == 0 && rawTime > lastTime) {
+        lastTime += 1000;
+        SkString debugOut;
+        debugOut.appendS32(time - maker.fDebugTimeBase);
+        debugOut.append(" apply id=");
+        debugOut.append(_id);
+        debugOut.append("; ");
+        debugOut.append(fActive->fAnimators[0]->_id);
+        debugOut.append("=");
+        debugOut.appendS32(rawTime - fActive->fState[0].fStartTime);
+        debugOut.append(")");
+        SkDebugf("%s\n", debugOut.c_str());
+    }
+#endif
+    fActive->start();
+    if (restore)
+        fActive->initializeSave();
+    int animators = fActive->fAnimators.count();
+    for (int inner = 0; inner < animators; inner++) {
+        SkAnimateBase* animate = fActive->fAnimators[inner];
+        if (animate->fChanged) {
+            animate->fChanged = false;
+            animate->fStart = rawTime;
+    //      SkTypedArray values;
+    //      int count = animate->fValues.count();
+    //      values.setCount(count);
+    //      memcpy(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count);
+            animate->onEndElement(maker);
+    //      if (memcmp(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count) != 0) {
+                fActive->append(this);
+                fActive->start();
+    //      }
+        }
+        SkMSec time = fActive->getTime(rawTime, inner);
+        SkActive::SkState& state = fActive->fState[inner];
+        if (SkMSec_LT(rawTime, state.fStartTime)) {
+            if (fEnabling) {
+                animate->fDelayed = true;
+                maker.delayEnable(this, state.fStartTime);
+            }
+            continue;
+        } else
+            animate->fDelayed = false;
+        SkMSec innerTime = fLastTime = state.getRelativeTime(time);
+        if (restore) 
+            fActive->restoreInterpolatorValues(inner);
+        if (animate->fReset) {
+            if (transition != SkApply::kTransition_reverse) {
+                if (SkMSec_LT(state.fBegin + state.fDuration, innerTime)) {
+                    if (animate->fResetPending) {
+                        innerTime = 0;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+                }
+            } else if (innerTime == 0) {
+                    if (animate->fResetPending) {
+                        innerTime = state.fBegin + state.fDuration;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+            }
+        }
+        int count = animate->components();
+        SkAutoSTMalloc<16, SkOperand> values(count);
+        SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues(
+            innerTime, values.get());
+        result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result);
+        if ((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result ||
+                transition == SkApply::kTransition_reverse && fLastTime == 0) && state.fUnpostedEndEvent) {
+//          SkDEBUGF(("interpolate: post on end\n"));
+            state.fUnpostedEndEvent = false;
+            maker.postOnEnd(animate, state.fBegin + state.fDuration);
+            maker.fAdjustedStart = 0;    // !!! left over from synchronizing animation days, undoubtably out of date (and broken)
+        }
+        if (animate->formula.size() > 0) {
+            if (fLastTime > animate->dur)
+                fLastTime = animate->dur;
+            SkTypedArray formulaValues;
+            formulaValues.setCount(count);
+            bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL, 
+                animate->getValuesType(), animate->formula);
+            SkASSERT(success);
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, formulaValues.begin(), count, animate->getValuesType(), innerTime);
+        } else {
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, values.get(), count, animate->getValuesType(), innerTime);
+        }
+    }
+    return result;
+}
+
+void SkApply::initialize() {
+    if (scope == NULL)
+        return;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return;
+    scope->initialize();
+}
+
+void SkApply::onEndElement(SkAnimateMaker& maker) 
+{
+    SkDrawable* scopePtr = scope;
+    while (scopePtr && scopePtr->isApply()) {
+        SkApply* scopedApply = (SkApply*) scopePtr;
+        if (scopedApply->scope == this) {
+            maker.setErrorCode(SkDisplayXMLParserError::kApplyScopesItself);
+            return;
+        }
+        scopePtr = scopedApply->scope;
+    }
+    if (mode == kMode_create)
+        return;
+    if (scope != NULL && steps >= 0 && scope->isApply() == false && scope->isDrawable())
+        scope->setSteps(steps);
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* anim = *animPtr;
+        //for reusing apply statements with dynamic scope
+        if (anim->fTarget == NULL || anim->fTargetIsScope) {
+            anim->fTargetIsScope = true;
+            if (scope)
+                anim->fTarget = scope;
+            else
+                anim->setTarget(maker);
+            anim->onEndElement(maker);  // allows animate->fFieldInfo to be set
+        }
+        if (scope != NULL && steps >= 0 && anim->fTarget != scope && anim->fTarget->isDrawable())
+            anim->fTarget->setSteps(steps);
+    }
+}
+
+const SkMemberInfo* SkApply::preferredChild(SkDisplayTypes type) {
+    SkASSERT(SkDisplayType::IsAnimate(type) == false);
+    fContainsScope = true;
+    return getMember("scope"); // !!! cwap! need to refer to member through enum like kScope instead
+}
+
+void SkApply::refresh(SkAnimateMaker& maker) {
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* animate = *animPtr;
+        animate->onEndElement(maker);
+    }
+    if (fActive)
+        fActive->resetInterpolators();
+}
+
+void SkApply::reset() {
+    if (fActive)
+        fActive->resetState();
+}
+
+bool SkApply::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) { //   replace to/formula strings in animators of the form xxx.step with the step value, if xxx.step is in scope
+    if (resolveField(maker, apply, &dynamicScope) == false)
+        return true;    // failed
+    SkAnimateBase** endPtr = fAnimators.end();
+    SkAnimateBase** origPtr = ((SkApply*) original)->fAnimators.begin();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < endPtr; ) {
+        SkAnimateBase* animator = *animPtr++;
+        maker.resolveID(animator, *origPtr++);
+        if (resolveField(maker, this, &animator->target) == false)
+            return true;
+        if (resolveField(maker, this, &animator->from) == false)
+            return true;
+        if (resolveField(maker, this, &animator->to) == false)
+            return true;
+        if (resolveField(maker, this, &animator->formula) == false)
+            return true;
+    }
+//  setEmbedded();
+    onEndElement(maker);
+    return false; // succeeded
+}
+
+bool SkApply::resolveField(SkAnimateMaker& maker, SkDisplayable* parent, SkString* str) {
+    const char* script = str->c_str();
+    if (str->startsWith("#string:") == false)
+        return true;
+    script += sizeof("#string:") - 1;
+    return SkAnimatorScript::EvaluateString(maker, this, parent, script, str);
+}
+
+void SkApply::save(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo * info = animate->fFieldInfo;
+    SkDisplayable* target = getTarget(animate);
+//  if (animate->hasExecute())
+//      info = animate->getResolvedInfo();
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return; // nothing to save
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    bool useLast = true;
+// !!! this all may be unneeded, at least in the dynamic case ??
+    int activeIndex = fActive->fDrawIndex + index;
+    SkTDOperandArray last;
+    if (fActive->fSaveRestore[activeIndex] == NULL) {
+        fActive->fSaveRestore[activeIndex] = new SkOperand[count];
+        useLast = false;
+    } else {
+        last.setCount(count);
+        memcpy(last.begin(), fActive->fSaveRestore[activeIndex], count * sizeof(SkOperand));
+    }
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, fActive->fSaveRestore[activeIndex], count);
+        if (useLast)
+            info->setValue(target, last.begin(), count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success == true);
+        SkASSERT(scriptValue.fType == SkType_Float);
+        fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand;
+        if (useLast) {
+            SkScriptValue scriptValue;
+            scriptValue.fType = type;
+            scriptValue.fOperand = last[0];
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    }
+// !!!  end of unneeded
+}
+
+bool SkApply::setProperty(int index, SkScriptValue& scriptValue) {
+    switch (index) {
+        case SK_PROPERTY(animator): {
+            SkAnimateBase* animate = (SkAnimateBase*) scriptValue.fOperand.fDisplayable;
+            SkASSERT(animate->isAnimate());
+            *fAnimators.append() = animate;
+            return true; 
+        }
+        case SK_PROPERTY(steps):
+            steps = scriptValue.fOperand.fS32;
+            if (fActive)
+                fActive->setSteps(steps);
+            return true;
+    }
+    return false;
+}
+
+void SkApply::setSteps(int _steps) { 
+    steps = _steps; 
+}
+
+#ifdef SK_DEBUG
+void SkApply::validate() {
+    if (fActive)
+        fActive->validate();
+}
+#endif
+
+
+