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