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

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: try to get more debug info 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') | tools/dm_flags.py » ('j') | 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, 8));
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 //#define SK_SHOW_HAIRCLIP_STATS
276 #ifdef SK_SHOW_HAIRCLIP_STATS
277 static int gKillClip, gRejectClip, gClipCount;
278 #endif
279
280 static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const Sk Rect* insetClip, const SkRect* outsetClip,
281 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
282 if (insetClip) {
283 SkASSERT(outsetClip);
284 #ifdef SK_SHOW_HAIRCLIP_STATS
reed1 2016/05/11 19:47:50 I'm fine with these, or fine if we remove all this
285 gClipCount += 1;
286 #endif
287 SkRect bounds = compute_nocheck_quad_bounds(pts);
288 if (!geometric_overlap(*outsetClip, bounds)) {
289 #ifdef SK_SHOW_HAIRCLIP_STATS
290 gRejectClip += 1;
291 #endif
292 return;
293 } else if (geometric_contains(*insetClip, bounds)) {
294 clip = nullptr;
295 #ifdef SK_SHOW_HAIRCLIP_STATS
296 gKillClip += 1;
297 #endif
298 }
299 #ifdef SK_SHOW_HAIRCLIP_STATS
300 if (0 == gClipCount % 256)
301 SkDebugf("kill %g reject %g total %d\n", 1.0*gKillClip / gClipCount, 1.0*gRejectClip/gClipCount, gClipCount);
302 #endif
303 }
304
305 hair_quad(pts, clip, blitter, level, lineproc);
306 }
307
242 static inline Sk2s abs(const Sk2s& value) { 308 static inline Sk2s abs(const Sk2s& value) {
243 return Sk2s::Max(value, Sk2s(0)-value); 309 return Sk2s::Max(value, Sk2s(0)-value);
244 } 310 }
245 311
246 static inline SkScalar max_component(const Sk2s& value) { 312 static inline SkScalar max_component(const Sk2s& value) {
247 SkScalar components[2]; 313 SkScalar components[2];
248 value.store(components); 314 value.store(components);
249 return SkTMax(components[0], components[1]); 315 return SkTMax(components[0], components[1]);
250 } 316 }
251 317
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 Sk2s min = Sk2s::Load(pts); 388 Sk2s min = Sk2s::Load(pts);
323 Sk2s max = min; 389 Sk2s max = min;
324 for (int i = 1; i < 4; ++i) { 390 for (int i = 1; i < 4; ++i) {
325 Sk2s pair = Sk2s::Load(pts+i); 391 Sk2s pair = Sk2s::Load(pts+i);
326 min = Sk2s::Min(min, pair); 392 min = Sk2s::Min(min, pair);
327 max = Sk2s::Max(max, pair); 393 max = Sk2s::Max(max, pair);
328 } 394 }
329 return { min[0], min[1], max[0], max[1] }; 395 return { min[0], min[1], max[0], max[1] };
330 } 396 }
331 397
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, 398 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) { 399 SkBlitter* blitter, int level, SkScan::HairRgnProc linepro c) {
359 if (insetClip) { 400 if (insetClip) {
360 SkASSERT(outsetClip); 401 SkASSERT(outsetClip);
361 #ifdef SK_SHOW_HAIRCLIP_STATS 402 #ifdef SK_SHOW_HAIRCLIP_STATS
362 gClipCount += 1; 403 gClipCount += 1;
363 #endif 404 #endif
364 SkRect bounds = compute_nocheck_cubic_bounds(pts); 405 SkRect bounds = compute_nocheck_cubic_bounds(pts);
365 if (!geometric_overlap(*outsetClip, bounds)) { 406 if (!geometric_overlap(*outsetClip, bounds)) {
366 #ifdef SK_SHOW_HAIRCLIP_STATS 407 #ifdef SK_SHOW_HAIRCLIP_STATS
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
505 insetStorage.inset(1, 1); 546 insetStorage.inset(1, 1);
506 if (is_inverted(insetStorage)) { 547 if (is_inverted(insetStorage)) {
507 /* 548 /*
508 * our bounds checks assume the rects are never inverted. If in setting has 549 * 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 550 * 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 551 * quick-accept, so we just mark the rect as empty (so the quic k-accept check
511 * will always fail. 552 * will always fail.
512 */ 553 */
513 insetStorage.setEmpty(); // just so we don't pass an inverted rect 554 insetStorage.setEmpty(); // just so we don't pass an inverted rect
514 } 555 }
515 insetClip = &insetStorage; 556 » » » if (rclip.isRect()) {
reed1 2016/05/11 19:47:50 funny indenting (tabs?)
557 » » » » insetClip = &insetStorage;
558 » » » }
516 outsetClip = &outsetStorage; 559 outsetClip = &outsetStorage;
517 } 560 }
518 } 561 }
519 562
520 SkPath::RawIter iter(path); 563 SkPath::RawIter iter(path);
521 SkPoint pts[4], firstPt, lastPt; 564 SkPoint pts[4], firstPt, lastPt;
522 SkPath::Verb verb, prevVerb; 565 SkPath::Verb verb, prevVerb;
523 SkAutoConicToQuads converter; 566 SkAutoConicToQuads converter;
524 567
525 if (SkPaint::kButt_Cap != capStyle) { 568 if (SkPaint::kButt_Cap != capStyle) {
526 prevVerb = SkPath::kDone_Verb; 569 prevVerb = SkPath::kDone_Verb;
527 } 570 }
528 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 571 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
529 switch (verb) { 572 switch (verb) {
530 case SkPath::kMove_Verb: 573 case SkPath::kMove_Verb:
531 firstPt = lastPt = pts[0]; 574 firstPt = lastPt = pts[0];
532 break; 575 break;
533 case SkPath::kLine_Verb: 576 case SkPath::kLine_Verb:
534 if (SkPaint::kButt_Cap != capStyle) { 577 if (SkPaint::kButt_Cap != capStyle) {
535 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2); 578 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
536 } 579 }
537 lineproc(pts, 2, clip, blitter); 580 lineproc(pts, 2, clip, blitter);
538 lastPt = pts[1]; 581 lastPt = pts[1];
539 break; 582 break;
540 case SkPath::kQuad_Verb: 583 case SkPath::kQuad_Verb:
541 if (SkPaint::kButt_Cap != capStyle) { 584 if (SkPaint::kButt_Cap != capStyle) {
542 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); 585 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
543 } 586 }
544 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); 587 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad _level(pts), lineproc);
545 lastPt = pts[2]; 588 lastPt = pts[2];
546 break; 589 break;
547 case SkPath::kConic_Verb: { 590 case SkPath::kConic_Verb: {
548 if (SkPaint::kButt_Cap != capStyle) { 591 if (SkPaint::kButt_Cap != capStyle) {
549 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); 592 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
550 } 593 }
551 // how close should the quads be to the original conic? 594 // how close should the quads be to the original conic?
552 const SkScalar tol = SK_Scalar1 / 4; 595 const SkScalar tol = SK_Scalar1 / 4;
553 const SkPoint* quadPts = converter.computeQuads(pts, 596 const SkPoint* quadPts = converter.computeQuads(pts,
554 iter.conicWeight(), tol); 597 iter.conicWeight(), tol);
555 for (int i = 0; i < converter.countQuads(); ++i) { 598 for (int i = 0; i < converter.countQuads(); ++i) {
556 int level = compute_quad_level(quadPts); 599 int level = compute_quad_level(quadPts);
557 hairquad(quadPts, clip, blitter, level, lineproc); 600 hairquad(quadPts, clip, insetClip, outsetClip, blitter, leve l, lineproc);
558 quadPts += 2; 601 quadPts += 2;
559 } 602 }
560 lastPt = pts[2]; 603 lastPt = pts[2];
561 break; 604 break;
562 } 605 }
563 case SkPath::kCubic_Verb: { 606 case SkPath::kCubic_Verb: {
564 if (SkPaint::kButt_Cap != capStyle) { 607 if (SkPaint::kButt_Cap != capStyle) {
565 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4); 608 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
566 } 609 }
567 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSu bdivideLevel, lineproc); 610 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSu bdivideLevel, lineproc);
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
679 722
680 SkAAClipBlitterWrapper wrap; 723 SkAAClipBlitterWrapper wrap;
681 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { 724 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
682 wrap.init(clip, blitter); 725 wrap.init(clip, blitter);
683 blitter = wrap.getBlitter(); 726 blitter = wrap.getBlitter();
684 clipRgn = &wrap.getRgn(); 727 clipRgn = &wrap.getRgn();
685 } 728 }
686 AntiHairLineRgn(pts, count, clipRgn, blitter); 729 AntiHairLineRgn(pts, count, clipRgn, blitter);
687 } 730 }
688 } 731 }
OLDNEW
« no previous file with comments | « gm/bug5252.cpp ('k') | tools/dm_flags.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698