| OLD | NEW |
| 1 // Copyright (C) 2013 Google Inc. All rights reserved. | 1 // Copyright (C) 2013 Google Inc. All rights reserved. |
| 2 // | 2 // |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // Redistribution and use in source and binary forms, with or without |
| 4 // modification, are permitted provided that the following conditions are | 4 // modification, are permitted provided that the following conditions are |
| 5 // met: | 5 // met: |
| 6 // | 6 // |
| 7 // * Redistributions of source code must retain the above copyright | 7 // * Redistributions of source code must retain the above copyright |
| 8 // notice, this list of conditions and the following disclaimer. | 8 // notice, this list of conditions and the following disclaimer. |
| 9 // * Redistributions in binary form must reproduce the above | 9 // * Redistributions in binary form must reproduce the above |
| 10 // copyright notice, this list of conditions and the following disclaimer | 10 // copyright notice, this list of conditions and the following disclaimer |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 #include "platform/graphics/StrokeData.h" | 29 #include "platform/graphics/StrokeData.h" |
| 30 #include "platform/graphics/paint/PaintFlags.h" | 30 #include "platform/graphics/paint/PaintFlags.h" |
| 31 #include "third_party/skia/include/effects/SkDashPathEffect.h" | 31 #include "third_party/skia/include/effects/SkDashPathEffect.h" |
| 32 #include "wtf/PtrUtil.h" | 32 #include "wtf/PtrUtil.h" |
| 33 #include <memory> | 33 #include <memory> |
| 34 | 34 |
| 35 namespace blink { | 35 namespace blink { |
| 36 | 36 |
| 37 static const int dashRatio = 3; // Ratio of the length of a dash to its width. | |
| 38 | |
| 39 void StrokeData::setLineDash(const DashArray& dashes, float dashOffset) { | 37 void StrokeData::setLineDash(const DashArray& dashes, float dashOffset) { |
| 40 // FIXME: This is lifted directly off SkiaSupport, lines 49-74 | 38 // FIXME: This is lifted directly off SkiaSupport, lines 49-74 |
| 41 // so it is not guaranteed to work correctly. | 39 // so it is not guaranteed to work correctly. |
| 42 size_t dashLength = dashes.size(); | 40 size_t dashLength = dashes.size(); |
| 43 if (!dashLength) { | 41 if (!dashLength) { |
| 44 // If no dash is set, revert to solid stroke | 42 // If no dash is set, revert to solid stroke |
| 45 // FIXME: do we need to set NoStroke in some cases? | 43 // FIXME: do we need to set NoStroke in some cases? |
| 46 m_style = SolidStroke; | 44 m_style = SolidStroke; |
| 47 m_dash.reset(); | 45 m_dash.reset(); |
| 48 return; | 46 return; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 64 flags->setStrokeJoin(m_lineJoin); | 62 flags->setStrokeJoin(m_lineJoin); |
| 65 flags->setStrokeMiter(SkFloatToScalar(m_miterLimit)); | 63 flags->setStrokeMiter(SkFloatToScalar(m_miterLimit)); |
| 66 | 64 |
| 67 setupPaintDashPathEffect(flags, length); | 65 setupPaintDashPathEffect(flags, length); |
| 68 } | 66 } |
| 69 | 67 |
| 70 void StrokeData::setupPaintDashPathEffect(PaintFlags* flags, int length) const { | 68 void StrokeData::setupPaintDashPathEffect(PaintFlags* flags, int length) const { |
| 71 if (m_dash) { | 69 if (m_dash) { |
| 72 flags->setPathEffect(m_dash); | 70 flags->setPathEffect(m_dash); |
| 73 } else if (strokeIsDashed(m_thickness, m_style)) { | 71 } else if (strokeIsDashed(m_thickness, m_style)) { |
| 74 float width = | 72 float dashLength = m_thickness; |
| 75 m_style == DashedStroke ? dashRatio * m_thickness : m_thickness; | 73 float gapLength = dashLength; |
| 76 | 74 if (m_style == DashedStroke) { |
| 77 // Truncate the width, since we don't want fuzzy dots or dashes. | 75 dashLength *= StrokeData::dashLengthRatio(m_thickness); |
| 78 int dashLength = static_cast<int>(width); | 76 gapLength *= StrokeData::dashGapRatio(m_thickness); |
| 79 // Subtract off the endcaps, since they're rendered separately. | |
| 80 int distance = length - 2 * static_cast<int>(m_thickness); | |
| 81 int phase = 1; | |
| 82 if (dashLength > 1) { | |
| 83 // Determine how many dashes or dots we should have. | |
| 84 int numDashes = distance / dashLength; | |
| 85 int remainder = distance % dashLength; | |
| 86 // Adjust the phase to center the dashes within the line. | |
| 87 if (numDashes % 2) { | |
| 88 // Odd: shift right a full dash, minus half the remainder. | |
| 89 phase = dashLength - remainder / 2; | |
| 90 } else { | |
| 91 // Even: shift right half a dash, minus half the remainder. | |
| 92 phase = (dashLength - remainder) / 2; | |
| 93 } | |
| 94 } | 77 } |
| 95 SkScalar dashLengthSk = SkIntToScalar(dashLength); | 78 // Account for modification to effective length in |
| 96 SkScalar intervals[2] = {dashLengthSk, dashLengthSk}; | 79 // GraphicsContext::adjustLineToPixelBoundaries |
| 97 flags->setPathEffect( | 80 length -= 2 * m_thickness; |
| 98 SkDashPathEffect::Make(intervals, 2, SkIntToScalar(phase))); | 81 if (length <= dashLength) { |
| 82 // No space for dashes |
| 83 flags->setPathEffect(0); |
| 84 } else if (length <= 2 * dashLength + gapLength) { |
| 85 // Exactly 2 dashes proportionally sized |
| 86 float multiplier = length / (2 * dashLength + gapLength); |
| 87 SkScalar intervals[2] = {dashLength * multiplier, gapLength * multiplier}; |
| 88 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); |
| 89 } else { |
| 90 float gap = gapLength; |
| 91 if (m_style == DashedStroke) |
| 92 gap = selectBestDashGap(length, dashLength, gapLength); |
| 93 SkScalar intervals[2] = {dashLength, gap}; |
| 94 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); |
| 95 } |
| 99 } else if (m_style == DottedStroke) { | 96 } else if (m_style == DottedStroke) { |
| 100 flags->setStrokeCap((PaintFlags::Cap)RoundCap); | 97 flags->setStrokeCap((PaintFlags::Cap)RoundCap); |
| 101 // Adjust the width to get equal dot spacing as much as possible. | 98 // Adjust the width to get equal dot spacing as much as possible. |
| 102 float perDotLength = m_thickness * 2; | 99 float perDotLength = m_thickness * 2; |
| 103 static float epsilon = 1.0e-2f; | 100 if (length < perDotLength) { |
| 104 if (length < perDotLength + m_thickness) { | 101 // Not enoguh space for 2 dots. Just draw 1 by giving a gap that is |
| 105 // Exactly 2 dots with whatever space we can get | 102 // bigger than the length. |
| 106 SkScalar intervals[2] = {0, length - m_thickness - epsilon}; | 103 SkScalar intervals[2] = {0, perDotLength}; |
| 107 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); | 104 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); |
| 108 return; | 105 return; |
| 109 } | 106 } |
| 110 | 107 |
| 111 // Determine what number of dots gives the minimum deviation from | 108 static const float epsilon = 1.0e-2f; |
| 112 // idealGap between dots. Set the gap to that width. | 109 float gap = selectBestDashGap(length, m_thickness, m_thickness); |
| 113 float minNumDots = floorf((length + m_thickness) / perDotLength); | 110 SkScalar intervals[2] = {0, gap + m_thickness - epsilon}; |
| 114 float maxNumDots = minNumDots + 1; | 111 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); |
| 115 float minGap = (length - minNumDots * m_thickness) / (minNumDots - 1); | |
| 116 float maxGap = (length - maxNumDots * m_thickness) / (maxNumDots - 1); | |
| 117 if (fabs(minGap - m_thickness) < fabs(maxGap - m_thickness)) { | |
| 118 SkScalar intervals[2] = {0, minGap + m_thickness - epsilon}; | |
| 119 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); | |
| 120 } else { | |
| 121 SkScalar intervals[2] = {0, maxGap + m_thickness - epsilon}; | |
| 122 flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); | |
| 123 } | |
| 124 } else { | 112 } else { |
| 125 // TODO(schenney): WavyStroke https://crbug.com/229574 | 113 // TODO(schenney): WavyStroke https://crbug.com/229574 |
| 126 flags->setPathEffect(0); | 114 flags->setPathEffect(0); |
| 127 } | 115 } |
| 128 } | 116 } |
| 129 | 117 |
| 130 bool StrokeData::strokeIsDashed(float width, StrokeStyle style) { | 118 bool StrokeData::strokeIsDashed(float width, StrokeStyle style) { |
| 131 return style == DashedStroke || (style == DottedStroke && width <= 3); | 119 return style == DashedStroke || (style == DottedStroke && width <= 3); |
| 132 } | 120 } |
| 133 | 121 |
| 122 float StrokeData::selectBestDashGap(float strokeLength, |
| 123 float dashLength, |
| 124 float gapLength) { |
| 125 // Determine what number of dashes gives the minimum deviation from |
| 126 // gapLength between dashes. Set the gap to that width. |
| 127 float minNumDashes = |
| 128 floorf((strokeLength + gapLength) / (dashLength + gapLength)); |
| 129 float maxNumDashes = minNumDashes + 1; |
| 130 float minGap = |
| 131 (strokeLength - minNumDashes * dashLength) / (minNumDashes - 1); |
| 132 float maxGap = |
| 133 (strokeLength - maxNumDashes * dashLength) / (maxNumDashes - 1); |
| 134 return fabs(minGap - gapLength) < fabs(maxGap - gapLength) ? minGap : maxGap; |
| 135 } |
| 136 |
| 134 } // namespace blink | 137 } // namespace blink |
| OLD | NEW |