/src/mozilla-central/dom/svg/SVGGeometryElement.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "SVGGeometryElement.h" |
8 | | |
9 | | #include "DOMSVGPoint.h" |
10 | | #include "gfxPlatform.h" |
11 | | #include "mozilla/gfx/2D.h" |
12 | | #include "mozilla/dom/SVGLengthBinding.h" |
13 | | #include "nsComputedDOMStyle.h" |
14 | | #include "nsSVGUtils.h" |
15 | | #include "nsSVGLength2.h" |
16 | | #include "SVGContentUtils.h" |
17 | | |
18 | | using namespace mozilla; |
19 | | using namespace mozilla::gfx; |
20 | | using namespace mozilla::dom; |
21 | | |
22 | | nsSVGElement::NumberInfo SVGGeometryElement::sNumberInfo = |
23 | | { &nsGkAtoms::pathLength, 0, false }; |
24 | | |
25 | | //---------------------------------------------------------------------- |
26 | | // Implementation |
27 | | |
28 | | SVGGeometryElement::SVGGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
29 | | : SVGGeometryElementBase(std::move(aNodeInfo)) |
30 | 0 | { |
31 | 0 | } |
32 | | |
33 | | nsSVGElement::NumberAttributesInfo |
34 | | SVGGeometryElement::GetNumberInfo() |
35 | 0 | { |
36 | 0 | return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1); |
37 | 0 | } |
38 | | |
39 | | nsresult |
40 | | SVGGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
41 | | const nsAttrValue* aValue, |
42 | | const nsAttrValue* aOldValue, |
43 | | nsIPrincipal* aSubjectPrincipal, |
44 | | bool aNotify) |
45 | 0 | { |
46 | 0 | if (mCachedPath && |
47 | 0 | aNamespaceID == kNameSpaceID_None && |
48 | 0 | AttributeDefinesGeometry(aName)) { |
49 | 0 | mCachedPath = nullptr; |
50 | 0 | } |
51 | 0 | return SVGGeometryElementBase::AfterSetAttr(aNamespaceID, aName, |
52 | 0 | aValue, aOldValue, |
53 | 0 | aSubjectPrincipal, |
54 | 0 | aNotify); |
55 | 0 | } |
56 | | |
57 | | bool |
58 | | SVGGeometryElement::IsNodeOfType(uint32_t aFlags) const |
59 | 0 | { |
60 | 0 | return !(aFlags & ~eSHAPE); |
61 | 0 | } |
62 | | |
63 | | bool |
64 | | SVGGeometryElement::AttributeDefinesGeometry(const nsAtom *aName) |
65 | 0 | { |
66 | 0 | if (aName == nsGkAtoms::pathLength) { |
67 | 0 | return true; |
68 | 0 | } |
69 | 0 | |
70 | 0 | // Check for nsSVGLength2 attribute |
71 | 0 | LengthAttributesInfo info = GetLengthInfo(); |
72 | 0 | for (uint32_t i = 0; i < info.mLengthCount; i++) { |
73 | 0 | if (aName == *info.mLengthInfo[i].mName) { |
74 | 0 | return true; |
75 | 0 | } |
76 | 0 | } |
77 | 0 |
|
78 | 0 | return false; |
79 | 0 | } |
80 | | |
81 | | bool |
82 | | SVGGeometryElement::GeometryDependsOnCoordCtx() |
83 | 0 | { |
84 | 0 | // Check the nsSVGLength2 attribute |
85 | 0 | LengthAttributesInfo info = const_cast<SVGGeometryElement*>(this)->GetLengthInfo(); |
86 | 0 | for (uint32_t i = 0; i < info.mLengthCount; i++) { |
87 | 0 | if (info.mLengths[i].GetSpecifiedUnitType() == |
88 | 0 | SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE) { |
89 | 0 | return true; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | return false; |
93 | 0 | } |
94 | | |
95 | | bool |
96 | | SVGGeometryElement::IsMarkable() |
97 | 0 | { |
98 | 0 | return false; |
99 | 0 | } |
100 | | |
101 | | void |
102 | | SVGGeometryElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) |
103 | 0 | { |
104 | 0 | } |
105 | | |
106 | | already_AddRefed<Path> |
107 | | SVGGeometryElement::GetOrBuildPath(const DrawTarget* aDrawTarget, |
108 | | FillRule aFillRule) |
109 | 0 | { |
110 | 0 | // We only cache the path if it matches the backend used for screen painting: |
111 | 0 | bool cacheable = aDrawTarget->GetBackendType() == |
112 | 0 | gfxPlatform::GetPlatform()->GetDefaultContentBackend(); |
113 | 0 |
|
114 | 0 | if (cacheable && mCachedPath && mCachedPath->GetFillRule() == aFillRule && |
115 | 0 | aDrawTarget->GetBackendType() == mCachedPath->GetBackendType()) { |
116 | 0 | RefPtr<Path> path(mCachedPath); |
117 | 0 | return path.forget(); |
118 | 0 | } |
119 | 0 | RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(aFillRule); |
120 | 0 | RefPtr<Path> path = BuildPath(builder); |
121 | 0 | if (cacheable) { |
122 | 0 | mCachedPath = path; |
123 | 0 | } |
124 | 0 | return path.forget(); |
125 | 0 | } |
126 | | |
127 | | already_AddRefed<Path> |
128 | | SVGGeometryElement::GetOrBuildPathForMeasuring() |
129 | 0 | { |
130 | 0 | RefPtr<DrawTarget> drawTarget = |
131 | 0 | gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); |
132 | 0 | FillRule fillRule = mCachedPath ? mCachedPath->GetFillRule() : GetFillRule(); |
133 | 0 | return GetOrBuildPath(drawTarget, fillRule); |
134 | 0 | } |
135 | | |
136 | | FillRule |
137 | | SVGGeometryElement::GetFillRule() |
138 | 0 | { |
139 | 0 | FillRule fillRule = FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero |
140 | 0 |
|
141 | 0 | RefPtr<ComputedStyle> computedStyle = |
142 | 0 | nsComputedDOMStyle::GetComputedStyleNoFlush(this, nullptr); |
143 | 0 |
|
144 | 0 | if (computedStyle) { |
145 | 0 | MOZ_ASSERT(computedStyle->StyleSVG()->mFillRule == StyleFillRule::Nonzero || |
146 | 0 | computedStyle->StyleSVG()->mFillRule == StyleFillRule::Evenodd); |
147 | 0 |
|
148 | 0 | if (computedStyle->StyleSVG()->mFillRule == StyleFillRule::Evenodd) { |
149 | 0 | fillRule = FillRule::FILL_EVEN_ODD; |
150 | 0 | } |
151 | 0 | } else { |
152 | 0 | // ReportToConsole |
153 | 0 | NS_WARNING("Couldn't get ComputedStyle for content in GetFillRule"); |
154 | 0 | } |
155 | 0 |
|
156 | 0 | return fillRule; |
157 | 0 | } |
158 | | |
159 | | float |
160 | | SVGGeometryElement::GetTotalLength() |
161 | 0 | { |
162 | 0 | RefPtr<Path> flat = GetOrBuildPathForMeasuring(); |
163 | 0 | return flat ? flat->ComputeLength() : 0.f; |
164 | 0 | } |
165 | | |
166 | | already_AddRefed<nsISVGPoint> |
167 | | SVGGeometryElement::GetPointAtLength(float distance, ErrorResult& rv) |
168 | 0 | { |
169 | 0 | RefPtr<Path> path = GetOrBuildPathForMeasuring(); |
170 | 0 | if (!path) { |
171 | 0 | rv.Throw(NS_ERROR_FAILURE); |
172 | 0 | return nullptr; |
173 | 0 | } |
174 | 0 | |
175 | 0 | nsCOMPtr<nsISVGPoint> point = |
176 | 0 | new DOMSVGPoint(path->ComputePointAtLength( |
177 | 0 | clamped(distance, 0.f, path->ComputeLength()))); |
178 | 0 | return point.forget(); |
179 | 0 | } |
180 | | |
181 | | float |
182 | | SVGGeometryElement::GetPathLengthScale(PathLengthScaleForType aFor) |
183 | 0 | { |
184 | 0 | MOZ_ASSERT(aFor == eForTextPath || aFor == eForStroking, |
185 | 0 | "Unknown enum"); |
186 | 0 | if (mPathLength.IsExplicitlySet()) { |
187 | 0 | float authorsPathLengthEstimate = mPathLength.GetAnimValue(); |
188 | 0 | if (authorsPathLengthEstimate > 0) { |
189 | 0 | RefPtr<Path> path = GetOrBuildPathForMeasuring(); |
190 | 0 | if (!path) { |
191 | 0 | // The path is empty or invalid so its length must be zero and |
192 | 0 | // we know that 0 / authorsPathLengthEstimate = 0. |
193 | 0 | return 0.0; |
194 | 0 | } |
195 | 0 | if (aFor == eForTextPath) { |
196 | 0 | // For textPath, a transform on the referenced path affects the |
197 | 0 | // textPath layout, so when calculating the actual path length |
198 | 0 | // we need to take that into account. |
199 | 0 | gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix()); |
200 | 0 | if (!matrix.IsIdentity()) { |
201 | 0 | RefPtr<PathBuilder> builder = |
202 | 0 | path->TransformedCopyToBuilder(ToMatrix(matrix)); |
203 | 0 | path = builder->Finish(); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | return path->ComputeLength() / authorsPathLengthEstimate; |
207 | 0 | } |
208 | 0 | } |
209 | 0 | return 1.0; |
210 | 0 | } |
211 | | |
212 | | already_AddRefed<SVGAnimatedNumber> |
213 | | SVGGeometryElement::PathLength() |
214 | 0 | { |
215 | 0 | return mPathLength.ToDOMAnimatedNumber(this); |
216 | 0 | } |