1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla SVG Project code.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #ifndef MOZILLA_SVGPATHDATA_H__
38 : #define MOZILLA_SVGPATHDATA_H__
39 :
40 : #include "nsCOMPtr.h"
41 : #include "nsDebug.h"
42 : #include "nsIContent.h"
43 : #include "nsINode.h"
44 : #include "nsIWeakReferenceUtils.h"
45 : #include "nsSVGElement.h"
46 : #include "nsTArray.h"
47 :
48 : #include <string.h>
49 :
50 : class gfxContext;
51 : class gfxFlattenedPath;
52 : class nsSVGPathDataParserToInternal; // IWYU pragma: keep
53 :
54 : struct gfxMatrix;
55 : struct nsSVGMark;
56 :
57 : namespace mozilla {
58 :
59 : /**
60 : * ATTENTION! WARNING! WATCH OUT!!
61 : *
62 : * Consumers that modify objects of this type absolutely MUST keep the DOM
63 : * wrappers for those lists (if any) in sync!! That's why this class is so
64 : * locked down.
65 : *
66 : * The DOM wrapper class for this class is DOMSVGPathSegList.
67 : *
68 : * This class is not called |class SVGPathSegList| for one very good reason;
69 : * this class does not provide a list of "SVGPathSeg" items, it provides an
70 : * array of floats into which path segments are encoded. See the paragraphs
71 : * that follow for why. Note that the Length() method returns the number of
72 : * floats in our array, not the number of encoded segments, and the index
73 : * operator indexes floats in the array, not segments. If this class were
74 : * called SVGPathSegList the names of these methods would be very misleading.
75 : *
76 : * The reason this class is designed in this way is because there are many
77 : * different types of path segment, each taking a different numbers of
78 : * arguments. We want to store the segments in an nsTArray to avoid individual
79 : * allocations for each item, but the different size of segments means we can't
80 : * have one single segment type for the nsTArray (not without using a space
81 : * wasteful union or something similar). Since the internal code does not need
82 : * to index into the list (the DOM wrapper does, but it handles that itself)
83 : * the obvious solution is to have the items in this class take up variable
84 : * width and have the internal code iterate over these lists rather than index
85 : * into them.
86 : *
87 : * Implementing indexing to segments with O(1) performance would require us to
88 : * allocate and maintain a separate segment index table (keeping that table in
89 : * sync when items are inserted or removed from the list). So long as the
90 : * internal code doesn't require indexing to segments, we can avoid that
91 : * overhead and additional complexity.
92 : *
93 : * Segment encoding: the first float in the encoding of a segment contains the
94 : * segment's type. The segment's type is encoded to/decoded from this float
95 : * using the static methods SVGPathSegUtils::EncodeType(PRUint32)/
96 : * SVGPathSegUtils::DecodeType(float). If the path segment type in question
97 : * takes any arguments then these follow the first float, and are in the same
98 : * order as they are given in a <path> element's 'd' attribute (NOT in the
99 : * order of the createSVGPathSegXxx() methods' arguments from the SVG DOM
100 : * interface SVGPathElement, which are different...grr). Consumers can use
101 : * SVGPathSegUtils::ArgCountForType(type) to determine how many arguments
102 : * there are (if any), and thus where the current encoded segment ends, and
103 : * where the next segment (if any) begins.
104 : */
105 : class SVGPathData
106 : {
107 : friend class SVGAnimatedPathSegList;
108 : friend class DOMSVGPathSegList;
109 : friend class DOMSVGPathSeg;
110 : friend class ::nsSVGPathDataParserToInternal;
111 : // nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
112 : // are responsible for that!
113 :
114 : public:
115 : typedef const float* const_iterator;
116 :
117 0 : SVGPathData(){}
118 0 : ~SVGPathData(){}
119 :
120 : // Only methods that don't make/permit modification to this list are public.
121 : // Only our friend classes can access methods that may change us.
122 :
123 : /// This may return an incomplete string on OOM, but that's acceptable.
124 : void GetValueAsString(nsAString& aValue) const;
125 :
126 0 : bool IsEmpty() const {
127 0 : return mData.IsEmpty();
128 : }
129 :
130 : #ifdef DEBUG
131 : /**
132 : * This method iterates over the encoded segment data and counts the number
133 : * of segments we currently have.
134 : */
135 : PRUint32 CountItems() const;
136 : #endif
137 :
138 : /**
139 : * Returns the number of *floats* in the encoding array, and NOT the number
140 : * of segments encoded in this object. (For that, see CountItems() above.)
141 : */
142 0 : PRUint32 Length() const {
143 0 : return mData.Length();
144 : }
145 :
146 : const float& operator[](PRUint32 aIndex) const {
147 : return mData[aIndex];
148 : }
149 :
150 : // Used by nsSMILCompositor to check if the cached base val is out of date
151 0 : bool operator==(const SVGPathData& rhs) const {
152 : // We use memcmp so that we don't need to worry that the data encoded in
153 : // the first float may have the same bit pattern as a NaN.
154 0 : return mData.Length() == rhs.mData.Length() &&
155 0 : memcmp(mData.Elements(), rhs.mData.Elements(),
156 0 : mData.Length() * sizeof(float)) == 0;
157 : }
158 :
159 : bool SetCapacity(PRUint32 aSize) {
160 : return mData.SetCapacity(aSize);
161 : }
162 :
163 : void Compact() {
164 : mData.Compact();
165 : }
166 :
167 :
168 : float GetPathLength() const;
169 :
170 : PRUint32 GetPathSegAtLength(float aLength) const;
171 :
172 : void GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const;
173 :
174 : /**
175 : * Returns true, except on OOM, in which case returns false.
176 : */
177 : bool GetSegmentLengths(nsTArray<double> *aLengths) const;
178 :
179 : /**
180 : * Returns true, except on OOM, in which case returns false.
181 : */
182 : bool GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aArray) const;
183 :
184 : already_AddRefed<gfxFlattenedPath>
185 : ToFlattenedPath(const gfxMatrix& aMatrix) const;
186 :
187 : void ConstructPath(gfxContext *aCtx) const;
188 :
189 0 : const_iterator begin() const { return mData.Elements(); }
190 0 : const_iterator end() const { return mData.Elements() + mData.Length(); }
191 :
192 : // Access to methods that can modify objects of this type is deliberately
193 : // limited. This is to reduce the chances of someone modifying objects of
194 : // this type without taking the necessary steps to keep DOM wrappers in sync.
195 : // If you need wider access to these methods, consider adding a method to
196 : // SVGAnimatedPathSegList and having that class act as an intermediary so it
197 : // can take care of keeping DOM wrappers in sync.
198 :
199 : protected:
200 : typedef float* iterator;
201 :
202 : /**
203 : * This may fail on OOM if the internal capacity needs to be increased, in
204 : * which case the list will be left unmodified.
205 : */
206 : nsresult CopyFrom(const SVGPathData& rhs);
207 :
208 : float& operator[](PRUint32 aIndex) {
209 : return mData[aIndex];
210 : }
211 :
212 : /**
213 : * This may fail (return false) on OOM if the internal capacity is being
214 : * increased, in which case the list will be left unmodified.
215 : */
216 0 : bool SetLength(PRUint32 aLength) {
217 0 : return mData.SetLength(aLength);
218 : }
219 :
220 : nsresult SetValueFromString(const nsAString& aValue);
221 :
222 0 : void Clear() {
223 0 : mData.Clear();
224 0 : }
225 :
226 : // Our DOM wrappers have direct access to our mData, so they directly
227 : // manipulate it rather than us implementing:
228 : //
229 : // * InsertItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
230 : // * ReplaceItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
231 : // * RemoveItem(PRUint32 aDataIndex);
232 : // * bool AppendItem(PRUint32 aType, const float *aArgs);
233 :
234 : nsresult AppendSeg(PRUint32 aType, ...); // variable number of float args
235 :
236 0 : iterator begin() { return mData.Elements(); }
237 0 : iterator end() { return mData.Elements() + mData.Length(); }
238 :
239 : nsTArray<float> mData;
240 : };
241 :
242 :
243 : /**
244 : * This SVGPathData subclass is for SVGPathSegListSMILType which needs to
245 : * have write access to the lists it works with.
246 : *
247 : * Instances of this class do not have DOM wrappers that need to be kept in
248 : * sync, so we can safely expose any protected base class methods required by
249 : * the SMIL code.
250 : */
251 : class SVGPathDataAndOwner : public SVGPathData
252 0 : {
253 : public:
254 0 : SVGPathDataAndOwner(nsSVGElement *aElement = nsnull)
255 0 : : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement)))
256 0 : {}
257 :
258 0 : void SetElement(nsSVGElement *aElement) {
259 0 : mElement = do_GetWeakReference(static_cast<nsINode*>(aElement));
260 0 : }
261 :
262 0 : nsSVGElement* Element() const {
263 0 : nsCOMPtr<nsIContent> e = do_QueryReferent(mElement);
264 0 : return static_cast<nsSVGElement*>(e.get());
265 : }
266 :
267 0 : nsresult CopyFrom(const SVGPathDataAndOwner& rhs) {
268 0 : mElement = rhs.mElement;
269 0 : return SVGPathData::CopyFrom(rhs);
270 : }
271 :
272 0 : bool IsIdentity() const {
273 0 : if (!mElement) {
274 0 : NS_ABORT_IF_FALSE(IsEmpty(), "target element propagation failure");
275 0 : return true;
276 : }
277 0 : return false;
278 : }
279 :
280 : /**
281 : * Exposed so that SVGPathData baseVals can be copied to
282 : * SVGPathDataAndOwner objects. Note that callers should also call
283 : * SetElement() when using this method!
284 : */
285 : using SVGPathData::CopyFrom;
286 :
287 : // Exposed since SVGPathData objects can be modified.
288 : using SVGPathData::iterator;
289 : using SVGPathData::operator[];
290 : using SVGPathData::SetLength;
291 : using SVGPathData::begin;
292 : using SVGPathData::end;
293 :
294 : private:
295 : // We must keep a weak reference to our element because we may belong to a
296 : // cached baseVal nsSMILValue. See the comments starting at:
297 : // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
298 : // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
299 : nsWeakPtr mElement;
300 : };
301 :
302 : } // namespace mozilla
303 :
304 : #endif // MOZILLA_SVGPATHDATA_H__
|