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 |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
216 } | 216 } |
217 // check whether m reverses the orientation | 217 // check whether m reverses the orientation |
218 SkASSERT(!m.hasPerspective()); | 218 SkASSERT(!m.hasPerspective()); |
219 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS caleY)) - | 219 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS caleY)) - |
220 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk ewY)); | 220 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk ewY)); |
221 if (det2x2 < 0) { | 221 if (det2x2 < 0) { |
222 *dir = SkPath::OppositeDirection(*dir); | 222 *dir = SkPath::OppositeDirection(*dir); |
223 } | 223 } |
224 return true; | 224 return true; |
225 } | 225 } |
226 | 226 |
robertphillips
2015/04/24 12:32:55
Seems like we should do this in terms of pixels.
bsalomon
2015/04/24 14:19:55
Are we not in device space here?
Chris Dalton
2015/04/24 18:36:27
Doing it in pixels is fine, it just comes at the c
| |
227 static inline bool are_colinear(const SkPoint& startPt, | |
228 const SkPoint& testPt, | |
229 const SkPoint& endPt, | |
230 SkScalar threshold = 1e-5f) { | |
231 // Returns whether the distance from testPt to the line defined by [startPt, endPt] is within | |
232 // a fraction of the distance from startPt to endPt. | |
233 // TODO: It may (or may not) make more sense for the threshold to instead be in terms of pixels. | |
234 SkVector ortho; | |
235 ortho.setOrthog(endPt - startPt); | |
236 return fabsf(ortho.dot(startPt) - ortho.dot(testPt)) < threshold * ortho.dot (ortho); | |
237 } | |
238 | |
227 static inline void add_line_to_segment(const SkPoint& pt, | 239 static inline void add_line_to_segment(const SkPoint& pt, |
228 SegmentArray* segments) { | 240 SegmentArray* segments, |
241 const SkPoint& pathStartPt) { | |
242 if (!segments->empty() && Segment::kLine == segments->back().fType) { | |
243 const SkPoint& prevLineStartPt = segments->count() > 2 ? | |
244 (*segments)[segments->count() - 2].endPt() : pathStartPt; | |
245 if (are_colinear(prevLineStartPt, segments->back().fPts[0], pt)) { | |
246 // Combine with the previous line, since it is colinear. | |
247 segments->back().fPts[0] = pt; | |
248 return; | |
249 } | |
250 } | |
229 segments->push_back(); | 251 segments->push_back(); |
230 segments->back().fType = Segment::kLine; | 252 segments->back().fType = Segment::kLine; |
231 segments->back().fPts[0] = pt; | 253 segments->back().fPts[0] = pt; |
232 } | 254 } |
233 | 255 |
234 static inline void add_quad_segment(const SkPoint pts[3], | 256 static inline void add_quad_segment(const SkPoint pts[3], |
235 SegmentArray* segments) { | 257 SegmentArray* segments, |
236 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { | 258 const SkPoint& pathStartPt) { |
259 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd || | |
260 are_colinear(pts[0], pts[1], pts[2])) { | |
237 if (pts[0] != pts[2]) { | 261 if (pts[0] != pts[2]) { |
238 add_line_to_segment(pts[2], segments); | 262 add_line_to_segment(pts[2], segments, pathStartPt); |
239 } | 263 } |
240 } else { | 264 } else { |
241 segments->push_back(); | 265 segments->push_back(); |
242 segments->back().fType = Segment::kQuad; | 266 segments->back().fType = Segment::kQuad; |
243 segments->back().fPts[0] = pts[1]; | 267 segments->back().fPts[0] = pts[1]; |
244 segments->back().fPts[1] = pts[2]; | 268 segments->back().fPts[1] = pts[2]; |
245 } | 269 } |
246 } | 270 } |
247 | 271 |
248 static inline void add_cubic_segments(const SkPoint pts[4], | 272 static inline void add_cubic_segments(const SkPoint pts[4], |
249 SkPath::Direction dir, | 273 SkPath::Direction dir, |
250 SegmentArray* segments) { | 274 SegmentArray* segments, |
275 const SkPoint& pathStartPt) { | |
251 SkSTArray<15, SkPoint, true> quads; | 276 SkSTArray<15, SkPoint, true> quads; |
252 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); | 277 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); |
253 int count = quads.count(); | 278 int count = quads.count(); |
254 for (int q = 0; q < count; q += 3) { | 279 for (int q = 0; q < count; q += 3) { |
255 add_quad_segment(&quads[q], segments); | 280 add_quad_segment(&quads[q], segments, pathStartPt); |
256 } | 281 } |
257 } | 282 } |
258 | 283 |
259 static bool get_segments(const SkPath& path, | 284 static bool get_segments(const SkPath& path, |
260 const SkMatrix& m, | 285 const SkMatrix& m, |
261 SegmentArray* segments, | 286 SegmentArray* segments, |
262 SkPoint* fanPt, | 287 SkPoint* fanPt, |
263 int* vCount, | 288 int* vCount, |
264 int* iCount) { | 289 int* iCount) { |
265 SkPath::Iter iter(path, true); | 290 SkPath::Iter iter(path, true); |
266 // This renderer over-emphasizes very thin path regions. We use the distance | 291 // This renderer over-emphasizes very thin path regions. We use the distance |
267 // to the path from the sample to compute coverage. Every pixel intersected | 292 // to the path from the sample to compute coverage. Every pixel intersected |
268 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't | 293 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't |
269 // notice that the sample may be close to a very thin area of the path and | 294 // notice that the sample may be close to a very thin area of the path and |
270 // thus should be very light. This is particularly egregious for degenerate | 295 // thus should be very light. This is particularly egregious for degenerate |
271 // line paths. We detect paths that are very close to a line (zero area) and | 296 // line paths. We detect paths that are very close to a line (zero area) and |
272 // draw nothing. | 297 // draw nothing. |
273 DegenerateTestData degenerateData; | 298 DegenerateTestData degenerateData; |
274 SkPath::Direction dir; | 299 SkPath::Direction dir; |
275 // get_direction can fail for some degenerate paths. | 300 // get_direction can fail for some degenerate paths. |
276 if (!get_direction(path, m, &dir)) { | 301 if (!get_direction(path, m, &dir)) { |
277 return false; | 302 return false; |
278 } | 303 } |
279 | 304 |
305 SkPoint pts[4]; | |
306 SkPath::Verb verb = iter.next(pts); | |
307 SkPoint pathStartPt; | |
308 m.mapPoints(&pathStartPt, pts, 1); | |
309 | |
280 for (;;) { | 310 for (;;) { |
281 SkPoint pts[4]; | |
282 SkPath::Verb verb = iter.next(pts); | |
283 switch (verb) { | 311 switch (verb) { |
284 case SkPath::kMove_Verb: | 312 case SkPath::kMove_Verb: |
285 m.mapPoints(pts, 1); | 313 m.mapPoints(pts, 1); |
286 update_degenerate_test(°enerateData, pts[0]); | 314 update_degenerate_test(°enerateData, pts[0]); |
287 break; | 315 break; |
288 case SkPath::kLine_Verb: { | 316 case SkPath::kLine_Verb: { |
289 m.mapPoints(&pts[1], 1); | 317 m.mapPoints(&pts[1], 1); |
290 update_degenerate_test(°enerateData, pts[1]); | 318 update_degenerate_test(°enerateData, pts[1]); |
291 add_line_to_segment(pts[1], segments); | 319 add_line_to_segment(pts[1], segments, pathStartPt); |
292 break; | 320 break; |
293 } | 321 } |
294 case SkPath::kQuad_Verb: | 322 case SkPath::kQuad_Verb: |
295 m.mapPoints(pts, 3); | 323 m.mapPoints(pts, 3); |
296 update_degenerate_test(°enerateData, pts[1]); | 324 update_degenerate_test(°enerateData, pts[1]); |
297 update_degenerate_test(°enerateData, pts[2]); | 325 update_degenerate_test(°enerateData, pts[2]); |
298 add_quad_segment(pts, segments); | 326 add_quad_segment(pts, segments, pathStartPt); |
299 break; | 327 break; |
300 case SkPath::kConic_Verb: { | 328 case SkPath::kConic_Verb: { |
301 m.mapPoints(pts, 3); | 329 m.mapPoints(pts, 3); |
302 SkScalar weight = iter.conicWeight(); | 330 SkScalar weight = iter.conicWeight(); |
303 SkAutoConicToQuads converter; | 331 SkAutoConicToQuads converter; |
304 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5 f); | 332 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5 f); |
305 for (int i = 0; i < converter.countQuads(); ++i) { | 333 for (int i = 0; i < converter.countQuads(); ++i) { |
306 update_degenerate_test(°enerateData, quadPts[2*i + 1]); | 334 update_degenerate_test(°enerateData, quadPts[2*i + 1]); |
307 update_degenerate_test(°enerateData, quadPts[2*i + 2]); | 335 update_degenerate_test(°enerateData, quadPts[2*i + 2]); |
308 add_quad_segment(quadPts + 2*i, segments); | 336 add_quad_segment(quadPts + 2*i, segments, pathStartPt); |
309 } | 337 } |
310 break; | 338 break; |
311 } | 339 } |
312 case SkPath::kCubic_Verb: { | 340 case SkPath::kCubic_Verb: { |
313 m.mapPoints(pts, 4); | 341 m.mapPoints(pts, 4); |
314 update_degenerate_test(°enerateData, pts[1]); | 342 update_degenerate_test(°enerateData, pts[1]); |
315 update_degenerate_test(°enerateData, pts[2]); | 343 update_degenerate_test(°enerateData, pts[2]); |
316 update_degenerate_test(°enerateData, pts[3]); | 344 update_degenerate_test(°enerateData, pts[3]); |
317 add_cubic_segments(pts, dir, segments); | 345 add_cubic_segments(pts, dir, segments, pathStartPt); |
318 break; | 346 break; |
319 }; | 347 }; |
320 case SkPath::kDone_Verb: | 348 case SkPath::kDone_Verb: |
321 if (degenerateData.isDegenerate()) { | 349 if (degenerateData.isDegenerate()) { |
322 return false; | 350 return false; |
323 } else { | 351 } else { |
352 if (segments->count() > 2 && | |
353 Segment::kLine == segments->front().fType && | |
354 Segment::kLine == segments->back().fType && | |
355 are_colinear((*segments)[segments->count() - 2].endPt(), | |
356 pathStartPt, | |
357 segments->front().endPt())) { | |
358 // First and last lines are colinear. | |
359 segments->pop_back(); | |
360 } | |
324 compute_vectors(segments, fanPt, dir, vCount, iCount); | 361 compute_vectors(segments, fanPt, dir, vCount, iCount); |
325 return true; | 362 return true; |
326 } | 363 } |
327 default: | 364 default: |
328 break; | 365 break; |
329 } | 366 } |
367 | |
368 verb = iter.next(pts); | |
330 } | 369 } |
331 } | 370 } |
332 | 371 |
333 struct QuadVertex { | 372 struct QuadVertex { |
334 SkPoint fPos; | 373 SkPoint fPos; |
335 SkPoint fUV; | 374 SkPoint fUV; |
336 SkScalar fD0; | 375 SkScalar fD0; |
337 SkScalar fD1; | 376 SkScalar fD1; |
338 }; | 377 }; |
339 | 378 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
418 verts[*v + 2].fUV.set(0, 0); | 457 verts[*v + 2].fUV.set(0, 0); |
419 verts[*v + 3].fUV.set(0, -SK_Scalar1); | 458 verts[*v + 3].fUV.set(0, -SK_Scalar1); |
420 verts[*v + 4].fUV.set(0, -SK_Scalar1); | 459 verts[*v + 4].fUV.set(0, -SK_Scalar1); |
421 | 460 |
422 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; | 461 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; |
423 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; | 462 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; |
424 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; | 463 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; |
425 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; | 464 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; |
426 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; | 465 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; |
427 | 466 |
428 idxs[*i + 0] = *v + 0; | 467 idxs[*i + 0] = *v + 3; |
429 idxs[*i + 1] = *v + 2; | 468 idxs[*i + 1] = *v + 1; |
430 idxs[*i + 2] = *v + 1; | 469 idxs[*i + 2] = *v + 2; |
431 | 470 |
432 idxs[*i + 3] = *v + 3; | 471 idxs[*i + 3] = *v + 4; |
433 idxs[*i + 4] = *v + 1; | 472 idxs[*i + 4] = *v + 3; |
434 idxs[*i + 5] = *v + 2; | 473 idxs[*i + 5] = *v + 2; |
435 | 474 |
436 idxs[*i + 6] = *v + 4; | 475 *i += 6; |
437 idxs[*i + 7] = *v + 3; | 476 |
438 idxs[*i + 8] = *v + 2; | 477 // Draw the interior fan if it exists. Colinear line segments have a lready been combined |
478 // by this point, so we know the fan exists as long as there are at least 3 segments. | |
479 if (count >= 3) { | |
480 idxs[*i + 0] = *v + 0; | |
481 idxs[*i + 1] = *v + 2; | |
482 idxs[*i + 2] = *v + 1; | |
483 | |
484 *i += 3; | |
485 } | |
439 | 486 |
440 *v += 5; | 487 *v += 5; |
441 *i += 9; | |
442 } else { | 488 } else { |
443 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; | 489 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; |
444 | 490 |
445 SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; | 491 SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; |
446 midVec.normalize(); | 492 midVec.normalize(); |
447 | 493 |
448 verts[*v + 0].fPos = fanPt; | 494 verts[*v + 0].fPos = fanPt; |
449 verts[*v + 1].fPos = qpts[0]; | 495 verts[*v + 1].fPos = qpts[0]; |
450 verts[*v + 2].fPos = qpts[2]; | 496 verts[*v + 2].fPos = qpts[2]; |
451 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; | 497 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; |
(...skipping 23 matching lines...) Expand all Loading... | |
475 idxs[*i + 1] = *v + 1; | 521 idxs[*i + 1] = *v + 1; |
476 idxs[*i + 2] = *v + 2; | 522 idxs[*i + 2] = *v + 2; |
477 idxs[*i + 3] = *v + 4; | 523 idxs[*i + 3] = *v + 4; |
478 idxs[*i + 4] = *v + 3; | 524 idxs[*i + 4] = *v + 3; |
479 idxs[*i + 5] = *v + 2; | 525 idxs[*i + 5] = *v + 2; |
480 | 526 |
481 idxs[*i + 6] = *v + 5; | 527 idxs[*i + 6] = *v + 5; |
482 idxs[*i + 7] = *v + 3; | 528 idxs[*i + 7] = *v + 3; |
483 idxs[*i + 8] = *v + 4; | 529 idxs[*i + 8] = *v + 4; |
484 | 530 |
485 idxs[*i + 9] = *v + 0; | 531 *i += 9; |
486 idxs[*i + 10] = *v + 2; | 532 |
487 idxs[*i + 11] = *v + 1; | 533 // Draw the interior fan if it exists. Colinear line segments have a lready been combined |
534 // by this point, so we know the fan exists as long as there are at least 3 segments. | |
535 if (count >= 3) { | |
536 idxs[*i + 0] = *v + 0; | |
537 idxs[*i + 1] = *v + 2; | |
538 idxs[*i + 2] = *v + 1; | |
539 | |
540 *i += 3; | |
541 } | |
488 | 542 |
489 *v += 6; | 543 *v += 6; |
490 *i += 12; | |
491 } | 544 } |
492 } | 545 } |
493 } | 546 } |
494 | 547 |
495 /////////////////////////////////////////////////////////////////////////////// | 548 /////////////////////////////////////////////////////////////////////////////// |
496 | 549 |
497 /* | 550 /* |
498 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first | 551 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first |
499 * two components of the vertex attribute. Coverage is based on signed | 552 * two components of the vertex attribute. Coverage is based on signed |
500 * distance with negative being inside, positive outside. The edge is specified in | 553 * distance with negative being inside, positive outside. The edge is specified in |
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
885 geometry.fColor = color; | 938 geometry.fColor = color; |
886 geometry.fViewMatrix = vm; | 939 geometry.fViewMatrix = vm; |
887 geometry.fPath = path; | 940 geometry.fPath = path; |
888 | 941 |
889 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry)); | 942 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry)); |
890 target->drawBatch(pipelineBuilder, batch, &devRect); | 943 target->drawBatch(pipelineBuilder, batch, &devRect); |
891 | 944 |
892 return true; | 945 return true; |
893 | 946 |
894 } | 947 } |
OLD | NEW |