| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkScan.h" | 8 #include "SkScan.h" |
| 9 #include "SkBlitter.h" | 9 #include "SkBlitter.h" |
| 10 #include "SkRasterClip.h" | 10 #include "SkRasterClip.h" |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 int idx = SkScalarCeilToInt(dx); | 207 int idx = SkScalarCeilToInt(dx); |
| 208 int idy = SkScalarCeilToInt(dy); | 208 int idy = SkScalarCeilToInt(dy); |
| 209 // use the cheap approx for distance | 209 // use the cheap approx for distance |
| 210 if (idx > idy) { | 210 if (idx > idy) { |
| 211 return idx + (idy >> 1); | 211 return idx + (idy >> 1); |
| 212 } else { | 212 } else { |
| 213 return idy + (idx >> 1); | 213 return idy + (idx >> 1); |
| 214 } | 214 } |
| 215 } | 215 } |
| 216 | 216 |
| 217 static void hairquad(const SkPoint pts[3], const SkRegion* clip, | 217 static void hair_quad(const SkPoint pts[3], const SkRegion* clip, |
| 218 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc
) { | 218 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc
) { |
| 219 SkASSERT(level <= kMaxQuadSubdivideLevel); | 219 SkASSERT(level <= kMaxQuadSubdivideLevel); |
| 220 | 220 |
| 221 SkQuadCoeff coeff(pts); | 221 SkQuadCoeff coeff(pts); |
| 222 | 222 |
| 223 const int lines = 1 << level; | 223 const int lines = 1 << level; |
| 224 Sk2s t(0); | 224 Sk2s t(0); |
| 225 Sk2s dt(SK_Scalar1 / lines); | 225 Sk2s dt(SK_Scalar1 / lines); |
| 226 | 226 |
| 227 SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1]; | 227 SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1]; |
| 228 SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp)); | 228 SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp)); |
| 229 | 229 |
| 230 tmp[0] = pts[0]; | 230 tmp[0] = pts[0]; |
| 231 Sk2s A = coeff.fA; | 231 Sk2s A = coeff.fA; |
| 232 Sk2s B = coeff.fB; | 232 Sk2s B = coeff.fB; |
| 233 Sk2s C = coeff.fC; | 233 Sk2s C = coeff.fC; |
| 234 for (int i = 1; i < lines; ++i) { | 234 for (int i = 1; i < lines; ++i) { |
| 235 t = t + dt; | 235 t = t + dt; |
| 236 ((A * t + B) * t + C).store(&tmp[i]); | 236 ((A * t + B) * t + C).store(&tmp[i]); |
| 237 } | 237 } |
| 238 tmp[lines] = pts[2]; | 238 tmp[lines] = pts[2]; |
| 239 lineproc(tmp, lines + 1, clip, blitter); | 239 lineproc(tmp, lines + 1, clip, blitter); |
| 240 } | 240 } |
| 241 | 241 |
| 242 static SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) { |
| 243 SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6)); |
| 244 |
| 245 Sk2s min = Sk2s::Load(pts); |
| 246 Sk2s max = min; |
| 247 for (int i = 1; i < 3; ++i) { |
| 248 Sk2s pair = Sk2s::Load(pts+i); |
| 249 min = Sk2s::Min(min, pair); |
| 250 max = Sk2s::Max(max, pair); |
| 251 } |
| 252 return { min[0], min[1], max[0], max[1] }; |
| 253 } |
| 254 |
| 255 static bool is_inverted(const SkRect& r) { |
| 256 return r.fLeft > r.fRight || r.fTop > r.fBottom; |
| 257 } |
| 258 |
| 259 // Can't call SkRect::intersects, since it cares about empty, and we don't (sinc
e we tracking |
| 260 // something to be stroked, so empty can still draw something (e.g. horizontal l
ine) |
| 261 static bool geometric_overlap(const SkRect& a, const SkRect& b) { |
| 262 SkASSERT(!is_inverted(a) && !is_inverted(b)); |
| 263 return a.fLeft < b.fRight && b.fLeft < a.fRight && |
| 264 a.fTop < b.fBottom && b.fTop < a.fBottom; |
| 265 } |
| 266 |
| 267 // Can't call SkRect::contains, since it cares about empty, and we don't (since
we tracking |
| 268 // something to be stroked, so empty can still draw something (e.g. horizontal l
ine) |
| 269 static bool geometric_contains(const SkRect& outer, const SkRect& inner) { |
| 270 SkASSERT(!is_inverted(outer) && !is_inverted(inner)); |
| 271 return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft && |
| 272 inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop; |
| 273 } |
| 274 |
| 275 static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const Sk
Rect* insetClip, const SkRect* outsetClip, |
| 276 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) { |
| 277 if (insetClip) { |
| 278 SkASSERT(outsetClip); |
| 279 SkRect bounds = compute_nocheck_quad_bounds(pts); |
| 280 if (!geometric_overlap(*outsetClip, bounds)) { |
| 281 return; |
| 282 } else if (geometric_contains(*insetClip, bounds)) { |
| 283 clip = nullptr; |
| 284 } |
| 285 } |
| 286 |
| 287 hair_quad(pts, clip, blitter, level, lineproc); |
| 288 } |
| 289 |
| 242 static inline Sk2s abs(const Sk2s& value) { | 290 static inline Sk2s abs(const Sk2s& value) { |
| 243 return Sk2s::Max(value, Sk2s(0)-value); | 291 return Sk2s::Max(value, Sk2s(0)-value); |
| 244 } | 292 } |
| 245 | 293 |
| 246 static inline SkScalar max_component(const Sk2s& value) { | 294 static inline SkScalar max_component(const Sk2s& value) { |
| 247 SkScalar components[2]; | 295 SkScalar components[2]; |
| 248 value.store(components); | 296 value.store(components); |
| 249 return SkTMax(components[0], components[1]); | 297 return SkTMax(components[0], components[1]); |
| 250 } | 298 } |
| 251 | 299 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 Sk2s min = Sk2s::Load(pts); | 370 Sk2s min = Sk2s::Load(pts); |
| 323 Sk2s max = min; | 371 Sk2s max = min; |
| 324 for (int i = 1; i < 4; ++i) { | 372 for (int i = 1; i < 4; ++i) { |
| 325 Sk2s pair = Sk2s::Load(pts+i); | 373 Sk2s pair = Sk2s::Load(pts+i); |
| 326 min = Sk2s::Min(min, pair); | 374 min = Sk2s::Min(min, pair); |
| 327 max = Sk2s::Max(max, pair); | 375 max = Sk2s::Max(max, pair); |
| 328 } | 376 } |
| 329 return { min[0], min[1], max[0], max[1] }; | 377 return { min[0], min[1], max[0], max[1] }; |
| 330 } | 378 } |
| 331 | 379 |
| 332 static bool is_inverted(const SkRect& r) { | |
| 333 return r.fLeft > r.fRight || r.fTop > r.fBottom; | |
| 334 } | |
| 335 | |
| 336 // Can't call SkRect::intersects, since it cares about empty, and we don't (sinc
e we tracking | |
| 337 // something to be stroked, so empty can still draw something (e.g. horizontal l
ine) | |
| 338 static bool geometric_overlap(const SkRect& a, const SkRect& b) { | |
| 339 SkASSERT(!is_inverted(a) && !is_inverted(b)); | |
| 340 return a.fLeft < b.fRight && b.fLeft < a.fRight && | |
| 341 a.fTop < b.fBottom && b.fTop < a.fBottom; | |
| 342 } | |
| 343 | |
| 344 // Can't call SkRect::contains, since it cares about empty, and we don't (since
we tracking | |
| 345 // something to be stroked, so empty can still draw something (e.g. horizontal l
ine) | |
| 346 static bool geometric_contains(const SkRect& outer, const SkRect& inner) { | |
| 347 SkASSERT(!is_inverted(outer) && !is_inverted(inner)); | |
| 348 return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft && | |
| 349 inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop; | |
| 350 } | |
| 351 | |
| 352 //#define SK_SHOW_HAIRCLIP_STATS | |
| 353 #ifdef SK_SHOW_HAIRCLIP_STATS | |
| 354 static int gKillClip, gRejectClip, gClipCount; | |
| 355 #endif | |
| 356 | |
| 357 static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const S
kRect* insetClip, const SkRect* outsetClip, | 380 static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const S
kRect* insetClip, const SkRect* outsetClip, |
| 358 SkBlitter* blitter, int level, SkScan::HairRgnProc linepro
c) { | 381 SkBlitter* blitter, int level, SkScan::HairRgnProc linepro
c) { |
| 359 if (insetClip) { | 382 if (insetClip) { |
| 360 SkASSERT(outsetClip); | 383 SkASSERT(outsetClip); |
| 361 #ifdef SK_SHOW_HAIRCLIP_STATS | |
| 362 gClipCount += 1; | |
| 363 #endif | |
| 364 SkRect bounds = compute_nocheck_cubic_bounds(pts); | 384 SkRect bounds = compute_nocheck_cubic_bounds(pts); |
| 365 if (!geometric_overlap(*outsetClip, bounds)) { | 385 if (!geometric_overlap(*outsetClip, bounds)) { |
| 366 #ifdef SK_SHOW_HAIRCLIP_STATS | |
| 367 gRejectClip += 1; | |
| 368 #endif | |
| 369 return; | 386 return; |
| 370 } else if (geometric_contains(*insetClip, bounds)) { | 387 } else if (geometric_contains(*insetClip, bounds)) { |
| 371 clip = nullptr; | 388 clip = nullptr; |
| 372 #ifdef SK_SHOW_HAIRCLIP_STATS | |
| 373 gKillClip += 1; | |
| 374 #endif | |
| 375 } | 389 } |
| 376 #ifdef SK_SHOW_HAIRCLIP_STATS | |
| 377 if (0 == gClipCount % 256) | |
| 378 SkDebugf("kill %g reject %g total %d\n", 1.0*gKillClip / gClipCount,
1.0*gRejectClip/gClipCount, gClipCount); | |
| 379 #endif | |
| 380 } | 390 } |
| 381 | 391 |
| 382 if (quick_cubic_niceness_check(pts)) { | 392 if (quick_cubic_niceness_check(pts)) { |
| 383 hair_cubic(pts, clip, blitter, lineproc); | 393 hair_cubic(pts, clip, blitter, lineproc); |
| 384 } else { | 394 } else { |
| 385 SkPoint tmp[13]; | 395 SkPoint tmp[13]; |
| 386 SkScalar tValues[3]; | 396 SkScalar tValues[3]; |
| 387 | 397 |
| 388 int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); | 398 int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); |
| 389 for (int i = 0; i < count; i++) { | 399 for (int i = 0; i < count; i++) { |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 insetStorage.inset(1, 1); | 515 insetStorage.inset(1, 1); |
| 506 if (is_inverted(insetStorage)) { | 516 if (is_inverted(insetStorage)) { |
| 507 /* | 517 /* |
| 508 * our bounds checks assume the rects are never inverted. If in
setting has | 518 * our bounds checks assume the rects are never inverted. If in
setting has |
| 509 * created that, we assume that the area is too small to safely
perform a | 519 * created that, we assume that the area is too small to safely
perform a |
| 510 * quick-accept, so we just mark the rect as empty (so the quic
k-accept check | 520 * quick-accept, so we just mark the rect as empty (so the quic
k-accept check |
| 511 * will always fail. | 521 * will always fail. |
| 512 */ | 522 */ |
| 513 insetStorage.setEmpty(); // just so we don't pass an inverted
rect | 523 insetStorage.setEmpty(); // just so we don't pass an inverted
rect |
| 514 } | 524 } |
| 515 insetClip = &insetStorage; | 525 if (rclip.isRect()) { |
| 526 insetClip = &insetStorage; |
| 527 } |
| 516 outsetClip = &outsetStorage; | 528 outsetClip = &outsetStorage; |
| 517 } | 529 } |
| 518 } | 530 } |
| 519 | 531 |
| 520 SkPath::RawIter iter(path); | 532 SkPath::RawIter iter(path); |
| 521 SkPoint pts[4], firstPt, lastPt; | 533 SkPoint pts[4], firstPt, lastPt; |
| 522 SkPath::Verb verb, prevVerb; | 534 SkPath::Verb verb, prevVerb; |
| 523 SkAutoConicToQuads converter; | 535 SkAutoConicToQuads converter; |
| 524 | 536 |
| 525 if (SkPaint::kButt_Cap != capStyle) { | 537 if (SkPaint::kButt_Cap != capStyle) { |
| 526 prevVerb = SkPath::kDone_Verb; | 538 prevVerb = SkPath::kDone_Verb; |
| 527 } | 539 } |
| 528 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | 540 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
| 529 switch (verb) { | 541 switch (verb) { |
| 530 case SkPath::kMove_Verb: | 542 case SkPath::kMove_Verb: |
| 531 firstPt = lastPt = pts[0]; | 543 firstPt = lastPt = pts[0]; |
| 532 break; | 544 break; |
| 533 case SkPath::kLine_Verb: | 545 case SkPath::kLine_Verb: |
| 534 if (SkPaint::kButt_Cap != capStyle) { | 546 if (SkPaint::kButt_Cap != capStyle) { |
| 535 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2); | 547 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2); |
| 536 } | 548 } |
| 537 lineproc(pts, 2, clip, blitter); | 549 lineproc(pts, 2, clip, blitter); |
| 538 lastPt = pts[1]; | 550 lastPt = pts[1]; |
| 539 break; | 551 break; |
| 540 case SkPath::kQuad_Verb: | 552 case SkPath::kQuad_Verb: |
| 541 if (SkPaint::kButt_Cap != capStyle) { | 553 if (SkPaint::kButt_Cap != capStyle) { |
| 542 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); | 554 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); |
| 543 } | 555 } |
| 544 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); | 556 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad
_level(pts), lineproc); |
| 545 lastPt = pts[2]; | 557 lastPt = pts[2]; |
| 546 break; | 558 break; |
| 547 case SkPath::kConic_Verb: { | 559 case SkPath::kConic_Verb: { |
| 548 if (SkPaint::kButt_Cap != capStyle) { | 560 if (SkPaint::kButt_Cap != capStyle) { |
| 549 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); | 561 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); |
| 550 } | 562 } |
| 551 // how close should the quads be to the original conic? | 563 // how close should the quads be to the original conic? |
| 552 const SkScalar tol = SK_Scalar1 / 4; | 564 const SkScalar tol = SK_Scalar1 / 4; |
| 553 const SkPoint* quadPts = converter.computeQuads(pts, | 565 const SkPoint* quadPts = converter.computeQuads(pts, |
| 554 iter.conicWeight(), tol); | 566 iter.conicWeight(), tol); |
| 555 for (int i = 0; i < converter.countQuads(); ++i) { | 567 for (int i = 0; i < converter.countQuads(); ++i) { |
| 556 int level = compute_quad_level(quadPts); | 568 int level = compute_quad_level(quadPts); |
| 557 hairquad(quadPts, clip, blitter, level, lineproc); | 569 hairquad(quadPts, clip, insetClip, outsetClip, blitter, leve
l, lineproc); |
| 558 quadPts += 2; | 570 quadPts += 2; |
| 559 } | 571 } |
| 560 lastPt = pts[2]; | 572 lastPt = pts[2]; |
| 561 break; | 573 break; |
| 562 } | 574 } |
| 563 case SkPath::kCubic_Verb: { | 575 case SkPath::kCubic_Verb: { |
| 564 if (SkPaint::kButt_Cap != capStyle) { | 576 if (SkPaint::kButt_Cap != capStyle) { |
| 565 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4); | 577 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4); |
| 566 } | 578 } |
| 567 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSu
bdivideLevel, lineproc); | 579 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSu
bdivideLevel, lineproc); |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 679 | 691 |
| 680 SkAAClipBlitterWrapper wrap; | 692 SkAAClipBlitterWrapper wrap; |
| 681 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { | 693 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { |
| 682 wrap.init(clip, blitter); | 694 wrap.init(clip, blitter); |
| 683 blitter = wrap.getBlitter(); | 695 blitter = wrap.getBlitter(); |
| 684 clipRgn = &wrap.getRgn(); | 696 clipRgn = &wrap.getRgn(); |
| 685 } | 697 } |
| 686 AntiHairLineRgn(pts, count, clipRgn, blitter); | 698 AntiHairLineRgn(pts, count, clipRgn, blitter); |
| 687 } | 699 } |
| 688 } | 700 } |
| OLD | NEW |