OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. |
3 * 2006 Rob Buis <buis@kde.org> | 3 * 2006 Rob Buis <buis@kde.org> |
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> | 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
5 * Copyright (C) 2013 Google Inc. All rights reserved. | 5 * Copyright (C) 2013 Google Inc. All rights reserved. |
6 * Copyright (C) 2013 Intel Corporation. All rights reserved. | 6 * Copyright (C) 2013 Intel Corporation. All rights reserved. |
7 * | 7 * |
8 * Redistribution and use in source and binary forms, with or without | 8 * Redistribution and use in source and binary forms, with or without |
9 * modification, are permitted provided that the following conditions | 9 * modification, are permitted provided that the following conditions |
10 * are met: | 10 * are met: |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 SkScalar y = WebCoreFloatToSkScalar(point.y()); | 76 SkScalar y = WebCoreFloatToSkScalar(point.y()); |
77 SkPath::FillType fillType = WebCoreWindRuleToSkFillType(rule); | 77 SkPath::FillType fillType = WebCoreWindRuleToSkFillType(rule); |
78 if (m_path.getFillType() != fillType) { | 78 if (m_path.getFillType() != fillType) { |
79 SkPath tmp(m_path); | 79 SkPath tmp(m_path); |
80 tmp.setFillType(fillType); | 80 tmp.setFillType(fillType); |
81 return tmp.contains(x, y); | 81 return tmp.contains(x, y); |
82 } | 82 } |
83 return m_path.contains(x, y); | 83 return m_path.contains(x, y); |
84 } | 84 } |
85 | 85 |
86 // FIXME: this method ignores the CTM and may yield inaccurate results for large
scales. | 86 // FIXME: this method ignores the CTM and may yield inaccurate results for large |
| 87 // scales. |
87 SkPath Path::strokePath(const StrokeData& strokeData) const { | 88 SkPath Path::strokePath(const StrokeData& strokeData) const { |
88 SkPaint paint; | 89 SkPaint paint; |
89 strokeData.setupPaint(&paint); | 90 strokeData.setupPaint(&paint); |
90 | 91 |
91 // Skia stroke resolution scale. This is multiplied by 4 internally | 92 // Skia stroke resolution scale. This is multiplied by 4 internally |
92 // (i.e. 1.0 corresponds to 1/4 pixel res). | 93 // (i.e. 1.0 corresponds to 1/4 pixel res). |
93 static const SkScalar kResScale = 0.3f; | 94 static const SkScalar kResScale = 0.3f; |
94 | 95 |
95 SkPath strokePath; | 96 SkPath strokePath; |
96 paint.getFillPath(m_path, &strokePath, nullptr, kResScale); | 97 paint.getFillPath(m_path, &strokePath, nullptr, kResScale); |
97 | 98 |
98 return strokePath; | 99 return strokePath; |
99 } | 100 } |
100 | 101 |
101 bool Path::strokeContains(const FloatPoint& point, | 102 bool Path::strokeContains(const FloatPoint& point, |
102 const StrokeData& strokeData) const { | 103 const StrokeData& strokeData) const { |
103 return strokePath(strokeData) | 104 return strokePath(strokeData) |
104 .contains(WebCoreFloatToSkScalar(point.x()), | 105 .contains(WebCoreFloatToSkScalar(point.x()), |
105 WebCoreFloatToSkScalar(point.y())); | 106 WebCoreFloatToSkScalar(point.y())); |
106 } | 107 } |
107 | 108 |
108 namespace { | 109 namespace { |
109 | 110 |
110 FloatRect pathBounds(const SkPath& path, Path::BoundsType boundsType) { | 111 FloatRect pathBounds(const SkPath& path, Path::BoundsType boundsType) { |
111 SkRect bounds; | 112 SkRect bounds; |
112 if (boundsType == Path::BoundsType::Conservative || | 113 if (boundsType == Path::BoundsType::Conservative || |
113 !TightBounds(path, &bounds) || | 114 !TightBounds(path, &bounds) || |
114 bounds | 115 // Workaround for https://bugs.chromium.org/p/skia/issues/detail?id=5555 . |
115 .isEmpty()) // workaround for https://bugs.chromium.org/p/skia/issues
/detail?id=5555 | 116 bounds.isEmpty()) |
116 return path.getBounds(); | 117 return path.getBounds(); |
117 | 118 |
118 DCHECK_EQ(boundsType, Path::BoundsType::Exact); | 119 DCHECK_EQ(boundsType, Path::BoundsType::Exact); |
119 return bounds; | 120 return bounds; |
120 } | 121 } |
121 | 122 |
122 } // anonymous ns | 123 } // anonymous ns |
123 | 124 |
124 // TODO(fmalita): evaluate returning exact bounds in all cases. | 125 // TODO(fmalita): evaluate returning exact bounds in all cases. |
125 FloatRect Path::boundingRect(BoundsType boundsType) const { | 126 FloatRect Path::boundingRect(BoundsType boundsType) const { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 break; | 160 break; |
160 case SkPath::kQuad_Verb: | 161 case SkPath::kQuad_Verb: |
161 pathElement.type = PathElementAddQuadCurveToPoint; | 162 pathElement.type = PathElementAddQuadCurveToPoint; |
162 pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); | 163 pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); |
163 break; | 164 break; |
164 case SkPath::kCubic_Verb: | 165 case SkPath::kCubic_Verb: |
165 pathElement.type = PathElementAddCurveToPoint; | 166 pathElement.type = PathElementAddCurveToPoint; |
166 pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); | 167 pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); |
167 break; | 168 break; |
168 case SkPath::kConic_Verb: { | 169 case SkPath::kConic_Verb: { |
169 // Approximate with quads. Use two for now, increase if more precision
is needed. | 170 // Approximate with quads. Use two for now, increase if more precision |
| 171 // is needed. |
170 const int kPow2 = 1; | 172 const int kPow2 = 1; |
171 const unsigned quadCount = 1 << kPow2; | 173 const unsigned quadCount = 1 << kPow2; |
172 SkPoint quads[1 + 2 * quadCount]; | 174 SkPoint quads[1 + 2 * quadCount]; |
173 SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), | 175 SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), |
174 quads, kPow2); | 176 quads, kPow2); |
175 | 177 |
176 pathElement.type = PathElementAddQuadCurveToPoint; | 178 pathElement.type = PathElementAddQuadCurveToPoint; |
177 for (unsigned i = 0; i < quadCount; ++i) { | 179 for (unsigned i = 0; i < quadCount; ++i) { |
178 pathElement.points = | 180 pathElement.points = |
179 convertPathPoints(pathPoints, &quads[1 + 2 * i], 2); | 181 convertPathPoints(pathPoints, &quads[1 + 2 * i], 2); |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 | 382 |
381 SkRect oval; | 383 SkRect oval; |
382 oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, | 384 oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, |
383 cy + radiusYScalar); | 385 cy + radiusYScalar); |
384 | 386 |
385 float sweep = endAngle - startAngle; | 387 float sweep = endAngle - startAngle; |
386 SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat); | 388 SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat); |
387 SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); | 389 SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); |
388 SkScalar s360 = SkIntToScalar(360); | 390 SkScalar s360 = SkIntToScalar(360); |
389 | 391 |
390 // We can't use SkPath::addOval(), because addOval() makes new sub-path. addOv
al() calls moveTo() and close() internally. | 392 // We can't use SkPath::addOval(), because addOval() makes a new sub-path. |
| 393 // addOval() calls moveTo() and close() internally. |
391 | 394 |
392 // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws n
othing. | 395 // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws |
| 396 // nothing. |
393 SkScalar s180 = SkIntToScalar(180); | 397 SkScalar s180 = SkIntToScalar(180); |
394 if (SkScalarNearlyEqual(sweepDegrees, s360)) { | 398 if (SkScalarNearlyEqual(sweepDegrees, s360)) { |
395 // SkPath::arcTo can't handle the sweepAngle that is equal to or greater tha
n 2Pi. | 399 // SkPath::arcTo can't handle the sweepAngle that is equal to or greater |
| 400 // than 2Pi. |
396 m_path.arcTo(oval, startDegrees, s180, false); | 401 m_path.arcTo(oval, startDegrees, s180, false); |
397 m_path.arcTo(oval, startDegrees + s180, s180, false); | 402 m_path.arcTo(oval, startDegrees + s180, s180, false); |
398 return; | 403 return; |
399 } | 404 } |
400 if (SkScalarNearlyEqual(sweepDegrees, -s360)) { | 405 if (SkScalarNearlyEqual(sweepDegrees, -s360)) { |
401 m_path.arcTo(oval, startDegrees, -s180, false); | 406 m_path.arcTo(oval, startDegrees, -s180, false); |
402 m_path.arcTo(oval, startDegrees - s180, -s180, false); | 407 m_path.arcTo(oval, startDegrees - s180, -s180, false); |
403 return; | 408 return; |
404 } | 409 } |
405 | 410 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 } | 464 } |
460 | 465 |
461 void Path::addRoundedRect(const FloatRect& rect, | 466 void Path::addRoundedRect(const FloatRect& rect, |
462 const FloatSize& roundingRadii) { | 467 const FloatSize& roundingRadii) { |
463 if (rect.isEmpty()) | 468 if (rect.isEmpty()) |
464 return; | 469 return; |
465 | 470 |
466 FloatSize radius(roundingRadii); | 471 FloatSize radius(roundingRadii); |
467 FloatSize halfSize(rect.width() / 2, rect.height() / 2); | 472 FloatSize halfSize(rect.width() / 2, rect.height() / 2); |
468 | 473 |
469 // Apply the SVG corner radius constraints, per the rect section of the SVG sh
apes spec: if | 474 // Apply the SVG corner radius constraints, per the rect section of the SVG |
470 // one of rx,ry is negative, then the other corner radius value is used. If bo
th values are | 475 // shapes spec: if one of rx,ry is negative, then the other corner radius |
471 // negative then rx = ry = 0. If rx is greater than half of the width of the r
ectangle | 476 // value is used. If both values are negative then rx = ry = 0. If rx is |
472 // then set rx to half of the width; ry is handled similarly. | 477 // greater than half of the width of the rectangle then set rx to half of the |
| 478 // width; ry is handled similarly. |
473 | 479 |
474 if (radius.width() < 0) | 480 if (radius.width() < 0) |
475 radius.setWidth((radius.height() < 0) ? 0 : radius.height()); | 481 radius.setWidth((radius.height() < 0) ? 0 : radius.height()); |
476 | 482 |
477 if (radius.height() < 0) | 483 if (radius.height() < 0) |
478 radius.setHeight(radius.width()); | 484 radius.setHeight(radius.width()); |
479 | 485 |
480 if (radius.width() > halfSize.width()) | 486 if (radius.width() > halfSize.width()) |
481 radius.setWidth(halfSize.width()); | 487 radius.setWidth(halfSize.width()); |
482 | 488 |
483 if (radius.height() > halfSize.height()) | 489 if (radius.height() > halfSize.height()) |
484 radius.setHeight(halfSize.height()); | 490 radius.setHeight(halfSize.height()); |
485 | 491 |
486 addPathForRoundedRect(rect, radius, radius, radius, radius); | 492 addPathForRoundedRect(rect, radius, radius, radius, radius); |
487 } | 493 } |
488 | 494 |
489 void Path::addRoundedRect(const FloatRect& rect, | 495 void Path::addRoundedRect(const FloatRect& rect, |
490 const FloatSize& topLeftRadius, | 496 const FloatSize& topLeftRadius, |
491 const FloatSize& topRightRadius, | 497 const FloatSize& topRightRadius, |
492 const FloatSize& bottomLeftRadius, | 498 const FloatSize& bottomLeftRadius, |
493 const FloatSize& bottomRightRadius) { | 499 const FloatSize& bottomRightRadius) { |
494 if (rect.isEmpty()) | 500 if (rect.isEmpty()) |
495 return; | 501 return; |
496 | 502 |
497 if (rect.width() < topLeftRadius.width() + topRightRadius.width() || | 503 if (rect.width() < topLeftRadius.width() + topRightRadius.width() || |
498 rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() || | 504 rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() || |
499 rect.height() < topLeftRadius.height() + bottomLeftRadius.height() || | 505 rect.height() < topLeftRadius.height() + bottomLeftRadius.height() || |
500 rect.height() < topRightRadius.height() + bottomRightRadius.height()) { | 506 rect.height() < topRightRadius.height() + bottomRightRadius.height()) { |
501 // If all the radii cannot be accommodated, return a rect. | 507 // If all the radii cannot be accommodated, return a rect. |
502 // FIXME: is this an error scenario, given that it appears the code in Float
RoundedRect::constrainRadii() | 508 // FIXME: Is this an error scenario, given that it appears the code in |
503 // should be always called first? Should we assert that this code is not rea
ched? | 509 // FloatRoundedRect::constrainRadii() should be always called first? Should |
504 // This fallback is very bad, since it means that radii that are just barely
too big due to rounding or snapping | 510 // we assert that this code is not reached? This fallback is very bad, since |
505 // will get completely ignored. | 511 // it means that radii that are just barely too big due to rounding or |
| 512 // snapping will get completely ignored. |
506 addRect(rect); | 513 addRect(rect); |
507 return; | 514 return; |
508 } | 515 } |
509 | 516 |
510 addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, | 517 addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, |
511 bottomRightRadius); | 518 bottomRightRadius); |
512 } | 519 } |
513 | 520 |
514 void Path::addPathForRoundedRect(const FloatRect& rect, | 521 void Path::addPathForRoundedRect(const FloatRect& rect, |
515 const FloatSize& topLeftRadius, | 522 const FloatSize& topLeftRadius, |
(...skipping 28 matching lines...) Expand all Loading... |
544 } | 551 } |
545 | 552 |
546 #if ENABLE(ASSERT) | 553 #if ENABLE(ASSERT) |
547 bool ellipseIsRenderable(float startAngle, float endAngle) { | 554 bool ellipseIsRenderable(float startAngle, float endAngle) { |
548 return (std::abs(endAngle - startAngle) < twoPiFloat) || | 555 return (std::abs(endAngle - startAngle) < twoPiFloat) || |
549 WebCoreFloatNearlyEqual(std::abs(endAngle - startAngle), twoPiFloat); | 556 WebCoreFloatNearlyEqual(std::abs(endAngle - startAngle), twoPiFloat); |
550 } | 557 } |
551 #endif | 558 #endif |
552 | 559 |
553 } // namespace blink | 560 } // namespace blink |
OLD | NEW |