Support FrameSpeedFactor for AnimatedImageVisual and AnimatedVectorImageVisual 47/316847/4
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 29 Aug 2024 08:01:33 +0000 (17:01 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 30 Aug 2024 01:15:57 +0000 (10:15 +0900)
Let we make factor for visual that control the speed of image frame animation.

By using speed factor, we can control animated vector image / animated image
frame rendering speed.

For now, we only support the value range as [0.01f 100.0f] for platform safety issue.
(Since it is depend on the thread sleep time. And didn't )
Also we don't implement negative speed factor yet.

Netagive speed factor, and zero speed factor support is TODO.

Change-Id: I53b511d9e9271db1463b54212c95150842981d17
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h

index 5aba45a0c6be27e4a5d37d6f431a9efe81ee39fd..26e4a94865deb7867526dd25bbf4f7567414d1ab 100644 (file)
@@ -98,7 +98,8 @@ int UtcDaliAnimatedImageVisualGetPropertyMap01(void)
       .Add(DevelVisual::Property::CORNER_RADIUS_POLICY, Visual::Transform::Policy::ABSOLUTE)
       .Add(DevelVisual::Property::BORDERLINE_WIDTH, 33.3f)
       .Add(DevelVisual::Property::BORDERLINE_COLOR, Color::RED)
-      .Add(DevelVisual::Property::BORDERLINE_OFFSET, 0.3f));
+      .Add(DevelVisual::Property::BORDERLINE_OFFSET, 0.3f)
+      .Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 2.0f));
 
   Property::Map resultMap;
   animatedImageVisual.CreatePropertyMap(resultMap);
@@ -172,6 +173,10 @@ int UtcDaliAnimatedImageVisualGetPropertyMap01(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_EQUALS(value->Get<float>(), 2.0f, TEST_LOCATION);
+
   // Natural size getted as desired size
   Vector2 naturalSize;
   animatedImageVisual.GetNaturalSize(naturalSize);
@@ -230,7 +235,8 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
       .Add("cornerRadiusPolicy", Visual::Transform::Policy::RELATIVE)
       .Add("borderlineWidth", 20.0f)
       .Add("borderlineColor", Vector4())
-      .Add("borderlineOffset", -1.0f));
+      .Add("borderlineOffset", -1.0f)
+      .Add("frameSpeedFactor", 0.5f));
 
   Property::Map resultMap;
   animatedImageVisual.CreatePropertyMap(resultMap);
@@ -326,6 +332,10 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.5f, TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -1049,6 +1059,7 @@ int UtcDaliAnimatedImageVisualAnimatedImage01(void)
     propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 2);
     propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 4);
     propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 20);
+    propertyMap.Insert(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 1.5f);
 
     VisualFactory factory = VisualFactory::Get();
     Visual::Base  visual  = factory.CreateVisual(propertyMap);
@@ -1301,6 +1312,7 @@ int UtcDaliAnimatedImageVisualMultiImage01(void)
     propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 4);
     propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 8);
     propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 100);
+    propertyMap.Insert(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 1.5f);
 
     VisualFactory factory = VisualFactory::Get();
     Visual::Base  visual  = factory.CreateVisual(propertyMap);
@@ -2052,7 +2064,7 @@ int UtcDaliAnimatedImageVisualDesiredSize(void)
   int desiredHeight = 20;
 
   // texture size have to keep it's ratio. So, the size of texture should be 20x20.
-  const int resultWidth = 20;
+  const int resultWidth  = 20;
   const int resultHeight = 20;
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(TEST_GIF_FILE_NAME, ImageDimensions(desiredWidth, desiredHeight));
index e8206b6ee2cdc1b7fefe02a89cf3e24d94bb4ff6..9ee13ac60a48b5280832c3d9d21eb877b870996e 100644 (file)
@@ -249,6 +249,7 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual03(void)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
     .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
     .Add(DevelImageVisual::Property::PLAY_RANGE, playRange)
+    .Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 2.0f)
     .Add(DevelVisual::Property::CORNER_RADIUS, 50.0f)
     .Add(DevelVisual::Property::BORDERLINE_WIDTH, 20.0f)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
@@ -313,6 +314,7 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
     .Add("redrawInScalingDown", false)
     .Add("enableFrameCache", false)
     .Add("notifyAfterRasterization", false)
