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 61static 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 72enum ClipType { 73 kNone_ClipType, 74 kRect_ClipType, 75 kRRect_ClipType, 76 kPath_ClipType, 77 kRegion_ClipType, 78 79 kLast_ClipType = kRRect_ClipType 80}; 81 82static const int kClipTypeCount = kLast_ClipType + 1; 83 84enum 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 96static const int kMatTypeCount = kLast_MatType + 1; 97 98// TODO: implement the rest of the draw ops 99enum 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 139static const int kNonSaveLayerDrawOpTypeCount = kLastNonSaveLayer_DrawOpType + 1; 140 141typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip, 142 DrawOpType draw, SkTDArray<DrawType>* expected, 143 int accumulatedClips); 144typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 145 ClipType clip, DrawOpType draw, 146 SkTDArray<DrawType>* expected, int accumulatedClips); 147typedef 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! 154static 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 194static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) { 195 if (nullptr == expected) { 196 // expected is nullptr 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 224static 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 257static void add_mat(MatType mat, SkTDArray<DrawType>* expected) { 258 if (nullptr == expected) { 259 // expected is nullptr 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 283static 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 325static 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. 346static 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 369static 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. 394static 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 417static 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 438static 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 466static 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(nullptr, nullptr); 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 504static 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(nullptr, nullptr); 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(nullptr, nullptr); 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 552static 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 568static 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, nullptr, 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, nullptr, 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 591static 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, nullptr, 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, nullptr, 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 620static 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, nullptr, 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, nullptr, 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, nullptr, 0); // these will get removed 635} 636 637////////////////////////////////////////////////////////////////////////////// 638 639#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE 640static 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 662static 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 726DEF_TEST(MatrixClipCollapse, reporter) { 727 test_collapse(reporter); 728} 729 730#endif 731