1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkAnimator.h"
11#include "SkAnimateMaker.h"
12#include "SkCanvas.h"
13#include "SkDisplayApply.h"
14#include "SkDisplayMovie.h"
15#include "SkDisplayTypes.h"
16#include "SkDisplayXMLParser.h"
17#include "SkStream.h"
18#include "SkScript.h"
19#include "SkScript2.h" //   compiled script experiment
20#include "SkSystemEventTypes.h"
21#include "SkTypedArray.h"
22#ifdef SK_BUILD_FOR_ANDROID
23#include "SkDrawExtraPathEffect.h"
24#endif
25#ifdef SK_DEBUG
26#include "SkTime.h"
27#endif
28
29#if defined SK_BUILD_FOR_WIN32 && defined SK_DEBUG
30    #define _static
31    extern const char gMathPrimerText[];
32    extern const char gMathPrimerBinary[];
33#else
34    #define _static static
35#endif
36
37_static const char gMathPrimerText[] =
38"<screenplay>"
39    "<Math id=\"Math\"/>"
40    "<Number id=\"Number\"/>"
41"</screenplay>";
42
43#define gMathPrimer gMathPrimerText
44
45SkAnimator::SkAnimator() : fMaker(NULL) {
46    initialize();
47}
48
49SkAnimator::~SkAnimator() {
50    SkDELETE(fMaker);
51}
52
53void SkAnimator::addExtras(SkExtras* extras) {
54    *fMaker->fExtras.append() = extras;
55}
56
57bool SkAnimator::appendStream(SkStream* stream) {
58    return decodeStream(stream);
59}
60
61bool SkAnimator::decodeMemory(const void* buffer, size_t size)
62{
63    fMaker->fFileName.reset();
64    SkDisplayXMLParser parser(*fMaker);
65    return parser.parse((const char*)buffer, size);
66}
67
68bool SkAnimator::decodeStream(SkStream* stream)
69{
70    SkDisplayXMLParser parser(*fMaker);
71    bool result = parser.parse(*stream);
72    fMaker->setErrorString();
73    return result;
74}
75
76bool SkAnimator::decodeDOM(const SkDOM& dom, const SkDOMNode* node)
77{
78    fMaker->fFileName.reset();
79    SkDisplayXMLParser parser(*fMaker);
80    return parser.parse(dom, node);
81}
82
83bool SkAnimator::decodeURI(const char uri[]) {
84//  SkDebugf("animator decode %s\n", uri);
85
86//    SkStream* stream = SkStream::GetURIStream(fMaker->fPrefix.c_str(), uri);
87    SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(uri));
88    if (stream.get()) {
89        this->setURIBase(uri);
90        return decodeStream(stream);
91    } else {
92        return false;
93    }
94}
95
96bool SkAnimator::doCharEvent(SkUnichar code) {
97    if (code == 0)
98        return false;
99    struct SkEventState state;
100    state.fCode = code;
101    fMaker->fEnableTime = fMaker->getAppTime();
102    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyChar, &state);
103    fMaker->notifyInval();
104    return result;
105}
106
107bool SkAnimator::doClickEvent(int clickState, SkScalar x, SkScalar y) {
108    SkASSERT(clickState >= 0 && clickState <= 2);
109    struct SkEventState state;
110    state.fX = x;
111    state.fY = y;
112    fMaker->fEnableTime = fMaker->getAppTime();
113    bool result = fMaker->fEvents.doEvent(*fMaker,
114        clickState == 0 ? SkDisplayEvent::kMouseDown :
115        clickState == 1 ? SkDisplayEvent::kMouseDrag :
116        SkDisplayEvent::kMouseUp, &state);
117    fMaker->notifyInval();
118    return result;
119}
120
121bool SkAnimator::doKeyEvent(SkKey code) {
122    if (code == 0)
123        return false;
124    struct SkEventState state;
125    state.fCode = code;
126    fMaker->fEnableTime = fMaker->getAppTime();
127    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPress, &state);
128    fMaker->notifyInval();
129    return result;
130}
131
132bool SkAnimator::doKeyUpEvent(SkKey code) {
133    if (code == 0)
134        return false;
135    struct SkEventState state;
136    state.fCode = code;
137    fMaker->fEnableTime = fMaker->getAppTime();
138    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPressUp, &state);
139    fMaker->notifyInval();
140    return result;
141}
142
143bool SkAnimator::doUserEvent(const SkEvent& evt) {
144    fMaker->fEnableTime = fMaker->getAppTime();
145    return onEvent(evt);
146}
147
148SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkPaint* paint, SkMSec time) {
149    if (paint == NULL)
150        return draw(canvas, time);
151    fMaker->fScreenplay.time = time;
152    fMaker->fCanvas = canvas;
153    fMaker->fPaint = paint;
154    fMaker->fDisplayList.fHasUnion = false;
155    int result = fMaker->fDisplayList.draw(*fMaker, time);
156    if (result)
157        result += fMaker->fDisplayList.fHasUnion;
158    return (DifferenceType) result;
159}
160
161SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkMSec time) {
162    SkPaint paint;
163    return draw(canvas, &paint, time);
164}
165
166#ifdef SK_DEBUG
167void SkAnimator::eventDone(const SkEvent& ) {
168}
169#endif
170
171bool SkAnimator::findClickEvent(SkScalar x, SkScalar y) {
172    struct SkEventState state;
173    state.fDisable = true;
174    state.fX = x;
175    state.fY = y;
176    fMaker->fEnableTime = fMaker->getAppTime();
177    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kMouseDown, &state);
178    fMaker->notifyInval();
179    return result;
180}
181
182const SkAnimator* SkAnimator::getAnimator(const SkDisplayable* displayable) const {
183    if (displayable->getType() != SkType_Movie)
184        return NULL;
185    const SkDisplayMovie* movie = (const SkDisplayMovie*) displayable;
186    return movie->getAnimator();
187}
188
189const SkDisplayable* SkAnimator::getElement(const char* id) {
190    SkDisplayable* element;
191    if (fMaker->find(id, &element) == false)
192        return NULL;
193    return (const SkDisplayable*) element;
194}
195
196SkElementType SkAnimator::getElementType(const SkDisplayable* ae) {
197    SkDisplayable* element = (SkDisplayable*) ae;
198    const SkMemberInfo* info = SkDisplayType::GetMembers(fMaker, element->getType(), NULL);
199    return (SkElementType) SkDisplayType::Find(fMaker, info);
200}
201
202SkElementType SkAnimator::getElementType(const char* id) {
203    const SkDisplayable* element = getElement(id);
204    return getElementType(element);
205}
206
207const SkMemberInfo* SkAnimator::getField(const SkDisplayable* ae, const char* field) {
208    SkDisplayable* element = (SkDisplayable*) ae;
209    const SkMemberInfo* info = element->getMember(field);
210    return (const SkMemberInfo*) info;
211}
212
213const SkMemberInfo* SkAnimator::getField(const char* elementID, const char* field) {
214    const SkDisplayable* element = getElement(elementID);
215    return getField(element, field);
216}
217
218SkFieldType SkAnimator::getFieldType(const SkMemberInfo* ai) {
219    const SkMemberInfo* info = (const SkMemberInfo*) ai;
220    return (SkFieldType) info->getType();
221}
222
223SkFieldType SkAnimator::getFieldType(const char* id, const char* fieldID) {
224    const SkMemberInfo* field = getField(id, fieldID);
225    return getFieldType(field);
226}
227
228static bool getArrayCommon(const SkDisplayable* ae, const SkMemberInfo* ai,
229                           int index, SkOperand* operand) {
230    const SkDisplayable* element = (const SkDisplayable*) ae;
231    const SkMemberInfo* info = (const SkMemberInfo*) ai;
232    SkASSERT(info->fType == SkType_Array);
233    return info->getArrayValue(element, index, operand);
234}
235
236int32_t SkAnimator::getArrayInt(const SkDisplayable* ae,
237        const SkMemberInfo* ai, int index) {
238    SkOperand operand;
239    bool result = getArrayCommon(ae, ai, index, &operand);
240    return result ? operand.fS32 : SK_NaN32;
241}
242
243int32_t SkAnimator::getArrayInt(const char* id, const char* fieldID, int index) {
244    const SkDisplayable* element = getElement(id);
245    if (element == NULL)
246        return SK_NaN32;
247    const SkMemberInfo* field = getField(element, fieldID);
248    if (field == NULL)
249        return SK_NaN32;
250    return getArrayInt(element, field, index);
251}
252
253SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae,
254        const SkMemberInfo* ai, int index) {
255    SkOperand operand;
256    bool result = getArrayCommon(ae, ai, index, &operand);
257    return result ? operand.fScalar : SK_ScalarNaN;
258}
259
260SkScalar SkAnimator::getArrayScalar(const char* id, const char* fieldID, int index) {
261    const SkDisplayable* element = getElement(id);
262    if (element == NULL)
263        return SK_ScalarNaN;
264    const SkMemberInfo* field = getField(element, fieldID);
265    if (field == NULL)
266        return SK_ScalarNaN;
267    return getArrayScalar(element, field, index);
268}
269
270const char* SkAnimator::getArrayString(const SkDisplayable* ae,
271        const SkMemberInfo* ai, int index) {
272    SkOperand operand;
273    bool result = getArrayCommon(ae, ai, index, &operand);
274    return result ? operand.fString->c_str() : NULL;
275}
276
277const char* SkAnimator::getArrayString(const char* id, const char* fieldID, int index) {
278    const SkDisplayable* element = getElement(id);
279    if (element == NULL)
280        return NULL;
281    const SkMemberInfo* field = getField(element, fieldID);
282    if (field == NULL)
283        return NULL;
284    return getArrayString(element, field, index);
285}
286
287SkMSec SkAnimator::getInterval() {
288    return fMaker->fMinimumInterval == (SkMSec) -1 ? 0 : fMaker->fMinimumInterval;
289}
290
291void SkAnimator::getInvalBounds(SkRect* inval) {
292    if (fMaker->fDisplayList.fHasUnion) {
293        inval->fLeft = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fLeft);
294        inval->fTop = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fTop);
295        inval->fRight = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fRight);
296        inval->fBottom = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fBottom);
297    } else {
298        inval->fLeft = inval->fTop = -SK_ScalarMax;
299        inval->fRight = inval->fBottom = SK_ScalarMax;
300    }
301}
302
303const SkXMLParserError* SkAnimator::getParserError() {
304    return &fMaker->fError;
305}
306
307const char* SkAnimator::getParserErrorString() {
308    if (fMaker->fErrorString.size() == 0 && fMaker->fError.hasError())
309        fMaker->setErrorString();
310    return fMaker->fErrorString.c_str();
311}
312
313int32_t SkAnimator::getInt(const SkDisplayable* element, const SkMemberInfo* info) {
314    if (info->fType != SkType_MemberProperty) {
315        SkOperand operand;
316        if (info->getType() == SkType_Int) {
317            info->getValue(element, &operand, 1);
318            return operand.fS32;
319        }
320        return SK_NaN32;
321    }
322    SkScriptValue scriptValue;
323    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
324    if (success && scriptValue.fType == SkType_Int)
325        return scriptValue.fOperand.fS32;
326    return SK_NaN32;
327}
328
329int32_t SkAnimator::getInt(const char* id, const char* fieldID) {
330    const SkDisplayable* element = getElement(id);
331    if (element == NULL)
332        return SK_NaN32;
333    const SkMemberInfo* field = getField(element, fieldID);
334    if (field == NULL)
335        return SK_NaN32;
336    return getInt(element, field);
337}
338
339SkScalar SkAnimator::getScalar(const SkDisplayable* element, const SkMemberInfo* info) {
340    if (info->fType != SkType_MemberProperty) {
341        SkOperand operand;
342        if (info->getType() == SkType_Float) {
343            info->getValue(element, &operand, 1);
344            return operand.fScalar;
345        }
346        return SK_ScalarNaN;
347    }
348    SkScriptValue scriptValue;
349    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
350    if (success && scriptValue.fType == SkType_Float)
351        return scriptValue.fOperand.fScalar;
352    return SK_ScalarNaN;
353}
354
355SkScalar SkAnimator::getScalar(const char* id, const char* fieldID) {
356    const SkDisplayable* element = getElement(id);
357    if (element == NULL)
358        return SK_ScalarNaN;
359    const SkMemberInfo* field = getField(element, fieldID);
360    if (field == NULL)
361        return SK_ScalarNaN;
362    return getScalar(element, field);
363}
364
365const char* SkAnimator::getString(const SkDisplayable* ae,
366        const SkMemberInfo* ai) {
367    const SkDisplayable* element = (const SkDisplayable*) ae;
368    const SkMemberInfo* info = (const SkMemberInfo*) ai;
369    SkString* temp;
370    info->getString(element, &temp);
371    return temp->c_str();
372}
373
374const char* SkAnimator::getString(const char* id, const char* fieldID) {
375    const SkDisplayable* element = getElement(id);
376    if (element == NULL)
377        return NULL;
378    const SkMemberInfo* field = getField(element, fieldID);
379    if (field == NULL)
380        return NULL;
381    return getString(element, field);
382}
383
384const char* SkAnimator::getURIBase() {
385    return fMaker->fPrefix.c_str();
386}
387
388void SkAnimator::initialize() {
389    SkDELETE(fMaker);
390    fMaker = SkNEW_ARGS(SkAnimateMaker, (this, NULL, NULL));
391    decodeMemory(gMathPrimer, sizeof(gMathPrimer)-1);
392#ifdef SK_BUILD_FOR_ANDROID
393    InitializeSkExtraPathEffects(this);
394#endif
395}
396
397
398#ifdef SK_DEBUG
399bool SkAnimator::isTrackingEvents() {
400    return false;
401}
402#endif
403
404bool SkAnimator::onEvent(const SkEvent& evt) {
405#ifdef SK_DEBUG
406    SkAnimator* root = fMaker->getRoot();
407    if (root == NULL)
408        root = this;
409    if (root->isTrackingEvents())
410        root->eventDone(evt);
411#endif
412    if (evt.isType(SK_EventType_OnEnd)) {
413        SkEventState eventState;
414        SkDEBUGCODE(bool success =) evt.findPtr("anim", (void**) &eventState.fDisplayable);
415        SkASSERT(success);
416        SkDEBUGCODE(success =) evt.findS32("time", (int32_t*) &fMaker->fEnableTime);
417        SkASSERT(success);
418        fMaker->fAdjustedStart = fMaker->getAppTime() - fMaker->fEnableTime;
419        fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kOnEnd, &eventState);
420        fMaker->fAdjustedStart = 0;
421        goto inval;
422    }
423    if (evt.isType(SK_EventType_Delay)) {
424        fMaker->doDelayedEvent();
425        goto inval;
426    }
427    {
428        const char* id = evt.findString("id");
429        if (id == NULL)
430            return false;
431        SkDisplayable** firstMovie = fMaker->fMovies.begin();
432        SkDisplayable** endMovie = fMaker->fMovies.end();
433        for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
434            SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
435            movie->doEvent(evt);
436        }
437        {
438            SkDisplayable* event;
439            if (fMaker->find(id, &event) == false)
440                return false;
441    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
442            SkString debugOut;
443            SkMSec realTime = fMaker->getAppTime();
444            debugOut.appendS32(realTime - fMaker->fDebugTimeBase);
445            debugOut.append(" onEvent id=");
446            debugOut.append(id);
447    #endif
448            SkMSec time = evt.getFast32();
449            if (time != 0) {
450                SkMSec app  = fMaker->getAppTime();
451                fMaker->setEnableTime(app, time);
452    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
453                debugOut.append(" time=");
454                debugOut.appendS32(time - fMaker->fDebugTimeBase);
455                debugOut.append(" adjust=");
456                debugOut.appendS32(fMaker->fAdjustedStart);
457    #endif
458            }
459    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
460            SkDebugf("%s\n", debugOut.c_str());
461    #endif
462            SkASSERT(event->isEvent());
463            SkDisplayEvent* displayEvent = (SkDisplayEvent*) event;
464            displayEvent->populateInput(*fMaker, evt);
465            displayEvent->enableEvent(*fMaker);
466        }
467    }
468inval:
469    fMaker->notifyInval();
470    return true;
471}
472
473void SkAnimator::onEventPost(SkEvent* evt, SkEventSinkID sinkID)
474{
475#ifdef SK_DEBUG
476    SkAnimator* root = fMaker->getRoot();
477    if (root) {
478        root->onEventPost(evt, sinkID);
479        return;
480    }
481#else
482    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
483#endif
484    evt->setTargetID(sinkID)->post();
485}
486
487void SkAnimator::onEventPostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
488{
489#ifdef SK_DEBUG
490    SkAnimator* root = fMaker->getRoot();
491    if (root) {
492        root->onEventPostTime(evt, sinkID, time);
493        return;
494    }
495#else
496    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
497#endif
498    evt->setTargetID(sinkID)->postTime(time);
499}
500
501void SkAnimator::reset() {
502    fMaker->fDisplayList.reset();
503}
504
505SkEventSinkID SkAnimator::getHostEventSinkID() const {
506    return fMaker->fHostEventSinkID;
507}
508
509void SkAnimator::setHostEventSinkID(SkEventSinkID target) {
510    fMaker->fHostEventSinkID = target;
511}
512
513void SkAnimator::onSetHostHandler(Handler ) {
514}
515
516void SkAnimator::setJavaOwner(Handler ) {
517}
518
519bool SkAnimator::setArrayString(const char* id, const char* fieldID, const char** array, int num)
520{
521    SkTypedArray tArray(SkType_String);
522    tArray.setCount(num);
523    for (int i = 0; i < num; i++) {
524        SkOperand op;
525        op.fString = new SkString(array[i]);
526        tArray[i] = op;
527    }
528    return setArray(id, fieldID, tArray);
529}
530bool SkAnimator::setArrayInt(const char* id, const char* fieldID, const int* array, int num)
531{
532    SkTypedArray tArray(SkType_Int);
533    tArray.setCount(num);
534    for (int i = 0; i < num; i++) {
535        SkOperand op;
536        op.fS32 = array[i];
537        tArray[i] = op;
538    }
539    return setArray(id, fieldID, tArray);
540}
541
542bool SkAnimator::setArray(SkDisplayable* element, const SkMemberInfo* info, SkTypedArray array) {
543    if (info->fType != SkType_Array)
544        return false;   //the field is not an array
545    //i think we can handle the case where the displayable itself is an array differently from the
546    //case where it has an array - for one thing, if it is an array, i think we can change its type
547    //if it's not, we cannot
548    SkDisplayTypes type = element->getType();
549    if (type == SkType_Array) {
550        SkDisplayArray* dispArray = (SkDisplayArray*) element;
551        dispArray->values = array;
552        return true;
553    }
554    else
555        return false;   //currently i don't care about this case
556}
557
558bool SkAnimator::setArray(const char* id, const char* fieldID, SkTypedArray array) {
559    SkDisplayable* element = (SkDisplayable*) getElement(id);
560    //should I go ahead and change all 'NULL's to 'NULL'?
561    if (element == NULL)
562        return false;
563    const SkMemberInfo* field = getField(element, fieldID);
564    if (field == NULL)
565        return false;
566    return setArray(element, field, array);
567}
568
569bool SkAnimator::setInt(SkDisplayable* element, const SkMemberInfo* info, int32_t s32) {
570    if (info->fType != SkType_MemberProperty) {
571        SkOperand operand;
572        operand.fS32 = s32;
573        SkASSERT(info->getType() == SkType_Int);
574        info->setValue(element, &operand, 1);
575    } else {
576        SkScriptValue scriptValue;
577        scriptValue.fType = SkType_Int;
578        scriptValue.fOperand.fS32 = s32;
579        element->setProperty(info->propertyIndex(), scriptValue);
580    }
581    return true;
582}
583
584bool SkAnimator::setInt(const char* id, const char* fieldID, int32_t s32) {
585    SkDisplayable* element = (SkDisplayable*) getElement(id);
586    if (element == NULL)
587        return false;
588    const SkMemberInfo* field = getField(element, fieldID);
589    if (field == NULL)
590        return false;
591    return setInt(element, field, s32);
592}
593
594bool SkAnimator::setScalar(SkDisplayable* element, const SkMemberInfo* info, SkScalar scalar) {
595    if (info->fType != SkType_MemberProperty) {
596        SkOperand operand;
597        operand.fScalar = scalar;
598        SkASSERT(info->getType() == SkType_Float);
599        info->setValue(element, &operand, 1);
600    } else {
601        SkScriptValue scriptValue;
602        scriptValue.fType = SkType_Float;
603        scriptValue.fOperand.fScalar = scalar;
604        element->setProperty(info->propertyIndex(), scriptValue);
605    }
606    return true;
607}
608
609bool SkAnimator::setScalar(const char* id, const char* fieldID, SkScalar scalar) {
610    SkDisplayable* element = (SkDisplayable*) getElement(id);
611    if (element == NULL)
612        return false;
613    const SkMemberInfo* field = getField(element, fieldID);
614    if (field == NULL)
615        return false;
616    return setScalar(element, field, scalar);
617}
618
619bool SkAnimator::setString(SkDisplayable* element,
620        const SkMemberInfo* info, const char* str) {
621    // !!! until this is fixed, can't call script with global references from here
622    info->setValue(*fMaker, NULL, 0, info->fCount, element, info->getType(), str, strlen(str));
623    return true;
624}
625
626bool SkAnimator::setString(const char* id, const char* fieldID, const char* str) {
627    SkDisplayable* element = (SkDisplayable*) getElement(id);
628    if (element == NULL)
629        return false;
630    const SkMemberInfo* field = getField(element, fieldID);
631    if (field == NULL)
632        return false;
633    return setString(element, field, str);
634}
635
636void SkAnimator::setTimeline(const Timeline& timeline) {
637    fMaker->fTimeline = &timeline;
638}
639
640void SkAnimator::setURIBase(const char* uri) {
641    if (uri)
642    {
643        const char* tail = strrchr(uri, '/');
644        if (tail) {
645            SkString prefix(uri, tail - uri + 1);
646            if (uri[0] != '.' /*SkStream::IsAbsoluteURI(uri)*/)
647                fMaker->fPrefix.reset();
648            fMaker->fPrefix.append(prefix);
649            fMaker->fFileName.set(tail + 1);
650        } else
651            fMaker->fFileName.set(uri);
652    }
653}
654
655#ifdef SK_DEBUG
656bool SkAnimator::NoLeaks() {
657#ifdef SK_BUILD_FOR_MAC
658    if (SkDisplayable::fAllocations.count() == 0)
659        return true;
660//  return SkDisplayable::fAllocationCount == 0;
661    SkDebugf("!!! leaked %d displayables:\n", SkDisplayable::fAllocations.count());
662    for (SkDisplayable** leak = SkDisplayable::fAllocations.begin(); leak < SkDisplayable::fAllocations.end(); leak++)
663        SkDebugf("%08x %s\n", *leak, (*leak)->id);
664#endif
665    return false;
666}
667#endif
668
669#ifdef SK_SUPPORT_UNITTEST
670#include "SkAnimatorScript.h"
671#include "SkBase64.h"
672#include "SkParse.h"
673#include "SkMemberInfo.h"
674
675#define unittestline(type)  { #type , type::UnitTest }
676#endif
677
678
679#ifdef SK_SUPPORT_UNITTEST
680void SkAnimator::Init(bool runUnitTests) {
681    if (runUnitTests == false)
682        return;
683    static const struct {
684        const char* fTypeName;
685        void (*fUnitTest)( );
686    } gUnitTests[] = {
687        unittestline(SkBase64),
688        unittestline(SkDisplayType),
689        unittestline(SkParse),
690        unittestline(SkScriptEngine),
691//      unittestline(SkScriptEngine2),  // compiled script experiment
692        unittestline(SkAnimatorScript)
693    };
694    for (int i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++)
695    {
696        SkDebugf("SkAnimator: Running UnitTest for %s\n", gUnitTests[i].fTypeName);
697        gUnitTests[i].fUnitTest();
698        SkDebugf("SkAnimator: End UnitTest for %s\n", gUnitTests[i].fTypeName);
699    }
700}
701#else
702void SkAnimator::Init(bool) {}
703#endif
704
705void SkAnimator::Term() {
706}
707