void SkDisplayEvent::onEndElement(SkAnimateMaker& maker) { if (kind == kUser) return; maker.fEvents.addEvent(this); if (kind == kOnEnd) { bool found = maker.find(target.c_str(), &fTarget); SkASSERT(found); SkASSERT(fTarget && fTarget->isAnimate()); SkAnimateBase* animate = (SkAnimateBase*) fTarget; animate->setHasEndEvent(); } }
void SkAnimateBase::setTarget(SkAnimateMaker& maker) { if (target.size()) { SkAnimatorScript engine(maker, this, SkType_Displayable); const char* script = target.c_str(); SkScriptValue scriptValue; bool success = engine.evaluateScript(&script, &scriptValue); if (success && scriptValue.fType == SkType_Displayable) fTarget = scriptValue.fOperand.fDrawable; else if (maker.find(target.c_str(), (SkDisplayable**) &fTarget) == false) { if (fApply->getMode() == SkApply::kMode_create) return; // may not be an error if (engine.getError() != SkScriptEngine::kNoError) maker.setScriptError(engine); else { maker.setErrorNoun(target); maker.setErrorCode(SkDisplayXMLParserError::kTargetIDNotFound); } return; } if (fApply && fApply->getMode() != SkApply::kMode_create) target.reset(); } }
bool SkAnimatorScript::Unbox(void* m, SkScriptValue* scriptValue) { SkAnimateMaker* maker = (SkAnimateMaker*) m; SkASSERT((unsigned) scriptValue->fType == (unsigned) SkType_Displayable); SkDisplayable* displayable = (SkDisplayable*) scriptValue->fOperand.fObject; SkDisplayTypes type = displayable->getType(); switch (displayable->getType()) { case SkType_Array: { SkDisplayArray* boxedValue = (SkDisplayArray*) displayable; scriptValue->fOperand.fArray = &boxedValue->values; } break; case SkType_Boolean: { SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable; scriptValue->fOperand.fS32 = boxedValue->value; } break; case SkType_Int: { SkDisplayInt* boxedValue = (SkDisplayInt*) displayable; scriptValue->fOperand.fS32 = boxedValue->value; } break; case SkType_Float: { SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable; scriptValue->fOperand.fScalar = boxedValue->value; } break; case SkType_String: { SkDisplayString* boxedValue = (SkDisplayString*) displayable; scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (boxedValue->value)); } break; default: { const char* id = NULL; bool success = maker->findKey(displayable, &id); SkASSERT(success); scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id)); type = SkType_String; } } scriptValue->fType = type; return true; }
void SkInclude::onEndElement(SkAnimateMaker& maker) { maker.fInInclude = true; if (src.size() == 0 || maker.decodeURI(src.c_str()) == false) { if (maker.getErrorCode() != SkXMLParserError::kNoError || maker.getNativeCode() != -1) { maker.setInnerError(&maker, src); maker.setErrorCode(SkDisplayXMLParserError::kInInclude); } else { maker.setErrorNoun(src); maker.setErrorCode(SkDisplayXMLParserError::kIncludeNameUnknownOrMissing); } } maker.fInInclude = false; }
void SkDisplayEvent::populateInput(SkAnimateMaker& maker, const SkEvent& fEvent) { const SkMetaData& meta = fEvent.getMetaData(); SkMetaData::Iter iter(meta); SkMetaData::Type type; int number; const char* name; while ((name = iter.next(&type, &number)) != NULL) { if (name[0] == '\0') continue; SkDisplayable* displayable; SkInput* input; for (int index = 0; index < fChildren.count(); index++) { displayable = fChildren[index]; if (displayable->getType() != SkType_Input) continue; input = (SkInput*) displayable; if (input->name.equals(name)) goto found; } if (!maker.find(name, &displayable) || displayable->getType() != SkType_Input) continue; input = (SkInput*) displayable; found: switch (type) { case SkMetaData::kS32_Type: meta.findS32(name, &input->fInt); break; case SkMetaData::kScalar_Type: meta.findScalar(name, &input->fFloat); break; case SkMetaData::kPtr_Type: SkASSERT(0); break; // !!! not handled for now case SkMetaData::kString_Type: input->string.set(meta.findString(name)); break; default: SkASSERT(0); } } // re-evaluate all animators that may have built their values from input strings for (SkDisplayable** childPtr = fChildren.begin(); childPtr < fChildren.end(); childPtr++) { SkDisplayable* displayable = *childPtr; if (displayable->isApply() == false) continue; SkApply* apply = (SkApply*) displayable; apply->refresh(maker); } }
bool SkGroup::resolveIDs(SkAnimateMaker& maker, SkDisplayable* orig, SkApply* apply) { SkGroup* original = (SkGroup*) orig; SkTDDrawableArray& originalChildren = original->fChildren; SkDrawable** originalPtr = originalChildren.begin(); SkDrawable** ptr = fChildren.begin(); SkDrawable** end = fChildren.end(); SkDrawable** origChild = ((SkGroup*) orig)->fChildren.begin(); while (ptr < end) { SkDrawable* drawable = *ptr++; maker.resolveID(drawable, *origChild++); if (drawable->resolveIDs(maker, *originalPtr++, apply) == true) return true; // failed } return false; }
bool SkDump::enable(SkAnimateMaker& maker ) { if (script.size() > 0) return evaluate(maker); bool hasAttr = false; if (events > 0) hasAttr |= maker.fDumpEvents = true; if (posts > 0) hasAttr |= maker.fDumpPosts = true; if (groups > 0) hasAttr |= maker.fDumpGConditions = true; if ((hasAttr |= (eventList > 0)) == true) maker.fEvents.dump(maker); if ((hasAttr |= (name.size() > 0)) == true) maker.dump(name.c_str()); if (displayList > 0 || displayList != 0 && hasAttr == false) maker.fDisplayList.dump(&maker); return true; }
void SkDisplayMovie::buildMovie() { if (fMovieBuilt) return; SkAnimateMaker* movieMaker = fMovie.fMaker; SkAnimateMaker* parentMaker = movieMaker->fParentMaker; if (src.size() == 0 || parentMaker == NULL) return; movieMaker->fPrefix.set(parentMaker->fPrefix); fDecodedSuccessfully = fMovie.fMaker->decodeURI(src.c_str()); if (fDecodedSuccessfully == false) { if (movieMaker->getErrorCode() != SkXMLParserError::kNoError || movieMaker->getNativeCode() != -1) { movieMaker->setInnerError(parentMaker, src); parentMaker->setErrorCode(SkDisplayXMLParserError::kInMovie); } else { parentMaker->setErrorNoun(src); parentMaker->setErrorCode(SkDisplayXMLParserError::kMovieNameUnknownOrMissing); } } fMovieBuilt = true; }
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); } } }
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::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; }
bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType, const char rawValue[], size_t rawValueLen) const { SkString valueStr(rawValue, rawValueLen); SkScriptValue scriptValue; scriptValue.fType = SkType_Unknown; scriptValue.fOperand.fS32 = 0; SkDisplayTypes type = getType(); SkAnimatorScript engine(maker, displayable, type); if (arrayStorage) displayable = NULL; bool success = true; void* untypedStorage = NULL; if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction) untypedStorage = (SkTDOperandArray*) memberData(displayable); if (type == SkType_ARGB) { // for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first // it's enough to expand the colors into 0xFFxxyyzz const char* poundPos; while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) { size_t offset = poundPos - valueStr.c_str(); if (valueStr.size() - offset < 4) break; char r = poundPos[1]; char g = poundPos[2]; char b = poundPos[3]; if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false) break; char hex = poundPos[4]; if (is_hex(hex) == false) { valueStr.insertUnichar(offset + 1, r); valueStr.insertUnichar(offset + 3, g); valueStr.insertUnichar(offset + 5, b); } *(char*) poundPos = '0'; // overwrite '#' valueStr.insert(offset + 1, "xFF"); } } if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB) goto scriptCommon; switch (type) { case SkType_String: #if 0 if (displayable && displayable->isAnimate()) { goto noScriptString; } if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) { SkASSERT(sizeof("string") == sizeof("script")); char* stringHeader = valueStr.writable_str(); memcpy(&stringHeader[1], "script", sizeof("script") - 1); rawValue = valueStr.c_str(); goto noScriptString; } else #endif if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0) goto noScriptString; valueStr.remove(0, 8); case SkType_Unknown: case SkType_Int: case SkType_MSec: // for the purposes of script, MSec is treated as a Scalar case SkType_Point: case SkType_3D_Point: case SkType_Float: case SkType_Array: scriptCommon: { const char* script = valueStr.c_str(); success = engine.evaluateScript(&script, &scriptValue); if (success == false) { maker.setScriptError(engine); return false; } } SkASSERT(success); if (scriptValue.fType == SkType_Displayable) { if (type == SkType_String) { const char* charPtr; maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr); scriptValue.fOperand.fString = new SkString(charPtr); scriptValue.fType = SkType_String; engine.SkScriptEngine::track(scriptValue.fOperand.fString); break; } SkASSERT(SkDisplayType::IsDisplayable(&maker, type)); if (displayable) displayable->setReference(this, scriptValue.fOperand.fDisplayable); else arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable; return true; } if (type != scriptValue.fType) { if (scriptValue.fType == SkType_Array) { engine.forget(scriptValue.getArray()); goto writeStruct; // real structs have already been written by script } switch (type) { case SkType_String: success = engine.convertTo(SkType_String, &scriptValue); break; case SkType_MSec: case SkType_Float: success = engine.convertTo(SkType_Float, &scriptValue); break; case SkType_Int: success = engine.convertTo(SkType_Int, &scriptValue); break; case SkType_Array: success = engine.convertTo(arrayType(), &scriptValue); // !!! incomplete; create array of appropriate type and add scriptValue to it SkASSERT(0); break; case SkType_Displayable: case SkType_Drawable: return false; // no way to convert other types to this default: // to avoid warnings break; } if (success == false) return false; } if (type == SkType_MSec) scriptValue.fOperand.fMSec = SkScalarMulRound(scriptValue.fOperand.fScalar, 1000); scriptValue.fType = type; break; noScriptString: case SkType_DynamicString: if (fType == SkType_MemberProperty && displayable) { SkString string(rawValue, rawValueLen); SkScriptValue scriptValue; scriptValue.fOperand.fString = &string; scriptValue.fType = SkType_String; displayable->setProperty(propertyIndex(), scriptValue); } else if (displayable) { SkString* string = (SkString*) memberData(displayable); string->set(rawValue, rawValueLen); } else { SkASSERT(arrayStorage->count() == 1); arrayStorage->begin()->fString->set(rawValue, rawValueLen); } goto dirty; case SkType_Base64: { SkBase64 base64; base64.decode(rawValue, rawValueLen); *(SkBase64* ) untypedStorage = base64; } goto dirty; default: SkASSERT(0); break; } // if (SkDisplayType::IsStruct(type) == false) { writeStruct: if (writeValue(displayable, arrayStorage, storageOffset, maxStorage, untypedStorage, outType, scriptValue)) { maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType); return false; } } dirty: if (displayable) displayable->dirty(); return true; }
void SkDrawShape1DPathEffect::onEndElement(SkAnimateMaker& maker) { if (addPath == nullptr || (addPath->isPath() == false && addPath->isApply() == false)) maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error else fPathEffect = new SkShape1DPathEffect(this, &maker); }
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; }
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 SkSaveLayer::onEndElement(SkAnimateMaker& maker) { if (!bounds) maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds); INHERITED::onEndElement(maker); }
bool SkAnimate::resolveCommon(SkAnimateMaker& maker) { if (fTarget == NULL) // if NULL, recall onEndElement after apply closes and sets target to scope return false; INHERITED::onEndElement(maker); return maker.hasError() == false; }
bool SkAdd::enable(SkAnimateMaker& maker ) { SkDisplayTypes type = getType(); SkDisplayList& displayList = maker.fDisplayList; SkTDDrawableArray* parentList = displayList.getDrawList(); if (type == SkType_Add) { if (use == NULL) // not set in apply yet return true; } bool skipAddToParent = true; SkASSERT(type != SkType_Replace || where); SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING; SkGroup* parentGroup = NULL; SkGroup* thisGroup = NULL; int index = where ? displayList.findGroup(where, &parentList, &parentGroup, &thisGroup, &grandList) : 0; if (index < 0) return true; int max = parentList->count(); if (where == NULL && type == SkType_Move) index = max; if (offset != SK_MaxS32) { index += offset; if (index > max) { maker.setErrorCode(SkDisplayXMLParserError::kIndexOutOfRange); return true; // caller should not add } } if (offset < 0 && where == NULL) index += max + 1; switch (type) { case SkType_Add: if (offset == SK_MaxS32 && where == NULL) { if (use->isDrawable()) { skipAddToParent = mode == kMode_immediate; if (skipAddToParent) { if (where == NULL) { SkTDDrawableArray* useParentList; index = displayList.findGroup(this, &useParentList, &parentGroup, &thisGroup, &grandList); if (index >= 0) { parentGroup->markCopySize(index); parentGroup->markCopySet(index); useParentList->begin()[index] = use; break; } } *parentList->append() = use; } } break; } else { if (thisGroup) thisGroup->markCopySize(index); *parentList->insert(index) = use; if (thisGroup) thisGroup->markCopySet(index); if (use->isApply()) ((SkApply*) use)->setEmbedded(); } break; case SkType_Move: { int priorLocation = parentList->find(use); if (priorLocation < 0) break; *parentList->insert(index) = use; if (index < priorLocation) priorLocation++; parentList->remove(priorLocation); } break; case SkType_Remove: { SkDisplayable* old = (*parentList)[index]; if (((SkRemove*)(this))->fDelete) { delete old; goto noHelperNeeded; } for (int inner = 0; inner < maker.fChildren.count(); inner++) { SkDisplayable* child = maker.fChildren[inner]; if (child == old || child->contains(old)) goto noHelperNeeded; } if (maker.fHelpers.find(old) < 0) maker.helperAdd(old); noHelperNeeded: parentList->remove(index); } break; case SkType_Replace: if (thisGroup) { thisGroup->markCopySize(index); if (thisGroup->markedForDelete(index)) { SkDisplayable* old = (*parentList)[index]; if (maker.fHelpers.find(old) < 0) maker.helperAdd(old); } } (*parentList)[index] = use; if (thisGroup) thisGroup->markCopySet(index); break; default: SkASSERT(0); } if (type == SkType_Remove) return true; if (use->hasEnable()) use->enable(maker); return skipAddToParent; // append if indirect: *parentList->append() = this; }