+    .Add("frameSpeedFactor", 0.5f)
     .Add("cornerRadius", cornerRadius)
     .Add("borderlineWidth", borderlineWidth)
     .Add("borderlineColor", borderlineColor)
@@ -391,6 +393,10 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<bool>() == false);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_CHECK(value->Get<float>() == 0.5f);
+
   value = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
   DALI_TEST_CHECK(value);
   DALI_TEST_EQUALS(value->Get<Vector4>(), Vector4(cornerRadius, cornerRadius, cornerRadius, cornerRadius), TEST_LOCATION);
@@ -1920,6 +1926,119 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
   END_TEST;
 }
 
+int UtcDaliAnimatedVectorImageVisualFrameSpeedFactor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliAnimatedVectorImageVisualFrameSpeedFactor");
+
+  Property::Map propertyMap;
+  propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
+    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
+    .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
+
+  Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New(true);
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+  Vector2 controlSize(20.f, 30.f);
+  actor.SetProperty(Actor::Property::SIZE, controlSize);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  Property::Value* value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 1.0f, TEST_LOCATION); // Check default value is 1.0f
+
+  Property::Map attributes;
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 0.5f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.5f, TEST_LOCATION);
+
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 8.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 8.0f, TEST_LOCATION);
+
+  // TODO : Below logic might be changed in future.
+
+  // Clampled by maximum frame speed factor.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 100.0f + 1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 100.0f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 0.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor 2.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, -1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor 3.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, -100.0f - 1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliAnimatedVectorImageVisualPropertyNotification(void)
 {
   ToolkitTestApplication application;
index 500f86e7d94801cf25db6c655c5648d2e238a5b0..a8c2c7555c825b13d83ca831c4c69ba666b4e369 100644 (file)
@@ -218,7 +218,20 @@ enum Type
    * If this property is true, ImageVisual ignores mDesiredSize.
    * @note Used by the ImageVisual. The default is false.
    */
-  SYNCHRONOUS_SIZING = ORIENTATION_CORRECTION + 18
+  SYNCHRONOUS_SIZING = ORIENTATION_CORRECTION + 18,
+
+  /**
+   * @brief Specifies a speed factor for the animated image frame.
+   * @details Name "frameSpeedFactor", type Property::FLOAT.
+   *
+   * The speed factor is a multiplier of the normal velocity of the animation. Values between [0,1] will
+   * slow down the animation and values above one will speed up the animation.
+   *
+   * @note The range of this value is clamped between [0.01f ~ 100.0f].
+   *       It might be supported out of bound, and negative value in future.
+   * @note It is used in the AnimatedImageVisual and AnimatedVectorImageVisual. The default is 1.0f.
+   */
+  FRAME_SPEED_FACTOR = ORIENTATION_CORRECTION + 19,
 };
 
 } //namespace Property
index 803979ab7d04f97958a150557e5a55c4466fc4ed..6b46d1c324c384eea005e2aa7bcf367ac2193995 100644 (file)
@@ -106,11 +106,23 @@ static constexpr Vector4  FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 static constexpr auto     LOOP_FOREVER = -1;
 static constexpr auto     FIRST_LOOP   = 0u;
 
+constexpr float MINIMUM_FRAME_SPEED_FACTOR(0.01f);
+constexpr float MAXIMUM_FRAME_SPEED_FACTOR(100.0f);
+
 constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
