* 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"
#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
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;
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,
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);
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) {
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) {
return;
}
switch (mat) {
case kNone_MatType:
break;
case kTranslate_MatType:
case kScale_MatType:
case kSkew_MatType:
case kRotate_MatType:
case kConcat_MatType:
case kSetMatrix_MatType:
*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);
}
}
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);
}
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;
}
for (int i = 0; i < accumulatedClips; ++i) {
add_clip(clip, mat, expected);
}
add_mat(mat, expected);
}
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);
}
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);
}
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;
}
}
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;
}
}
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;
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;
}
}
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;
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;
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;
}
canvas->restore();
*expected->append() = RESTORE;
if (kNone_MatType != mat || kNone_ClipType != clip) {
*expected->append() = RESTORE;
}
}
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);
}
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);
canvas->save();
(*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
canvas->restore();
(*emitMC)(canvas, mat, clip, draw, nullptr, 0);
}
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);
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);
}
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);
canvas->save();
(*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
canvas->restore();
canvas->save();
(*emitMC)(canvas, mat, clip, draw, nullptr, 1);
canvas->save();
(*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2);
canvas->restore();
canvas->restore();
(*emitMC)(canvas, mat, clip, draw, nullptr, 0);
}
#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, ", 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;
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