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_to_segment(const SkPoint& pt, SegmentArray* segments
) { |
224 const SkMatrix& m, | 222 segments->push_back(); |
225 SegmentArray* segments, | 223 segments->back().fType = Segment::kLine; |
226 SkPoint* fanPt, | 224 segments->back().fPts[0] = pt; |
227 int* vCount, | 225 } |
228 int* iCount) { | 226 |
| 227 static inline void add_quad_segment(const SkPoint pts[3], SegmentArray* segments
) { |
| 228 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2])
< kCloseSqd) { |
| 229 if (pts[0] != pts[2]) { |
| 230 add_line_to_segment(pts[2], segments); |
| 231 } |
| 232 } else { |
| 233 segments->push_back(); |
| 234 segments->back().fType = Segment::kQuad; |
| 235 segments->back().fPts[0] = pts[1]; |
| 236 segments->back().fPts[1] = pts[2]; |
| 237 } |
| 238 } |
| 239 |
| 240 static inline void add_cubic_segments(const SkPoint pts[4], |
| 241 SkPath::Direction dir, |
| 242 SegmentArray* segments) { |
| 243 SkSTArray<15, SkPoint, true> quads; |
| 244 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); |
| 245 int count = quads.count(); |
| 246 for (int q = 0; q < count; q += 3) { |
| 247 add_quad_segment(&quads[q], segments); |
| 248 } |
| 249 } |
| 250 |
| 251 static bool get_segments(const SkPath& path, |
| 252 const SkMatrix& m, |
| 253 SegmentArray* segments, |
| 254 SkPoint* fanPt, |
| 255 int* vCount, |
| 256 int* iCount) { |
229 SkPath::Iter iter(path, true); | 257 SkPath::Iter iter(path, true); |
230 // This renderer over-emphasizes very thin path regions. We use the distance | 258 // 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 | 259 // 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 | 260 // 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 | 261 // 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 | 262 // 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 | 263 // line paths. We detect paths that are very close to a line (zero area) and |
236 // draw nothing. | 264 // draw nothing. |
237 DegenerateTestData degenerateData; | 265 DegenerateTestData degenerateData; |
238 SkPath::Direction dir; | 266 SkPath::Direction dir; |
239 // get_direction can fail for some degenerate paths. | 267 // get_direction can fail for some degenerate paths. |
240 if (!get_direction(path, m, &dir)) { | 268 if (!get_direction(path, m, &dir)) { |
241 return false; | 269 return false; |
242 } | 270 } |
243 | 271 |
244 for (;;) { | 272 for (;;) { |
245 GrPoint pts[4]; | 273 GrPoint pts[4]; |
246 SkPath::Verb verb = iter.next(pts); | 274 SkPath::Verb verb = iter.next(pts); |
247 switch (verb) { | 275 switch (verb) { |
248 case SkPath::kMove_Verb: | 276 case SkPath::kMove_Verb: |
249 m.mapPoints(pts, 1); | 277 m.mapPoints(pts, 1); |
250 update_degenerate_test(°enerateData, pts[0]); | 278 update_degenerate_test(°enerateData, pts[0]); |
251 break; | 279 break; |
252 case SkPath::kLine_Verb: { | 280 case SkPath::kLine_Verb: { |
253 m.mapPoints(pts + 1, 1); | 281 m.mapPoints(&pts[1], 1); |
254 update_degenerate_test(°enerateData, pts[1]); | 282 update_degenerate_test(°enerateData, pts[1]); |
255 segments->push_back(); | 283 add_line_to_segment(pts[1], segments); |
256 segments->back().fType = Segment::kLine; | |
257 segments->back().fPts[0] = pts[1]; | |
258 break; | 284 break; |
259 } | 285 } |
260 case SkPath::kQuad_Verb: | 286 case SkPath::kQuad_Verb: |
261 m.mapPoints(pts + 1, 2); | 287 m.mapPoints(pts, 3); |
262 update_degenerate_test(°enerateData, pts[1]); | 288 update_degenerate_test(°enerateData, pts[1]); |
263 update_degenerate_test(°enerateData, pts[2]); | 289 update_degenerate_test(°enerateData, pts[2]); |
264 segments->push_back(); | 290 add_quad_segment(pts, segments); |
265 segments->back().fType = Segment::kQuad; | |
266 segments->back().fPts[0] = pts[1]; | |
267 segments->back().fPts[1] = pts[2]; | |
268 break; | 291 break; |
269 case SkPath::kCubic_Verb: { | 292 case SkPath::kCubic_Verb: { |
270 m.mapPoints(pts, 4); | 293 m.mapPoints(pts, 4); |
271 update_degenerate_test(°enerateData, pts[1]); | 294 update_degenerate_test(°enerateData, pts[1]); |
272 update_degenerate_test(°enerateData, pts[2]); | 295 update_degenerate_test(°enerateData, pts[2]); |
273 update_degenerate_test(°enerateData, pts[3]); | 296 update_degenerate_test(°enerateData, pts[3]); |
274 // unlike quads and lines, the pts[0] will also be read (in | 297 add_cubic_segments(pts, dir, segments); |
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; | 298 break; |
286 }; | 299 }; |
287 case SkPath::kDone_Verb: | 300 case SkPath::kDone_Verb: |
288 if (degenerateData.isDegenerate()) { | 301 if (degenerateData.isDegenerate()) { |
289 return false; | 302 return false; |
290 } else { | 303 } else { |
291 compute_vectors(segments, fanPt, dir, vCount, iCount); | 304 compute_vectors(segments, fanPt, dir, vCount, iCount); |
292 return true; | 305 return true; |
293 } | 306 } |
294 default: | 307 default: |
(...skipping 10 matching lines...) Expand all Loading... |
305 }; | 318 }; |
306 | 319 |
307 struct Draw { | 320 struct Draw { |
308 Draw() : fVertexCnt(0), fIndexCnt(0) {} | 321 Draw() : fVertexCnt(0), fIndexCnt(0) {} |
309 int fVertexCnt; | 322 int fVertexCnt; |
310 int fIndexCnt; | 323 int fIndexCnt; |
311 }; | 324 }; |
312 | 325 |
313 typedef SkTArray<Draw, true> DrawArray; | 326 typedef SkTArray<Draw, true> DrawArray; |
314 | 327 |
315 void create_vertices(const SegmentArray& segments, | 328 static void create_vertices(const SegmentArray& segments, |
316 const SkPoint& fanPt, | 329 const SkPoint& fanPt, |
317 DrawArray* draws, | 330 DrawArray* draws, |
318 QuadVertex* verts, | 331 QuadVertex* verts, |
319 uint16_t* idxs) { | 332 uint16_t* idxs) { |
320 Draw* draw = &draws->push_back(); | 333 Draw* draw = &draws->push_back(); |
321 // alias just to make vert/index assignments easier to read. | 334 // alias just to make vert/index assignments easier to read. |
322 int* v = &draw->fVertexCnt; | 335 int* v = &draw->fVertexCnt; |
323 int* i = &draw->fIndexCnt; | 336 int* i = &draw->fIndexCnt; |
324 | 337 |
325 int count = segments.count(); | 338 int count = segments.count(); |
326 for (int a = 0; a < count; ++a) { | 339 for (int a = 0; a < count; ++a) { |
327 const Segment& sega = segments[a]; | 340 const Segment& sega = segments[a]; |
328 int b = (a + 1) % count; | 341 int b = (a + 1) % count; |
329 const Segment& segb = segments[b]; | 342 const Segment& segb = segments[b]; |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
452 idxs[*i + 9] = *v + 0; | 465 idxs[*i + 9] = *v + 0; |
453 idxs[*i + 10] = *v + 2; | 466 idxs[*i + 10] = *v + 2; |
454 idxs[*i + 11] = *v + 1; | 467 idxs[*i + 11] = *v + 1; |
455 | 468 |
456 *v += 6; | 469 *v += 6; |
457 *i += 12; | 470 *i += 12; |
458 } | 471 } |
459 } | 472 } |
460 } | 473 } |
461 | 474 |
462 } | |
463 | |
464 /////////////////////////////////////////////////////////////////////////////// | 475 /////////////////////////////////////////////////////////////////////////////// |
465 | 476 |
466 /* | 477 /* |
467 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first | 478 * 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 | 479 * two components of the vertex attribute. Coverage is based on signed |
469 * distance with negative being inside, positive outside. The edge is specified
in | 480 * 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 | 481 * 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 | 482 * 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. | 483 * attempt to trim to a portion of the infinite quad. |
473 * Requires shader derivative instruction support. | 484 * Requires shader derivative instruction support. |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 vOffset, // start vertex | 688 vOffset, // start vertex |
678 0, // start index | 689 0, // start index |
679 draw.fVertexCnt, | 690 draw.fVertexCnt, |
680 draw.fIndexCnt, | 691 draw.fIndexCnt, |
681 &devBounds); | 692 &devBounds); |
682 vOffset += draw.fVertexCnt; | 693 vOffset += draw.fVertexCnt; |
683 } | 694 } |
684 | 695 |
685 return true; | 696 return true; |
686 } | 697 } |
OLD | NEW |