| /* | |
| * 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 "include/core/SkCanvas.h" | |
| #include "include/core/SkPicture.h" | |
| #include "src/core/SkPictureFlat.h" | |
| #include "src/core/SkPictureRecord.h" | |
| #include "tests/Test.h" | |
| #include "tools/debugger/DebugCanvas.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) { | |
| DebugCanvas 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 = std::max(expected.count(), actual.count()); | |
| for (int i = 0; i < max; ++i) { | |
| if (i < expected.count()) { | |
| SkDebugf("%16s, ", DrawCommand::GetCommandString(expected[i])); | |
| } else { | |
| SkDebugf("%16s, ", " "); | |
| } | |
| if (i < actual.count()) { | |
| SkDebugf("%s\n", DrawCommand::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 |