1 /* 2 * Copyright 2011 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 "SkCanvas.h" 9 #include "SkPath.h" 10 #include "Test.h" 11 12 #define DIMENSION 32 13 14 static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path, 15 const SkPaint& paint, bool shouldDraw) { 16 SkBitmap bm; 17 bm.allocN32Pixels(DIMENSION, DIMENSION); 18 SkASSERT(DIMENSION*4 == bm.rowBytes()); // ensure no padding on each row 19 bm.eraseColor(SK_ColorTRANSPARENT); 20 21 SkCanvas canvas(bm); 22 SkPaint p(paint); 23 p.setColor(SK_ColorWHITE); 24 25 canvas.drawPath(path, p); 26 27 size_t count = DIMENSION * DIMENSION; 28 const SkPMColor* ptr = bm.getAddr32(0, 0); 29 30 SkPMColor andValue = ~0U; 31 SkPMColor orValue = 0; 32 for (size_t i = 0; i < count; ++i) { 33 SkPMColor c = ptr[i]; 34 andValue &= c; 35 orValue |= c; 36 } 37 38 // success means we drew everywhere or nowhere (depending on shouldDraw) 39 bool success = shouldDraw ? (~0U == andValue) : (0 == orValue); 40 41 if (!success) { 42 const char* str; 43 if (shouldDraw) { 44 str = "Path expected to draw everywhere, but didn't. "; 45 } else { 46 str = "Path expected to draw nowhere, but did. "; 47 } 48 ERRORF(reporter, "%s style[%d] cap[%d] join[%d] antialias[%d]" 49 " filltype[%d] ptcount[%d]", str, paint.getStyle(), 50 paint.getStrokeCap(), paint.getStrokeJoin(), 51 paint.isAntiAlias(), path.getFillType(), path.countPoints()); 52 // uncomment this if you want to step in to see the failure 53 // canvas.drawPath(path, p); 54 } 55 } 56 57 enum DrawCaps { 58 kDontDrawCaps, 59 kDrawCaps 60 }; 61 62 static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw, 63 DrawCaps drawCaps) { 64 static const SkPaint::Cap gCaps[] = { 65 SkPaint::kButt_Cap, 66 SkPaint::kRound_Cap, 67 SkPaint::kSquare_Cap 68 }; 69 static const SkPaint::Join gJoins[] = { 70 SkPaint::kMiter_Join, 71 SkPaint::kRound_Join, 72 SkPaint::kBevel_Join 73 }; 74 static const SkPaint::Style gStyles[] = { 75 SkPaint::kFill_Style, 76 SkPaint::kStroke_Style, 77 SkPaint::kStrokeAndFill_Style 78 }; 79 for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { 80 for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) { 81 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { 82 if (drawCaps && SkPaint::kButt_Cap != gCaps[cap] 83 && SkPaint::kFill_Style != gStyles[style]) { 84 continue; 85 } 86 87 SkPaint paint; 88 paint.setStrokeWidth(SkIntToScalar(10)); 89 90 paint.setStrokeCap(gCaps[cap]); 91 paint.setStrokeJoin(gJoins[join]); 92 paint.setStyle(gStyles[style]); 93 94 paint.setAntiAlias(false); 95 drawAndTest(reporter, path, paint, shouldDraw); 96 paint.setAntiAlias(true); 97 drawAndTest(reporter, path, paint, shouldDraw); 98 } 99 } 100 } 101 } 102 103 #define CX (SkIntToScalar(DIMENSION) / 2) 104 #define CY (SkIntToScalar(DIMENSION) / 2) 105 106 static void make_empty(SkPath*) {} 107 static void make_M(SkPath* path) { path->moveTo(CX, CY); } 108 static void make_MM(SkPath* path) { path->moveTo(CX, CY); path->moveTo(CX, CY); } 109 static void make_MZM(SkPath* path) { path->moveTo(CX, CY); path->close(); path->moveTo(CX, CY); } 110 static void make_L(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); } 111 static void make_Q(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); } 112 static void make_C(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); } 113 114 /* Two invariants are tested: How does an empty/degenerate path draw? 115 * - if the path is drawn inverse, it should draw everywhere 116 * - if the path is drawn non-inverse, it should draw nowhere 117 * 118 * Things to iterate on: 119 * - path (empty, degenerate line/quad/cubic w/ and w/o close 120 * - paint style 121 * - path filltype 122 * - path stroke variants (e.g. caps, joins, width) 123 */ 124 static void test_emptydrawing(skiatest::Reporter* reporter) { 125 static void (*gMakeProc[])(SkPath*) = { 126 make_empty, make_M, make_MM, make_MZM, make_L, make_Q, make_C 127 }; 128 static SkPath::FillType gFills[] = { 129 SkPath::kWinding_FillType, 130 SkPath::kEvenOdd_FillType, 131 SkPath::kInverseWinding_FillType, 132 SkPath::kInverseEvenOdd_FillType 133 }; 134 for (int doClose = 0; doClose < 2; ++doClose) { 135 for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) { 136 SkPath path; 137 gMakeProc[i](&path); 138 if (doClose) { 139 path.close(); 140 } 141 /* zero length segments and close following moves draw round and square caps */ 142 bool allowCaps = make_L == gMakeProc[i] || make_Q == gMakeProc[i] 143 || make_C == gMakeProc[i] || make_MZM == gMakeProc[i]; 144 allowCaps |= SkToBool(doClose); 145 for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { 146 path.setFillType(gFills[fill]); 147 bool shouldDraw = path.isInverseFillType(); 148 iter_paint(reporter, path, shouldDraw, allowCaps ? kDrawCaps : kDontDrawCaps); 149 } 150 } 151 } 152 } 153 154 DEF_TEST(EmptyPath, reporter) { 155 test_emptydrawing(reporter); 156 } 157