Chromium Code Reviews| 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 |