+
+/**
+ * @brief Safety method to calculate interval with speed factor.
+ */
+template<typename T>
+inline uint32_t CalculateInterval(const T interval, const float frameSpeedFactor)
+{
+  return DALI_LIKELY(Dali::Equals(frameSpeedFactor, 1.0f)) ? static_cast<uint32_t>(interval) : static_cast<uint32_t>(static_cast<float>(interval) / (frameSpeedFactor));
+}
 } // namespace
 
 /**
@@ -262,6 +274,7 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image
   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
   mMaskingData(),
   mDesiredSize(desiredSize),
+  mFrameSpeedFactor(1.0f),
   mFrameCount(0),
   mImageSize(),
   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
@@ -418,6 +431,7 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+  map.Insert(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, mFrameSpeedFactor);
 }
 
 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -578,6 +592,10 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
       }
+      else if(keyValue.first == FRAME_SPEED_FACTOR)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, keyValue.second);
+      }
     }
   }
   // Load image immediately if LOAD_POLICY requires it
@@ -666,7 +684,7 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
         mFrameDelay = frameDelay;
         if(DALI_LIKELY(mImageCache))
         {
-          mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
+          mImageCache->SetInterval(CalculateInterval(mFrameDelay, mFrameSpeedFactor));
         }
       }
       break;
@@ -810,6 +828,22 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR:
+    {
+      float frameSpeedFactor = 1.0f;
+      if(value.Get(frameSpeedFactor))
+      {
+        // TODO : Could we remove this limitation?
+        Dali::ClampInPlace(frameSpeedFactor, MINIMUM_FRAME_SPEED_FACTOR, MAXIMUM_FRAME_SPEED_FACTOR);
+
+        if(!Dali::Equals(mFrameSpeedFactor, frameSpeedFactor))
+        {
+          mFrameSpeedFactor = frameSpeedFactor;
+        }
+      }
+      break;
+    }
   }
 }
 
@@ -946,7 +980,7 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t first
   {
     if(mFrameCount > SINGLE_IMAGE_COUNT)
     {
-      mFrameDelayTimer = Timer::New(firstInterval);
+      mFrameDelayTimer = Timer::New(CalculateInterval(firstInterval, mFrameSpeedFactor));
       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
       mFrameDelayTimer.Start();
     }
@@ -1032,7 +1066,7 @@ void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, b
     {
       if(mFrameDelayTimer && interval > 0u)
       {
-        mFrameDelayTimer.SetInterval(interval);
+        mFrameDelayTimer.SetInterval(CalculateInterval(interval, mFrameSpeedFactor));
       }
       mImpl->mRenderer.SetTextures(textureSet);
       CheckMaskTexture();
@@ -1106,7 +1140,7 @@ bool AnimatedImageVisual::DisplayNextFrame()
         mImpl->mRenderer.SetTextures(textureSet);
         CheckMaskTexture();
       }
-      mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
+      mFrameDelayTimer.SetInterval(CalculateInterval(mImageCache->GetFrameInterval(frameIndex), mFrameSpeedFactor));
     }
 
     mCurrentFrameIndex = frameIndex;
index 34863a2187e198c2800be319ea6af69fbe5da82a..9bec65210055479d8c3cb8bd950c7d6058063453 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_ANIMATED_IMAGE_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,9 +20,9 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/actors/actor-devel.h>
-#include <dali/public-api/adaptor-framework/window.h>
 #include <dali/devel-api/adaptor-framework/animated-image-loading.h>
 #include <dali/public-api/adaptor-framework/timer.h>
+#include <dali/public-api/adaptor-framework/window.h>
 #include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/math/vector4.h>
@@ -295,6 +295,7 @@ private:
   Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;
   TextureManager::MaskingDataPointer              mMaskingData;
   Dali::ImageDimensions                           mDesiredSize;
+  float                                           mFrameSpeedFactor;
 
   // Shared variables
   uint32_t        mFrameCount; // Number of frames
index 21c60ff0054dc3488d677d443ee9c8853d34fbd4..8d27cea5a03a1a74e2d849145791996d726d2c93 100644 (file)
@@ -65,6 +65,9 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(LOOPING_MODE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::LoopingMode, AUTO_REVERSE)
 DALI_ENUM_TO_STRING_TABLE_END(LOOPING_MODE)
 
+constexpr float MINIMUM_FRAME_SPEED_FACTOR(0.01f);
+constexpr float MAXIMUM_FRAME_SPEED_FACTOR(100.0f);
+
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
 #endif
@@ -98,6 +101,7 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory
   mPlacementActor(),
   mPlayState(DevelImageVisual::PlayState::STOPPED),
   mEventCallback(nullptr),
+  mFrameSpeedFactor(1.0f),
   mLastSentPlayStateId(0u),
   mLoadFailed(false),
   mRendererAdded(false),
@@ -214,6 +218,7 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
   map.Insert(Toolkit::DevelImageVisual::Property::ENABLE_FRAME_CACHE, mEnableFrameCache);
   map.Insert(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, mNotifyAfterRasterization);
+  map.Insert(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, mFrameSpeedFactor);
 }
 
 void AnimatedVectorImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -282,6 +287,10 @@ void AnimatedVectorImageVisual::DoSetProperties(const Property::Map& propertyMap
       {
         DoSetProperty(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, keyValue.second);
       }
+      else if(keyValue.first == FRAME_SPEED_FACTOR)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, keyValue.second);
+      }
     }
   }
 
@@ -417,6 +426,25 @@ void AnimatedVectorImageVisual::DoSetProperty(Property::Index index, const Prope
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR:
+    {
+      float frameSpeedFactor = 1.0f;
+      if(value.Get(frameSpeedFactor))
+      {
+        // TODO : Could we remove this limitation?
+        Dali::ClampInPlace(frameSpeedFactor, MINIMUM_FRAME_SPEED_FACTOR, MAXIMUM_FRAME_SPEED_FACTOR);
+
+        if(!Dali::Equals(mFrameSpeedFactor, frameSpeedFactor))
+        {
+          mFrameSpeedFactor = frameSpeedFactor;
+
+          mAnimationData.frameSpeedFactor = mFrameSpeedFactor;
+          mAnimationData.resendFlag |= VectorAnimationTask::RESEND_FRAME_SPEED_FACTOR;
+        }
+      }
+      break;
+    }
   }
 }
 
index be59bae42382d37e876eb45f0363ccb19cd396fb..183ea2c5a5e08fdb8afb1b20c61cc8cefb27ffff 100644 (file)
@@ -240,6 +240,7 @@ private:
   WeakHandle<Actor>                  mPlacementActor;
   DevelImageVisual::PlayState::Type  mPlayState;
   CallbackBase*                      mEventCallback; // Not owned
+  float                              mFrameSpeedFactor;
 
   uint32_t mLastSentPlayStateId;
 
index 65c70078848904c108afde8a82dd80ec38ca6be6..855b8b6de3ef700154542647e6e7d767f6d6c6ee 100644 (file)
@@ -64,6 +64,11 @@ uint64_t GetNanoseconds()
 }
 #endif
 
+int64_t CalculateFrameDurationMicroSeconds(const float frameRate, const float frameSpeedFactor)
+{
+  return static_cast<int64_t>(MICROSECONDS_PER_SECOND / static_cast<double>(frameRate * frameSpeedFactor));
+}
+
 } // unnamed namespace
 
 VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
@@ -84,6 +89,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mNextFrameStartTime(),
   mFrameDurationMicroSeconds(MICROSECONDS_PER_SECOND / 60.0f),
   mFrameRate(60.0f),
+  mFrameSpeedFactor(1.0f),
   mCurrentFrame(0),
   mTotalFrame(0),
   mStartFrame(0),
@@ -240,8 +246,9 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
 
   mEndFrame = mTotalFrame - 1;
 
-  mFrameRate                 = mVectorRenderer.GetFrameRate();
-  mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate;
+  mFrameRate = mVectorRenderer.GetFrameRate();
+
+  mFrameDurationMicroSeconds = CalculateFrameDurationMicroSeconds(mFrameRate, mFrameSpeedFactor);
 
   mLoadRequest = false;
   {
@@ -796,31 +803,37 @@ uint32_t VectorAnimationTask::GetStoppedFrame(uint32_t startFrame, uint32_t endF
   return frame;
 }
 
+/// Event & VectorAnimationThread called after Rasterize() finished.
 VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool renderNow)
 {
   // std::chrono::time_point template has second parameter duration which defaults to the std::chrono::steady_clock supported
   // duration. In some C++11 implementations it is a milliseconds duration, so it fails to compile unless mNextFrameStartTime
   // is casted to use the default duration.
-  mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
-  auto current        = std::chrono::steady_clock::now();
-  mDroppedFrames      = 0;
+  auto current = std::chrono::steady_clock::now();
 
   if(renderNow)
   {
     mNextFrameStartTime = current;
+    mDroppedFrames      = 0;
   }
-  else if(mNextFrameStartTime < current)
+  else
   {
-    uint32_t droppedFrames = 0;
+    const auto durationMicroSeconds = std::chrono::microseconds(mFrameDurationMicroSeconds);
 
-    while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)) && droppedFrames < mTotalFrame)
+    mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds);
+    if(mNextFrameStartTime < current)
     {
-      droppedFrames++;
-      mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
-    }
+      uint32_t droppedFrames = 0;
 
-    mNextFrameStartTime = current;
-    mDroppedFrames      = droppedFrames;
+      while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds) && droppedFrames < mTotalFrame)
+      {
+        droppedFrames++;
+        mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds);
+      }
+
+      mNextFrameStartTime = current;
+      mDroppedFrames      = droppedFrames;
+    }
   }
 
   return mNextFrameStartTime;
@@ -882,6 +895,14 @@ void VectorAnimationTask::ApplyAnimationData()
       mNotifyAfterRasterization = animationData.notifyAfterRasterization;
     }
 
+    if(animationData.resendFlag & VectorAnimationTask::RESEND_FRAME_SPEED_FACTOR)
+    {
+      mFrameSpeedFactor = animationData.frameSpeedFactor;
+
+      // Recalculate frame duration with new frame speed factor.
+      mFrameDurationMicroSeconds = CalculateFrameDurationMicroSeconds(mFrameRate, mFrameSpeedFactor);
+    }
+
     if(animationData.resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
     {
       mVectorRenderer.InvalidateBuffer();
index c741be1c7d83b58de9767052ea4b7b6f05de94f1..20f3b7cdafd5949e4f93aa0cca8a449240c5fcf1 100644 (file)
@@ -77,6 +77,7 @@ public:
     RESEND_NEED_RESOURCE_READY        = 1 << 7,
     RESEND_DYNAMIC_PROPERTY           = 1 << 8,
     RESEND_NOTIFY_AFTER_RASTERIZATION = 1 << 9,
+    RESEND_FRAME_SPEED_FACTOR         = 1 << 10,
   };
 
   /**
@@ -96,6 +97,7 @@ public:
       height(0),
       loopCount(-1),
       playStateId(0),
+      frameSpeedFactor(1.0f),
       notifyAfterRasterization(false)
     {
     }
@@ -112,7 +114,9 @@ public:
       height                   = rhs.height;
       loopCount                = rhs.loopCount;
       playStateId              = rhs.playStateId;
+      frameSpeedFactor         = rhs.frameSpeedFactor;
       notifyAfterRasterization = rhs.notifyAfterRasterization;
+
       dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
       return *this;
     }
@@ -128,6 +132,7 @@ public:
     uint32_t                             height;
     int32_t                              loopCount;
     uint32_t                             playStateId;
+    float                                frameSpeedFactor;
     bool                                 notifyAfterRasterization;
   };
 
@@ -406,6 +411,7 @@ private:
   TimePoint                            mNextFrameStartTime;
   int64_t                              mFrameDurationMicroSeconds;
   float                                mFrameRate;
+  float                                mFrameSpeedFactor;
   uint32_t                             mCurrentFrame;
   uint32_t                             mTotalFrame;
   uint32_t                             mStartFrame;
index 53a271b3b904bb7ca5fc86a2d3314fe093e1c032..ad0c4067533ef1d75eb9a62a628fca24ab41344b 100644 (file)
@@ -221,6 +221,7 @@ const char* const ENABLE_BROKEN_IMAGE("enableBrokenImage");
 const char* const ENABLE_FRAME_CACHE("enableFrameCache");
 const char* const NOTIFY_AFTER_RASTERIZATION("notifyAfterRasterization");
 const char* const SYNCHRONOUS_SIZING("synchronousSizing");
+const char* const FRAME_SPEED_FACTOR("frameSpeedFactor");
 
 // Text visual
 const char* const TEXT_PROPERTY("text");
index 2a5a68d02b9e0979ce27e280c2f09123e5e3debb..6aa50b8d8eb871a8a802a0b1bccaacef47d25c5c 100644 (file)
@@ -117,6 +117,7 @@ extern const char* const ENABLE_BROKEN_IMAGE;
 extern const char* const ENABLE_FRAME_CACHE;
 extern const char* const NOTIFY_AFTER_RASTERIZATION;
 extern const char* const SYNCHRONOUS_SIZING;
+extern const char* const FRAME_SPEED_FACTOR;
 
 // Text visual
 extern const char* const TEXT_PROPERTY;