| /* |
| * 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 SK_COLLAPSE_MATRIX_CLIP_STATE |
| |
| // Enable/disable debugging helper code |
| //#define TEST_COLLAPSE_MATRIX_CLIP_STATE 1 |
| |
| // 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 |
| |
| kLastNonSaveLayer_DrawOpType = kRect_DrawOpType, |
| |
| // saveLayer's have to handled apart from the other draw operations |
| // since they also alter the save/restore structure. |
| kSaveLayer_DrawOpType, |
| }; |
| |
| static const int kNonSaveLayerDrawOpTypeCount = kLastNonSaveLayer_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 (nullptr == expected) { |
| // expected is nullptr 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 (nullptr == expected) { |
| // expected is nullptr 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) { |
| emit_clip(canvas, clip); |
| emit_mat(canvas, mat); |
| |
| if (kNone_DrawOpType == draw) { |
| return; |
| } |
| |
| 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) { |
| emit_mat(canvas, mat); |
| emit_clip(canvas, clip); |
| |
| if (kNone_DrawOpType == draw) { |
| return; |
| } |
| |
| // 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) { |
| emit_mat(canvas, mat); |
| emit_clip(canvas, clip); |
| emit_mat(canvas, mat); |
| emit_clip(canvas, clip); |
| |
| if (kNone_DrawOpType == draw) { |
| return; |
| } |
| |
| 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) { |
| emit_mat(canvas, mat); |
| emit_clip(canvas, clip); |
| emit_clip(canvas, clip); |
| |
| if (kNone_DrawOpType == draw) { |
| return; |
| } |
| |
| 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 (kNone_MatType != mat || kNone_ClipType != clip) { |
| *expected->append() = SAVE; |
| } |
| (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); |
| *expected->append() = SAVE_LAYER; |
| // TODO: widen testing to exercise saveLayer's parameters |
| canvas->saveLayer(nullptr, nullptr); |
| if (needsSaveRestore) { |
| *expected->append() = SAVE; |
| } |
| (*emitMC)(canvas, mat, clip, draw, expected, 1); |
| emit_draw(canvas, draw, expected); |
| if (needsSaveRestore) { |
| *expected->append() = RESTORE; |
| } |
| canvas->restore(); |
| *expected->append() = RESTORE; |
| if (kNone_MatType != mat || kNone_ClipType != clip) { |
| *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); |
| |
| if (kNone_MatType != mat || kNone_ClipType != clip) { |
| *expected->append() = SAVE; |
| } |
| (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); |
| *expected->append() = SAVE_LAYER; |
| // TODO: widen testing to exercise saveLayer's parameters |
| canvas->saveLayer(nullptr, nullptr); |
| (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1); |
| if (kNone_MatType != mat || kNone_ClipType != clip) { |
| *expected->append() = SAVE; |
| } |
| *expected->append() = SAVE_LAYER; |
| // TODO: widen testing to exercise saveLayer's parameters |
| canvas->saveLayer(nullptr, nullptr); |
| if (needsSaveRestore) { |
| *expected->append() = SAVE; |
| } |
| (*emitMC)(canvas, mat, clip, draw, expected, 1); |
| emit_draw(canvas, draw, expected); |
| if (needsSaveRestore) { |
| *expected->append() = RESTORE; |
| } |
| canvas->restore(); // for saveLayer |
| *expected->append() = RESTORE; // for saveLayer |
| if (kNone_MatType != mat || kNone_ClipType != clip) { |
| *expected->append() = RESTORE; |
| } |
| canvas->restore(); |
| // required to match forced SAVE_LAYER |
| *expected->append() = RESTORE; |
| if (kNone_MatType != mat || kNone_ClipType != clip) { |
| *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, nullptr, 0); // these get fused into later ops |
| canvas->save(); |
| (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); |
| canvas->restore(); |
| (*emitMC)(canvas, mat, clip, draw, nullptr, 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, nullptr, 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, nullptr, 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, nullptr, 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, nullptr, 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, nullptr, 0); // these will get removed |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| #ifdef SK_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 < kNonSaveLayerDrawOpTypeCount; ++n) { |
| #ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE |
| static int testID = -1; |
| ++testID; |
| if (testID < -1) { |
| continue; |
| } |
| SkDebugf("test: %d\n", testID); |
| #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 TEST_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 TEST_COLLAPSE_MATRIX_CLIP_STATE |
| if (expected[i] != actual[i]) { |
| print(expected, actual); |
| } |
| #endif |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| DEF_TEST(MatrixClipCollapse, reporter) { |
| test_collapse(reporter); |
| } |
| |
| #endif |