blob: de6af362eb1aa7d2c67d59d4a8946166b8f74c0a [file] [log] [blame]
robertphillips@google.com1f718462014-02-06 14:22:47 +00001/*
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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +000023// be completely removed by the matrix/clip collapse. Note: every save in
robertphillips@google.com1f718462014-02-06 14:22:47 +000024// 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +000033// combinations. They contribute to the expected result by outputting the
robertphillips@google.com1f718462014-02-06 14:22:47 +000034// 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +000044// add comparison of rendered before & after images?
robertphillips@google.com1f718462014-02-06 14:22:47 +000045// - 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
robertphillips2de59392015-02-11 13:18:14 -080055#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
robertphillips@google.com105a4a52014-02-11 15:10:40 +000056
57// Enable/disable debugging helper code
58//#define TEST_COLLAPSE_MATRIX_CLIP_STATE 1
robertphillips@google.com1f718462014-02-06 14:22:47 +000059
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
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000132 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,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000137};
138
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000139static const int kNonSaveLayerDrawOpTypeCount = kLastNonSaveLayer_DrawOpType + 1;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000140
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000141typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip,
142 DrawOpType draw, SkTDArray<DrawType>* expected,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000143 int accumulatedClips);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000144typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
145 ClipType clip, DrawOpType draw,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000146 SkTDArray<DrawType>* expected, int accumulatedClips);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000147typedef void (*PFEmitStruct)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
148 ClipType clip, PFEmitBody emitBody, DrawOpType draw,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000149 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) {
halcanary96fcdcc2015-08-27 07:41:13 -0700195 if (nullptr == expected) {
196 // expected is nullptr if this clip will be fused into later clips
robertphillips@google.com1f718462014-02-06 14:22:47 +0000197 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) {
halcanary96fcdcc2015-08-27 07:41:13 -0700258 if (nullptr == expected) {
259 // expected is nullptr if this matrix call will be fused into later ones
robertphillips@google.com1f718462014-02-06 14:22:47 +0000260 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000271 case kSetMatrix_MatType:
robertphillips@google.com1f718462014-02-06 14:22:47 +0000272 // 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 }
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000281}
robertphillips@google.com1f718462014-02-06 14:22:47 +0000282
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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000325static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip,
326 DrawOpType draw, SkTDArray<DrawType>* expected,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000327 int accumulatedClips) {
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000328 emit_clip(canvas, clip);
329 emit_mat(canvas, mat);
330
robertphillips@google.com1f718462014-02-06 14:22:47 +0000331 if (kNone_DrawOpType == draw) {
332 return;
333 }
334
robertphillips@google.com1f718462014-02-06 14:22:47 +0000335 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.
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000346static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
347 DrawOpType draw, SkTDArray<DrawType>* expected,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000348 int accumulatedClips) {
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000349 emit_mat(canvas, mat);
350 emit_clip(canvas, clip);
351
robertphillips@google.com1f718462014-02-06 14:22:47 +0000352 if (kNone_DrawOpType == draw) {
353 return;
354 }
355
robertphillips@google.com1f718462014-02-06 14:22:47 +0000356 // 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000369static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
370 DrawOpType draw, SkTDArray<DrawType>* expected,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000371 int accumulatedClips) {
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000372 emit_mat(canvas, mat);
373 emit_clip(canvas, clip);
374 emit_mat(canvas, mat);
375 emit_clip(canvas, clip);
376
robertphillips@google.com1f718462014-02-06 14:22:47 +0000377 if (kNone_DrawOpType == draw) {
378 return;
379 }
380
robertphillips@google.com1f718462014-02-06 14:22:47 +0000381 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) {
commit-bot@chromium.org1cb6d1a2014-02-18 18:13:34 +0000397 emit_mat(canvas, mat);
398 emit_clip(canvas, clip);
399 emit_clip(canvas, clip);
400
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000401 if (kNone_DrawOpType == draw) {
402 return;
403 }
404
robertphillips@google.com1f718462014-02-06 14:22:47 +0000405 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000417static void emit_body0(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000418 ClipType clip, DrawOpType draw,
419 SkTDArray<DrawType>* expected, int accumulatedClips) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000420 bool needsSaveRestore = kNone_DrawOpType != draw &&
robertphillips@google.com1f718462014-02-06 14:22:47 +0000421 (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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000438static void emit_body1(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000439 ClipType clip, DrawOpType draw,
440 SkTDArray<DrawType>* expected, int accumulatedClips) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000441 bool needsSaveRestore = kNone_DrawOpType != draw &&
robertphillips@google.com1f718462014-02-06 14:22:47 +0000442 (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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000466static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000467 ClipType clip, DrawOpType draw,
468 SkTDArray<DrawType>* expected, int accumulatedClips) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000469 bool needsSaveRestore = kNone_DrawOpType != draw &&
robertphillips@google.com1f718462014-02-06 14:22:47 +0000470 (kNone_MatType != mat || kNone_ClipType != clip);
471
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000472 if (kNone_MatType != mat || kNone_ClipType != clip) {
473 *expected->append() = SAVE;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000474 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000475 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1);
476 *expected->append() = SAVE_LAYER;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000477 // TODO: widen testing to exercise saveLayer's parameters
halcanary96fcdcc2015-08-27 07:41:13 -0700478 canvas->saveLayer(nullptr, nullptr);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000479 if (needsSaveRestore) {
480 *expected->append() = SAVE;
481 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000482 (*emitMC)(canvas, mat, clip, draw, expected, 1);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000483 emit_draw(canvas, draw, expected);
484 if (needsSaveRestore) {
485 *expected->append() = RESTORE;
486 }
487 canvas->restore();
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000488 *expected->append() = RESTORE;
489 if (kNone_MatType != mat || kNone_ClipType != clip) {
robertphillips@google.com1f718462014-02-06 14:22:47 +0000490 *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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000504static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000505 ClipType clip, DrawOpType draw,
506 SkTDArray<DrawType>* expected, int accumulatedClips) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000507 bool needsSaveRestore = kNone_DrawOpType != draw &&
robertphillips@google.com1f718462014-02-06 14:22:47 +0000508 (kNone_MatType != mat || kNone_ClipType != clip);
509
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000510 if (kNone_MatType != mat || kNone_ClipType != clip) {
511 *expected->append() = SAVE;
512 }
skia.committer@gmail.comc05d2852014-02-20 03:01:56 +0000513 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000514 *expected->append() = SAVE_LAYER;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000515 // TODO: widen testing to exercise saveLayer's parameters
halcanary96fcdcc2015-08-27 07:41:13 -0700516 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000517 (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1);
518 if (kNone_MatType != mat || kNone_ClipType != clip) {
519 *expected->append() = SAVE;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000520 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000521 *expected->append() = SAVE_LAYER;
robertphillips@google.com1f718462014-02-06 14:22:47 +0000522 // TODO: widen testing to exercise saveLayer's parameters
halcanary96fcdcc2015-08-27 07:41:13 -0700523 canvas->saveLayer(nullptr, nullptr);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000524 if (needsSaveRestore) {
525 *expected->append() = SAVE;
526 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000527 (*emitMC)(canvas, mat, clip, draw, expected, 1);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000528 emit_draw(canvas, draw, expected);
529 if (needsSaveRestore) {
530 *expected->append() = RESTORE;
531 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000532 canvas->restore(); // for saveLayer
533 *expected->append() = RESTORE; // for saveLayer
534 if (kNone_MatType != mat || kNone_ClipType != clip) {
robertphillips@google.com1f718462014-02-06 14:22:47 +0000535 *expected->append() = RESTORE;
536 }
537 canvas->restore();
robertphillips@google.com1f718462014-02-06 14:22:47 +0000538 // required to match forced SAVE_LAYER
539 *expected->append() = RESTORE;
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000540 if (kNone_MatType != mat || kNone_ClipType != clip) {
541 *expected->append() = RESTORE;
542 }
robertphillips@google.com1f718462014-02-06 14:22:47 +0000543}
544
545//////////////////////////////////////////////////////////////////////////////
546
547// Emit:
548// Save
549// some body
550// Restore
551// Note: the outer save/restore are provided by beginRecording/endRecording
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000552static void emit_struct0(SkCanvas* canvas,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000553 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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000568static void emit_struct1(SkCanvas* canvas,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000569 PFEmitMC emitMC, MatType mat, ClipType clip,
570 PFEmitBody emitBody, DrawOpType draw,
571 SkTDArray<DrawType>* expected) {
halcanary96fcdcc2015-08-27 07:41:13 -0700572 (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these get fused into later ops
robertphillips@google.com1f718462014-02-06 14:22:47 +0000573 canvas->save();
574 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
575 canvas->restore();
halcanary96fcdcc2015-08-27 07:41:13 -0700576 (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed
robertphillips@google.com1f718462014-02-06 14:22:47 +0000577}
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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000591static void emit_struct2(SkCanvas* canvas,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000592 PFEmitMC emitMC, MatType mat, ClipType clip,
593 PFEmitBody emitBody, DrawOpType draw,
594 SkTDArray<DrawType>* expected) {
halcanary96fcdcc2015-08-27 07:41:13 -0700595 (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops
robertphillips@google.com1f718462014-02-06 14:22:47 +0000596 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();
halcanary96fcdcc2015-08-27 07:41:13 -0700602 (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get removed
robertphillips@google.com1f718462014-02-06 14:22:47 +0000603}
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
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000620static void emit_struct3(SkCanvas* canvas,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000621 PFEmitMC emitMC, MatType mat, ClipType clip,
622 PFEmitBody emitBody, DrawOpType draw,
623 SkTDArray<DrawType>* expected) {
halcanary96fcdcc2015-08-27 07:41:13 -0700624 (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get fused into later ops
robertphillips@google.com1f718462014-02-06 14:22:47 +0000625 canvas->save();
626 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
627 canvas->restore();
628 canvas->save();
halcanary96fcdcc2015-08-27 07:41:13 -0700629 (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops
robertphillips@google.com1f718462014-02-06 14:22:47 +0000630 canvas->save();
631 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2);
632 canvas->restore();
633 canvas->restore();
halcanary96fcdcc2015-08-27 07:41:13 -0700634 (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed
robertphillips@google.com1f718462014-02-06 14:22:47 +0000635}
636
637//////////////////////////////////////////////////////////////////////////////
638
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000639#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
robertphillips@google.com1f718462014-02-06 14:22:47 +0000640static 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 };
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000665 PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000666 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) {
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000673 for (int n = 0; n < kNonSaveLayerDrawOpTypeCount; ++n) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000674#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
robertphillips@google.com1f718462014-02-06 14:22:47 +0000675 static int testID = -1;
676 ++testID;
677 if (testID < -1) {
678 continue;
679 }
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000680 SkDebugf("test: %d\n", testID);
robertphillips@google.com1f718462014-02-06 14:22:47 +0000681#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);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000689 (*gStructure[i])(canvas,
690 gMCs[k],
robertphillips@google.com1f718462014-02-06 14:22:47 +0000691 (MatType) l,
692 (ClipType) m,
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000693 gBody[j],
694 (DrawOpType) n,
robertphillips@google.com1f718462014-02-06 14:22:47 +0000695 &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()) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000703#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
robertphillips@google.com1f718462014-02-06 14:22:47 +0000704 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]);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000711#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
robertphillips@google.com1f718462014-02-06 14:22:47 +0000712 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
robertphillips@google.com940e3ba2014-02-06 14:57:20 +0000730#endif