| OLD | NEW |
| (Empty) |
| 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((SkScalarMul(pre, x3 - x4) - SkScalarMul(x1 - x2, post) / d, | |
| 43 (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 implement
ed | |
| 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 cap
acity | |
| 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 < m
axVBSize) && | |
| 78 !path.isInverseFillType() && isOpaque && !requiresAACircle && !antiA
lias && | |
| 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 ver
t 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 rig
ht angle ? | |
| 173 // if (SkScalarNearlyZero(dotProd)) {...
} | |
| 174 | |
| 175 SkScalar sinHalfAngleSq = | |
| 176 SkScalarHalf(SK_Scalar1 + dotPro
d); | |
| 177 if (sinHalfAngleSq >= invMiterLimitSq) { | |
| 178 // Find the miter point (or points i
f it is further | |
| 179 // than the miter limit) | |
| 180 const SkPoint pt2 = *pt0+v0, pt3 = *
pt1+v1; | |
| 181 if (intersection(*pt0, pt2, *pt1, pt
3, 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 = radius*radi
us / sinHalfAngleSq; | |
| 188 const SkScalar sqRLimit = | |
| 189 SkScalarMul(sqMiterLimit
, rSq); | |
| 190 if (sqDist0 > sqRLimit || sqDist
1 > sqRLimit) { | |
| 191 if (sqDist1 > sqRLimit) { | |
| 192 v1.setLength(SkScalarSqr
t(sqRLimit)); | |
| 193 miterPt[1] = *pt1+v1; | |
| 194 } else { | |
| 195 miterPt[1] = miterPt[0]; | |
| 196 } | |
| 197 if (sqDist0 > sqRLimit) { | |
| 198 v0.setLength(SkScalarSqr
t(sqRLimit)); | |
| 199 miterPt[0] = *pt0+v0; | |
| 200 } | |
| 201 } else { | |
| 202 miterPt[1] = miterPt[0]; | |
| 203 } | |
| 204 useMiterPoint = true; | |
| 205 } | |
| 206 } | |
| 207 if (useMiterPoint && (miterPt[1] == mite
rPt[0])) { | |
| 208 break; | |
| 209 } | |
| 210 } | |
| 211 default: | |
| 212 case SkPaint::kBevel_Join: | |
| 213 { | |
| 214 // Note : This currently causes some ove
rdraw where both | |
| 215 // lines initially intersect. We'
d need to add | |
| 216 // another line intersection chec
k here if the | |
| 217 // overdraw becomes an issue inst
ead of using the | |
| 218 // current point directly. | |
| 219 | |
| 220 // Add center point | |
| 221 *verts++ = pts[0]; // Use current point
directly | |
| 222 // This idx is passed the current point
so increment it | |
| 223 ++idx1; | |
| 224 // Add center triangle | |
| 225 *idxs++ = idx0; | |
| 226 *idxs++ = vCount; | |
| 227 *idxs++ = idx1; | |
| 228 vCount++; | |
| 229 iCount += 3; | |
| 230 } | |
| 231 break; | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 } | |
| 236 *verts++ = l1a; | |
| 237 *verts++ = l2a; | |
| 238 lastPt = verts; | |
| 239 *verts++ = l1b; | |
| 240 *verts++ = l2b; | |
| 241 | |
| 242 if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) { | |
| 243 firstPt[idx0] = miterPt[0]; | |
| 244 firstPt[idx1] = miterPt[1]; | |
| 245 } | |
| 246 | |
| 247 // 1st triangle | |
| 248 *idxs++ = vCount+0; | |
| 249 *idxs++ = vCount+2; | |
| 250 *idxs++ = vCount+1; | |
| 251 // 2nd triangle | |
| 252 *idxs++ = vCount+1; | |
| 253 *idxs++ = vCount+2; | |
| 254 *idxs++ = vCount+3; | |
| 255 | |
| 256 vCount += 4; | |
| 257 iCount += 6; | |
| 258 } | |
| 259 } | |
| 260 break; | |
| 261 case SkPath::kQuad_Verb: | |
| 262 case SkPath::kCubic_Verb: | |
| 263 SkDEBUGFAIL("Curves not supported!"); | |
| 264 default: | |
| 265 // Unhandled cases | |
| 266 SkASSERT(false); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 if (isOpen) { | |
| 271 // Add caps | |
| 272 switch (stroke.getCap()) { | |
| 273 case SkPaint::kSquare_Cap: | |
| 274 firstPt[0] -= firstDir; | |
| 275 firstPt[1] -= firstDir; | |
| 276 lastPt [0] += dir; | |
| 277 lastPt [1] += dir; | |
| 278 break; | |
| 279 case SkPaint::kRound_Cap: | |
| 280 SkDEBUGFAIL("Round caps not supported!"); | |
| 281 default: // No cap | |
| 282 break; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 SkASSERT(vCount <= maxVertexCount); | |
| 287 SkASSERT(iCount <= maxIndexCount); | |
| 288 | |
| 289 if (vCount > 0) { | |
| 290 target->drawIndexed(kTriangles_GrPrimitiveType, | |
| 291 0, // start vertex | |
| 292 0, // start index | |
| 293 vCount, | |
| 294 iCount); | |
| 295 } | |
| 296 | |
| 297 return true; | |
| 298 } | |
| OLD | NEW |