SVGLengthListSMILType.cpp (11254B)
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 "SVGLengthListSMILType.h" 8 9 #include <math.h> 10 11 #include <algorithm> 12 13 #include "SVGLengthList.h" 14 #include "mozilla/SMILValue.h" 15 #include "nsMathUtils.h" 16 17 namespace mozilla { 18 19 /*static*/ 20 SVGLengthListSMILType SVGLengthListSMILType::sSingleton; 21 22 //---------------------------------------------------------------------- 23 // nsISMILType implementation 24 25 void SVGLengthListSMILType::InitValue(SMILValue& aValue) const { 26 MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); 27 28 aValue.mU.mPtr = new SVGLengthListAndInfo(); 29 aValue.mType = this; 30 } 31 32 void SVGLengthListSMILType::DestroyValue(SMILValue& aValue) const { 33 MOZ_ASSERT(aValue.mType == this, "Unexpected SMIL value type"); 34 delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr); 35 aValue.mU.mPtr = nullptr; 36 aValue.mType = SMILNullType::Singleton(); 37 } 38 39 nsresult SVGLengthListSMILType::Assign(SMILValue& aDest, 40 const SMILValue& aSrc) const { 41 MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types"); 42 MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL value"); 43 44 const SVGLengthListAndInfo* src = 45 static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr); 46 SVGLengthListAndInfo* dest = 47 static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr); 48 49 return dest->CopyFrom(*src); 50 } 51 52 bool SVGLengthListSMILType::IsEqual(const SMILValue& aLeft, 53 const SMILValue& aRight) const { 54 MOZ_ASSERT(aLeft.mType == aRight.mType, "Incompatible SMIL types"); 55 MOZ_ASSERT(aLeft.mType == this, "Unexpected type for SMIL value"); 56 57 return *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) == 58 *static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr); 59 } 60 61 nsresult SVGLengthListSMILType::Add(SMILValue& aDest, 62 const SMILValue& aValueToAdd, 63 uint32_t aCount) const { 64 MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL type"); 65 MOZ_ASSERT(aValueToAdd.mType == this, "Incompatible SMIL type"); 66 67 SVGLengthListAndInfo& dest = 68 *static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr); 69 const SVGLengthListAndInfo& valueToAdd = 70 *static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr); 71 72 // To understand this code, see the comments documenting our InitValue() 73 // method, and documenting SVGLengthListAndInfo::CanZeroPadList(). 74 75 // Note that *this* method actually may safely zero pad a shorter list 76 // regardless of the value returned by CanZeroPadList() for that list, 77 // just so long as the shorter list is being added *to* the longer list 78 // and *not* vice versa! It's okay in the case of adding a shorter list to a 79 // longer list because during the add operation we'll end up adding the 80 // zeros to actual specified values. It's *not* okay in the case of adding a 81 // longer list to a shorter list because then we end up adding to implicit 82 // zeros when we'd actually need to add to whatever the underlying values 83 // should be, not zeros, and those values are not explicit or otherwise 84 // available. 85 86 if (valueToAdd.IsIdentity()) { // Adding identity value - no-op 87 return NS_OK; 88 } 89 90 if (dest.IsIdentity()) { // Adding *to* an identity value 91 if (!dest.SetLength(valueToAdd.Length())) { 92 return NS_ERROR_OUT_OF_MEMORY; 93 } 94 for (uint32_t i = 0; i < dest.Length(); ++i) { 95 dest[i].SetValueAndUnit(valueToAdd[i].GetValueInCurrentUnits() * aCount, 96 valueToAdd[i].GetUnit()); 97 } 98 dest.SetInfo( 99 valueToAdd.Element(), valueToAdd.Axis(), 100 valueToAdd.CanZeroPadList()); // propagate target element info! 101 return NS_OK; 102 } 103 MOZ_ASSERT(dest.Element() == valueToAdd.Element(), 104 "adding values from different elements...?"); 105 106 // Zero-pad our |dest| list, if necessary. 107 if (dest.Length() < valueToAdd.Length()) { 108 if (!dest.CanZeroPadList()) { 109 // SVGContentUtils::ReportToConsole 110 return NS_ERROR_FAILURE; 111 } 112 113 MOZ_ASSERT(valueToAdd.CanZeroPadList(), 114 "values disagree about attribute's zero-paddibility"); 115 116 uint32_t i = dest.Length(); 117 if (!dest.SetLength(valueToAdd.Length())) { 118 return NS_ERROR_OUT_OF_MEMORY; 119 } 120 for (; i < valueToAdd.Length(); ++i) { 121 dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit()); 122 } 123 } 124 125 for (uint32_t i = 0; i < valueToAdd.Length(); ++i) { 126 float valToAdd; 127 if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) { 128 valToAdd = valueToAdd[i].GetValueInCurrentUnits(); 129 } else { 130 // If units differ, we use the unit of the item in 'dest'. 131 // We leave it to the frame code to check that values are finite. 132 valToAdd = valueToAdd[i].GetValueInSpecifiedUnit( 133 dest[i].GetUnit(), dest.Element(), dest.Axis()); 134 } 135 dest[i].SetValueAndUnit( 136 dest[i].GetValueInCurrentUnits() + valToAdd * aCount, 137 dest[i].GetUnit()); 138 } 139 140 // propagate target element info! 141 dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(), 142 dest.CanZeroPadList() && valueToAdd.CanZeroPadList()); 143 144 return NS_OK; 145 } 146 147 nsresult SVGLengthListSMILType::ComputeDistance(const SMILValue& aFrom, 148 const SMILValue& aTo, 149 double& aDistance) const { 150 MOZ_ASSERT(aFrom.mType == this, "Unexpected SMIL type"); 151 MOZ_ASSERT(aTo.mType == this, "Incompatible SMIL type"); 152 153 const SVGLengthListAndInfo& from = 154 *static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr); 155 const SVGLengthListAndInfo& to = 156 *static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr); 157 158 // To understand this code, see the comments documenting our InitValue() 159 // method, and documenting SVGLengthListAndInfo::CanZeroPadList(). 160 161 NS_ASSERTION((from.CanZeroPadList() == to.CanZeroPadList()) || 162 (from.CanZeroPadList() && from.IsEmpty()) || 163 (to.CanZeroPadList() && to.IsEmpty()), 164 "Only \"zero\" SMILValues from the SMIL engine should " 165 "return true for CanZeroPadList() when the attribute " 166 "being animated can't be zero padded"); 167 168 if ((from.Length() < to.Length() && !from.CanZeroPadList()) || 169 (to.Length() < from.Length() && !to.CanZeroPadList())) { 170 // SVGContentUtils::ReportToConsole 171 return NS_ERROR_FAILURE; 172 } 173 174 // We return the root of the sum of the squares of the deltas between the 175 // user unit values of the lengths at each correspanding index. In the 176 // general case, paced animation is probably not useful, but this strategy at 177 // least does the right thing for paced animation in the face of simple 178 // 'values' lists such as: 179 // 180 // values="100 200 300; 101 201 301; 110 210 310" 181 // 182 // I.e. half way through the simple duration we'll get "105 205 305". 183 184 double total = 0.0; 185 186 uint32_t i = 0; 187 for (; i < from.Length() && i < to.Length(); ++i) { 188 double f = from[i].GetValueInPixels(from.Element(), from.Axis()); 189 double t = to[i].GetValueInPixels(to.Element(), to.Axis()); 190 double delta = t - f; 191 total += delta * delta; 192 } 193 194 // In the case that from.Length() != to.Length(), one of the following loops 195 // will run. (OK since CanZeroPadList()==true for the other list.) 196 197 for (; i < from.Length(); ++i) { 198 double f = from[i].GetValueInPixels(from.Element(), from.Axis()); 199 total += f * f; 200 } 201 for (; i < to.Length(); ++i) { 202 double t = to[i].GetValueInPixels(to.Element(), to.Axis()); 203 total += t * t; 204 } 205 206 float distance = sqrt(total); 207 if (!std::isfinite(distance)) { 208 return NS_ERROR_FAILURE; 209 } 210 aDistance = distance; 211 return NS_OK; 212 } 213 214 nsresult SVGLengthListSMILType::Interpolate(const SMILValue& aStartVal, 215 const SMILValue& aEndVal, 216 double aUnitDistance, 217 SMILValue& aResult) const { 218 MOZ_ASSERT(aStartVal.mType == aEndVal.mType, 219 "Trying to interpolate different types"); 220 MOZ_ASSERT(aStartVal.mType == this, "Unexpected types for interpolation"); 221 MOZ_ASSERT(aResult.mType == this, "Unexpected result type"); 222 223 const SVGLengthListAndInfo& start = 224 *static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr); 225 const SVGLengthListAndInfo& end = 226 *static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr); 227 SVGLengthListAndInfo& result = 228 *static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr); 229 230 // To understand this code, see the comments documenting our InitValue() 231 // method, and documenting SVGLengthListAndInfo::CanZeroPadList(). 232 233 NS_ASSERTION((start.CanZeroPadList() == end.CanZeroPadList()) || 234 (start.CanZeroPadList() && start.IsEmpty()) || 235 (end.CanZeroPadList() && end.IsEmpty()), 236 "Only \"zero\" SMILValues from the SMIL engine should " 237 "return true for CanZeroPadList() when the attribute " 238 "being animated can't be zero padded"); 239 240 if ((start.Length() < end.Length() && !start.CanZeroPadList()) || 241 (end.Length() < start.Length() && !end.CanZeroPadList())) { 242 // SVGContentUtils::ReportToConsole 243 return NS_ERROR_FAILURE; 244 } 245 246 if (!result.SetLength(std::max(start.Length(), end.Length()))) { 247 return NS_ERROR_OUT_OF_MEMORY; 248 } 249 250 // If units differ, we use the unit of the nearest item. 251 // We leave it to the frame code to check that values are finite. 252 bool useEndUnits = (aUnitDistance > 0.5); 253 254 uint32_t i = 0; 255 for (; i < start.Length() && i < end.Length(); ++i) { 256 float s, e; 257 uint8_t unit; 258 if (start[i].GetUnit() == end[i].GetUnit()) { 259 unit = start[i].GetUnit(); 260 s = start[i].GetValueInCurrentUnits(); 261 e = end[i].GetValueInCurrentUnits(); 262 } else if (useEndUnits) { 263 unit = end[i].GetUnit(); 264 s = start[i].GetValueInSpecifiedUnit(unit, end.Element(), end.Axis()); 265 e = end[i].GetValueInCurrentUnits(); 266 } else { 267 unit = start[i].GetUnit(); 268 s = start[i].GetValueInCurrentUnits(); 269 e = end[i].GetValueInSpecifiedUnit(unit, start.Element(), start.Axis()); 270 } 271 result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, unit); 272 } 273 274 // In the case that start.Length() != end.Length(), one of the following 275 // loops will run. (Okay, since CanZeroPadList()==true for the other list.) 276 277 for (; i < start.Length(); ++i) { 278 result[i].SetValueAndUnit( 279 start[i].GetValueInCurrentUnits() - 280 start[i].GetValueInCurrentUnits() * aUnitDistance, 281 start[i].GetUnit()); 282 } 283 for (; i < end.Length(); ++i) { 284 result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance, 285 end[i].GetUnit()); 286 } 287 288 // propagate target element info! 289 result.SetInfo(end.Element(), end.Axis(), 290 start.CanZeroPadList() && end.CanZeroPadList()); 291 292 return NS_OK; 293 } 294 295 } // namespace mozilla