Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Side by Side Diff: src/core/SkScan_Hairline.cpp

Issue 1943403006: fix hairline clip (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fixed indenting; removed debugging Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gm/bug5252.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « gm/bug5252.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698