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