OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2012 Google Inc. | 3 * Copyright 2012 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #include "GrAAConvexPathRenderer.h" | 9 #include "GrAAConvexPathRenderer.h" |
10 | 10 |
11 #include "GrContext.h" | 11 #include "GrContext.h" |
12 #include "GrDrawState.h" | 12 #include "GrDrawState.h" |
13 #include "GrDrawTargetCaps.h" | 13 #include "GrDrawTargetCaps.h" |
14 #include "GrEffect.h" | 14 #include "GrEffect.h" |
15 #include "GrPathUtils.h" | 15 #include "GrPathUtils.h" |
16 #include "GrTBackendEffectFactory.h" | 16 #include "GrTBackendEffectFactory.h" |
17 #include "SkString.h" | 17 #include "SkString.h" |
18 #include "SkStrokeRec.h" | 18 #include "SkStrokeRec.h" |
19 #include "SkTrace.h" | 19 #include "SkTrace.h" |
20 | 20 |
21 #include "gl/GrGLEffect.h" | 21 #include "gl/GrGLEffect.h" |
22 #include "gl/GrGLSL.h" | 22 #include "gl/GrGLSL.h" |
23 | 23 |
24 GrAAConvexPathRenderer::GrAAConvexPathRenderer() { | 24 GrAAConvexPathRenderer::GrAAConvexPathRenderer() { |
25 } | 25 } |
26 | 26 |
27 namespace { | |
28 | |
29 struct Segment { | 27 struct Segment { |
30 enum { | 28 enum { |
31 // These enum values are assumed in member functions below. | 29 // These enum values are assumed in member functions below. |
32 kLine = 0, | 30 kLine = 0, |
33 kQuad = 1, | 31 kQuad = 1, |
34 } fType; | 32 } fType; |
35 | 33 |
36 // line uses one pt, quad uses 2 pts | 34 // line uses one pt, quad uses 2 pts |
37 GrPoint fPts[2]; | 35 GrPoint fPts[2]; |
38 // normal to edge ending at each pt | 36 // normal to edge ending at each pt |
(...skipping 11 matching lines...) Expand all Loading... | |
50 return fPts[fType]; | 48 return fPts[fType]; |
51 }; | 49 }; |
52 const SkPoint& endNorm() const { | 50 const SkPoint& endNorm() const { |
53 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad); | 51 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad); |
54 return fNorms[fType]; | 52 return fNorms[fType]; |
55 }; | 53 }; |
56 }; | 54 }; |
57 | 55 |
58 typedef SkTArray<Segment, true> SegmentArray; | 56 typedef SkTArray<Segment, true> SegmentArray; |
59 | 57 |
60 void center_of_mass(const SegmentArray& segments, SkPoint* c) { | 58 static void center_of_mass(const SegmentArray& segments, SkPoint* c) { |
61 SkScalar area = 0; | 59 SkScalar area = 0; |
62 SkPoint center = {0, 0}; | 60 SkPoint center = {0, 0}; |
63 int count = segments.count(); | 61 int count = segments.count(); |
64 SkPoint p0 = {0, 0}; | 62 SkPoint p0 = {0, 0}; |
65 if (count > 2) { | 63 if (count > 2) { |
66 // We translate the polygon so that the first point is at the origin. | 64 // We translate the polygon so that the first point is at the origin. |
67 // This avoids some precision issues with small area polygons far away | 65 // This avoids some precision issues with small area polygons far away |
68 // from the origin. | 66 // from the origin. |
69 p0 = segments[0].endPt(); | 67 p0 = segments[0].endPt(); |
70 SkPoint pi; | 68 SkPoint pi; |
(...skipping 30 matching lines...) Expand all Loading... | |
101 area *= 3; | 99 area *= 3; |
102 area = SkScalarDiv(SK_Scalar1, area); | 100 area = SkScalarDiv(SK_Scalar1, area); |
103 center.fX = SkScalarMul(center.fX, area); | 101 center.fX = SkScalarMul(center.fX, area); |
104 center.fY = SkScalarMul(center.fY, area); | 102 center.fY = SkScalarMul(center.fY, area); |
105 // undo the translate of p0 to the origin. | 103 // undo the translate of p0 to the origin. |
106 *c = center + p0; | 104 *c = center + p0; |
107 } | 105 } |
108 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY)); | 106 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY)); |
109 } | 107 } |
110 | 108 |
111 void compute_vectors(SegmentArray* segments, | 109 static void compute_vectors(SegmentArray* segments, |
112 SkPoint* fanPt, | 110 SkPoint* fanPt, |
113 SkPath::Direction dir, | 111 SkPath::Direction dir, |
114 int* vCount, | 112 int* vCount, |
115 int* iCount) { | 113 int* iCount) { |
116 center_of_mass(*segments, fanPt); | 114 center_of_mass(*segments, fanPt); |
117 int count = segments->count(); | 115 int count = segments->count(); |
118 | 116 |
119 // Make the normals point towards the outside | 117 // Make the normals point towards the outside |
120 GrPoint::Side normSide; | 118 GrPoint::Side normSide; |
121 if (dir == SkPath::kCCW_Direction) { | 119 if (dir == SkPath::kCCW_Direction) { |
122 normSide = GrPoint::kRight_Side; | 120 normSide = GrPoint::kRight_Side; |
123 } else { | 121 } else { |
124 normSide = GrPoint::kLeft_Side; | 122 normSide = GrPoint::kLeft_Side; |
125 } | 123 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
170 kInitial, | 168 kInitial, |
171 kPoint, | 169 kPoint, |
172 kLine, | 170 kLine, |
173 kNonDegenerate | 171 kNonDegenerate |
174 } fStage; | 172 } fStage; |
175 GrPoint fFirstPoint; | 173 GrPoint fFirstPoint; |
176 GrVec fLineNormal; | 174 GrVec fLineNormal; |
177 SkScalar fLineC; | 175 SkScalar fLineC; |
178 }; | 176 }; |
179 | 177 |
180 void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) { | 178 static const SkScalar kClose = (SK_Scalar1 / 16); |
181 static const SkScalar TOL = (SK_Scalar1 / 16); | 179 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose); |
182 static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL); | |
183 | 180 |
181 static void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) { | |
184 switch (data->fStage) { | 182 switch (data->fStage) { |
185 case DegenerateTestData::kInitial: | 183 case DegenerateTestData::kInitial: |
186 data->fFirstPoint = pt; | 184 data->fFirstPoint = pt; |
187 data->fStage = DegenerateTestData::kPoint; | 185 data->fStage = DegenerateTestData::kPoint; |
188 break; | 186 break; |
189 case DegenerateTestData::kPoint: | 187 case DegenerateTestData::kPoint: |
190 if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) { | 188 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) { |
191 data->fLineNormal = pt - data->fFirstPoint; | 189 data->fLineNormal = pt - data->fFirstPoint; |
192 data->fLineNormal.normalize(); | 190 data->fLineNormal.normalize(); |
193 data->fLineNormal.setOrthog(data->fLineNormal); | 191 data->fLineNormal.setOrthog(data->fLineNormal); |
194 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint); | 192 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint); |
195 data->fStage = DegenerateTestData::kLine; | 193 data->fStage = DegenerateTestData::kLine; |
196 } | 194 } |
197 break; | 195 break; |
198 case DegenerateTestData::kLine: | 196 case DegenerateTestData::kLine: |
199 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) { | 197 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) { |
200 data->fStage = DegenerateTestData::kNonDegenerate; | 198 data->fStage = DegenerateTestData::kNonDegenerate; |
201 } | 199 } |
202 case DegenerateTestData::kNonDegenerate: | 200 case DegenerateTestData::kNonDegenerate: |
203 break; | 201 break; |
204 default: | 202 default: |
205 GrCrash("Unexpected degenerate test stage."); | 203 GrCrash("Unexpected degenerate test stage."); |
206 } | 204 } |
207 } | 205 } |
208 | 206 |
209 inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Directi on* dir) { | 207 static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath:: Direction* dir) { |
210 if (!path.cheapComputeDirection(dir)) { | 208 if (!path.cheapComputeDirection(dir)) { |
211 return false; | 209 return false; |
212 } | 210 } |
213 // check whether m reverses the orientation | 211 // check whether m reverses the orientation |
214 GrAssert(!m.hasPerspective()); | 212 GrAssert(!m.hasPerspective()); |
215 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS caleY)) - | 213 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS caleY)) - |
216 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk ewY)); | 214 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk ewY)); |
217 if (det2x2 < 0) { | 215 if (det2x2 < 0) { |
218 *dir = SkPath::OppositeDirection(*dir); | 216 *dir = SkPath::OppositeDirection(*dir); |
219 } | 217 } |
220 return true; | 218 return true; |
221 } | 219 } |
222 | 220 |
223 bool get_segments(const SkPath& path, | 221 static inline void add_line_segment(const SkPoint pts[2], |
224 const SkMatrix& m, | 222 SegmentArray* segments, |
robertphillips
2013/08/13 16:29:55
rm degenerateTestData param?
bsalomon
2013/08/13 16:55:56
right, thanks. I was originally doing the deg. che
| |
225 SegmentArray* segments, | 223 DegenerateTestData* degenerateTestData) { |
226 SkPoint* fanPt, | 224 segments->push_back(); |
227 int* vCount, | 225 segments->back().fType = Segment::kLine; |
228 int* iCount) { | 226 segments->back().fPts[0] = pts[1]; |
227 } | |
228 | |
229 static inline void add_quad_segment(const SkPoint pts[3], | |
230 SegmentArray* segments, | |
robertphillips
2013/08/13 16:29:55
rm degenerateTestData param?
bsalomon
2013/08/13 16:55:56
Done.
| |
231 DegenerateTestData* degenerateTestData) { | |
robertphillips
2013/08/13 16:29:55
Do we care if pts[0] == pts[2]?
bsalomon
2013/08/13 16:55:56
Done. (made it skip if so and added test case to t
| |
232 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { | |
233 SkPoint linePts[2] = {pts[0], pts[2]}; | |
robertphillips
2013/08/13 16:29:55
Couldn't we just pass &pts[1]?
bsalomon
2013/08/13 16:55:56
Yes. I originally had it so that we didn't transfo
| |
234 add_line_segment(linePts, segments, degenerateTestData); | |
235 } else { | |
236 segments->push_back(); | |
237 segments->back().fType = Segment::kQuad; | |
238 segments->back().fPts[0] = pts[1]; | |
239 segments->back().fPts[1] = pts[2]; | |
240 } | |
241 } | |
242 | |
243 static inline void add_cubic_segments(const SkPoint pts[4], | |
244 SkPath::Direction dir, | |
245 SegmentArray* segments, | |
robertphillips
2013/08/13 16:29:55
rm degenerateTestData?
bsalomon
2013/08/13 16:55:56
Done.
| |
246 DegenerateTestData* degenerateTestData) { | |
247 SkSTArray<15, SkPoint, true> quads; | |
248 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); | |
249 int count = quads.count(); | |
250 for (int q = 0; q < count; q += 3) { | |
251 add_quad_segment(&quads[q], segments, degenerateTestData); | |
252 } | |
253 } | |
254 | |
255 static bool get_segments(const SkPath& path, | |
256 const SkMatrix& m, | |
257 SegmentArray* segments, | |
258 SkPoint* fanPt, | |
259 int* vCount, | |
260 int* iCount) { | |
229 SkPath::Iter iter(path, true); | 261 SkPath::Iter iter(path, true); |
230 // This renderer over-emphasizes very thin path regions. We use the distance | 262 // This renderer over-emphasizes very thin path regions. We use the distance |
231 // to the path from the sample to compute coverage. Every pixel intersected | 263 // to the path from the sample to compute coverage. Every pixel intersected |
232 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't | 264 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't |
233 // notice that the sample may be close to a very thin area of the path and | 265 // notice that the sample may be close to a very thin area of the path and |
234 // thus should be very light. This is particularly egregious for degenerate | 266 // thus should be very light. This is particularly egregious for degenerate |
235 // line paths. We detect paths that are very close to a line (zero area) and | 267 // line paths. We detect paths that are very close to a line (zero area) and |
236 // draw nothing. | 268 // draw nothing. |
237 DegenerateTestData degenerateData; | 269 DegenerateTestData degenerateData; |
238 SkPath::Direction dir; | 270 SkPath::Direction dir; |
239 // get_direction can fail for some degenerate paths. | 271 // get_direction can fail for some degenerate paths. |
240 if (!get_direction(path, m, &dir)) { | 272 if (!get_direction(path, m, &dir)) { |
241 return false; | 273 return false; |
242 } | 274 } |
243 | 275 |
244 for (;;) { | 276 for (;;) { |
245 GrPoint pts[4]; | 277 GrPoint pts[4]; |
246 SkPath::Verb verb = iter.next(pts); | 278 SkPath::Verb verb = iter.next(pts); |
247 switch (verb) { | 279 switch (verb) { |
248 case SkPath::kMove_Verb: | 280 case SkPath::kMove_Verb: |
249 m.mapPoints(pts, 1); | 281 m.mapPoints(pts, 1); |
250 update_degenerate_test(°enerateData, pts[0]); | 282 update_degenerate_test(°enerateData, pts[0]); |
251 break; | 283 break; |
252 case SkPath::kLine_Verb: { | 284 case SkPath::kLine_Verb: { |
robertphillips
2013/08/13 16:29:55
Do we really need to map pts[0] here?
bsalomon
2013/08/13 16:55:56
Done.
| |
253 m.mapPoints(pts + 1, 1); | 285 m.mapPoints(pts, 2); |
254 update_degenerate_test(°enerateData, pts[1]); | 286 update_degenerate_test(°enerateData, pts[1]); |
255 segments->push_back(); | 287 add_line_segment(pts, segments, °enerateData); |
256 segments->back().fType = Segment::kLine; | |
257 segments->back().fPts[0] = pts[1]; | |
258 break; | 288 break; |
259 } | 289 } |
260 case SkPath::kQuad_Verb: | 290 case SkPath::kQuad_Verb: |
261 m.mapPoints(pts + 1, 2); | 291 m.mapPoints(pts, 3); |
262 update_degenerate_test(°enerateData, pts[1]); | 292 update_degenerate_test(°enerateData, pts[1]); |
263 update_degenerate_test(°enerateData, pts[2]); | 293 update_degenerate_test(°enerateData, pts[2]); |
264 segments->push_back(); | 294 add_quad_segment(pts, segments, °enerateData); |
265 segments->back().fType = Segment::kQuad; | |
266 segments->back().fPts[0] = pts[1]; | |
267 segments->back().fPts[1] = pts[2]; | |
268 break; | 295 break; |
269 case SkPath::kCubic_Verb: { | 296 case SkPath::kCubic_Verb: { |
270 m.mapPoints(pts, 4); | 297 m.mapPoints(pts, 4); |
271 update_degenerate_test(°enerateData, pts[1]); | 298 update_degenerate_test(°enerateData, pts[1]); |
272 update_degenerate_test(°enerateData, pts[2]); | 299 update_degenerate_test(°enerateData, pts[2]); |
273 update_degenerate_test(°enerateData, pts[3]); | 300 update_degenerate_test(°enerateData, pts[3]); |
274 // unlike quads and lines, the pts[0] will also be read (in | 301 add_cubic_segments(pts, dir, segments, °enerateData); |
275 // convertCubicToQuads). | |
276 SkSTArray<15, SkPoint, true> quads; | |
277 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &qu ads); | |
278 int count = quads.count(); | |
279 for (int q = 0; q < count; q += 3) { | |
280 segments->push_back(); | |
281 segments->back().fType = Segment::kQuad; | |
282 segments->back().fPts[0] = quads[q + 1]; | |
283 segments->back().fPts[1] = quads[q + 2]; | |
284 } | |
285 break; | 302 break; |
286 }; | 303 }; |
287 case SkPath::kDone_Verb: | 304 case SkPath::kDone_Verb: |
288 if (degenerateData.isDegenerate()) { | 305 if (degenerateData.isDegenerate()) { |
289 return false; | 306 return false; |
290 } else { | 307 } else { |
291 compute_vectors(segments, fanPt, dir, vCount, iCount); | 308 compute_vectors(segments, fanPt, dir, vCount, iCount); |
292 return true; | 309 return true; |
293 } | 310 } |
294 default: | 311 default: |
(...skipping 10 matching lines...) Expand all Loading... | |
305 }; | 322 }; |
306 | 323 |
307 struct Draw { | 324 struct Draw { |
308 Draw() : fVertexCnt(0), fIndexCnt(0) {} | 325 Draw() : fVertexCnt(0), fIndexCnt(0) {} |
309 int fVertexCnt; | 326 int fVertexCnt; |
310 int fIndexCnt; | 327 int fIndexCnt; |
311 }; | 328 }; |
312 | 329 |
313 typedef SkTArray<Draw, true> DrawArray; | 330 typedef SkTArray<Draw, true> DrawArray; |
314 | 331 |
315 void create_vertices(const SegmentArray& segments, | 332 static void create_vertices(const SegmentArray& segments, |
316 const SkPoint& fanPt, | 333 const SkPoint& fanPt, |
317 DrawArray* draws, | 334 DrawArray* draws, |
318 QuadVertex* verts, | 335 QuadVertex* verts, |
319 uint16_t* idxs) { | 336 uint16_t* idxs) { |
320 Draw* draw = &draws->push_back(); | 337 Draw* draw = &draws->push_back(); |
321 // alias just to make vert/index assignments easier to read. | 338 // alias just to make vert/index assignments easier to read. |
322 int* v = &draw->fVertexCnt; | 339 int* v = &draw->fVertexCnt; |
323 int* i = &draw->fIndexCnt; | 340 int* i = &draw->fIndexCnt; |
324 | 341 |
325 int count = segments.count(); | 342 int count = segments.count(); |
326 for (int a = 0; a < count; ++a) { | 343 for (int a = 0; a < count; ++a) { |
327 const Segment& sega = segments[a]; | 344 const Segment& sega = segments[a]; |
328 int b = (a + 1) % count; | 345 int b = (a + 1) % count; |
329 const Segment& segb = segments[b]; | 346 const Segment& segb = segments[b]; |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
452 idxs[*i + 9] = *v + 0; | 469 idxs[*i + 9] = *v + 0; |
453 idxs[*i + 10] = *v + 2; | 470 idxs[*i + 10] = *v + 2; |
454 idxs[*i + 11] = *v + 1; | 471 idxs[*i + 11] = *v + 1; |
455 | 472 |
456 *v += 6; | 473 *v += 6; |
457 *i += 12; | 474 *i += 12; |
458 } | 475 } |
459 } | 476 } |
460 } | 477 } |
461 | 478 |
462 } | |
463 | |
464 /////////////////////////////////////////////////////////////////////////////// | 479 /////////////////////////////////////////////////////////////////////////////// |
465 | 480 |
466 /* | 481 /* |
467 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first | 482 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first |
468 * two components of the vertex attribute. Coverage is based on signed | 483 * two components of the vertex attribute. Coverage is based on signed |
469 * distance with negative being inside, positive outside. The edge is specified in | 484 * distance with negative being inside, positive outside. The edge is specified in |
470 * window space (y-down). If either the third or fourth component of the interpo lated | 485 * window space (y-down). If either the third or fourth component of the interpo lated |
471 * vertex coord is > 0 then the pixel is considered outside the edge. This is us ed to | 486 * vertex coord is > 0 then the pixel is considered outside the edge. This is us ed to |
472 * attempt to trim to a portion of the infinite quad. | 487 * attempt to trim to a portion of the infinite quad. |
473 * Requires shader derivative instruction support. | 488 * Requires shader derivative instruction support. |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
677 vOffset, // start vertex | 692 vOffset, // start vertex |
678 0, // start index | 693 0, // start index |
679 draw.fVertexCnt, | 694 draw.fVertexCnt, |
680 draw.fIndexCnt, | 695 draw.fIndexCnt, |
681 &devBounds); | 696 &devBounds); |
682 vOffset += draw.fVertexCnt; | 697 vOffset += draw.fVertexCnt; |
683 } | 698 } |
684 | 699 |
685 return true; | 700 return true; |
686 } | 701 } |
OLD | NEW |