SVGAnimatedPathSegList.cpp (11717B)
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 "SVGAnimatedPathSegList.h" 8 9 #include <utility> 10 11 #include "SVGPathSegListSMILType.h" 12 #include "mozilla/SMILValue.h" 13 #include "mozilla/StaticPrefs_dom.h" 14 #include "mozilla/dom/SVGElement.h" 15 #include "mozilla/dom/SVGPathElementBinding.h" 16 #include "mozilla/dom/SVGPathSegment.h" 17 18 using namespace mozilla::dom; 19 20 // See the comments in this file's header! 21 22 namespace mozilla { 23 24 nsresult SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue) { 25 // We don't need to call DidChange* here - we're only called by 26 // SVGElement::ParseAttribute under Element::SetAttr, 27 // which takes care of notifying. 28 return mBaseVal.SetValueFromString(NS_ConvertUTF16toUTF8(aValue)); 29 } 30 31 enum class PositionType { Absolute, Relative }; 32 33 static StyleEndPoint<float> MakeEndPoint(PositionType type, float x, float y) { 34 if (type == PositionType::Absolute) { 35 return StyleEndPoint<float>::ToPosition({x, y}); 36 } else { 37 return StyleEndPoint<float>::ByCoordinate({x, y}); 38 } 39 } 40 41 static StyleCurveControlPoint<float> MakeControlPoint(PositionType type, 42 float x, float y) { 43 if (type == PositionType::Absolute) { 44 return StyleCurveControlPoint<float>::Absolute({x, y}); 45 } else { 46 const auto rcp = 47 StyleRelativeControlPoint<float>{{x, y}, StyleControlReference::Start}; 48 return StyleCurveControlPoint<float>::Relative(rcp); 49 } 50 } 51 52 static StyleAxisEndPoint<float> MakeAxisEndPoint(PositionType type, 53 float end_point) { 54 if (type == PositionType::Absolute) { 55 const auto pos = StyleAxisPosition<float>::LengthPercent(end_point); 56 return StyleAxisEndPoint<float>::ToPosition(pos); 57 } else { 58 return StyleAxisEndPoint<float>::ByCoordinate(end_point); 59 } 60 } 61 62 class MOZ_STACK_CLASS SVGPathSegmentInitWrapper final { 63 public: 64 explicit SVGPathSegmentInitWrapper(const SVGPathSegmentInit& aSVGPathSegment) 65 : mInit(aSVGPathSegment) {} 66 67 bool IsMove() const { 68 return mInit.mType.EqualsLiteral("M") || mInit.mType.EqualsLiteral("m"); 69 } 70 71 bool IsArc() const { 72 return mInit.mType.EqualsLiteral("A") || mInit.mType.EqualsLiteral("a"); 73 } 74 75 bool IsValid() const { 76 if (mInit.mType.Length() != 1) { 77 return false; 78 } 79 auto expectedArgCount = ArgCountForType(mInit.mType.First()); 80 if (expectedArgCount < 0 || 81 mInit.mValues.Length() != uint32_t(expectedArgCount)) { 82 return false; 83 } 84 if (IsArc() && 85 !(IsValidFlag(mInit.mValues[3]) && IsValidFlag(mInit.mValues[4]))) { 86 return false; 87 } 88 return true; 89 } 90 91 StylePathCommand ToStylePathCommand() const { 92 MOZ_ASSERT(IsValid(), "Trying to convert invalid SVGPathSegment"); 93 switch (mInit.mType.First()) { 94 case 'M': 95 return StylePathCommand::Move(MakeEndPoint( 96 PositionType::Absolute, mInit.mValues[0], mInit.mValues[1])); 97 case 'm': 98 return StylePathCommand::Move(MakeEndPoint( 99 PositionType::Relative, mInit.mValues[0], mInit.mValues[1])); 100 case 'L': 101 return StylePathCommand::Line(MakeEndPoint( 102 PositionType::Absolute, mInit.mValues[0], mInit.mValues[1])); 103 case 'l': 104 return StylePathCommand::Line(MakeEndPoint( 105 PositionType::Relative, mInit.mValues[0], mInit.mValues[1])); 106 case 'C': 107 return StylePathCommand::CubicCurve( 108 MakeEndPoint(PositionType::Absolute, mInit.mValues[4], 109 mInit.mValues[5]), 110 MakeControlPoint(PositionType::Absolute, mInit.mValues[0], 111 mInit.mValues[1]), 112 MakeControlPoint(PositionType::Absolute, mInit.mValues[2], 113 mInit.mValues[3])); 114 case 'c': 115 return StylePathCommand::CubicCurve( 116 MakeEndPoint(PositionType::Relative, mInit.mValues[4], 117 mInit.mValues[5]), 118 MakeControlPoint(PositionType::Relative, mInit.mValues[0], 119 mInit.mValues[1]), 120 MakeControlPoint(PositionType::Relative, mInit.mValues[2], 121 mInit.mValues[3])); 122 case 'Q': 123 return StylePathCommand::QuadCurve( 124 MakeEndPoint(PositionType::Absolute, mInit.mValues[2], 125 mInit.mValues[3]), 126 MakeControlPoint(PositionType::Absolute, mInit.mValues[0], 127 mInit.mValues[1])); 128 case 'q': 129 return StylePathCommand::QuadCurve( 130 MakeEndPoint(PositionType::Relative, mInit.mValues[2], 131 mInit.mValues[3]), 132 MakeControlPoint(PositionType::Relative, mInit.mValues[0], 133 mInit.mValues[1])); 134 case 'A': 135 return StylePathCommand::Arc( 136 MakeEndPoint(PositionType::Absolute, mInit.mValues[5], 137 mInit.mValues[6]), 138 StyleArcRadii<float>(mInit.mValues[0], 139 StyleOptional<float>::Some(mInit.mValues[1])), 140 mInit.mValues[4] ? StyleArcSweep::Cw : StyleArcSweep::Ccw, 141 mInit.mValues[3] ? StyleArcSize::Large : StyleArcSize::Small, 142 mInit.mValues[2]); 143 case 'a': 144 return StylePathCommand::Arc( 145 MakeEndPoint(PositionType::Relative, mInit.mValues[5], 146 mInit.mValues[6]), 147 StyleArcRadii<float>(mInit.mValues[0], 148 StyleOptional<float>::Some(mInit.mValues[1])), 149 mInit.mValues[4] ? StyleArcSweep::Cw : StyleArcSweep::Ccw, 150 mInit.mValues[3] ? StyleArcSize::Large : StyleArcSize::Small, 151 mInit.mValues[2]); 152 case 'H': 153 return StylePathCommand::HLine( 154 MakeAxisEndPoint(PositionType::Absolute, mInit.mValues[0])); 155 case 'h': 156 return StylePathCommand::HLine( 157 MakeAxisEndPoint(PositionType::Relative, mInit.mValues[0])); 158 case 'V': 159 return StylePathCommand::VLine( 160 MakeAxisEndPoint(PositionType::Absolute, mInit.mValues[0])); 161 case 'v': 162 return StylePathCommand::VLine( 163 MakeAxisEndPoint(PositionType::Relative, mInit.mValues[0])); 164 case 'S': 165 return StylePathCommand::SmoothCubic( 166 MakeEndPoint(PositionType::Absolute, mInit.mValues[2], 167 mInit.mValues[3]), 168 MakeControlPoint(PositionType::Absolute, mInit.mValues[0], 169 mInit.mValues[1])); 170 case 's': 171 return StylePathCommand::SmoothCubic( 172 MakeEndPoint(PositionType::Relative, mInit.mValues[2], 173 mInit.mValues[3]), 174 MakeControlPoint(PositionType::Relative, mInit.mValues[0], 175 mInit.mValues[1])); 176 case 'T': 177 return StylePathCommand::SmoothQuad(MakeEndPoint( 178 PositionType::Absolute, mInit.mValues[0], mInit.mValues[1])); 179 case 't': 180 return StylePathCommand::SmoothQuad(MakeEndPoint( 181 PositionType::Relative, mInit.mValues[0], mInit.mValues[1])); 182 } 183 return StylePathCommand::Close(); 184 } 185 186 private: 187 static bool IsValidFlag(float aFlag) { 188 return aFlag == 0.0f || aFlag == 1.0f; 189 } 190 191 static int32_t ArgCountForType(char aType) { 192 switch (ToLowerCase(aType)) { 193 case 'z': 194 return 0; 195 case 'm': 196 case 'l': 197 return 2; 198 case 'c': 199 return 6; 200 case 'q': 201 return 4; 202 case 'a': 203 return 7; 204 case 'h': 205 case 'v': 206 return 1; 207 case 's': 208 return 4; 209 case 't': 210 return 2; 211 } 212 return -1; 213 } 214 215 const SVGPathSegmentInit& mInit; 216 }; 217 218 void SVGAnimatedPathSegList::SetBaseValueFromPathSegments( 219 const Sequence<SVGPathSegmentInit>& aValues) { 220 AutoTArray<StylePathCommand, 10> pathData; 221 if (!aValues.IsEmpty() && SVGPathSegmentInitWrapper(aValues[0]).IsMove()) { 222 for (const auto& value : aValues) { 223 SVGPathSegmentInitWrapper seg(value); 224 if (!seg.IsValid()) { 225 break; 226 } 227 pathData.AppendElement(seg.ToStylePathCommand()); 228 } 229 } 230 if (pathData.IsEmpty()) { 231 mBaseVal.Clear(); 232 return; 233 } 234 Servo_CreatePathDataFromCommands(&pathData, &mBaseVal.RawData()); 235 } 236 237 void SVGAnimatedPathSegList::ClearBaseValue() { 238 mBaseVal.Clear(); 239 // Caller notifies 240 } 241 242 nsresult SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue, 243 SVGElement* aElement) { 244 // Note that a new animation may totally change the number of items in the 245 // animVal list, either replacing what was essentially a mirror of the 246 // baseVal list, or else replacing and overriding an existing animation. 247 // Unfortunately it is not possible for us to reliably distinguish between 248 // calls to this method that are setting a new sample for an existing 249 // animation, and calls that are setting the first sample of an animation 250 // that will override an existing animation. 251 252 if (!mAnimVal) { 253 mAnimVal = MakeUnique<SVGPathData>(); 254 } 255 *mAnimVal = aNewAnimValue; 256 aElement->DidAnimatePathSegList(); 257 return NS_OK; 258 } 259 260 void SVGAnimatedPathSegList::ClearAnimValue(SVGElement* aElement) { 261 mAnimVal = nullptr; 262 aElement->DidAnimatePathSegList(); 263 } 264 265 bool SVGAnimatedPathSegList::IsRendered() const { 266 return mAnimVal ? !mAnimVal->IsEmpty() : !mBaseVal.IsEmpty(); 267 } 268 269 UniquePtr<SMILAttr> SVGAnimatedPathSegList::ToSMILAttr(SVGElement* aElement) { 270 return MakeUnique<SMILAnimatedPathSegList>(this, aElement); 271 } 272 273 nsresult SVGAnimatedPathSegList::SMILAnimatedPathSegList::ValueFromString( 274 const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/, 275 SMILValue& aValue, bool& aPreventCachingOfSandwich) const { 276 SMILValue val(SVGPathSegListSMILType::Singleton()); 277 SVGPathDataAndInfo* list = static_cast<SVGPathDataAndInfo*>(val.mU.mPtr); 278 nsresult rv = list->SetValueFromString(NS_ConvertUTF16toUTF8(aStr)); 279 if (NS_SUCCEEDED(rv)) { 280 list->SetElement(mElement); 281 aValue = std::move(val); 282 } 283 return rv; 284 } 285 286 SMILValue SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue() 287 const { 288 // To benefit from Return Value Optimization and avoid copy constructor calls 289 // due to our use of return-by-value, we must return the exact same object 290 // from ALL return points. This function must only return THIS variable: 291 SMILValue tmp(SVGPathSegListSMILType::Singleton()); 292 auto* list = static_cast<SVGPathDataAndInfo*>(tmp.mU.mPtr); 293 list->CopyFrom(mVal->mBaseVal); 294 list->SetElement(mElement); 295 return tmp; 296 } 297 298 nsresult SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue( 299 const SMILValue& aValue) { 300 NS_ASSERTION(aValue.mType == SVGPathSegListSMILType::Singleton(), 301 "Unexpected type to assign animated value"); 302 if (aValue.mType == SVGPathSegListSMILType::Singleton()) { 303 mVal->SetAnimValue(*static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr), 304 mElement); 305 } 306 return NS_OK; 307 } 308 309 void SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue() { 310 if (mVal->mAnimVal) { 311 mVal->ClearAnimValue(mElement); 312 } 313 } 314 315 size_t SVGAnimatedPathSegList::SizeOfExcludingThis( 316 MallocSizeOf aMallocSizeOf) const { 317 size_t total = mBaseVal.SizeOfExcludingThis(aMallocSizeOf); 318 if (mAnimVal) { 319 mAnimVal->SizeOfIncludingThis(aMallocSizeOf); 320 } 321 return total; 322 } 323 324 } // namespace mozilla