1 /* 2 * Copyright 2012 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 "GrStrokePathRenderer.h" 9 10 #include "GrDrawTarget.h" 11 #include "SkPath.h" 12 #include "SkStrokeRec.h" 13 14 static bool is_clockwise(const SkVector& before, const SkVector& after) { 15 return before.cross(after) > 0; 16 } 17 18 enum IntersectionType { 19 kNone_IntersectionType, 20 kIn_IntersectionType, 21 kOut_IntersectionType 22 }; 23 24 static IntersectionType intersection(const SkPoint& p1, const SkPoint& p2, 25 const SkPoint& p3, const SkPoint& p4, 26 SkPoint& res) { 27 // Store the values for fast access and easy 28 // equations-to-code conversion 29 SkScalar x1 = p1.x(), x2 = p2.x(), x3 = p3.x(), x4 = p4.x(); 30 SkScalar y1 = p1.y(), y2 = p2.y(), y3 = p3.y(), y4 = p4.y(); 31 32 SkScalar d = SkScalarMul(x1 - x2, y3 - y4) - SkScalarMul(y1 - y2, x3 - x4); 33 // If d is zero, there is no intersection 34 if (SkScalarNearlyZero(d)) { 35 return kNone_IntersectionType; 36 } 37 38 // Get the x and y 39 SkScalar pre = SkScalarMul(x1, y2) - SkScalarMul(y1, x2), 40 post = SkScalarMul(x3, y4) - SkScalarMul(y3, x4); 41 // Compute the point of intersection 42 res.set(SkScalarDiv(SkScalarMul(pre, x3 - x4) - SkScalarMul(x1 - x2, post), d), 43 SkScalarDiv(SkScalarMul(pre, y3 - y4) - SkScalarMul(y1 - y2, post), d)); 44 45 // Check if the x and y coordinates are within both lines 46 return (res.x() < GrMin(x1, x2) || res.x() > GrMax(x1, x2) || 47 res.x() < GrMin(x3, x4) || res.x() > GrMax(x3, x4) || 48 res.y() < GrMin(y1, y2) || res.y() > GrMax(y1, y2) || 49 res.y() < GrMin(y3, y4) || res.y() > GrMax(y3, y4)) ? 50 kOut_IntersectionType : kIn_IntersectionType; 51 } 52 53 GrStrokePathRenderer::GrStrokePathRenderer() { 54 } 55 56 bool GrStrokePathRenderer::canDrawPath(const SkPath& path, 57 const SkStrokeRec& stroke, 58 const GrDrawTarget* target, 59 bool antiAlias) const { 60 // FIXME : put the proper condition once GrDrawTarget::isOpaque is implemented 61 const bool isOpaque = true; // target->isOpaque(); 62 63 // FIXME : remove this requirement once we have AA circles and implement the 64 // circle joins/caps appropriately in the ::onDrawPath() function. 65 const bool requiresAACircle = (stroke.getCap() == SkPaint::kRound_Cap) || 66 (stroke.getJoin() == SkPaint::kRound_Join); 67 68 // Indices being stored in uint16, we don't want to overflow the indices capacity 69 static const int maxVBSize = 1 << 16; 70 const int maxNbVerts = (path.countPoints() + 1) * 5; 71 72 // Check that the path contains no curved lines, only straight lines 73 static const uint32_t unsupportedMask = SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask; 74 75 // Must not be filled nor hairline nor semi-transparent 76 // Note : May require a check to path.isConvex() if AA is supported 77 return ((stroke.getStyle() == SkStrokeRec::kStroke_Style) && (maxNbVerts < maxVBSize) && 78 !path.isInverseFillType() && isOpaque && !requiresAACircle && !antiAlias && 79 ((path.getSegmentMasks() & unsupportedMask) == 0)); 80 } 81 82 bool GrStrokePathRenderer::onDrawPath(const SkPath& origPath, 83 const SkStrokeRec& stroke, 84 GrDrawTarget* target, 85 bool antiAlias) { 86 if (origPath.isEmpty()) { 87 return true; 88 } 89 90 SkScalar width = stroke.getWidth(); 91 if (width <= 0) { 92 return false; 93 } 94 95 // Get the join type 96 SkPaint::Join join = stroke.getJoin(); 97 SkScalar miterLimit = stroke.getMiter(); 98 SkScalar sqMiterLimit = SkScalarMul(miterLimit, miterLimit); 99 if ((join == SkPaint::kMiter_Join) && (miterLimit <= SK_Scalar1)) { 100 // If the miter limit is small, treat it as a bevel join 101 join = SkPaint::kBevel_Join; 102 } 103 const bool isMiter = (join == SkPaint::kMiter_Join); 104 const bool isBevel = (join == SkPaint::kBevel_Join); 105 SkScalar invMiterLimit = isMiter ? SK_Scalar1 / miterLimit : 0; 106 SkScalar invMiterLimitSq = SkScalarMul(invMiterLimit, invMiterLimit); 107 108 // Allocate vertices 109 const int nbQuads = origPath.countPoints() + 1; // Could be "-1" if path is not closed 110 const int extraVerts = isMiter || isBevel ? 1 : 0; 111 const int maxVertexCount = nbQuads * (4 + extraVerts); 112 const int maxIndexCount = nbQuads * (6 + extraVerts * 3); // Each extra vert adds a triangle 113 target->drawState()->setDefaultVertexAttribs(); 114 GrDrawTarget::AutoReleaseGeometry arg(target, maxVertexCount, maxIndexCount); 115 if (!arg.succeeded()) { 116 return false; 117 } 118 SkPoint* verts = reinterpret_cast<SkPoint*>(arg.vertices()); 119 uint16_t* idxs = reinterpret_cast<uint16_t*>(arg.indices()); 120 int vCount = 0, iCount = 0; 121 122 // Transform the path into a list of triangles 123 SkPath::Iter iter(origPath, false); 124 SkPoint pts[4]; 125 const SkScalar radius = SkScalarMul(width, 0.5f); 126 SkPoint *firstPt = verts, *lastPt = NULL; 127 SkVector firstDir, dir; 128 firstDir.set(0, 0); 129 dir.set(0, 0); 130 bool isOpen = true; 131 for(SkPath::Verb v = iter.next(pts); v != SkPath::kDone_Verb; v = iter.next(pts)) { 132 switch(v) { 133 case SkPath::kMove_Verb: 134 // This will already be handled as pts[0] of the 1st line 135 break; 136 case SkPath::kClose_Verb: 137 isOpen = (lastPt == NULL); 138 break; 139 case SkPath::kLine_Verb: 140 { 141 SkVector v0 = dir; 142 dir = pts[1] - pts[0]; 143 if (dir.setLength(radius)) { 144 SkVector dirT; 145 dirT.set(dir.fY, -dir.fX); // Get perpendicular direction 146 SkPoint l1a = pts[0]+dirT, l1b = pts[1]+dirT, 147 l2a = pts[0]-dirT, l2b = pts[1]-dirT; 148 SkPoint miterPt[2]; 149 bool useMiterPoint = false; 150 int idx0(-1), idx1(-1); 151 if (NULL == lastPt) { 152 firstDir = dir; 153 } else { 154 SkVector v1 = dir; 155 if (v0.normalize() && v1.normalize()) { 156 SkScalar dotProd = v0.dot(v1); 157 // No need for bevel or miter join if the angle 158 // is either 0 or 180 degrees 159 if (!SkScalarNearlyZero(dotProd + SK_Scalar1) && 160 !SkScalarNearlyZero(dotProd - SK_Scalar1)) { 161 bool ccw = !is_clockwise(v0, v1); 162 int offset = ccw ? 1 : 0; 163 idx0 = vCount-2+offset; 164 idx1 = vCount+offset; 165 const SkPoint* pt0 = &(lastPt[offset]); 166 const SkPoint* pt1 = ccw ? &l2a : &l1a; 167 switch(join) { 168 case SkPaint::kMiter_Join: 169 { 170 // *Note : Logic is from MiterJoiner 171 172 // FIXME : Special case if we have a right angle ? 173 // if (SkScalarNearlyZero(dotProd)) {...} 174 175 SkScalar sinHalfAngleSq = 176 SkScalarHalf(SK_Scalar1 + dotProd); 177 if (sinHalfAngleSq >= invMiterLimitSq) { 178 // Find the miter point (or points if it is further 179 // than the miter limit) 180 const SkPoint pt2 = *pt0+v0, pt3 = *pt1+v1; 181 if (intersection(*pt0, pt2, *pt1, pt3, miterPt[0]) != 182 kNone_IntersectionType) { 183 SkPoint miterPt0 = miterPt[0] - *pt0; 184 SkPoint miterPt1 = miterPt[0] - *pt1; 185 SkScalar sqDist0 = miterPt0.dot(miterPt0); 186 SkScalar sqDist1 = miterPt1.dot(miterPt1); 187 const SkScalar rSq = 188 SkScalarDiv(SkScalarMul(radius, radius), 189 sinHalfAngleSq); 190 const SkScalar sqRLimit = 191 SkScalarMul(sqMiterLimit, rSq); 192 if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) { 193 if (sqDist1 > sqRLimit) { 194 v1.setLength(SkScalarSqrt(sqRLimit)); 195 miterPt[1] = *pt1+v1; 196 } else { 197 miterPt[1] = miterPt[0]; 198 } 199 if (sqDist0 > sqRLimit) { 200 v0.setLength(SkScalarSqrt(sqRLimit)); 201 miterPt[0] = *pt0+v0; 202 } 203 } else { 204 miterPt[1] = miterPt[0]; 205 } 206 useMiterPoint = true; 207 } 208 } 209 if (useMiterPoint && (miterPt[1] == miterPt[0])) { 210 break; 211 } 212 } 213 default: 214 case SkPaint::kBevel_Join: 215 { 216 // Note : This currently causes some overdraw where both 217 // lines initially intersect. We'd need to add 218 // another line intersection check here if the 219 // overdraw becomes an issue instead of using the 220 // current point directly. 221 222 // Add center point 223 *verts++ = pts[0]; // Use current point directly 224 // This idx is passed the current point so increment it 225 ++idx1; 226 // Add center triangle 227 *idxs++ = idx0; 228 *idxs++ = vCount; 229 *idxs++ = idx1; 230 vCount++; 231 iCount += 3; 232 } 233 break; 234 } 235 } 236 } 237 } 238 *verts++ = l1a; 239 *verts++ = l2a; 240 lastPt = verts; 241 *verts++ = l1b; 242 *verts++ = l2b; 243 244 if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) { 245 firstPt[idx0] = miterPt[0]; 246 firstPt[idx1] = miterPt[1]; 247 } 248 249 // 1st triangle 250 *idxs++ = vCount+0; 251 *idxs++ = vCount+2; 252 *idxs++ = vCount+1; 253 // 2nd triangle 254 *idxs++ = vCount+1; 255 *idxs++ = vCount+2; 256 *idxs++ = vCount+3; 257 258 vCount += 4; 259 iCount += 6; 260 } 261 } 262 break; 263 case SkPath::kQuad_Verb: 264 case SkPath::kCubic_Verb: 265 SkDEBUGFAIL("Curves not supported!"); 266 default: 267 // Unhandled cases 268 SkASSERT(false); 269 } 270 } 271 272 if (isOpen) { 273 // Add caps 274 switch (stroke.getCap()) { 275 case SkPaint::kSquare_Cap: 276 firstPt[0] -= firstDir; 277 firstPt[1] -= firstDir; 278 lastPt [0] += dir; 279 lastPt [1] += dir; 280 break; 281 case SkPaint::kRound_Cap: 282 SkDEBUGFAIL("Round caps not supported!"); 283 default: // No cap 284 break; 285 } 286 } 287 288 SkASSERT(vCount <= maxVertexCount); 289 SkASSERT(iCount <= maxIndexCount); 290 291 if (vCount > 0) { 292 target->drawIndexed(kTriangles_GrPrimitiveType, 293 0, // start vertex 294 0, // start index 295 vCount, 296 iCount); 297 } 298 299 return true; 300 } 301