OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 University of Szeged | 2 * Copyright (C) 2011 University of Szeged |
3 * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> | 3 * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> |
4 * All rights reserved. | 4 * All rights reserved. |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 25 matching lines...) Expand all Loading... |
36 LayoutSVGRect::LayoutSVGRect(SVGRectElement* node) | 36 LayoutSVGRect::LayoutSVGRect(SVGRectElement* node) |
37 : LayoutSVGShape(node) | 37 : LayoutSVGShape(node) |
38 , m_usePathFallback(false) | 38 , m_usePathFallback(false) |
39 { | 39 { |
40 } | 40 } |
41 | 41 |
42 LayoutSVGRect::~LayoutSVGRect() | 42 LayoutSVGRect::~LayoutSVGRect() |
43 { | 43 { |
44 } | 44 } |
45 | 45 |
| 46 void LayoutSVGRect::styleDidChange(StyleDifference diff, const ComputedStyle* ol
dStyle) |
| 47 { |
| 48 if (diff.needsFullLayout() && oldStyle) { |
| 49 const SVGComputedStyle& oldSvgStyle = oldStyle->svgStyle(); |
| 50 const SVGComputedStyle& svgStyle = style()->svgStyle(); |
| 51 if (oldSvgStyle.rx() != svgStyle.rx() |
| 52 || oldSvgStyle.ry() != svgStyle.ry() |
| 53 || oldSvgStyle.vectorEffect() != svgStyle.vectorEffect() |
| 54 || definitelyHasSimpleStroke(oldSvgStyle) != definitelyHasSimpleStro
ke(svgStyle)) |
| 55 setNeedsShapeUpdate(); |
| 56 } |
| 57 |
| 58 // Superclass will take care of calling clientStyleChanged. |
| 59 LayoutSVGShape::styleDidChange(diff, oldStyle); |
| 60 } |
| 61 |
46 void LayoutSVGRect::updateShapeFromElement() | 62 void LayoutSVGRect::updateShapeFromElement() |
47 { | 63 { |
48 // Before creating a new object we need to clear the cached bounding box | |
49 // to avoid using garbage. | |
50 m_fillBoundingBox = FloatRect(); | |
51 m_strokeBoundingBox = FloatRect(); | |
52 m_usePathFallback = false; | 64 m_usePathFallback = false; |
53 SVGRectElement* rect = toSVGRectElement(element()); | |
54 ASSERT(rect); | |
55 | 65 |
56 SVGLengthContext lengthContext(rect); | 66 // Fallback to LayoutSVGShape and path-based hit detection if the rect |
| 67 // has rounded corners or a non-scaling or non-simple stroke. |
| 68 SVGLengthContext lengthContext(toSVGRectElement(element())); |
| 69 if (lengthContext.valueForLength(styleRef().svgStyle().rx(), styleRef(), SVG
LengthMode::Width) > 0 |
| 70 || lengthContext.valueForLength(styleRef().svgStyle().ry(), styleRef(),
SVGLengthMode::Height) > 0 |
| 71 || hasNonScalingStroke() |
| 72 || !definitelyHasSimpleStroke(style()->svgStyle())) { |
| 73 LayoutSVGShape::updateShapeFromElement(); |
| 74 m_usePathFallback = true; |
| 75 return; |
| 76 } |
| 77 |
| 78 clearPath(); |
| 79 } |
| 80 |
| 81 void LayoutSVGRect::updateStrokeAndFillBoundingBoxes() |
| 82 { |
| 83 SVGLengthContext lengthContext(toSVGRectElement(element())); |
57 FloatSize boundingBoxSize( | 84 FloatSize boundingBoxSize( |
58 lengthContext.valueForLength(styleRef().width(), styleRef(), SVGLengthMo
de::Width), | 85 lengthContext.valueForLength(styleRef().width(), styleRef(), SVGLengthMo
de::Width), |
59 lengthContext.valueForLength(styleRef().height(), styleRef(), SVGLengthM
ode::Height)); | 86 lengthContext.valueForLength(styleRef().height(), styleRef(), SVGLengthM
ode::Height)); |
60 | 87 |
61 // Spec: "A negative value is an error." | 88 // Spec: "A negative value is an error." |
62 if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0) | 89 if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0) { |
| 90 m_fillBoundingBox = FloatRect(); |
| 91 m_strokeBoundingBox = FloatRect(); |
63 return; | 92 return; |
| 93 } |
64 | 94 |
65 // Spec: "A value of zero disables rendering of the element." | 95 if (m_usePathFallback) { |
66 if (!boundingBoxSize.isEmpty()) { | 96 // Spec: "A value of zero disables rendering of the element." so we can
skip |
67 // Fallback to LayoutSVGShape and path-based hit detection if the rect | 97 // the path fallback and rely on the existing bounding box calculation. |
68 // has rounded corners or a non-scaling or non-simple stroke. | 98 if (!boundingBoxSize.isEmpty()) { |
69 if (lengthContext.valueForLength(styleRef().svgStyle().rx(), styleRef(),
SVGLengthMode::Width) > 0 | 99 LayoutSVGShape::updateStrokeAndFillBoundingBoxes(); |
70 || lengthContext.valueForLength(styleRef().svgStyle().ry(), styleRef
(), SVGLengthMode::Height) > 0 | |
71 || hasNonScalingStroke() | |
72 || !definitelyHasSimpleStroke()) { | |
73 LayoutSVGShape::updateShapeFromElement(); | |
74 m_usePathFallback = true; | |
75 return; | 100 return; |
76 } | 101 } |
| 102 m_usePathFallback = false; |
| 103 clearPath(); |
77 } | 104 } |
78 | 105 |
79 m_fillBoundingBox = FloatRect( | 106 m_fillBoundingBox = FloatRect( |
80 FloatPoint( | 107 FloatPoint( |
81 lengthContext.valueForLength(styleRef().svgStyle().x(), styleRef(),
SVGLengthMode::Width), | 108 lengthContext.valueForLength(styleRef().svgStyle().x(), styleRef(),
SVGLengthMode::Width), |
82 lengthContext.valueForLength(styleRef().svgStyle().y(), styleRef(),
SVGLengthMode::Height)), | 109 lengthContext.valueForLength(styleRef().svgStyle().y(), styleRef(),
SVGLengthMode::Height)), |
83 boundingBoxSize); | 110 boundingBoxSize); |
84 m_strokeBoundingBox = m_fillBoundingBox; | 111 m_strokeBoundingBox = m_fillBoundingBox; |
85 if (style()->svgStyle().hasStroke()) | 112 if (style()->svgStyle().hasStroke()) |
86 m_strokeBoundingBox.inflate(strokeWidth() / 2); | 113 m_strokeBoundingBox.inflate(strokeWidth() / 2); |
87 } | 114 } |
88 | 115 |
89 bool LayoutSVGRect::shapeDependentStrokeContains(const FloatPoint& point) | 116 bool LayoutSVGRect::shapeDependentStrokeContains(const FloatPoint& point) |
90 { | 117 { |
91 // The optimized code below does not support non-simple strokes so we need | 118 // The optimized code below does not support non-simple strokes so we need |
92 // to fall back to LayoutSVGShape::shapeDependentStrokeContains in these cas
es. | 119 // to fall back to LayoutSVGShape::shapeDependentStrokeContains in these cas
es. |
93 if (m_usePathFallback || !definitelyHasSimpleStroke()) { | 120 if (m_usePathFallback || !definitelyHasSimpleStroke(style()->svgStyle())) { |
94 if (!hasPath()) | 121 if (!m_usePathFallback) |
95 LayoutSVGShape::updateShapeFromElement(); | 122 LayoutSVGShape::updateShapeFromElement(); |
96 return LayoutSVGShape::shapeDependentStrokeContains(point); | 123 return LayoutSVGShape::shapeDependentStrokeContains(point); |
97 } | 124 } |
98 | 125 |
99 const float halfStrokeWidth = strokeWidth() / 2; | 126 const float halfStrokeWidth = strokeWidth() / 2; |
100 const float halfWidth = m_fillBoundingBox.width() / 2; | 127 const float halfWidth = m_fillBoundingBox.width() / 2; |
101 const float halfHeight = m_fillBoundingBox.height() / 2; | 128 const float halfHeight = m_fillBoundingBox.height() / 2; |
102 | 129 |
103 const FloatPoint fillBoundingBoxCenter = FloatPoint(m_fillBoundingBox.x() +
halfWidth, m_fillBoundingBox.y() + halfHeight); | 130 const FloatPoint fillBoundingBoxCenter = FloatPoint(m_fillBoundingBox.x() +
halfWidth, m_fillBoundingBox.y() + halfHeight); |
104 const float absDeltaX = std::abs(point.x() - fillBoundingBoxCenter.x()); | 131 const float absDeltaX = std::abs(point.x() - fillBoundingBoxCenter.x()); |
105 const float absDeltaY = std::abs(point.y() - fillBoundingBoxCenter.y()); | 132 const float absDeltaY = std::abs(point.y() - fillBoundingBoxCenter.y()); |
106 | 133 |
107 if (!(absDeltaX <= halfWidth + halfStrokeWidth && absDeltaY <= halfHeight +
halfStrokeWidth)) | 134 if (!(absDeltaX <= halfWidth + halfStrokeWidth && absDeltaY <= halfHeight +
halfStrokeWidth)) |
108 return false; | 135 return false; |
109 | 136 |
110 return (halfWidth - halfStrokeWidth <= absDeltaX) | 137 return (halfWidth - halfStrokeWidth <= absDeltaX) |
111 || (halfHeight - halfStrokeWidth <= absDeltaY); | 138 || (halfHeight - halfStrokeWidth <= absDeltaY); |
112 } | 139 } |
113 | 140 |
114 bool LayoutSVGRect::shapeDependentFillContains(const FloatPoint& point, const Wi
ndRule fillRule) const | 141 bool LayoutSVGRect::shapeDependentFillContains(const FloatPoint& point, const Wi
ndRule fillRule) const |
115 { | 142 { |
116 if (m_usePathFallback) | 143 if (m_usePathFallback) |
117 return LayoutSVGShape::shapeDependentFillContains(point, fillRule); | 144 return LayoutSVGShape::shapeDependentFillContains(point, fillRule); |
118 return m_fillBoundingBox.contains(point.x(), point.y()); | 145 return m_fillBoundingBox.contains(point.x(), point.y()); |
119 } | 146 } |
120 | 147 |
121 // Returns true if the stroke is continuous and definitely uses miter joins. | 148 // Returns true if the stroke is continuous and definitely uses miter joins. |
122 bool LayoutSVGRect::definitelyHasSimpleStroke() const | 149 bool LayoutSVGRect::definitelyHasSimpleStroke(const SVGComputedStyle& svgStyle)
const |
123 { | 150 { |
124 const SVGComputedStyle& svgStyle = style()->svgStyle(); | |
125 | |
126 // The four angles of a rect are 90 degrees. Using the formula at: | 151 // The four angles of a rect are 90 degrees. Using the formula at: |
127 // http://www.w3.org/TR/SVG/painting.html#StrokeMiterlimitProperty | 152 // http://www.w3.org/TR/SVG/painting.html#StrokeMiterlimitProperty |
128 // when the join style of the rect is "miter", the ratio of the miterLength | 153 // when the join style of the rect is "miter", the ratio of the miterLength |
129 // to the stroke-width is found to be | 154 // to the stroke-width is found to be |
130 // miterLength / stroke-width = 1 / sin(45 degrees) | 155 // miterLength / stroke-width = 1 / sin(45 degrees) |
131 // = 1 / (1 / sqrt(2)) | 156 // = 1 / (1 / sqrt(2)) |
132 // = sqrt(2) | 157 // = sqrt(2) |
133 // = 1.414213562373095... | 158 // = 1.414213562373095... |
134 // When sqrt(2) exceeds the miterlimit, then the join style switches to | 159 // When sqrt(2) exceeds the miterlimit, then the join style switches to |
135 // "bevel". When the miterlimit is greater than or equal to sqrt(2) then | 160 // "bevel". When the miterlimit is greater than or equal to sqrt(2) then |
136 // the join style remains "miter". | 161 // the join style remains "miter". |
137 // | 162 // |
138 // An approximation of sqrt(2) is used here because at certain precise | 163 // An approximation of sqrt(2) is used here because at certain precise |
139 // miterlimits, the join style used might not be correct (e.g. a miterlimit | 164 // miterlimits, the join style used might not be correct (e.g. a miterlimit |
140 // of 1.4142135 should result in bevel joins, but may be drawn using miter | 165 // of 1.4142135 should result in bevel joins, but may be drawn using miter |
141 // joins). | 166 // joins). |
142 return svgStyle.strokeDashArray()->isEmpty() | 167 return svgStyle.strokeDashArray()->isEmpty() |
143 && svgStyle.joinStyle() == MiterJoin | 168 && svgStyle.joinStyle() == MiterJoin |
144 && svgStyle.strokeMiterLimit() >= 1.5; | 169 && svgStyle.strokeMiterLimit() >= 1.5; |
145 } | 170 } |
146 | 171 |
147 } | 172 } |
OLD | NEW |