MatrixClipCollapseTest.cpp 24 KB

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