tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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