1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "Test.h" 9 #include "SkCanvas.h" 10 #include "SkDebugCanvas.h" 11 #include "SkPicture.h" 12 #include "SkPictureFlat.h" 13 #include "SkPictureRecord.h" 14 15 // This test exercises the Matrix/Clip State collapsing system. It generates 16 // example skps and the compares the actual stored operations to the expected 17 // operations. The test works by emitting canvas operations at three levels: 18 // overall structure, bodies that draw something and model/clip state changes. 19 // 20 // Structure methods only directly emit save and restores but call the 21 // ModelClip and Body helper methods to fill in the structure. Since they only 22 // emit saves and restores the operations emitted by the structure methods will 23 // be completely removed by the matrix/clip collapse. Note: every save in 24 // a structure method is followed by a call to a ModelClip helper. 25 // 26 // Body methods only directly emit draw ops and saveLayer/restore pairs but call 27 // the ModelClip helper methods. Since the body methods emit the ops that cannot 28 // be collapsed (i.e., draw ops, saveLayer/restore) they also generate the 29 // expected result information. Note: every saveLayer in a body method is 30 // followed by a call to a ModelClip helper. 31 // 32 // The ModelClip methods output matrix and clip ops in various orders and 33 // combinations. They contribute to the expected result by outputting the 34 // expected matrix & clip ops. Note that, currently, the entire clip stack 35 // is output for each MC state so the clip operations accumulate down the 36 // save/restore stack. 37 38 // TODOs: 39 // check on clip offsets 40 // - not sure if this is possible. The desire is to verify that the clip 41 // operations' offsets point to the correct follow-on operations. This 42 // could be difficult since there is no good way to communicate the 43 // offset stored in the SkPicture to the debugger's clip objects 44 // add comparison of rendered before & after images? 45 // - not sure if this would be useful since it somewhat duplicates the 46 // correctness test of running render_pictures in record mode and 47 // rendering before and after images. Additionally the matrix/clip collapse 48 // is sure to cause some small differences so an automated test might 49 // yield too many false positives. 50 // run the matrix/clip collapse system on the 10K skp set 51 // - this should give us warm fuzzies that the matrix clip collapse 52 // system is ready for prime time 53 // bench the recording times with/without matrix/clip collapsing 54 55 #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE 56 57 // Enable/disable debugging helper code 58 //#define TEST_COLLAPSE_MATRIX_CLIP_STATE 1 59 60 // Extract the command ops from the input SkPicture 61 static void gets_ops(SkPicture& input, SkTDArray<DrawType>* ops) { 62 SkDebugCanvas debugCanvas(input.width(), input.height()); 63 debugCanvas.setBounds(input.width(), input.height()); 64 input.draw(&debugCanvas); 65 66 ops->setCount(debugCanvas.getSize()); 67 for (int i = 0; i < debugCanvas.getSize(); ++i) { 68 (*ops)[i] = debugCanvas.getDrawCommandAt(i)->getType(); 69 } 70 } 71 72 enum ClipType { 73 kNone_ClipType, 74 kRect_ClipType, 75 kRRect_ClipType, 76 kPath_ClipType, 77 kRegion_ClipType, 78 79 kLast_ClipType = kRRect_ClipType 80 }; 81 82 static const int kClipTypeCount = kLast_ClipType + 1; 83 84 enum MatType { 85 kNone_MatType, 86 kTranslate_MatType, 87 kScale_MatType, 88 kSkew_MatType, 89 kRotate_MatType, 90 kConcat_MatType, 91 kSetMatrix_MatType, 92 93 kLast_MatType = kScale_MatType 94 }; 95 96 static const int kMatTypeCount = kLast_MatType + 1; 97 98 // TODO: implement the rest of the draw ops 99 enum DrawOpType { 100 kNone_DrawOpType, 101 #if 0 102 kBitmap_DrawOpType, 103 kBitmapMatrix_DrawOpType, 104 kBitmapNone_DrawOpType, 105 kBitmapRectToRect_DrawOpType, 106 #endif 107 kClear_DrawOpType, 108 #if 0 109 kData_DrawOpType, 110 #endif 111 kOval_DrawOpType, 112 #if 0 113 kPaint_DrawOpType, 114 kPath_DrawOpType, 115 kPicture_DrawOpType, 116 kPoints_DrawOpType, 117 kPosText_DrawOpType, 118 kPosTextTopBottom_DrawOpType, 119 kPosTextH_DrawOpType, 120 kPosTextHTopBottom_DrawOpType, 121 #endif 122 kRect_DrawOpType, 123 kRRect_DrawOpType, 124 #if 0 125 kSprite_DrawOpType, 126 kText_DrawOpType, 127 kTextOnPath_DrawOpType, 128 kTextTopBottom_DrawOpType, 129 kDrawVertices_DrawOpType, 130 #endif 131 132 kLastNonSaveLayer_DrawOpType = kRect_DrawOpType, 133 134 // saveLayer's have to handled apart from the other draw operations 135 // since they also alter the save/restore structure. 136 kSaveLayer_DrawOpType, 137 }; 138 139 static const int kNonSaveLayerDrawOpTypeCount = kLastNonSaveLayer_DrawOpType + 1; 140 141 typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip, 142 DrawOpType draw, SkTDArray<DrawType>* expected, 143 int accumulatedClips); 144 typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 145 ClipType clip, DrawOpType draw, 146 SkTDArray<DrawType>* expected, int accumulatedClips); 147 typedef void (*PFEmitStruct)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 148 ClipType clip, PFEmitBody emitBody, DrawOpType draw, 149 SkTDArray<DrawType>* expected); 150 151 ////////////////////////////////////////////////////////////////////////////// 152 153 // TODO: expand the testing to include the different ops & AA types! 154 static void emit_clip(SkCanvas* canvas, ClipType clip) { 155 switch (clip) { 156 case kNone_ClipType: 157 break; 158 case kRect_ClipType: { 159 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 160 canvas->clipRect(r, SkRegion::kIntersect_Op, true); 161 break; 162 } 163 case kRRect_ClipType: { 164 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 165 SkRRect rr; 166 rr.setRectXY(r, 10, 10); 167 canvas->clipRRect(rr, SkRegion::kIntersect_Op, true); 168 break; 169 } 170 case kPath_ClipType: { 171 SkPath p; 172 p.moveTo(5.0f, 5.0f); 173 p.lineTo(50.0f, 50.0f); 174 p.lineTo(100.0f, 5.0f); 175 p.close(); 176 canvas->clipPath(p, SkRegion::kIntersect_Op, true); 177 break; 178 } 179 case kRegion_ClipType: { 180 SkIRect rects[2] = { 181 { 1, 1, 55, 55 }, 182 { 45, 45, 99, 99 }, 183 }; 184 SkRegion r; 185 r.setRects(rects, 2); 186 canvas->clipRegion(r, SkRegion::kIntersect_Op); 187 break; 188 } 189 default: 190 SkASSERT(0); 191 } 192 } 193 194 static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) { 195 if (NULL == expected) { 196 // expected is NULL if this clip will be fused into later clips 197 return; 198 } 199 200 switch (clip) { 201 case kNone_ClipType: 202 break; 203 case kRect_ClipType: 204 *expected->append() = CONCAT; 205 *expected->append() = CLIP_RECT; 206 break; 207 case kRRect_ClipType: 208 *expected->append() = CONCAT; 209 *expected->append() = CLIP_RRECT; 210 break; 211 case kPath_ClipType: 212 *expected->append() = CONCAT; 213 *expected->append() = CLIP_PATH; 214 break; 215 case kRegion_ClipType: 216 *expected->append() = CONCAT; 217 *expected->append() = CLIP_REGION; 218 break; 219 default: 220 SkASSERT(0); 221 } 222 } 223 224 static void emit_mat(SkCanvas* canvas, MatType mat) { 225 switch (mat) { 226 case kNone_MatType: 227 break; 228 case kTranslate_MatType: 229 canvas->translate(5.0f, 5.0f); 230 break; 231 case kScale_MatType: 232 canvas->scale(1.1f, 1.1f); 233 break; 234 case kSkew_MatType: 235 canvas->skew(1.1f, 1.1f); 236 break; 237 case kRotate_MatType: 238 canvas->rotate(1.0f); 239 break; 240 case kConcat_MatType: { 241 SkMatrix m; 242 m.setTranslate(1.0f, 1.0f); 243 canvas->concat(m); 244 break; 245 } 246 case kSetMatrix_MatType: { 247 SkMatrix m; 248 m.setTranslate(1.0f, 1.0f); 249 canvas->setMatrix(m); 250 break; 251 } 252 default: 253 SkASSERT(0); 254 } 255 } 256 257 static void add_mat(MatType mat, SkTDArray<DrawType>* expected) { 258 if (NULL == expected) { 259 // expected is NULL if this matrix call will be fused into later ones 260 return; 261 } 262 263 switch (mat) { 264 case kNone_MatType: 265 break; 266 case kTranslate_MatType: // fall thru 267 case kScale_MatType: // fall thru 268 case kSkew_MatType: // fall thru 269 case kRotate_MatType: // fall thru 270 case kConcat_MatType: // fall thru 271 case kSetMatrix_MatType: 272 // TODO: this system currently converts a setMatrix to concat. If we wanted to 273 // really preserve the setMatrix semantics we should keep it a setMatrix. I'm 274 // not sure if this is a good idea though since this would keep things like pinch 275 // zoom from working. 276 *expected->append() = CONCAT; 277 break; 278 default: 279 SkASSERT(0); 280 } 281 } 282 283 static void emit_draw(SkCanvas* canvas, DrawOpType draw, SkTDArray<DrawType>* expected) { 284 switch (draw) { 285 case kNone_DrawOpType: 286 break; 287 case kClear_DrawOpType: 288 canvas->clear(SK_ColorRED); 289 *expected->append() = DRAW_CLEAR; 290 break; 291 case kOval_DrawOpType: { 292 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 293 SkPaint p; 294 canvas->drawOval(r, p); 295 *expected->append() = DRAW_OVAL; 296 break; 297 } 298 case kRect_DrawOpType: { 299 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 300 SkPaint p; 301 canvas->drawRect(r, p); 302 *expected->append() = DRAW_RECT; 303 break; 304 } 305 case kRRect_DrawOpType: { 306 SkRect r = SkRect::MakeLTRB(10.0f, 10.0f, 90.0f, 90.0f); 307 SkRRect rr; 308 rr.setRectXY(r, 5.0f, 5.0f); 309 SkPaint p; 310 canvas->drawRRect(rr, p); 311 *expected->append() = DRAW_RRECT; 312 break; 313 } 314 default: 315 SkASSERT(0); 316 } 317 } 318 319 ////////////////////////////////////////////////////////////////////////////// 320 321 // Emit: 322 // clip 323 // matrix 324 // Simple case - the clip isn't effect by the matrix 325 static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip, 326 DrawOpType draw, SkTDArray<DrawType>* expected, 327 int accumulatedClips) { 328 emit_clip(canvas, clip); 329 emit_mat(canvas, mat); 330 331 if (kNone_DrawOpType == draw) { 332 return; 333 } 334 335 for (int i = 0; i < accumulatedClips; ++i) { 336 add_clip(clip, mat, expected); 337 } 338 add_mat(mat, expected); 339 } 340 341 // Emit: 342 // matrix 343 // clip 344 // Emitting the matrix first is more challenging since the matrix has to be 345 // pushed across (i.e., applied to) the clip. 346 static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 347 DrawOpType draw, SkTDArray<DrawType>* expected, 348 int accumulatedClips) { 349 emit_mat(canvas, mat); 350 emit_clip(canvas, clip); 351 352 if (kNone_DrawOpType == draw) { 353 return; 354 } 355 356 // the matrix & clip order will be reversed once collapsed! 357 for (int i = 0; i < accumulatedClips; ++i) { 358 add_clip(clip, mat, expected); 359 } 360 add_mat(mat, expected); 361 } 362 363 // Emit: 364 // matrix 365 // clip 366 // matrix 367 // clip 368 // This tests that the matrices and clips coalesce when collapsed 369 static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 370 DrawOpType draw, SkTDArray<DrawType>* expected, 371 int accumulatedClips) { 372 emit_mat(canvas, mat); 373 emit_clip(canvas, clip); 374 emit_mat(canvas, mat); 375 emit_clip(canvas, clip); 376 377 if (kNone_DrawOpType == draw) { 378 return; 379 } 380 381 for (int i = 0; i < accumulatedClips; ++i) { 382 add_clip(clip, mat, expected); 383 add_clip(clip, mat, expected); 384 } 385 add_mat(mat, expected); 386 } 387 388 // Emit: 389 // matrix 390 // clip 391 // clip 392 // This tests accumulation of clips in same transform state. It also tests pushing 393 // of the matrix across both the clips. 394 static void emit_mat_clip_clip(SkCanvas* canvas, MatType mat, ClipType clip, 395 DrawOpType draw, SkTDArray<DrawType>* expected, 396 int accumulatedClips) { 397 emit_mat(canvas, mat); 398 emit_clip(canvas, clip); 399 emit_clip(canvas, clip); 400 401 if (kNone_DrawOpType == draw) { 402 return; 403 } 404 405 for (int i = 0; i < accumulatedClips; ++i) { 406 add_clip(clip, mat, expected); 407 add_clip(clip, mat, expected); 408 } 409 add_mat(mat, expected); 410 } 411 412 ////////////////////////////////////////////////////////////////////////////// 413 414 // Emit: 415 // matrix & clip calls 416 // draw op 417 static void emit_body0(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 418 ClipType clip, DrawOpType draw, 419 SkTDArray<DrawType>* expected, int accumulatedClips) { 420 bool needsSaveRestore = kNone_DrawOpType != draw && 421 (kNone_MatType != mat || kNone_ClipType != clip); 422 423 if (needsSaveRestore) { 424 *expected->append() = SAVE; 425 } 426 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 427 emit_draw(canvas, draw, expected); 428 if (needsSaveRestore) { 429 *expected->append() = RESTORE; 430 } 431 } 432 433 // Emit: 434 // matrix & clip calls 435 // draw op 436 // matrix & clip calls 437 // draw op 438 static void emit_body1(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 439 ClipType clip, DrawOpType draw, 440 SkTDArray<DrawType>* expected, int accumulatedClips) { 441 bool needsSaveRestore = kNone_DrawOpType != draw && 442 (kNone_MatType != mat || kNone_ClipType != clip); 443 444 if (needsSaveRestore) { 445 *expected->append() = SAVE; 446 } 447 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 448 emit_draw(canvas, draw, expected); 449 if (needsSaveRestore) { 450 *expected->append() = RESTORE; 451 *expected->append() = SAVE; 452 } 453 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2); 454 emit_draw(canvas, draw, expected); 455 if (needsSaveRestore) { 456 *expected->append() = RESTORE; 457 } 458 } 459 460 // Emit: 461 // matrix & clip calls 462 // SaveLayer 463 // matrix & clip calls 464 // draw op 465 // Restore 466 static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 467 ClipType clip, DrawOpType draw, 468 SkTDArray<DrawType>* expected, int accumulatedClips) { 469 bool needsSaveRestore = kNone_DrawOpType != draw && 470 (kNone_MatType != mat || kNone_ClipType != clip); 471 472 if (kNone_MatType != mat || kNone_ClipType != clip) { 473 *expected->append() = SAVE; 474 } 475 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); 476 *expected->append() = SAVE_LAYER; 477 // TODO: widen testing to exercise saveLayer's parameters 478 canvas->saveLayer(NULL, NULL); 479 if (needsSaveRestore) { 480 *expected->append() = SAVE; 481 } 482 (*emitMC)(canvas, mat, clip, draw, expected, 1); 483 emit_draw(canvas, draw, expected); 484 if (needsSaveRestore) { 485 *expected->append() = RESTORE; 486 } 487 canvas->restore(); 488 *expected->append() = RESTORE; 489 if (kNone_MatType != mat || kNone_ClipType != clip) { 490 *expected->append() = RESTORE; 491 } 492 } 493 494 // Emit: 495 // matrix & clip calls 496 // SaveLayer 497 // matrix & clip calls 498 // SaveLayer 499 // matrix & clip calls 500 // draw op 501 // Restore 502 // matrix & clip calls (will be ignored) 503 // Restore 504 static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 505 ClipType clip, DrawOpType draw, 506 SkTDArray<DrawType>* expected, int accumulatedClips) { 507 bool needsSaveRestore = kNone_DrawOpType != draw && 508 (kNone_MatType != mat || kNone_ClipType != clip); 509 510 if (kNone_MatType != mat || kNone_ClipType != clip) { 511 *expected->append() = SAVE; 512 } 513 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); 514 *expected->append() = SAVE_LAYER; 515 // TODO: widen testing to exercise saveLayer's parameters 516 canvas->saveLayer(NULL, NULL); 517 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1); 518 if (kNone_MatType != mat || kNone_ClipType != clip) { 519 *expected->append() = SAVE; 520 } 521 *expected->append() = SAVE_LAYER; 522 // TODO: widen testing to exercise saveLayer's parameters 523 canvas->saveLayer(NULL, NULL); 524 if (needsSaveRestore) { 525 *expected->append() = SAVE; 526 } 527 (*emitMC)(canvas, mat, clip, draw, expected, 1); 528 emit_draw(canvas, draw, expected); 529 if (needsSaveRestore) { 530 *expected->append() = RESTORE; 531 } 532 canvas->restore(); // for saveLayer 533 *expected->append() = RESTORE; // for saveLayer 534 if (kNone_MatType != mat || kNone_ClipType != clip) { 535 *expected->append() = RESTORE; 536 } 537 canvas->restore(); 538 // required to match forced SAVE_LAYER 539 *expected->append() = RESTORE; 540 if (kNone_MatType != mat || kNone_ClipType != clip) { 541 *expected->append() = RESTORE; 542 } 543 } 544 545 ////////////////////////////////////////////////////////////////////////////// 546 547 // Emit: 548 // Save 549 // some body 550 // Restore 551 // Note: the outer save/restore are provided by beginRecording/endRecording 552 static void emit_struct0(SkCanvas* canvas, 553 PFEmitMC emitMC, MatType mat, ClipType clip, 554 PFEmitBody emitBody, DrawOpType draw, 555 SkTDArray<DrawType>* expected) { 556 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 0); 557 } 558 559 // Emit: 560 // Save 561 // matrix & clip calls 562 // Save 563 // some body 564 // Restore 565 // matrix & clip calls (will be ignored) 566 // Restore 567 // Note: the outer save/restore are provided by beginRecording/endRecording 568 static void emit_struct1(SkCanvas* canvas, 569 PFEmitMC emitMC, MatType mat, ClipType clip, 570 PFEmitBody emitBody, DrawOpType draw, 571 SkTDArray<DrawType>* expected) { 572 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 573 canvas->save(); 574 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 575 canvas->restore(); 576 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 577 } 578 579 // Emit: 580 // Save 581 // matrix & clip calls 582 // Save 583 // some body 584 // Restore 585 // Save 586 // some body 587 // Restore 588 // matrix & clip calls (will be ignored) 589 // Restore 590 // Note: the outer save/restore are provided by beginRecording/endRecording 591 static void emit_struct2(SkCanvas* canvas, 592 PFEmitMC emitMC, MatType mat, ClipType clip, 593 PFEmitBody emitBody, DrawOpType draw, 594 SkTDArray<DrawType>* expected) { 595 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 596 canvas->save(); 597 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 598 canvas->restore(); 599 canvas->save(); 600 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 601 canvas->restore(); 602 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed 603 } 604 605 // Emit: 606 // Save 607 // matrix & clip calls 608 // Save 609 // some body 610 // Restore 611 // Save 612 // matrix & clip calls 613 // Save 614 // some body 615 // Restore 616 // Restore 617 // matrix & clip calls (will be ignored) 618 // Restore 619 // Note: the outer save/restore are provided by beginRecording/endRecording 620 static void emit_struct3(SkCanvas* canvas, 621 PFEmitMC emitMC, MatType mat, ClipType clip, 622 PFEmitBody emitBody, DrawOpType draw, 623 SkTDArray<DrawType>* expected) { 624 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops 625 canvas->save(); 626 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 627 canvas->restore(); 628 canvas->save(); 629 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 630 canvas->save(); 631 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2); 632 canvas->restore(); 633 canvas->restore(); 634 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 635 } 636 637 ////////////////////////////////////////////////////////////////////////////// 638 639 #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE 640 static void print(const SkTDArray<DrawType>& expected, const SkTDArray<DrawType>& actual) { 641 SkDebugf("\n\nexpected %d --- actual %d\n", expected.count(), actual.count()); 642 int max = SkMax32(expected.count(), actual.count()); 643 644 for (int i = 0; i < max; ++i) { 645 if (i < expected.count()) { 646 SkDebugf("%16s, ", SkDrawCommand::GetCommandString(expected[i])); 647 } else { 648 SkDebugf("%16s, ", " "); 649 } 650 651 if (i < actual.count()) { 652 SkDebugf("%s\n", SkDrawCommand::GetCommandString(actual[i])); 653 } else { 654 SkDebugf("\n"); 655 } 656 } 657 SkDebugf("\n\n"); 658 SkASSERT(0); 659 } 660 #endif 661 662 static void test_collapse(skiatest::Reporter* reporter) { 663 PFEmitStruct gStructure[] = { emit_struct0, emit_struct1, emit_struct2, emit_struct3 }; 664 PFEmitBody gBody[] = { emit_body0, emit_body1, emit_body2, emit_body3 }; 665 PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip, 666 emit_double_mat_and_clip, emit_mat_clip_clip }; 667 668 for (size_t i = 0; i < SK_ARRAY_COUNT(gStructure); ++i) { 669 for (size_t j = 0; j < SK_ARRAY_COUNT(gBody); ++j) { 670 for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) { 671 for (int l = 0; l < kMatTypeCount; ++l) { 672 for (int m = 0; m < kClipTypeCount; ++m) { 673 for (int n = 0; n < kNonSaveLayerDrawOpTypeCount; ++n) { 674 #ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 675 static int testID = -1; 676 ++testID; 677 if (testID < -1) { 678 continue; 679 } 680 SkDebugf("test: %d\n", testID); 681 #endif 682 683 SkTDArray<DrawType> expected, actual; 684 685 SkPicture picture; 686 687 // Note: beginRecording/endRecording add a save/restore pair 688 SkCanvas* canvas = picture.beginRecording(100, 100); 689 (*gStructure[i])(canvas, 690 gMCs[k], 691 (MatType) l, 692 (ClipType) m, 693 gBody[j], 694 (DrawOpType) n, 695 &expected); 696 picture.endRecording(); 697 698 gets_ops(picture, &actual); 699 700 REPORTER_ASSERT(reporter, expected.count() == actual.count()); 701 702 if (expected.count() != actual.count()) { 703 #ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 704 print(expected, actual); 705 #endif 706 continue; 707 } 708 709 for (int i = 0; i < expected.count(); ++i) { 710 REPORTER_ASSERT(reporter, expected[i] == actual[i]); 711 #ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 712 if (expected[i] != actual[i]) { 713 print(expected, actual); 714 } 715 #endif 716 break; 717 } 718 } 719 } 720 } 721 } 722 } 723 } 724 } 725 726 DEF_TEST(MatrixClipCollapse, reporter) { 727 test_collapse(reporter); 728 } 729 730 #endif 731