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 |