aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/MatrixClipCollapseTest.cpp
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 14:22:47 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 14:22:47 +0000
commit1f71846e5c0ee85bb4e4c2b42044eefdebbcf2df (patch)
tree370183e6b4861840c17a502c4a118929240b9760 /tests/MatrixClipCollapseTest.cpp
parent4f7c61583b16e3056cf3350fcef42dcc6d3483b7 (diff)
Add test harness for matrix/clip state collapsing
Diffstat (limited to 'tests/MatrixClipCollapseTest.cpp')
-rw-r--r--tests/MatrixClipCollapseTest.cpp716
1 files changed, 716 insertions, 0 deletions
diff --git a/tests/MatrixClipCollapseTest.cpp b/tests/MatrixClipCollapseTest.cpp
new file mode 100644
index 0000000000..5e462729fd
--- /dev/null
+++ b/tests/MatrixClipCollapseTest.cpp
@@ -0,0 +1,716 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkCanvas.h"
+#include "SkDebugCanvas.h"
+#include "SkPicture.h"
+#include "SkPictureFlat.h"
+#include "SkPictureRecord.h"
+
+// This test exercises the Matrix/Clip State collapsing system. It generates
+// example skps and the compares the actual stored operations to the expected
+// operations. The test works by emitting canvas operations at three levels:
+// overall structure, bodies that draw something and model/clip state changes.
+//
+// Structure methods only directly emit save and restores but call the
+// ModelClip and Body helper methods to fill in the structure. Since they only
+// emit saves and restores the operations emitted by the structure methods will
+// be completely removed by the matrix/clip collapse. Note: every save in
+// a structure method is followed by a call to a ModelClip helper.
+//
+// Body methods only directly emit draw ops and saveLayer/restore pairs but call
+// the ModelClip helper methods. Since the body methods emit the ops that cannot
+// be collapsed (i.e., draw ops, saveLayer/restore) they also generate the
+// expected result information. Note: every saveLayer in a body method is
+// followed by a call to a ModelClip helper.
+//
+// The ModelClip methods output matrix and clip ops in various orders and
+// combinations. They contribute to the expected result by outputting the
+// expected matrix & clip ops. Note that, currently, the entire clip stack
+// is output for each MC state so the clip operations accumulate down the
+// save/restore stack.
+
+// TODOs:
+// check on clip offsets
+// - not sure if this is possible. The desire is to verify that the clip
+// operations' offsets point to the correct follow-on operations. This
+// could be difficult since there is no good way to communicate the
+// offset stored in the SkPicture to the debugger's clip objects
+// add comparison of rendered before & after images?
+// - not sure if this would be useful since it somewhat duplicates the
+// correctness test of running render_pictures in record mode and
+// rendering before and after images. Additionally the matrix/clip collapse
+// is sure to cause some small differences so an automated test might
+// yield too many false positives.
+// run the matrix/clip collapse system on the 10K skp set
+// - this should give us warm fuzzies that the matrix clip collapse
+// system is ready for prime time
+// bench the recording times with/without matrix/clip collapsing
+
+#ifdef COLLAPSE_MATRIX_CLIP_STATE
+
+// Extract the command ops from the input SkPicture
+static void gets_ops(SkPicture& input, SkTDArray<DrawType>* ops) {
+ SkDebugCanvas debugCanvas(input.width(), input.height());
+ debugCanvas.setBounds(input.width(), input.height());
+ input.draw(&debugCanvas);
+
+ ops->setCount(debugCanvas.getSize());
+ for (int i = 0; i < debugCanvas.getSize(); ++i) {
+ (*ops)[i] = debugCanvas.getDrawCommandAt(i)->getType();
+ }
+}
+
+enum ClipType {
+ kNone_ClipType,
+ kRect_ClipType,
+ kRRect_ClipType,
+ kPath_ClipType,
+ kRegion_ClipType,
+
+ kLast_ClipType = kRRect_ClipType
+};
+
+static const int kClipTypeCount = kLast_ClipType + 1;
+
+enum MatType {
+ kNone_MatType,
+ kTranslate_MatType,
+ kScale_MatType,
+ kSkew_MatType,
+ kRotate_MatType,
+ kConcat_MatType,
+ kSetMatrix_MatType,
+
+ kLast_MatType = kScale_MatType
+};
+
+static const int kMatTypeCount = kLast_MatType + 1;
+
+// TODO: implement the rest of the draw ops
+enum DrawOpType {
+ kNone_DrawOpType,
+#if 0
+ kBitmap_DrawOpType,
+ kBitmapMatrix_DrawOpType,
+ kBitmapNone_DrawOpType,
+ kBitmapRectToRect_DrawOpType,
+#endif
+ kClear_DrawOpType,
+#if 0
+ kData_DrawOpType,
+#endif
+ kOval_DrawOpType,
+#if 0
+ kPaint_DrawOpType,
+ kPath_DrawOpType,
+ kPicture_DrawOpType,
+ kPoints_DrawOpType,
+ kPosText_DrawOpType,
+ kPosTextTopBottom_DrawOpType,
+ kPosTextH_DrawOpType,
+ kPosTextHTopBottom_DrawOpType,
+#endif
+ kRect_DrawOpType,
+ kRRect_DrawOpType,
+#if 0
+ kSprite_DrawOpType,
+ kText_DrawOpType,
+ kTextOnPath_DrawOpType,
+ kTextTopBottom_DrawOpType,
+ kDrawVertices_DrawOpType,
+#endif
+
+ kLast_DrawOpType = kRect_DrawOpType
+};
+
+static const int kDrawOpTypeCount = kLast_DrawOpType + 1;
+
+typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip,
+ DrawOpType draw, SkTDArray<DrawType>* expected,
+ int accumulatedClips);
+typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, DrawOpType draw,
+ SkTDArray<DrawType>* expected, int accumulatedClips);
+typedef void (*PFEmitStruct)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, PFEmitBody emitBody, DrawOpType draw,
+ SkTDArray<DrawType>* expected);
+
+//////////////////////////////////////////////////////////////////////////////
+
+// TODO: expand the testing to include the different ops & AA types!
+static void emit_clip(SkCanvas* canvas, ClipType clip) {
+ switch (clip) {
+ case kNone_ClipType:
+ break;
+ case kRect_ClipType: {
+ SkRect r = SkRect::MakeLTRB(10, 10, 90, 90);
+ canvas->clipRect(r, SkRegion::kIntersect_Op, true);
+ break;
+ }
+ case kRRect_ClipType: {
+ SkRect r = SkRect::MakeLTRB(10, 10, 90, 90);
+ SkRRect rr;
+ rr.setRectXY(r, 10, 10);
+ canvas->clipRRect(rr, SkRegion::kIntersect_Op, true);
+ break;
+ }
+ case kPath_ClipType: {
+ SkPath p;
+ p.moveTo(5.0f, 5.0f);
+ p.lineTo(50.0f, 50.0f);
+ p.lineTo(100.0f, 5.0f);
+ p.close();
+ canvas->clipPath(p, SkRegion::kIntersect_Op, true);
+ break;
+ }
+ case kRegion_ClipType: {
+ SkIRect rects[2] = {
+ { 1, 1, 55, 55 },
+ { 45, 45, 99, 99 },
+ };
+ SkRegion r;
+ r.setRects(rects, 2);
+ canvas->clipRegion(r, SkRegion::kIntersect_Op);
+ break;
+ }
+ default:
+ SkASSERT(0);
+ }
+}
+
+static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) {
+ if (NULL == expected) {
+ // expected is NULL if this clip will be fused into later clips
+ return;
+ }
+
+ switch (clip) {
+ case kNone_ClipType:
+ break;
+ case kRect_ClipType:
+ *expected->append() = CONCAT;
+ *expected->append() = CLIP_RECT;
+ break;
+ case kRRect_ClipType:
+ *expected->append() = CONCAT;
+ *expected->append() = CLIP_RRECT;
+ break;
+ case kPath_ClipType:
+ *expected->append() = CONCAT;
+ *expected->append() = CLIP_PATH;
+ break;
+ case kRegion_ClipType:
+ *expected->append() = CONCAT;
+ *expected->append() = CLIP_REGION;
+ break;
+ default:
+ SkASSERT(0);
+ }
+}
+
+static void emit_mat(SkCanvas* canvas, MatType mat) {
+ switch (mat) {
+ case kNone_MatType:
+ break;
+ case kTranslate_MatType:
+ canvas->translate(5.0f, 5.0f);
+ break;
+ case kScale_MatType:
+ canvas->scale(1.1f, 1.1f);
+ break;
+ case kSkew_MatType:
+ canvas->skew(1.1f, 1.1f);
+ break;
+ case kRotate_MatType:
+ canvas->rotate(1.0f);
+ break;
+ case kConcat_MatType: {
+ SkMatrix m;
+ m.setTranslate(1.0f, 1.0f);
+ canvas->concat(m);
+ break;
+ }
+ case kSetMatrix_MatType: {
+ SkMatrix m;
+ m.setTranslate(1.0f, 1.0f);
+ canvas->setMatrix(m);
+ break;
+ }
+ default:
+ SkASSERT(0);
+ }
+}
+
+static void add_mat(MatType mat, SkTDArray<DrawType>* expected) {
+ if (NULL == expected) {
+ // expected is NULL if this matrix call will be fused into later ones
+ return;
+ }
+
+ switch (mat) {
+ case kNone_MatType:
+ break;
+ case kTranslate_MatType: // fall thru
+ case kScale_MatType: // fall thru
+ case kSkew_MatType: // fall thru
+ case kRotate_MatType: // fall thru
+ case kConcat_MatType: // fall thru
+ case kSetMatrix_MatType:
+ // TODO: this system currently converts a setMatrix to concat. If we wanted to
+ // really preserve the setMatrix semantics we should keep it a setMatrix. I'm
+ // not sure if this is a good idea though since this would keep things like pinch
+ // zoom from working.
+ *expected->append() = CONCAT;
+ break;
+ default:
+ SkASSERT(0);
+ }
+}
+
+static void emit_draw(SkCanvas* canvas, DrawOpType draw, SkTDArray<DrawType>* expected) {
+ switch (draw) {
+ case kNone_DrawOpType:
+ break;
+ case kClear_DrawOpType:
+ canvas->clear(SK_ColorRED);
+ *expected->append() = DRAW_CLEAR;
+ break;
+ case kOval_DrawOpType: {
+ SkRect r = SkRect::MakeLTRB(10, 10, 90, 90);
+ SkPaint p;
+ canvas->drawOval(r, p);
+ *expected->append() = DRAW_OVAL;
+ break;
+ }
+ case kRect_DrawOpType: {
+ SkRect r = SkRect::MakeLTRB(10, 10, 90, 90);
+ SkPaint p;
+ canvas->drawRect(r, p);
+ *expected->append() = DRAW_RECT;
+ break;
+ }
+ case kRRect_DrawOpType: {
+ SkRect r = SkRect::MakeLTRB(10.0f, 10.0f, 90.0f, 90.0f);
+ SkRRect rr;
+ rr.setRectXY(r, 5.0f, 5.0f);
+ SkPaint p;
+ canvas->drawRRect(rr, p);
+ *expected->append() = DRAW_RRECT;
+ break;
+ }
+ default:
+ SkASSERT(0);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Emit:
+// clip
+// matrix
+// Simple case - the clip isn't effect by the matrix
+static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip,
+ DrawOpType draw, SkTDArray<DrawType>* expected,
+ int accumulatedClips) {
+ if (kNone_DrawOpType == draw) {
+ return;
+ }
+
+ emit_clip(canvas, clip);
+ emit_mat(canvas, mat);
+
+ for (int i = 0; i < accumulatedClips; ++i) {
+ add_clip(clip, mat, expected);
+ }
+ add_mat(mat, expected);
+}
+
+// Emit:
+// matrix
+// clip
+// Emitting the matrix first is more challenging since the matrix has to be
+// pushed across (i.e., applied to) the clip.
+static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
+ DrawOpType draw, SkTDArray<DrawType>* expected,
+ int accumulatedClips) {
+ if (kNone_DrawOpType == draw) {
+ return;
+ }
+
+ emit_mat(canvas, mat);
+ emit_clip(canvas, clip);
+
+ // the matrix & clip order will be reversed once collapsed!
+ for (int i = 0; i < accumulatedClips; ++i) {
+ add_clip(clip, mat, expected);
+ }
+ add_mat(mat, expected);
+}
+
+// Emit:
+// matrix
+// clip
+// matrix
+// clip
+// This tests that the matrices and clips coalesce when collapsed
+static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip,
+ DrawOpType draw, SkTDArray<DrawType>* expected,
+ int accumulatedClips) {
+ if (kNone_DrawOpType == draw) {
+ return;
+ }
+
+ emit_mat(canvas, mat);
+ emit_clip(canvas, clip);
+ emit_mat(canvas, mat);
+ emit_clip(canvas, clip);
+
+ for (int i = 0; i < accumulatedClips; ++i) {
+ add_clip(clip, mat, expected);
+ add_clip(clip, mat, expected);
+ }
+ add_mat(mat, expected);
+}
+
+// Emit:
+// matrix
+// clip
+// clip
+// This tests accumulation of clips in same transform state. It also tests pushing
+// of the matrix across both the clips.
+static void emit_mat_clip_clip(SkCanvas* canvas, MatType mat, ClipType clip,
+ DrawOpType draw, SkTDArray<DrawType>* expected,
+ int accumulatedClips) {
+ if (kNone_DrawOpType == draw) {
+ return;
+ }
+
+ emit_mat(canvas, mat);
+ emit_clip(canvas, clip);
+ emit_clip(canvas, clip);
+
+ for (int i = 0; i < accumulatedClips; ++i) {
+ add_clip(clip, mat, expected);
+ add_clip(clip, mat, expected);
+ }
+ add_mat(mat, expected);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Emit:
+// matrix & clip calls
+// draw op
+static void emit_body0(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, DrawOpType draw,
+ SkTDArray<DrawType>* expected, int accumulatedClips) {
+ bool needsSaveRestore = kNone_DrawOpType != draw &&
+ (kNone_MatType != mat || kNone_ClipType != clip);
+
+ if (needsSaveRestore) {
+ *expected->append() = SAVE;
+ }
+ (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1);
+ emit_draw(canvas, draw, expected);
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+}
+
+// Emit:
+// matrix & clip calls
+// draw op
+// matrix & clip calls
+// draw op
+static void emit_body1(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, DrawOpType draw,
+ SkTDArray<DrawType>* expected, int accumulatedClips) {
+ bool needsSaveRestore = kNone_DrawOpType != draw &&
+ (kNone_MatType != mat || kNone_ClipType != clip);
+
+ if (needsSaveRestore) {
+ *expected->append() = SAVE;
+ }
+ (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1);
+ emit_draw(canvas, draw, expected);
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ *expected->append() = SAVE;
+ }
+ (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2);
+ emit_draw(canvas, draw, expected);
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+}
+
+// Emit:
+// matrix & clip calls
+// SaveLayer
+// matrix & clip calls
+// draw op
+// Restore
+static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, DrawOpType draw,
+ SkTDArray<DrawType>* expected, int accumulatedClips) {
+ bool needsSaveRestore = kNone_DrawOpType != draw &&
+ (kNone_MatType != mat || kNone_ClipType != clip);
+
+ if (needsSaveRestore) {
+ *expected->append() = SAVE_LAYER;
+ }
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+ // TODO: widen testing to exercise saveLayer's parameters
+ canvas->saveLayer(NULL, NULL);
+ if (needsSaveRestore) {
+ *expected->append() = SAVE;
+ }
+ (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2);
+ emit_draw(canvas, draw, expected);
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+ canvas->restore();
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+}
+
+// Emit:
+// matrix & clip calls
+// SaveLayer
+// matrix & clip calls
+// SaveLayer
+// matrix & clip calls
+// draw op
+// Restore
+// matrix & clip calls (will be ignored)
+// Restore
+static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat,
+ ClipType clip, DrawOpType draw,
+ SkTDArray<DrawType>* expected, int accumulatedClips) {
+ bool needsSaveRestore = kNone_DrawOpType != draw &&
+ (kNone_MatType != mat || kNone_ClipType != clip);
+
+ // This saveLayer will always be forced b.c. we currently can't tell
+ // ahead of time if it will be empty (see comment in SkMatrixClipStateMgr::save)
+ *expected->append() = SAVE_LAYER;
+
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+ // TODO: widen testing to exercise saveLayer's parameters
+ canvas->saveLayer(NULL, NULL);
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+ if (needsSaveRestore) {
+ *expected->append() = SAVE_LAYER;
+ }
+ // TODO: widen testing to exercise saveLayer's parameters
+ canvas->saveLayer(NULL, NULL);
+ if (needsSaveRestore) {
+ *expected->append() = SAVE;
+ }
+ (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+3);
+ emit_draw(canvas, draw, expected);
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+ canvas->restore();
+ if (needsSaveRestore) {
+ *expected->append() = RESTORE;
+ }
+ canvas->restore();
+
+ // required to match forced SAVE_LAYER
+ *expected->append() = RESTORE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Emit:
+// Save
+// some body
+// Restore
+// Note: the outer save/restore are provided by beginRecording/endRecording
+static void emit_struct0(SkCanvas* canvas,
+ PFEmitMC emitMC, MatType mat, ClipType clip,
+ PFEmitBody emitBody, DrawOpType draw,
+ SkTDArray<DrawType>* expected) {
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 0);
+}
+
+// Emit:
+// Save
+// matrix & clip calls
+// Save
+// some body
+// Restore
+// matrix & clip calls (will be ignored)
+// Restore
+// Note: the outer save/restore are provided by beginRecording/endRecording
+static void emit_struct1(SkCanvas* canvas,
+ PFEmitMC emitMC, MatType mat, ClipType clip,
+ PFEmitBody emitBody, DrawOpType draw,
+ SkTDArray<DrawType>* expected) {
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+ canvas->save();
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
+ canvas->restore();
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed
+}
+
+// Emit:
+// Save
+// matrix & clip calls
+// Save
+// some body
+// Restore
+// Save
+// some body
+// Restore
+// matrix & clip calls (will be ignored)
+// Restore
+// Note: the outer save/restore are provided by beginRecording/endRecording
+static void emit_struct2(SkCanvas* canvas,
+ PFEmitMC emitMC, MatType mat, ClipType clip,
+ PFEmitBody emitBody, DrawOpType draw,
+ SkTDArray<DrawType>* expected) {
+ (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops
+ canvas->save();
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
+ canvas->restore();
+ canvas->save();
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
+ canvas->restore();
+ (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed
+}
+
+// Emit:
+// Save
+// matrix & clip calls
+// Save
+// some body
+// Restore
+// Save
+// matrix & clip calls
+// Save
+// some body
+// Restore
+// Restore
+// matrix & clip calls (will be ignored)
+// Restore
+// Note: the outer save/restore are provided by beginRecording/endRecording
+static void emit_struct3(SkCanvas* canvas,
+ PFEmitMC emitMC, MatType mat, ClipType clip,
+ PFEmitBody emitBody, DrawOpType draw,
+ SkTDArray<DrawType>* expected) {
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops
+ canvas->save();
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
+ canvas->restore();
+ canvas->save();
+ (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops
+ canvas->save();
+ (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2);
+ canvas->restore();
+ canvas->restore();
+ (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef COLLAPSE_MATRIX_CLIP_STATE
+static void print(const SkTDArray<DrawType>& expected, const SkTDArray<DrawType>& actual) {
+ SkDebugf("\n\nexpected %d --- actual %d\n", expected.count(), actual.count());
+ int max = SkMax32(expected.count(), actual.count());
+
+ for (int i = 0; i < max; ++i) {
+ if (i < expected.count()) {
+ SkDebugf("%16s, ", SkDrawCommand::GetCommandString(expected[i]));
+ } else {
+ SkDebugf("%16s, ", " ");
+ }
+
+ if (i < actual.count()) {
+ SkDebugf("%s\n", SkDrawCommand::GetCommandString(actual[i]));
+ } else {
+ SkDebugf("\n");
+ }
+ }
+ SkDebugf("\n\n");
+ SkASSERT(0);
+}
+#endif
+
+static void test_collapse(skiatest::Reporter* reporter) {
+ PFEmitStruct gStructure[] = { emit_struct0, emit_struct1, emit_struct2, emit_struct3 };
+ PFEmitBody gBody[] = { emit_body0, emit_body1, emit_body2, emit_body3 };
+ PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip,
+ emit_double_mat_and_clip, emit_mat_clip_clip };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gStructure); ++i) {
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gBody); ++j) {
+ for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) {
+ for (int l = 0; l < kMatTypeCount; ++l) {
+ for (int m = 0; m < kClipTypeCount; ++m) {
+ for (int n = 0; n < kDrawOpTypeCount; ++n) {
+#ifdef COLLAPSE_MATRIX_CLIP_STATE
+ static int testID = -1;
+ ++testID;
+ if (testID < -1) {
+ continue;
+ }
+#endif
+
+ SkTDArray<DrawType> expected, actual;
+
+ SkPicture picture;
+
+ // Note: beginRecording/endRecording add a save/restore pair
+ SkCanvas* canvas = picture.beginRecording(100, 100);
+ (*gStructure[i])(canvas,
+ gMCs[k],
+ (MatType) l,
+ (ClipType) m,
+ gBody[j],
+ (DrawOpType) n,
+ &expected);
+ picture.endRecording();
+
+ gets_ops(picture, &actual);
+
+ REPORTER_ASSERT(reporter, expected.count() == actual.count());
+
+ if (expected.count() != actual.count()) {
+#ifdef COLLAPSE_MATRIX_CLIP_STATE
+ print(expected, actual);
+#endif
+ continue;
+ }
+
+ for (int i = 0; i < expected.count(); ++i) {
+ REPORTER_ASSERT(reporter, expected[i] == actual[i]);
+#ifdef COLLAPSE_MATRIX_CLIP_STATE
+ if (expected[i] != actual[i]) {
+ print(expected, actual);
+ }
+#endif
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+DEF_TEST(MatrixClipCollapse, reporter) {
+ test_collapse(reporter);
+}
+
+#endif \ No newline at end of file