Home | History | Annotate | Download | only in animator
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      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 #include "SkAnimatorScript2.h"
      9 #include "SkAnimateBase.h"
     10 #include "SkAnimateMaker.h"
     11 #include "SkDisplayTypes.h"
     12 #include "SkExtras.h"
     13 #include "SkMemberInfo.h"
     14 #include "SkOpArray.h"
     15 #include "SkParse.h"
     16 #include "SkScript2.h"
     17 #include "SkScriptCallBack.h"
     18 
     19 static const SkDisplayEnumMap gEnumMaps[] = {
     20 	{ SkType_AddMode, "indirect|immediate" },
     21 	{ SkType_Align, "left|center|right" },
     22 	{ SkType_ApplyMode, "immediate|once" },
     23 	{ SkType_ApplyTransition, "reverse" },
     24 	{ SkType_BitmapEncoding, "jpeg|png" },
     25 	{ SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" },
     26 	{ SkType_Boolean, "false|true" },
     27 	{ SkType_Cap, "butt|round|square" },
     28 	{ SkType_EventCode, "none|up|down|left|right|back|end|OK|send|leftSoftKey|rightSoftKey|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash" },
     29 	{ SkType_EventKind, "none|keyChar|keyPress|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
     30 	{ SkType_EventMode, "deferred|immediate" },
     31 	{ SkType_FillType, "winding|evenOdd" },
     32 	{ SkType_FilterType, "none|bilinear" },
     33 	{ SkType_FromPathMode, "normal|angle|position" },
     34 	{ SkType_Join, "miter|round|blunt" },
     35 	{ SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" },
     36 	{ SkType_PathDirection, "cw|ccw" },
     37 	{ SkType_Style, "fill|stroke|strokeAndFill" },
     38 	{ SkType_TextBoxAlign, "start|center|end" },
     39 	{ SkType_TextBoxMode, "oneLine|lineBreak" },
     40 	{ SkType_TileMode, "clamp|repeat|mirror" },
     41 	{ SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|"
     42 		"srcATop|dstATop|xor|darken|lighten" },
     43 };
     44 
     45 static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps);
     46 
     47 
     48 class SkAnimatorScript_Box : public SkScriptCallBackConvert {
     49 public:
     50 	SkAnimatorScript_Box() {}
     51 
     52 	~SkAnimatorScript_Box() {
     53 		for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
     54 			delete *dispPtr;
     55 	}
     56 
     57 	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
     58 		SkDisplayable* displayable;
     59 		switch (type) {
     60 			case SkOperand2::kArray: {
     61 				SkDisplayArray* boxedValue = new SkDisplayArray(*operand->fArray);
     62 				displayable = boxedValue;
     63 				} break;
     64 			case SkOperand2::kS32: {
     65 				SkDisplayInt* boxedValue = new SkDisplayInt;
     66 				displayable = boxedValue;
     67 				boxedValue->value = operand->fS32;
     68 				} break;
     69 			case SkOperand2::kScalar: {
     70 				SkDisplayFloat* boxedValue = new SkDisplayFloat;
     71 				displayable = boxedValue;
     72 				boxedValue->value = operand->fScalar;
     73 				} break;
     74 			case SkOperand2::kString: {
     75 				SkDisplayString* boxedValue = new SkDisplayString(*operand->fString);
     76 				displayable = boxedValue;
     77 				} break;
     78 			case SkOperand2::kObject:
     79 				return true;
     80 			default:
     81 				SkASSERT(0);
     82 				return false;
     83 		}
     84 		track(displayable);
     85 		operand->fObject = (void*) displayable;
     86 		return true;
     87 	}
     88 
     89 	virtual SkOperand2::OpType getReturnType(int index) {
     90 		return SkOperand2::kObject;
     91 	}
     92 
     93 	virtual Type getType() const {
     94 		return kBox;
     95 	}
     96 
     97 	void track(SkDisplayable* displayable) {
     98 		SkASSERT(fTrackDisplayable.find(displayable) < 0);
     99 		*fTrackDisplayable.append() = displayable;
    100 	}
    101 
    102 	SkTDDisplayableArray fTrackDisplayable;
    103 };
    104 
    105 
    106 class SkAnimatorScript_Enum : public SkScriptCallBackProperty {
    107 public:
    108 	SkAnimatorScript_Enum(const char* tokens) : fTokens(tokens) {}
    109 
    110 	virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
    111 		return SkAnimatorScript2::MapEnums(fTokens, name, len, &value->fS32);
    112 	}
    113 
    114 private:
    115 	const char* fTokens;
    116 };
    117 
    118 	// !!! if type is string, call invoke
    119 	// if any other type, return original value
    120 		// distinction is undone: could do this by returning index == 0 only if param is string
    121 		// still, caller of getParamTypes will attempt to convert param to string (I guess)
    122 class SkAnimatorScript_Eval : public SkScriptCallBackFunction {
    123 public:
    124 	SkAnimatorScript_Eval(SkAnimatorScript2* engine) : fEngine(engine) {}
    125 
    126 	virtual bool getIndex(const char* name, int len, size_t* result) {
    127 		if (SK_LITERAL_STR_EQUAL("eval", name, len) != 0)
    128 			return false;
    129 		*result = 0;
    130 		return true;
    131 	}
    132 
    133 	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
    134 		types->setCount(1);
    135 		SkOperand2::OpType* type = types->begin();
    136 		type[0] = SkOperand2::kString;
    137 	}
    138 
    139 	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
    140 		SkAnimatorScript2 engine(fEngine->getMaker(), fEngine->getWorking(),
    141 			SkAnimatorScript2::ToDisplayType(fEngine->getReturnType()));
    142 		SkOperand2* op = params->begin();
    143 		const char* script = op->fString->c_str();
    144 		SkScriptValue2 value;
    145 		return engine.evaluateScript(&script, &value);
    146 		SkASSERT(value.fType == fEngine->getReturnType());
    147 		*answer = value.fOperand;
    148 		// !!! incomplete ?
    149 		return true;
    150 	}
    151 
    152 private:
    153 	SkAnimatorScript2* fEngine;
    154 };
    155 
    156 class SkAnimatorScript_ID : public SkScriptCallBackProperty {
    157 public:
    158 	SkAnimatorScript_ID(SkAnimatorScript2* engine) : fEngine(engine) {}
    159 
    160 	virtual bool getIndex(const char* token, int len, size_t* result) {
    161 		SkDisplayable* displayable;
    162 		bool success = fEngine->getMaker().find(token, len, &displayable);
    163 		if (success == false) {
    164 			*result = 0;
    165 		} else {
    166 			*result = (size_t) displayable;
    167 			SkDisplayable* working = fEngine->getWorking();
    168 			if (displayable->canContainDependents() && working && working->isAnimate()) {
    169 				SkAnimateBase* animator = (SkAnimateBase*) working;
    170 				if (animator->isDynamic()) {
    171 					SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
    172 					depend->addDependent(working);
    173 				}
    174 			}
    175 		}
    176 		return true;
    177 	}
    178 
    179 	virtual bool getResult(size_t ref, SkOperand2* answer) {
    180 		answer->fObject = (void*) ref;
    181 		return true;
    182 	}
    183 
    184 	virtual SkOperand2::OpType getReturnType(size_t index) {
    185 		return index == 0 ? SkOperand2::kString : SkOperand2::kObject;
    186 	}
    187 
    188 private:
    189 	SkAnimatorScript2* fEngine;
    190 };
    191 
    192 
    193 class SkAnimatorScript_Member : public SkScriptCallBackMember {
    194 public:
    195 
    196 	SkAnimatorScript_Member(SkAnimatorScript2* engine) : fEngine(engine) {}
    197 
    198 	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
    199 		SkDisplayable* displayable = (SkDisplayable*) object;
    200 		SkString name(member, len);
    201 		SkDisplayable* named = displayable->contains(name);
    202 		if (named) {
    203 			ref->fType = SkOperand2::kObject;
    204 			ref->fOperand.fObject = named;
    205 			return true;
    206 		}
    207 		const SkMemberInfo* info = displayable->getMember(name.c_str());
    208 		if (info == NULL)
    209 			return false;	// !!! add additional error info?
    210 		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
    211 		ref->fOperand.fObject = (void*) info;
    212 		return true;
    213 	}
    214 
    215 	bool invoke(size_t ref, void* object, SkOperand2* value) {
    216 		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
    217 		SkDisplayable* displayable = (SkDisplayable*) object;
    218 		if (info->fType == SkType_MemberProperty) {
    219 			if (displayable->getProperty2(info->propertyIndex(), value) == false) {
    220 				return false;
    221 			}
    222 		}
    223 		return fEngine->evalMemberCommon(info, displayable, value);
    224 	}
    225 
    226 	SkAnimatorScript2* fEngine;
    227 };
    228 
    229 
    230 class SkAnimatorScript_MemberFunction : public SkScriptCallBackMemberFunction {
    231 public:
    232 	SkAnimatorScript_MemberFunction(SkAnimatorScript2* engine) : fEngine(engine) {}
    233 
    234 	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
    235 		SkDisplayable* displayable = (SkDisplayable*) object;
    236 		SkString name(member, len);
    237 		const SkMemberInfo* info = displayable->getMember(name.c_str());
    238 		if (info == NULL || info->fType != SkType_MemberFunction)
    239 			return false;	// !!! add additional error info?
    240 		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
    241 		ref->fOperand.fObject = (void*) info;
    242 		return true;
    243 	}
    244 
    245 	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
    246 		types->setCount(3);
    247 		SkOperand2::OpType* type = types->begin();
    248 		type[0] = type[1] = type[2] = SkOperand2::kS32;
    249 	}
    250 
    251 	bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value)
    252 	{
    253 		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
    254 		SkDisplayable* displayable = (SkDisplayable*) object;
    255 		displayable->executeFunction2(displayable, info->functionIndex(), params, info->getType(),
    256 			value);
    257 		return fEngine->evalMemberCommon(info, displayable, value);
    258 	}
    259 
    260 	SkAnimatorScript2* fEngine;
    261 };
    262 
    263 
    264 class SkAnimatorScript_NamedColor : public SkScriptCallBackProperty {
    265 public:
    266 	virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
    267 		return SkParse::FindNamedColor(name, len, (SkColor*) &value->fS32) != NULL;
    268 	}
    269 };
    270 
    271 
    272 class SkAnimatorScript_RGB : public SkScriptCallBackFunction {
    273 public:
    274 	virtual bool getIndex(const char* name, int len, size_t* result) {
    275 		if (SK_LITERAL_STR_EQUAL("rgb", name, len) != 0)
    276 			return false;
    277 		*result = 0;
    278 		return true;
    279 	}
    280 
    281 	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
    282 		types->setCount(3);
    283 		SkOperand2::OpType* type = types->begin();
    284 		type[0] = type[1] = type[2] = SkOperand2::kS32;
    285 	}
    286 
    287 	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
    288 		SkASSERT(index == 0);
    289 		unsigned result = 0xFF000000;
    290 		int shift = 16;
    291 		for (int index = 0; index < 3; index++) {
    292 			result |= SkClampMax(params->begin()[index].fS32, 255) << shift;
    293 			shift -= 8;
    294 		}
    295 		answer->fS32 = result;
    296 		return true;
    297 	}
    298 
    299 };
    300 
    301 
    302 class SkAnimatorScript_Unbox : public SkScriptCallBackConvert {
    303 public:
    304 	SkAnimatorScript_Unbox(SkAnimatorScript2* engine) : fEngine(engine) {}
    305 
    306 	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
    307 		SkASSERT(type == SkOperand2::kObject);
    308 		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
    309 		switch (displayable->getType()) {
    310 			case SkType_Array: {
    311 				SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
    312 				operand->fArray = new SkOpArray(SkAnimatorScript2::ToOpType(boxedValue->values.getType()));
    313 				int count = boxedValue->values.count();
    314 				operand->fArray->setCount(count);
    315 				memcpy(operand->fArray->begin(), boxedValue->values.begin(), count * sizeof(SkOperand2));
    316 				fEngine->track(operand->fArray);
    317 				} break;
    318 			case SkType_Boolean: {
    319 				SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
    320 				operand->fS32 = boxedValue->value;
    321 				} break;
    322 			case SkType_Int: {
    323 				SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
    324 				operand->fS32 = boxedValue->value;
    325 				} break;
    326 			case SkType_Float: {
    327 				SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
    328 				operand->fScalar = boxedValue->value;
    329 				} break;
    330 			case SkType_String: {
    331 				SkDisplayString* boxedValue = (SkDisplayString*) displayable;
    332 				operand->fString = SkNEW_ARGS(SkString, (boxedValue->value));
    333 				} break;
    334 			default: {
    335 				const char* id;
    336 				bool success = fEngine->getMaker().findKey(displayable, &id);
    337 				SkASSERT(success);
    338 				operand->fString = SkNEW_ARGS(SkString, (id));
    339 			}
    340 		}
    341 		return true;
    342 	}
    343 
    344 	virtual SkOperand2::OpType getReturnType(int /*index*/, SkOperand2* operand) {
    345 		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
    346 		switch (displayable->getType()) {
    347 			case SkType_Array:
    348 				return SkOperand2::kArray;
    349 			case SkType_Int:
    350 				return SkOperand2::kS32;
    351 			case SkType_Float:
    352 				return SkOperand2::kScalar;
    353 			case SkType_String:
    354 			default:
    355 				return SkOperand2::kString;
    356 		}
    357 	}
    358 
    359 	virtual Type getType() const {
    360 		return kUnbox;
    361 	}
    362 
    363 	SkAnimatorScript2* fEngine;
    364 };
    365 
    366 SkAnimatorScript2::SkAnimatorScript2(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) :
    367 		SkScriptEngine2(ToOpType(type)), fMaker(maker), fWorking(working) {
    368 	*fCallBackArray.append() = new SkAnimatorScript_Member(this);
    369 	*fCallBackArray.append() = new SkAnimatorScript_MemberFunction(this);
    370 	*fCallBackArray.append() = new SkAnimatorScript_Box();
    371 	*fCallBackArray.append() = new SkAnimatorScript_Unbox(this);
    372 	*fCallBackArray.append() = new SkAnimatorScript_ID(this);
    373 	if (type == SkType_ARGB) {
    374 		*fCallBackArray.append() = new SkAnimatorScript_RGB();
    375 		*fCallBackArray.append() = new SkAnimatorScript_NamedColor();
    376 	}
    377 	if (SkDisplayType::IsEnum(&maker, type)) {
    378 		// !!! for SpiderMonkey, iterate through the enum values, and map them to globals
    379 		const SkDisplayEnumMap& map = GetEnumValues(type);
    380 		*fCallBackArray.append() = new SkAnimatorScript_Enum(map.fValues);
    381 	}
    382 	*fCallBackArray.append() = new SkAnimatorScript_Eval(this);
    383 #if 0		// !!! no extra support for now
    384 	for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
    385 		SkExtras* extra = *extraPtr;
    386 		if (extra->fExtraCallBack)
    387 			*fCallBackArray.append() = new propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
    388 	}
    389 #endif
    390 }
    391 
    392 SkAnimatorScript2::~SkAnimatorScript2() {
    393 	SkScriptCallBack** end = fCallBackArray.end();
    394 	for (SkScriptCallBack** ptr = fCallBackArray.begin(); ptr < end; ptr++)
    395 		delete *ptr;
    396 }
    397 
    398 bool SkAnimatorScript2::evalMemberCommon(const SkMemberInfo* info,
    399 		SkDisplayable* displayable, SkOperand2* value) {
    400 	SkDisplayTypes original;
    401 	SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
    402 	if (info->fType == SkType_Array)
    403 		type = SkType_Array;
    404 	switch (type) {
    405 		case SkType_ARGB:
    406 			type = SkType_Int;
    407 		case SkType_Boolean:
    408 		case SkType_Int:
    409 		case SkType_MSec:
    410 		case SkType_Float:
    411 			SkASSERT(info->getCount() == 1);
    412 			if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction)
    413 				value->fS32 = *(int32_t*) info->memberData(displayable);	// OK for SkScalar too
    414 			if (type == SkType_MSec) {
    415 				value->fScalar = SkScalarDiv((SkScalar) value->fS32, 1000); // dividing two ints is the same as dividing two scalars
    416 				type = SkType_Float;
    417 			}
    418 			break;
    419 		case SkType_String: {
    420 			SkString* displayableString;
    421 			if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
    422 				info->getString(displayable, &displayableString);
    423 				value->fString = new SkString(*displayableString);
    424 			}
    425 			} break;
    426 		case SkType_Array: {
    427 			SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete
    428 			SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable);
    429 			if (displayable->getType() == SkType_Array) {
    430 				SkDisplayArray* typedArray = (SkDisplayArray*) displayable;
    431 				original = typedArray->values.getType();
    432 			}
    433 			SkASSERT(original != SkType_Unknown);
    434 			SkOpArray* array = value->fArray = new SkOpArray(ToOpType(original));
    435 			track(array);
    436 			int count = displayableArray->count();
    437 			if (count > 0) {
    438 				array->setCount(count);
    439 				memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand2));
    440 			}
    441 			} break;
    442 		default:
    443 			SkASSERT(0); // unimplemented
    444 	}
    445 	return true;
    446 }
    447 
    448 const SkDisplayEnumMap& SkAnimatorScript2::GetEnumValues(SkDisplayTypes type) {
    449 	int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type,
    450 		sizeof(SkDisplayEnumMap));
    451 	SkASSERT(index >= 0);
    452 	return gEnumMaps[index];
    453 }
    454 
    455 SkDisplayTypes SkAnimatorScript2::ToDisplayType(SkOperand2::OpType type) {
    456 	int val = type;
    457 	switch (val) {
    458 		case SkOperand2::kNoType:
    459 			return SkType_Unknown;
    460 		case SkOperand2::kS32:
    461 			return SkType_Int;
    462 		case SkOperand2::kScalar:
    463 			return SkType_Float;
    464 		case SkOperand2::kString:
    465 			return SkType_String;
    466 		case SkOperand2::kArray:
    467 			return SkType_Array;
    468 		case SkOperand2::kObject:
    469 			return SkType_Displayable;
    470 		default:
    471 			SkASSERT(0);
    472 			return SkType_Unknown;
    473 	}
    474 }
    475 
    476 SkOperand2::OpType SkAnimatorScript2::ToOpType(SkDisplayTypes type) {
    477 	if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
    478 		return SkOperand2::kObject;
    479 	if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
    480 		return SkOperand2::kS32;
    481 	switch (type) {
    482 		case SkType_ARGB:
    483 		case SkType_MSec:
    484 		case SkType_Int:
    485 			return SkOperand2::kS32;
    486 		case SkType_Float:
    487 		case SkType_Point:
    488 		case SkType_3D_Point:
    489 			return SkOperand2::kScalar;
    490 		case SkType_Base64:
    491 		case SkType_DynamicString:
    492 		case SkType_String:
    493 			return SkOperand2::kString;
    494 		case SkType_Array:
    495 			return SkOperand2::kArray;
    496 		case SkType_Unknown:
    497 			return SkOperand2::kNoType;
    498 		default:
    499 			SkASSERT(0);
    500 			return SkOperand2::kNoType;
    501 	}
    502 }
    503 
    504 bool SkAnimatorScript2::MapEnums(const char* ptr, const char* match, size_t len, int* value) {
    505 	int index = 0;
    506 	bool more = true;
    507 	do {
    508 		const char* last = strchr(ptr, '|');
    509 		if (last == NULL) {
    510 			last = &ptr[strlen(ptr)];
    511 			more = false;
    512 		}
    513 		size_t length = last - ptr;
    514 		if (len == length && strncmp(ptr, match, length) == 0) {
    515 			*value = index;
    516 			return true;
    517 		}
    518 		index++;
    519 		ptr = last + 1;
    520 	} while (more);
    521 	return false;
    522 }
    523 
    524 #if defined SK_DEBUG
    525 
    526 #include "SkAnimator.h"
    527 
    528 static const char scriptTestSetup[]  =
    529 "<screenplay>"
    530 	"<apply>"
    531 		"<paint>"
    532 			"<emboss id='emboss' direction='[1,1,1]'  />"
    533 		"</paint>"
    534 		"<animateField id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>"
    535 		"<set lval='direction[0]' target='emboss' to='-1' />"
    536 	"</apply>"
    537 	"<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />"
    538 	"<color id='xColor' color='rgb(12,34,56)' />"
    539 	"<typedArray id='emptyArray' />"
    540 	"<typedArray id='intArray' values='[1, 4, 6]' />"
    541 	"<s32 id='idx' value='2' />"
    542 	"<s32 id='idy' value='2' />"
    543 	"<string id='alpha' value='abc' />"
    544 	"<rectangle id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />"
    545 	"<event id='evt'>"
    546 		"<input name='x' />"
    547 		"<apply scope='idy'>"
    548 			"<set field='value' to='evt.x.s32' />"
    549 		"</apply>"
    550 	"</event>"
    551 "</screenplay>";
    552 
    553 static const SkScriptNAnswer scriptTests[]  = {
    554 	{	"alpha+alpha", SkType_String, 0, 0, "abcabc" },
    555 	{	"0 ? Math.sin(0) : 1", SkType_Int, 1 },
    556 	{	"intArray[4]", SkType_Unknown },
    557 	{	"emptyArray[4]", SkType_Unknown },
    558 	{	"idx", SkType_Int, 2 },
    559 	{	"intArray.length", SkType_Int, 3 },
    560 	{	"intArray.values[0]", SkType_Int, 1 },
    561 	{	"intArray[0]", SkType_Int, 1 },
    562 	{	"idx.value", SkType_Int, 2 },
    563 	{	"alpha.value", SkType_String, 0, 0, "abc" },
    564 	{	"alpha", SkType_String, 0, 0, "abc" },
    565 	{	"alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" },
    566 	{	"alpha+idx", SkType_String, 0, 0, "abc2" },
    567 	{	"idx+alpha", SkType_String, 0, 0, "2abc" },
    568 	{	"intArray[idx]", SkType_Int, 6 },
    569 	{	"alpha.slice(1,2)", SkType_String, 0, 0, "b" },
    570 	{	"alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
    571 	{	"Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) },
    572 	{	"testRect.left+2", SkType_Float, 0, SkIntToScalar(3) },
    573 	{	"0 ? intArray[0] : 1", SkType_Int, 1 },
    574 	{	"0 ? intArray.values[0] : 1", SkType_Int, 1 },
    575 	{	"0 ? idx : 1", SkType_Int, 1 },
    576 	{	"0 ? idx.value : 1", SkType_Int, 1 },
    577 	{	"0 ? alpha.slice(1,2) : 1", SkType_Int, 1 },
    578 	{	"0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 },
    579 	{ "idy", SkType_Int, 3 }
    580 };
    581 
    582 #define SkScriptNAnswer_testCount	SK_ARRAY_COUNT(scriptTests)
    583 
    584 void SkAnimatorScript2::UnitTest() {
    585 #if defined(SK_SUPPORT_UNITTEST)
    586 	SkAnimator animator;
    587 	SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1));
    588 	SkEvent evt;
    589 	evt.setString("id", "evt");
    590 	evt.setS32("x", 3);
    591 	animator.doUserEvent(evt);
    592 	// set up animator with memory script above, then run value tests
    593 	for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
    594 		SkAnimatorScript2 engine(*animator.fMaker, NULL, scriptTests[index].fType);
    595 		SkScriptValue2 value;
    596 		const char* script = scriptTests[index].fScript;
    597 		bool success = engine.evaluateScript(&script, &value);
    598 		if (success == false) {
    599 			SkASSERT(scriptTests[index].fType == SkType_Unknown);
    600 			continue;
    601 		}
    602 		SkASSERT(value.fType == ToOpType(scriptTests[index].fType));
    603 		SkScalar error;
    604 		switch (value.fType) {
    605 			case SkOperand2::kS32:
    606 				SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
    607 				break;
    608 			case SkOperand2::kScalar:
    609 				error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
    610 				SkASSERT(error < SK_Scalar1 / 10000);
    611 				break;
    612 			case SkOperand2::kString:
    613 				SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
    614 				break;
    615 			default:
    616 				SkASSERT(0);
    617 		}
    618 	}
    619 #endif
    620 }
    621 
    622 #endif
    623 
    624