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