Line data Source code
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 "nsComputedDOMStyle.h"
13 : #include "nsSVGUtils.h"
14 : #include "nsSVGLength2.h"
15 : #include "SVGContentUtils.h"
16 :
17 : using namespace mozilla;
18 : using namespace mozilla::gfx;
19 :
20 : nsSVGElement::NumberInfo SVGGeometryElement::sNumberInfo =
21 : { &nsGkAtoms::pathLength, 0, false };
22 :
23 : //----------------------------------------------------------------------
24 : // Implementation
25 :
26 73 : SVGGeometryElement::SVGGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
27 73 : : SVGGeometryElementBase(aNodeInfo)
28 : {
29 73 : }
30 :
31 : nsSVGElement::NumberAttributesInfo
32 138 : SVGGeometryElement::GetNumberInfo()
33 : {
34 138 : return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1);
35 : }
36 :
37 : nsresult
38 184 : SVGGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
39 : const nsAttrValue* aValue,
40 : const nsAttrValue* aOldValue, bool aNotify)
41 : {
42 184 : if (mCachedPath &&
43 184 : aNamespaceID == kNameSpaceID_None &&
44 0 : AttributeDefinesGeometry(aName)) {
45 0 : mCachedPath = nullptr;
46 : }
47 184 : return SVGGeometryElementBase::AfterSetAttr(aNamespaceID, aName,
48 184 : aValue, aOldValue, aNotify);
49 : }
50 :
51 : bool
52 0 : SVGGeometryElement::AttributeDefinesGeometry(const nsIAtom *aName)
53 : {
54 0 : if (aName == nsGkAtoms::pathLength) {
55 0 : return true;
56 : }
57 :
58 : // Check for nsSVGLength2 attribute
59 0 : LengthAttributesInfo info = GetLengthInfo();
60 0 : for (uint32_t i = 0; i < info.mLengthCount; i++) {
61 0 : if (aName == *info.mLengthInfo[i].mName) {
62 0 : return true;
63 : }
64 : }
65 :
66 0 : return false;
67 : }
68 :
69 : bool
70 17 : SVGGeometryElement::GeometryDependsOnCoordCtx()
71 : {
72 : // Check the nsSVGLength2 attribute
73 17 : LengthAttributesInfo info = const_cast<SVGGeometryElement*>(this)->GetLengthInfo();
74 53 : for (uint32_t i = 0; i < info.mLengthCount; i++) {
75 36 : if (info.mLengths[i].GetSpecifiedUnitType() == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) {
76 0 : return true;
77 : }
78 : }
79 17 : return false;
80 : }
81 :
82 : bool
83 36 : SVGGeometryElement::IsMarkable()
84 : {
85 36 : return false;
86 : }
87 :
88 : void
89 0 : SVGGeometryElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
90 : {
91 0 : }
92 :
93 : already_AddRefed<Path>
94 72 : SVGGeometryElement::GetOrBuildPath(const DrawTarget& aDrawTarget,
95 : FillRule aFillRule)
96 : {
97 : // We only cache the path if it matches the backend used for screen painting:
98 72 : bool cacheable = aDrawTarget.GetBackendType() ==
99 72 : gfxPlatform::GetPlatform()->GetDefaultContentBackend();
100 :
101 : // Checking for and returning mCachedPath before checking the pref means
102 : // that the pref is only live on page reload (or app restart for SVG in
103 : // chrome). The benefit is that we avoid causing a CPU memory cache miss by
104 : // looking at the global variable that the pref's stored in.
105 72 : if (cacheable && mCachedPath) {
106 28 : if (aDrawTarget.GetBackendType() == mCachedPath->GetBackendType()) {
107 56 : RefPtr<Path> path(mCachedPath);
108 28 : return path.forget();
109 : }
110 : }
111 88 : RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule);
112 88 : RefPtr<Path> path = BuildPath(builder);
113 44 : if (cacheable && NS_SVGPathCachingEnabled()) {
114 44 : mCachedPath = path;
115 : }
116 44 : return path.forget();
117 : }
118 :
119 : already_AddRefed<Path>
120 0 : SVGGeometryElement::GetOrBuildPathForMeasuring()
121 : {
122 0 : return nullptr;
123 : }
124 :
125 : FillRule
126 0 : SVGGeometryElement::GetFillRule()
127 : {
128 0 : FillRule fillRule = FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero
129 :
130 : RefPtr<nsStyleContext> styleContext =
131 0 : nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, nullptr);
132 :
133 0 : if (styleContext) {
134 0 : MOZ_ASSERT(styleContext->StyleSVG()->mFillRule == StyleFillRule::Nonzero ||
135 : styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd);
136 :
137 0 : if (styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd) {
138 0 : fillRule = FillRule::FILL_EVEN_ODD;
139 : }
140 : } else {
141 : // ReportToConsole
142 0 : NS_WARNING("Couldn't get style context for content in GetFillRule");
143 : }
144 :
145 0 : return fillRule;
146 : }
147 :
148 : float
149 0 : SVGGeometryElement::GetTotalLength()
150 : {
151 0 : RefPtr<Path> flat = GetOrBuildPathForMeasuring();
152 0 : return flat ? flat->ComputeLength() : 0.f;
153 : }
154 :
155 : already_AddRefed<nsISVGPoint>
156 0 : SVGGeometryElement::GetPointAtLength(float distance, ErrorResult& rv)
157 : {
158 0 : RefPtr<Path> path = GetOrBuildPathForMeasuring();
159 0 : if (!path) {
160 0 : rv.Throw(NS_ERROR_FAILURE);
161 0 : return nullptr;
162 : }
163 :
164 0 : float totalLength = path->ComputeLength();
165 0 : if (mPathLength.IsExplicitlySet()) {
166 0 : float pathLength = mPathLength.GetAnimValue();
167 0 : if (pathLength <= 0) {
168 0 : rv.Throw(NS_ERROR_FAILURE);
169 0 : return nullptr;
170 : }
171 0 : distance *= totalLength / pathLength;
172 : }
173 0 : distance = std::max(0.f, distance);
174 0 : distance = std::min(totalLength, distance);
175 :
176 : nsCOMPtr<nsISVGPoint> point =
177 0 : new DOMSVGPoint(path->ComputePointAtLength(distance));
178 0 : return point.forget();
179 : }
180 :
181 : already_AddRefed<SVGAnimatedNumber>
182 0 : SVGGeometryElement::PathLength()
183 : {
184 0 : return mPathLength.ToDOMAnimatedNumber(this);
185 : }
|