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
gets_ops(SkPicture & input,SkTDArray<DrawType> * ops)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!
emit_clip(SkCanvas * canvas,ClipType clip)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
add_clip(ClipType clip,MatType mat,SkTDArray<DrawType> * expected)194 static 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
emit_mat(SkCanvas * canvas,MatType mat)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
add_mat(MatType mat,SkTDArray<DrawType> * expected)257 static 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
emit_draw(SkCanvas * canvas,DrawOpType draw,SkTDArray<DrawType> * expected)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
emit_clip_and_mat(SkCanvas * canvas,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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.
emit_mat_and_clip(SkCanvas * canvas,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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
emit_double_mat_and_clip(SkCanvas * canvas,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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.
emit_mat_clip_clip(SkCanvas * canvas,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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
emit_body0(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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
emit_body1(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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
emit_body2(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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(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
emit_body3(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,DrawOpType draw,SkTDArray<DrawType> * expected,int accumulatedClips)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(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
emit_struct0(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,PFEmitBody emitBody,DrawOpType draw,SkTDArray<DrawType> * expected)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
emit_struct1(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,PFEmitBody emitBody,DrawOpType draw,SkTDArray<DrawType> * expected)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, 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
emit_struct2(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,PFEmitBody emitBody,DrawOpType draw,SkTDArray<DrawType> * expected)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, 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
emit_struct3(SkCanvas * canvas,PFEmitMC emitMC,MatType mat,ClipType clip,PFEmitBody emitBody,DrawOpType draw,SkTDArray<DrawType> * expected)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, 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
print(const SkTDArray<DrawType> & expected,const SkTDArray<DrawType> & actual)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
test_collapse(skiatest::Reporter * reporter)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
DEF_TEST(MatrixClipCollapse,reporter)726 DEF_TEST(MatrixClipCollapse, reporter) {
727 test_collapse(reporter);
728 }
729
730 #endif
731