| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 #include "core/animation/KeyframeEffectModel.h" | 32 #include "core/animation/KeyframeEffectModel.h" |
| 33 | 33 |
| 34 #include "core/animation/TimedItem.h" | 34 #include "core/animation/TimedItem.h" |
| 35 #include "wtf/text/StringHash.h" | 35 #include "wtf/text/StringHash.h" |
| 36 | 36 |
| 37 namespace WebCore { | 37 namespace WebCore { |
| 38 | 38 |
| 39 Keyframe::Keyframe() | 39 bool Keyframe::compareOffsets(const RefPtrWillBeMember<Keyframe>& a, const RefPt
rWillBeMember<Keyframe>& b) |
| 40 : m_offset(nullValue()) | |
| 41 , m_composite(AnimationEffect::CompositeReplace) | |
| 42 , m_easing(LinearTimingFunction::preset()) | |
| 43 { } | |
| 44 | |
| 45 Keyframe::Keyframe(const Keyframe& copyFrom) | |
| 46 : m_offset(copyFrom.m_offset) | |
| 47 , m_composite(copyFrom.m_composite) | |
| 48 , m_easing(copyFrom.m_easing) | |
| 49 { | 40 { |
| 50 ASSERT(m_easing); | 41 return a->offset() < b->offset(); |
| 51 for (PropertyValueMap::const_iterator iter = copyFrom.m_propertyValues.begin
(); iter != copyFrom.m_propertyValues.end(); ++iter) | |
| 52 setPropertyValue(iter->key, iter->value.get()); | |
| 53 } | 42 } |
| 54 | 43 |
| 55 void Keyframe::setEasing(PassRefPtr<TimingFunction> easing) | 44 PropertySet KeyframeEffectModelBase::properties() const |
| 56 { | |
| 57 ASSERT(easing); | |
| 58 m_easing = easing; | |
| 59 } | |
| 60 | |
| 61 void Keyframe::setPropertyValue(CSSPropertyID property, const AnimatableValue* v
alue) | |
| 62 { | |
| 63 m_propertyValues.add(property, const_cast<AnimatableValue*>(value)); | |
| 64 } | |
| 65 | |
| 66 void Keyframe::clearPropertyValue(CSSPropertyID property) | |
| 67 { | |
| 68 m_propertyValues.remove(property); | |
| 69 } | |
| 70 | |
| 71 const AnimatableValue* Keyframe::propertyValue(CSSPropertyID property) const | |
| 72 { | |
| 73 ASSERT(m_propertyValues.contains(property)); | |
| 74 return m_propertyValues.get(property); | |
| 75 } | |
| 76 | |
| 77 PropertySet Keyframe::properties() const | |
| 78 { | |
| 79 // This is not used in time-critical code, so we probably don't need to | |
| 80 // worry about caching this result. | |
| 81 PropertySet properties; | |
| 82 for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter
!= m_propertyValues.end(); ++iter) | |
| 83 properties.add(*iter.keys()); | |
| 84 return properties; | |
| 85 } | |
| 86 | |
| 87 PassRefPtrWillBeRawPtr<Keyframe> Keyframe::cloneWithOffset(double offset) const | |
| 88 { | |
| 89 RefPtrWillBeRawPtr<Keyframe> theClone = clone(); | |
| 90 theClone->setOffset(offset); | |
| 91 return theClone.release(); | |
| 92 } | |
| 93 | |
| 94 void Keyframe::trace(Visitor* visitor) | |
| 95 { | |
| 96 visitor->trace(m_propertyValues); | |
| 97 } | |
| 98 | |
| 99 KeyframeEffectModel::KeyframeEffectModel(const KeyframeVector& keyframes) | |
| 100 : m_keyframes(keyframes) | |
| 101 { | |
| 102 } | |
| 103 | |
| 104 PropertySet KeyframeEffectModel::properties() const | |
| 105 { | 45 { |
| 106 PropertySet result; | 46 PropertySet result; |
| 107 if (!m_keyframes.size()) { | 47 if (!m_keyframes.size()) { |
| 108 return result; | 48 return result; |
| 109 } | 49 } |
| 110 result = m_keyframes[0]->properties(); | 50 result = m_keyframes[0]->properties(); |
| 111 for (size_t i = 1; i < m_keyframes.size(); i++) { | 51 for (size_t i = 1; i < m_keyframes.size(); i++) { |
| 112 PropertySet extras = m_keyframes[i]->properties(); | 52 PropertySet extras = m_keyframes[i]->properties(); |
| 113 for (PropertySet::const_iterator it = extras.begin(); it != extras.end()
; ++it) { | 53 for (PropertySet::const_iterator it = extras.begin(); it != extras.end()
; ++it) { |
| 114 result.add(*it); | 54 result.add(*it); |
| 115 } | 55 } |
| 116 } | 56 } |
| 117 return result; | 57 return result; |
| 118 } | 58 } |
| 119 | 59 |
| 120 PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > Ke
yframeEffectModel::sample(int iteration, double fraction, double iterationDurati
on) const | 60 PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > Ke
yframeEffectModelBase::sample(int iteration, double fraction, double iterationDu
ration) const |
| 121 { | 61 { |
| 122 ASSERT(iteration >= 0); | 62 ASSERT(iteration >= 0); |
| 123 ASSERT(!isNull(fraction)); | 63 ASSERT(!isNull(fraction)); |
| 124 ensureKeyframeGroups(); | 64 ensureKeyframeGroups(); |
| 125 ensureInterpolationEffect(); | 65 ensureInterpolationEffect(); |
| 126 | 66 |
| 127 return m_interpolationEffect->getActiveInterpolations(fraction, iterationDur
ation); | 67 return m_interpolationEffect->getActiveInterpolations(fraction, iterationDur
ation); |
| 128 } | 68 } |
| 129 | 69 |
| 130 KeyframeEffectModel::KeyframeVector KeyframeEffectModel::normalizedKeyframes(con
st KeyframeVector& keyframes) | 70 KeyframeEffectModelBase::KeyframeVector KeyframeEffectModelBase::normalizedKeyfr
ames(const KeyframeVector& keyframes) |
| 131 { | 71 { |
| 132 // keyframes [beginIndex, endIndex) will remain after removing all keyframes
if they are not | 72 // keyframes [beginIndex, endIndex) will remain after removing all keyframes
if they are not |
| 133 // loosely sorted by offset, and after removing keyframes with positional of
fset outide [0, 1]. | 73 // loosely sorted by offset, and after removing keyframes with positional of
fset outide [0, 1]. |
| 134 size_t beginIndex = 0; | 74 size_t beginIndex = 0; |
| 135 size_t endIndex = keyframes.size(); | 75 size_t endIndex = keyframes.size(); |
| 136 | 76 |
| 137 // Becomes the most recent keyframe with an explicit offset. | 77 // Becomes the most recent keyframe with an explicit offset. |
| 138 size_t lastIndex = endIndex; | 78 size_t lastIndex = endIndex; |
| 139 double lastOffset = std::numeric_limits<double>::quiet_NaN(); | 79 double lastOffset = std::numeric_limits<double>::quiet_NaN(); |
| 140 | 80 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 result[lastIndex + j]->setOffset(lastOffset + (offset -
lastOffset) * j / (i - lastIndex)); | 124 result[lastIndex + j]->setOffset(lastOffset + (offset -
lastOffset) * j / (i - lastIndex)); |
| 185 } | 125 } |
| 186 lastIndex = i; | 126 lastIndex = i; |
| 187 lastOffset = offset; | 127 lastOffset = offset; |
| 188 } | 128 } |
| 189 } | 129 } |
| 190 } | 130 } |
| 191 return result; | 131 return result; |
| 192 } | 132 } |
| 193 | 133 |
| 194 void KeyframeEffectModel::ensureKeyframeGroups() const | 134 |
| 135 void KeyframeEffectModelBase::ensureKeyframeGroups() const |
| 195 { | 136 { |
| 196 if (m_keyframeGroups) | 137 if (m_keyframeGroups) |
| 197 return; | 138 return; |
| 198 | 139 |
| 199 m_keyframeGroups = adoptPtrWillBeNoop(new KeyframeGroupMap); | 140 m_keyframeGroups = adoptPtrWillBeNoop(new KeyframeGroupMap); |
| 200 const KeyframeVector keyframes = normalizedKeyframes(getFrames()); | 141 const KeyframeVector keyframes = normalizedKeyframes(getFrames()); |
| 201 for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyfra
meIter != keyframes.end(); ++keyframeIter) { | 142 for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyfra
meIter != keyframes.end(); ++keyframeIter) { |
| 202 const Keyframe* keyframe = keyframeIter->get(); | 143 const Keyframe* keyframe = keyframeIter->get(); |
| 203 PropertySet keyframeProperties = keyframe->properties(); | 144 PropertySet keyframeProperties = keyframe->properties(); |
| 204 for (PropertySet::const_iterator propertyIter = keyframeProperties.begin
(); propertyIter != keyframeProperties.end(); ++propertyIter) { | 145 for (PropertySet::const_iterator propertyIter = keyframeProperties.begin
(); propertyIter != keyframeProperties.end(); ++propertyIter) { |
| 205 CSSPropertyID property = *propertyIter; | 146 CSSPropertyID property = *propertyIter; |
| 206 KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(proper
ty); | 147 KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(proper
ty); |
| 207 PropertySpecificKeyframeGroup* group; | 148 PropertySpecificKeyframeGroup* group; |
| 208 if (groupIter == m_keyframeGroups->end()) | 149 if (groupIter == m_keyframeGroups->end()) |
| 209 group = m_keyframeGroups->add(property, adoptPtrWillBeNoop(new P
ropertySpecificKeyframeGroup)).storedValue->value.get(); | 150 group = m_keyframeGroups->add(property, adoptPtrWillBeNoop(new P
ropertySpecificKeyframeGroup)).storedValue->value.get(); |
| 210 else | 151 else |
| 211 group = groupIter->value.get(); | 152 group = groupIter->value.get(); |
| 212 | 153 |
| 213 ASSERT(keyframe->composite() == AnimationEffect::CompositeReplace); | 154 ASSERT(keyframe->composite() == AnimationEffect::CompositeReplace); |
| 214 group->appendKeyframe(adoptPtrWillBeNoop( | 155 group->appendKeyframe(keyframe->createPropertySpecificKeyframe(prope
rty)); |
| 215 new PropertySpecificKeyframe(keyframe->offset(), keyframe->easin
g(), keyframe->propertyValue(property), keyframe->composite()))); | |
| 216 } | 156 } |
| 217 } | 157 } |
| 218 | 158 |
| 219 // Add synthetic keyframes. | 159 // Add synthetic keyframes. |
| 220 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_
keyframeGroups->end(); ++iter) { | 160 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_
keyframeGroups->end(); ++iter) { |
| 221 iter->value->addSyntheticKeyframeIfRequired(); | 161 iter->value->addSyntheticKeyframeIfRequired(this); |
| 222 iter->value->removeRedundantKeyframes(); | 162 iter->value->removeRedundantKeyframes(); |
| 223 } | 163 } |
| 224 } | 164 } |
| 225 | 165 |
| 226 void KeyframeEffectModel::ensureInterpolationEffect() const | 166 void KeyframeEffectModelBase::ensureInterpolationEffect() const |
| 227 { | 167 { |
| 228 if (m_interpolationEffect) | 168 if (m_interpolationEffect) |
| 229 return; | 169 return; |
| 230 m_interpolationEffect = InterpolationEffect::create(); | 170 m_interpolationEffect = InterpolationEffect::create(); |
| 231 | 171 |
| 232 for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter
!= m_keyframeGroups->end(); ++iter) { | 172 for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter
!= m_keyframeGroups->end(); ++iter) { |
| 233 const PropertySpecificKeyframeVector& keyframes = iter->value->keyframes
(); | 173 const PropertySpecificKeyframeVector& keyframes = iter->value->keyframes
(); |
| 234 ASSERT(keyframes[0]->composite() == AnimationEffect::CompositeReplace); | 174 ASSERT(keyframes[0]->composite() == AnimationEffect::CompositeReplace); |
| 235 const AnimatableValue* start; | |
| 236 const AnimatableValue* end = keyframes[0]->value(); | |
| 237 for (size_t i = 0; i < keyframes.size() - 1; i++) { | 175 for (size_t i = 0; i < keyframes.size() - 1; i++) { |
| 238 ASSERT(keyframes[i + 1]->composite() == AnimationEffect::CompositeRe
place); | 176 ASSERT(keyframes[i + 1]->composite() == AnimationEffect::CompositeRe
place); |
| 239 start = end; | |
| 240 end = keyframes[i + 1]->value(); | |
| 241 double applyFrom = i ? keyframes[i]->offset() : (-std::numeric_limit
s<double>::infinity()); | 177 double applyFrom = i ? keyframes[i]->offset() : (-std::numeric_limit
s<double>::infinity()); |
| 242 double applyTo = i == keyframes.size() - 2 ? std::numeric_limits<dou
ble>::infinity() : keyframes[i + 1]->offset(); | 178 double applyTo = i == keyframes.size() - 2 ? std::numeric_limits<dou
ble>::infinity() : keyframes[i + 1]->offset(); |
| 243 if (applyTo == 1) | 179 if (applyTo == 1) |
| 244 applyTo = std::numeric_limits<double>::infinity(); | 180 applyTo = std::numeric_limits<double>::infinity(); |
| 245 m_interpolationEffect->addInterpolation( | 181 |
| 246 LegacyStyleInterpolation::create( | 182 m_interpolationEffect->addInterpolation(keyframes[i]->createInterpol
ation(iter->key, keyframes[i + 1].get()), |
| 247 AnimatableValue::takeConstRef(start), | |
| 248 AnimatableValue::takeConstRef(end), iter->key), | |
| 249 keyframes[i]->easing(), keyframes[i]->offset(), keyframes[i + 1]
->offset(), applyFrom, applyTo); | 183 keyframes[i]->easing(), keyframes[i]->offset(), keyframes[i + 1]
->offset(), applyFrom, applyTo); |
| 250 } | 184 } |
| 251 } | 185 } |
| 252 } | 186 } |
| 253 | 187 |
| 254 bool KeyframeEffectModel::isReplaceOnly() | 188 bool KeyframeEffectModelBase::isReplaceOnly() |
| 255 { | 189 { |
| 256 ensureKeyframeGroups(); | 190 ensureKeyframeGroups(); |
| 257 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_
keyframeGroups->end(); ++iter) { | 191 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_
keyframeGroups->end(); ++iter) { |
| 258 const PropertySpecificKeyframeVector& keyframeVector = iter->value->keyf
rames(); | 192 const PropertySpecificKeyframeVector& keyframeVector = iter->value->keyf
rames(); |
| 259 for (size_t i = 0; i < keyframeVector.size(); ++i) { | 193 for (size_t i = 0; i < keyframeVector.size(); ++i) { |
| 260 if (keyframeVector[i]->composite() != AnimationEffect::CompositeRepl
ace) | 194 if (keyframeVector[i]->composite() != AnimationEffect::CompositeRepl
ace) |
| 261 return false; | 195 return false; |
| 262 } | 196 } |
| 263 } | 197 } |
| 264 return true; | 198 return true; |
| 265 } | 199 } |
| 266 | 200 |
| 267 void KeyframeEffectModel::trace(Visitor* visitor) | 201 void KeyframeEffectModelBase::trace(Visitor* visitor) |
| 268 { | 202 { |
| 269 visitor->trace(m_keyframes); | 203 visitor->trace(m_keyframes); |
| 270 visitor->trace(m_interpolationEffect); | 204 visitor->trace(m_interpolationEffect); |
| 271 #if ENABLE_OILPAN | 205 #if ENABLE_OILPAN |
| 272 visitor->trace(m_keyframeGroups); | 206 visitor->trace(m_keyframeGroups); |
| 273 #endif | 207 #endif |
| 274 } | 208 } |
| 275 | 209 |
| 276 KeyframeEffectModel::PropertySpecificKeyframe::PropertySpecificKeyframe(double o
ffset, PassRefPtr<TimingFunction> easing, const AnimatableValue* value, Composit
eOperation composite) | 210 Keyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, Pass
RefPtr<TimingFunction> easing, AnimationEffect::CompositeOperation composite) |
| 277 : m_offset(offset) | 211 : m_offset(offset) |
| 278 , m_easing(easing) | 212 , m_easing(easing) |
| 279 , m_composite(composite) | 213 , m_composite(composite) |
| 280 { | 214 { |
| 281 m_value = AnimatableValue::takeConstRef(value); | |
| 282 } | 215 } |
| 283 | 216 |
| 284 KeyframeEffectModel::PropertySpecificKeyframe::PropertySpecificKeyframe(double o
ffset, PassRefPtr<TimingFunction> easing, PassRefPtrWillBeRawPtr<AnimatableValue
> value, CompositeOperation composite) | 217 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::appendKeyframe(Pass
OwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> keyframe) |
| 285 : m_offset(offset) | |
| 286 , m_easing(easing) | |
| 287 , m_value(value) | |
| 288 , m_composite(composite) | |
| 289 { | |
| 290 ASSERT(!isNull(m_offset)); | |
| 291 } | |
| 292 | |
| 293 PassOwnPtrWillBeRawPtr<KeyframeEffectModel::PropertySpecificKeyframe> KeyframeEf
fectModel::PropertySpecificKeyframe::cloneWithOffset(double offset) const | |
| 294 { | |
| 295 return adoptPtrWillBeNoop(new PropertySpecificKeyframe(offset, m_easing, m_v
alue.get(), m_composite)); | |
| 296 } | |
| 297 | |
| 298 void KeyframeEffectModel::PropertySpecificKeyframe::trace(Visitor* visitor) | |
| 299 { | |
| 300 visitor->trace(m_value); | |
| 301 } | |
| 302 | |
| 303 void KeyframeEffectModel::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnP
trWillBeRawPtr<PropertySpecificKeyframe> keyframe) | |
| 304 { | 218 { |
| 305 ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->of
fset()); | 219 ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->of
fset()); |
| 306 m_keyframes.append(keyframe); | 220 m_keyframes.append(keyframe); |
| 307 } | 221 } |
| 308 | 222 |
| 309 void KeyframeEffectModel::PropertySpecificKeyframeGroup::removeRedundantKeyframe
s() | 223 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::removeRedundantKeyf
rames() |
| 310 { | 224 { |
| 311 // As an optimization, removes keyframes in the following categories, as | 225 // As an optimization, removes keyframes in the following categories, as |
| 312 // they will never be used by sample(). | 226 // they will never be used by sample(). |
| 313 // - End keyframes with the same offset as their neighbor | 227 // - End keyframes with the same offset as their neighbor |
| 314 // - Interior keyframes with the same offset as both their neighbors | 228 // - Interior keyframes with the same offset as both their neighbors |
| 315 // Note that synthetic keyframes must be added before this method is | 229 // Note that synthetic keyframes must be added before this method is |
| 316 // called. | 230 // called. |
| 317 ASSERT(m_keyframes.size() >= 2); | 231 ASSERT(m_keyframes.size() >= 2); |
| 318 for (int i = m_keyframes.size() - 1; i >= 0; --i) { | 232 for (int i = m_keyframes.size() - 1; i >= 0; --i) { |
| 319 double offset = m_keyframes[i]->offset(); | 233 double offset = m_keyframes[i]->offset(); |
| 320 bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset(
) == offset; | 234 bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset(
) == offset; |
| 321 bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.siz
e() - 1) || m_keyframes[i + 1]->offset() == offset; | 235 bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.siz
e() - 1) || m_keyframes[i + 1]->offset() == offset; |
| 322 if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor) | 236 if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor) |
| 323 m_keyframes.remove(i); | 237 m_keyframes.remove(i); |
| 324 } | 238 } |
| 325 ASSERT(m_keyframes.size() >= 2); | 239 ASSERT(m_keyframes.size() >= 2); |
| 326 } | 240 } |
| 327 | 241 |
| 328 void KeyframeEffectModel::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfR
equired() | 242 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::addSyntheticKeyfram
eIfRequired(const KeyframeEffectModelBase* context) |
| 329 { | 243 { |
| 330 ASSERT(!m_keyframes.isEmpty()); | 244 ASSERT(!m_keyframes.isEmpty()); |
| 331 if (m_keyframes.first()->offset() != 0.0) | 245 if (m_keyframes.first()->offset() != 0.0) |
| 332 m_keyframes.insert(0, adoptPtrWillBeNoop(new PropertySpecificKeyframe(0,
nullptr, AnimatableValue::neutralValue(), CompositeAdd))); | 246 m_keyframes.insert(0, m_keyframes.first()->neutralKeyframe(0, nullptr)); |
| 333 if (m_keyframes.last()->offset() != 1.0) | 247 if (m_keyframes.last()->offset() != 1.0) |
| 334 appendKeyframe(adoptPtrWillBeNoop(new PropertySpecificKeyframe(1, nullpt
r, AnimatableValue::neutralValue(), CompositeAdd))); | 248 appendKeyframe(m_keyframes.last()->neutralKeyframe(1, nullptr)); |
| 335 } | 249 } |
| 336 | 250 |
| 337 void KeyframeEffectModel::PropertySpecificKeyframeGroup::trace(Visitor* visitor) | 251 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::trace(Visitor* visi
tor) |
| 338 { | 252 { |
| 339 #if ENABLE_OILPAN | 253 #if ENABLE(OILPAN) |
| 340 visitor->trace(m_keyframes); | 254 visitor->trace(m_keyframes); |
| 341 #endif | 255 #endif |
| 342 } | 256 } |
| 343 | 257 |
| 344 } // namespace | 258 } // namespace |
| OLD | NEW |