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