[Tizen] Load bvh and facial animations from buffer 89/291689/1 accepted/tizen/7.0/unified/20230424.020513
authorhuayong.xu <huayong.xu@samsung.com>
Wed, 1 Mar 2023 10:18:13 +0000 (18:18 +0800)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 20 Apr 2023 08:26:10 +0000 (17:26 +0900)
This is a combination of 2 commits.

Make sure that global variables are initialized lazily in scene3d.

Global variables are initialized when scene3d so is loaded firstly.
This would allocate more memory in some cases.
This patch is to make the variables be initialized lazily.

Load bvh and facial animations from buffer

Let we allow to load bvh and facial animation from raw buffer stream.

Change-Id: I679539ce10e631101571dab95615404c41b9b2f3
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-BvhLoader.cpp
automated-tests/src/dali-scene3d/utc-Dali-FacialAnimation.cpp
dali-scene3d/public-api/loader/bvh-loader.cpp
dali-scene3d/public-api/loader/bvh-loader.h
dali-scene3d/public-api/loader/facial-animation-loader.cpp
dali-scene3d/public-api/loader/facial-animation-loader.h
dali-scene3d/public-api/loader/load-scene-metadata.cpp

index 2315bfd..d642ef4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
  *
  */
 
-#include <dali-test-suite-utils.h>
 #include <dali-scene3d/public-api/loader/bvh-loader.h>
+#include <dali-test-suite-utils.h>
+
+#include <fstream>
 
 using namespace Dali;
 using namespace Dali::Scene3D::Loader;
 
-int UtcDaliLoadBvh(void)
+namespace
 {
-  TestApplication application;
-
-  AnimationDefinition animDef = LoadBvh(TEST_RESOURCE_DIR "/test.bvh", "testBvh");
-
-  DALI_TEST_EQUAL(animDef.GetName(), "testBvh");
-  DALI_TEST_EQUAL(animDef.GetDuration(), 0.3f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "root");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "position");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::VECTOR3);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 0.3f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mNodeName, "root");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mPropertyName, "orientation");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mKeyFrames.GetType(), Property::Type::ROTATION);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mTimePeriod.durationSeconds, 0.3f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mNodeName, "first");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mPropertyName, "position");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mKeyFrames.GetType(), Property::Type::VECTOR3);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mTimePeriod.durationSeconds, 0.3f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mNodeName, "first");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mPropertyName, "orientation");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mKeyFrames.GetType(), Property::Type::ROTATION);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mTimePeriod.durationSeconds, 0.3f);
-
-  Actor root = Actor::New();
-  root.SetProperty(Actor::Property::NAME, "root");
-
-  Actor first = Actor::New();
-  first.SetProperty(Actor::Property::NAME, "first");
-  root.Add(first);
-
-  auto getActor = [&root](const Dali::Scene3D::Loader::AnimatedProperty& property) {
-    return root.FindChildByName(property.mNodeName);
-  };
-
-  Animation animation = animDef.ReAnimate(getActor);
-  DALI_TEST_EQUAL(animation.GetDuration(), animDef.GetDuration());
-
-  application.GetScene().Add(root);
+std::string ReadBufferFromFile(const std::string& url)
+{
+  std::string  rawString;
+  std::fstream fileStream;
 
-  application.SendNotification();
-  application.Render(20);
+  fileStream.open(url, std::ios::in | std::ios::binary);
+  if(!fileStream.is_open())
+  {
+    DALI_LOG_WARNING("stream open failed for: \"%s\", in mode: \"%d\".\n", url.c_str(), static_cast<int>(std::ios::in | std::ios::binary));
+  }
 
-  DALI_TEST_EQUALS(Vector2(0, 0), root.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
-  DALI_TEST_EQUALS(Vector2(0, 0), first.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
-  Vector3 rootWorldPositionBefore = root.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
-  Vector3 firstWorldPositionBefore = first.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+  // get length of file:
+  fileStream.seekg(0, std::ios::end);
+  auto length = fileStream.tellg();
+  fileStream.seekg(0, std::ios::beg);
 
-  animation.Play();
+  rawString.resize(length);
+  fileStream.read(rawString.data(), length);
 
-  application.SendNotification();
-  application.Render(1000);
+  fileStream.close();
 
-  DALI_TEST_EQUALS(Vector2(0, 10), root.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
-  DALI_TEST_EQUALS(Vector2(10, 0), first.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
+  return rawString;
+}
+} // namespace
 
-  Vector3 rootWorldPositionAfter = root.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
-  Vector3 firstWorldPositionAfter = first.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+int UtcDaliLoadBvh(void)
+{
+  TestApplication application;
 
-  DALI_TEST_EQUALS(Vector3(0, 10, 0), rootWorldPositionAfter - rootWorldPositionBefore, TEST_LOCATION);
-  DALI_TEST_EQUALS(Vector3(10, 10, 0), firstWorldPositionAfter - firstWorldPositionBefore, TEST_LOCATION);
+  for(uint32_t tc = 0; tc < 2; ++tc)
+  {
+    AnimationDefinition animDef;
+    tet_printf("UtcDaliLoadBvh testcase %u\n", tc);
+    switch(tc)
+    {
+      case 0: // Load bvh from url
+      {
+        animDef = LoadBvh(TEST_RESOURCE_DIR "/test.bvh", "testBvh");
+        break;
+      }
+      case 1: // Load bvh from buffer stream.
+      {
+        std::string rawString = ReadBufferFromFile(TEST_RESOURCE_DIR "/test.bvh");
+        animDef               = LoadBvhFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()), "testBvh");
+        break;
+      }
+    }
+
+    DALI_TEST_EQUAL(animDef.GetName(), "testBvh");
+    DALI_TEST_EQUAL(animDef.GetDuration(), 0.3f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "root");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "position");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::VECTOR3);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 0.3f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mNodeName, "root");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mPropertyName, "orientation");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mKeyFrames.GetType(), Property::Type::ROTATION);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mTimePeriod.durationSeconds, 0.3f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mNodeName, "first");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mPropertyName, "position");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mKeyFrames.GetType(), Property::Type::VECTOR3);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mTimePeriod.durationSeconds, 0.3f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mNodeName, "first");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mPropertyName, "orientation");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mKeyFrames.GetType(), Property::Type::ROTATION);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mTimePeriod.durationSeconds, 0.3f);
+
+    Actor root = Actor::New();
+    root.SetProperty(Actor::Property::NAME, "root");
+
+    Actor first = Actor::New();
+    first.SetProperty(Actor::Property::NAME, "first");
+    root.Add(first);
+
+    auto getActor = [&root](const Dali::Scene3D::Loader::AnimatedProperty& property) {
+      return root.FindChildByName(property.mNodeName);
+    };
+
+    Animation animation = animDef.ReAnimate(getActor);
+    DALI_TEST_EQUAL(animation.GetDuration(), animDef.GetDuration());
+
+    application.GetScene().Add(root);
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS(Vector2(0, 0), root.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
+    DALI_TEST_EQUALS(Vector2(0, 0), first.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
+    Vector3 rootWorldPositionBefore  = root.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+    Vector3 firstWorldPositionBefore = first.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+
+    animation.Play();
+
+    application.SendNotification();
+    application.Render(1000);
+
+    DALI_TEST_EQUALS(Vector2(0, 10), root.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
+    DALI_TEST_EQUALS(Vector2(10, 0), first.GetProperty<Vector2>(Actor::Property::POSITION), TEST_LOCATION);
+
+    Vector3 rootWorldPositionAfter  = root.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+    Vector3 firstWorldPositionAfter = first.GetProperty<Vector3>(Actor::Property::WORLD_POSITION);
+
+    DALI_TEST_EQUALS(Vector3(0, 10, 0), rootWorldPositionAfter - rootWorldPositionBefore, TEST_LOCATION);
+    DALI_TEST_EQUALS(Vector3(10, 10, 0), firstWorldPositionAfter - firstWorldPositionBefore, TEST_LOCATION);
+  }
 
   END_TEST;
 }
 
-
-
-int UtcDaliLoadBvhFailed(void)
+int UtcDaliLoadBvhFailed01(void)
 {
   TestApplication application;
 
@@ -101,3 +146,12 @@ int UtcDaliLoadBvhFailed(void)
   DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   END_TEST;
 }
+
+int UtcDaliLoadBvhFailed02(void)
+{
+  TestApplication application;
+
+  AnimationDefinition animDef = LoadBvhFromBuffer(nullptr, 0, "testBvh");
+  DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
+  END_TEST;
+}
\ No newline at end of file
index 100cd72..c130b2e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
  *
  */
 
-#include <dali-test-suite-utils.h>
 #include <dali-scene3d/public-api/loader/animation-definition.h>
 #include <dali-scene3d/public-api/loader/facial-animation-loader.h>
+#include <dali-test-suite-utils.h>
+
+#include <fstream>
 
 using namespace Dali;
 using namespace Dali::Scene3D::Loader;
 
+namespace
+{
+std::string ReadBufferFromFile(const std::string& url)
+{
+  std::string  rawString;
+  std::fstream fileStream;
+
+  fileStream.open(url, std::ios::in | std::ios::binary);
+  if(!fileStream.is_open())
+  {
+    DALI_LOG_WARNING("stream open failed for: \"%s\", in mode: \"%d\".\n", url.c_str(), static_cast<int>(std::ios::in | std::ios::binary));
+  }
+
+  // get length of file:
+  fileStream.seekg(0, std::ios::end);
+  auto length = fileStream.tellg();
+  fileStream.seekg(0, std::ios::beg);
+
+  rawString.resize(length);
+  fileStream.read(rawString.data(), length);
+
+  fileStream.close();
+
+  return rawString;
+}
+} // namespace
+
 int UtcDaliLoadFacialAnimation(void)
 {
   TestApplication app;
 
-  AnimationDefinition animDef = LoadFacialAnimation(TEST_RESOURCE_DIR "/facial-blendshape-animation.json");
-
-  std::string name = animDef.GetName();
-  DALI_TEST_EQUAL(name, "Facial_Blendshape_Animation");
-  DALI_TEST_EQUAL(animDef.GetDuration(), 14.966001f);
-  DALI_TEST_EQUAL(animDef.GetEndAction(), Animation::BAKE);
-  DALI_TEST_EQUAL(animDef.GetSpeedFactor(), 1.0f);
-  DALI_TEST_EQUAL(animDef.GetLoopCount(), 1);
-  DALI_TEST_EQUAL(animDef.GetPropertyCount(), 122);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "GEO_1");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "uBlendShapeWeight[0]");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 14.966001f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mNodeName, "GEO_2");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mPropertyName, "uBlendShapeWeight[1]");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.durationSeconds, 14.966001f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mNodeName, "GEO_3");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mPropertyName, "uBlendShapeWeight[2]");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.durationSeconds, 14.966001f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mNodeName, "GEO_4");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mPropertyName, "uBlendShapeWeight[7]");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.durationSeconds, 14.966001f);
-
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mNodeName, "GEO_5");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mPropertyName, "uBlendShapeWeight[19]");
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.durationSeconds, 14.966001f);
-
-  auto actor = Actor::New();
-  actor.SetProperty(Actor::Property::NAME, "GEO_1");
-
-  char        weightNameBuffer[32];
-  char* const pWeightName = weightNameBuffer + snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s", "uBlendShapeWeight");
-  for(int i = 0; i < 122; i++)
+  for(uint32_t tc = 0; tc < 2; ++tc)
   {
-    snprintf(pWeightName, sizeof(weightNameBuffer) - (pWeightName - weightNameBuffer), "[%d]", i);
-    std::string weightName{weightNameBuffer};
-    actor.RegisterProperty(weightName, 0.0f);
-  }
+    tet_printf("UtcDaliLoadFacialAnimation testcase %u\n", tc);
+    AnimationDefinition animDef;
+    switch(tc)
+    {
+      case 0: // Load bvh from url
+      {
+        animDef = LoadFacialAnimation(TEST_RESOURCE_DIR "/facial-blendshape-animation.json");
+        break;
+      }
+      case 1: // Load bvh from buffer stream.
+      {
+        std::string rawString = ReadBufferFromFile(TEST_RESOURCE_DIR "/facial-blendshape-animation.json");
+        animDef               = LoadFacialAnimationFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()));
+        break;
+      }
+    }
+
+    std::string name = animDef.GetName();
+    DALI_TEST_EQUAL(name, "Facial_Blendshape_Animation");
+    DALI_TEST_EQUAL(animDef.GetDuration(), 14.966001f);
+    DALI_TEST_EQUAL(animDef.GetEndAction(), Animation::BAKE);
+    DALI_TEST_EQUAL(animDef.GetSpeedFactor(), 1.0f);
+    DALI_TEST_EQUAL(animDef.GetLoopCount(), 1);
+    DALI_TEST_EQUAL(animDef.GetPropertyCount(), 122);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "GEO_1");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "uBlendShapeWeight[0]");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::FLOAT);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.delaySeconds, 0.0f);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 14.966001f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mNodeName, "GEO_2");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mPropertyName, "uBlendShapeWeight[1]");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mKeyFrames.GetType(), Property::Type::FLOAT);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.delaySeconds, 0.0f);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.durationSeconds, 14.966001f);
 
-  auto getActor = [&actor](const Dali::Scene3D::Loader::AnimatedProperty& property) {
-    return actor.FindChildByName(property.mNodeName);
-  };
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mNodeName, "GEO_3");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mPropertyName, "uBlendShapeWeight[2]");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mKeyFrames.GetType(), Property::Type::FLOAT);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.delaySeconds, 0.0f);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.durationSeconds, 14.966001f);
 
-  auto anim = animDef.ReAnimate(getActor);
-  DALI_TEST_EQUAL(anim.GetDuration(), animDef.GetDuration());
-  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.GetEndAction());
-  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.GetSpeedFactor());
-  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.GetLoopCount());
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mNodeName, "GEO_4");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mPropertyName, "uBlendShapeWeight[7]");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mKeyFrames.GetType(), Property::Type::FLOAT);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.delaySeconds, 0.0f);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.durationSeconds, 14.966001f);
+
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mNodeName, "GEO_5");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mPropertyName, "uBlendShapeWeight[19]");
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mKeyFrames.GetType(), Property::Type::FLOAT);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.delaySeconds, 0.0f);
+    DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.durationSeconds, 14.966001f);
+
+    auto actor = Actor::New();
+    actor.SetProperty(Actor::Property::NAME, "GEO_1");
+
+    char        weightNameBuffer[32];
+    char* const pWeightName = weightNameBuffer + snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s", "uBlendShapeWeight");
+    for(int i = 0; i < 122; i++)
+    {
+      snprintf(pWeightName, sizeof(weightNameBuffer) - (pWeightName - weightNameBuffer), "[%d]", i);
+      std::string weightName{weightNameBuffer};
+      actor.RegisterProperty(weightName, 0.0f);
+    }
+
+    auto getActor = [&actor](const Dali::Scene3D::Loader::AnimatedProperty& property) {
+      return actor.FindChildByName(property.mNodeName);
+    };
+
+    auto anim = animDef.ReAnimate(getActor);
+    DALI_TEST_EQUAL(anim.GetDuration(), animDef.GetDuration());
+    DALI_TEST_EQUAL(anim.GetEndAction(), animDef.GetEndAction());
+    DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.GetSpeedFactor());
+    DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.GetLoopCount());
+  }
 
   END_TEST;
 }
index 70cb297..3930d33 100644 (file)
@@ -19,6 +19,9 @@
 #include <dali-scene3d/public-api/loader/bvh-loader.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/file-stream.h>
+#include <dali/integration-api/debug.h>
+
 #include <fstream>
 #include <iostream>
 #include <memory>
@@ -84,7 +87,7 @@ void trim(std::string& s)
           s.end());
 }
 
-void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
+void ParseHierarchy(std::istream& file, std::shared_ptr<Joint>& joint)
 {
   std::string line;
   while(std::getline(file, line))
@@ -168,7 +171,7 @@ void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>
   }
 }
 
-void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
+void ParseMotion(std::istream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
 {
   std::vector<std::shared_ptr<Joint>> jointList;
   MakeList(hierarchy, jointList);
@@ -195,7 +198,7 @@ void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_
     }
   }
 
-  while(getline(file, line))
+  while(std::getline(file, line))
   {
     trim(line);
     std::istringstream stream(line);
@@ -242,16 +245,10 @@ void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_
   }
 }
 
-bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
+bool ParseBvh(std::istream& file, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
 {
-  std::ifstream file(path.data());
-  if(!file.is_open())
-  {
-    return false;
-  }
-
   std::string line;
-  while(getline(file, line))
+  while(std::getline(file, line))
   {
     trim(line);
     std::istringstream stream(line);
@@ -260,7 +257,7 @@ bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, s
     if(token == TOKEN_HIERARCHY.data())
     {
       std::string line;
-      while(getline(file, line))
+      while(std::getline(file, line))
       {
         trim(line);
         std::istringstream stream(line);
@@ -289,7 +286,7 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
 
   animationDefinition.SetName(animationName);
   animationDefinition.SetDuration(frameTime * (frameCount - 1));
-  float keyFrameInterval        = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
+  float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
 
   std::vector<std::shared_ptr<Joint>> jointList;
   MakeList(hierarchy, jointList);
@@ -300,14 +297,14 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
     for(uint32_t i = 0; i < jointList.size(); ++i)
     {
       AnimatedProperty translationProperty;
-      translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
-      translationProperty.mNodeName         = jointList[i]->name;
-      translationProperty.mPropertyName     = PROPERTY_NAME_POSITION.data();
+      translationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
+      translationProperty.mNodeName     = jointList[i]->name;
+      translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
 
       AnimatedProperty rotationProperty;
-      rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
-      rotationProperty.mNodeName         = jointList[i]->name;
-      rotationProperty.mPropertyName     = PROPERTY_NAME_ORIENTATION.data();
+      rotationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
+      rotationProperty.mNodeName     = jointList[i]->name;
+      rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
 
       translationProperty.mKeyFrames = Dali::KeyFrames::New();
       rotationProperty.mKeyFrames    = Dali::KeyFrames::New();
@@ -323,14 +320,14 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
 
   return animationDefinition;
 }
-} // namespace
 
-AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
+AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& animationName, const Vector3& scale)
 {
   uint32_t               frameCount = 0;
   float                  frameTime  = 0.0f;
   std::shared_ptr<Joint> rootJoint(new Joint);
-  if(!ParseBvh(path, frameCount, frameTime, rootJoint))
+
+  if(!ParseBvh(stream, frameCount, frameTime, rootJoint))
   {
     AnimationDefinition animationDefinition;
     return animationDefinition;
@@ -338,4 +335,42 @@ AnimationDefinition LoadBvh(const std::string& path, const std::string& animatio
 
   return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
 }
+} // namespace
+
+AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
+{
+  Dali::FileStream fileStream(path);
+  std::iostream&   stream = fileStream.GetStream();
+
+  if(stream.fail())
+  {
+    DALI_LOG_ERROR("Fail to load bvh file : %s\n", path.c_str());
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  return LoadBvhInternal(stream, animationName, scale);
+}
+
+AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, const Vector3& scale)
+{
+  if(rawBuffer == nullptr || rawBufferLength == 0)
+  {
+    DALI_LOG_ERROR("Fail to load bvh buffer : buffer is empty!\n");
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  Dali::FileStream fileStream(const_cast<uint8_t*>(rawBuffer), static_cast<size_t>(static_cast<uint32_t>(rawBufferLength)));
+  std::iostream&   stream = fileStream.GetStream();
+
+  if(stream.fail())
+  {
+    DALI_LOG_ERROR("Fail to load bvh buffer : buffer length : %d\n", rawBufferLength);
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  return LoadBvhInternal(stream, animationName, scale);
+}
 } // namespace Dali::Scene3D::Loader
\ No newline at end of file
index 9830031..950b2ea 100644 (file)
@@ -33,6 +33,17 @@ namespace Dali::Scene3D::Loader
  */
 DALI_SCENE3D_API AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale = Vector3::ONE);
 
+/**
+ * @brief Loads motion capture data from bvh data stream.
+ *
+ * @param[in] rawBuffer The bvh buffer.
+ * @param[in] rawBufferLength The length of buffer.
+ * @param[in] animationName Name of the motion capture animation
+ * @param[in] scale The scale factor to set on the position property manually.
+ * @return AnimationDefinition that includes joint animation information.
+ */
+DALI_SCENE3D_API AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, const Vector3& scale = Vector3::ONE);
+
 } // namespace Dali::Scene3D::Loader
 
 #endif // DALI_SCENE3D_LOADER_BVH_LOADER_H
\ No newline at end of file
index 93920a2..36eff86 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
@@ -71,57 +71,46 @@ std::vector<std::vector<float>> ReadBlendShapeKeys(const json_value_s& j)
   return result;\r
 }\r
 \r
-const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
-                                            .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
-                                            .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
-                                            .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
-                                            .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
-                                            .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
-                                            .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
-\r
-const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
-                                                 .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
-                                                 .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
-                                                 .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
-                                                 .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
-                                                 .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
-                                                 .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
-\r
-} // unnamed namespace\r
-\r
-namespace Scene3D\r
-{\r
-namespace Loader\r
+const js::Reader<BlendShape>& GetBlendShapeReader()\r
 {\r
-AnimationDefinition LoadFacialAnimation(const std::string& url)\r
-{\r
-  bool failed = false;\r
-  auto js     = LoadTextFile(url.c_str(), &failed);\r
-  if(failed)\r
-  {\r
-    ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
-  }\r
+  static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
+                                                     .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
+                                                     .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
+                                                     .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
+                                                     .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
+                                                     .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
+                                                     .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
+  return BLEND_SHAPE_READER;\r
+}\r
 \r
-  json::unique_ptr root(json_parse(js.c_str(), js.size()));\r
-  if(!root)\r
-  {\r
-    ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse " << url << ".";\r
-  }\r
+const js::Reader<FacialAnimation>& GetFacialAnimationReader()\r
+{\r
+  static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
+                                                          .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
+                                                          .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
+                                                          .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
+                                                          .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
+                                                          .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
+                                                          .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
+  return FACIAL_ANIMATION_READER;\r
+}\r
 \r
+Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::unique_ptr& root)\r
+{\r
   static bool setObjectReaders = true;\r
   if(setObjectReaders)\r
   {\r
     // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
-    js::SetObjectReader(BLEND_SHAPE_READER);\r
+    js::SetObjectReader(GetBlendShapeReader());\r
     setObjectReaders = false;\r
   }\r
 \r
   auto& rootObj = js::Cast<json_object_s>(*root);\r
 \r
   FacialAnimation facialAnimation;\r
-  FACIAL_ANIMATION_READER.Read(rootObj, facialAnimation);\r
+  GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
 \r
-  AnimationDefinition animationDefinition;\r
+  Dali::Scene3D::Loader::AnimationDefinition animationDefinition;\r
   animationDefinition.SetName(facialAnimation.mName.data());\r
   animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
 \r
@@ -138,11 +127,11 @@ AnimationDefinition LoadFacialAnimation(const std::string& url)
   {\r
     for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
     {\r
-      AnimatedProperty animatedProperty;\r
+      Dali::Scene3D::Loader::AnimatedProperty animatedProperty;\r
       animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());\r
-      animatedProperty.mNodeName = blendShape.mNodeName;\r
+      animatedProperty.mNodeName   = blendShape.mNodeName;\r
       std::stringstream weightPropertyStream;\r
-      weightPropertyStream << BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
+      weightPropertyStream << Dali::Scene3D::Loader::BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
       animatedProperty.mPropertyName = weightPropertyStream.str();\r
 \r
       animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
@@ -159,7 +148,40 @@ AnimationDefinition LoadFacialAnimation(const std::string& url)
 \r
   return animationDefinition;\r
 }\r
+} // unnamed namespace\r
 \r
+namespace Scene3D\r
+{\r
+namespace Loader\r
+{\r
+AnimationDefinition LoadFacialAnimation(const std::string& url)\r
+{\r
+  bool failed   = false;\r
+  auto jsonData = LoadTextFile(url.c_str(), &failed);\r
+  if(failed)\r
+  {\r
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
+  }\r
+\r
+  json::unique_ptr root(json_parse(jsonData.c_str(), jsonData.size()));\r
+  if(!root)\r
+  {\r
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json " << url << ".";\r
+  }\r
+\r
+  return LoadFacialAnimationInternal(root);\r
+}\r
+\r
+AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength)\r
+{\r
+  json::unique_ptr root(json_parse(rawBuffer, static_cast<size_t>(static_cast<uint32_t>(rawBufferLength))));\r
+  if(!root)\r
+  {\r
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json from buffer.";\r
+  }\r
+\r
+  return LoadFacialAnimationInternal(root);\r
+}\r
 } // namespace Loader\r
 } // namespace Scene3D\r
 } // namespace Dali\r
index b61d0a0..a1658fc 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_SCENE3D_LOADER_FACIAL_ANIMATION_LOADER_H\r
 \r
 /*\r
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
@@ -33,6 +33,18 @@ namespace Dali::Scene3D::Loader
  * @return An animation definition.\r
  */\r
 DALI_SCENE3D_API AnimationDefinition LoadFacialAnimation(const std::string& url);\r
+\r
+/**\r
+ * @brief Loads a facial animation encoded in a json file in the given data stream.\r
+ *\r
+ * Throws a DaliException on error.\r
+ *\r
+ * @param[in] rawBuffer The raw buffer containing the facial animation.\r
+ * @param[in] rawBufferLength The length of raw buffer.\r
+ *\r
+ * @return An animation definition.\r
+ */\r
+DALI_SCENE3D_API AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength);\r
 } // namespace Dali::Scene3D::Loader\r
 \r
 #endif // DALI_SCENE3D_LOADER_FACIAL_ANIMATION_LOADER_H\r
index 76c1056..826e713 100644 (file)
@@ -54,32 +54,43 @@ struct ImageData
   ImageData::SamplingMode::Type mSamplingMode{ImageData::SamplingMode::BOX_THEN_LINEAR}; ///< The sampling mode used to resize the image.\r
 };\r
 \r
-const std::map<std::string_view, ImageData::SamplingMode::Type> SAMPLING_MODE_TYPES{\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, NEAREST),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, LINEAR),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_NEAREST),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_LINEAR),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, NO_FILTER),\r
-  ENUM_STRING_MAPPING(ImageData::SamplingMode, DONT_CARE),\r
-};\r
+const std::map<std::string_view, ImageData::SamplingMode::Type>& GetStringSamplingModeTable()\r
+{\r
+  static const std::map<std::string_view, ImageData::SamplingMode::Type> SAMPLING_MODE_TYPES{\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, NEAREST),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, LINEAR),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_NEAREST),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_LINEAR),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, NO_FILTER),\r
+    ENUM_STRING_MAPPING(ImageData::SamplingMode, DONT_CARE),\r
+  };\r
+  return SAMPLING_MODE_TYPES;\r
+}\r
 \r
-ENUM_TYPE_FROM_STRING(ImageData::SamplingMode, SAMPLING_MODE_TYPES)\r
+ENUM_TYPE_FROM_STRING(ImageData::SamplingMode, GetStringSamplingModeTable())\r
 \r
 struct MetaData\r
 {\r
   std::vector<ImageData> mImageData;\r
 };\r
 \r
-const auto IMAGE_METADATA_READER = std::move(js::Reader<ImageData>()\r
-                                               .Register(*js::MakeProperty("uri", js::Read::String, &ImageData::mImageUri))\r
-                                               .Register(*js::MakeProperty("minWidth", js::Read::Number, &ImageData::mMinWidth))\r
-                                               .Register(*js::MakeProperty("minHeight", js::Read::Number, &ImageData::mMinHeight))\r
-                                               .Register(*js::MakeProperty("samplingMode", gt::ReadStringEnum<ImageData::SamplingMode>, &ImageData::mSamplingMode)));\r
-\r
-const auto METADATA_READER = std::move(js::Reader<MetaData>()\r
-                                         .Register(*js::MakeProperty("images", js::Read::Array<ImageData, js::ObjectReader<ImageData>::Read>, &MetaData::mImageData)));\r
+const js::Reader<ImageData>& GetImageMetaDataReader()\r
+{\r
+  static const auto IMAGE_METADATA_READER = std::move(js::Reader<ImageData>()\r
+                                                         .Register(*js::MakeProperty("uri", js::Read::String, &ImageData::mImageUri))\r
+                                                         .Register(*js::MakeProperty("minWidth", js::Read::Number, &ImageData::mMinWidth))\r
+                                                         .Register(*js::MakeProperty("minHeight", js::Read::Number, &ImageData::mMinHeight))\r
+                                                         .Register(*js::MakeProperty("samplingMode", gt::ReadStringEnum<ImageData::SamplingMode>, &ImageData::mSamplingMode)));\r
+  return IMAGE_METADATA_READER;\r
+}\r
 \r
+const js::Reader<MetaData>& GetMetaDataReader()\r
+{\r
+  static const auto METADATA_READER = std::move(js::Reader<MetaData>()\r
+                                                   .Register(*js::MakeProperty("images", js::Read::Array<ImageData, js::ObjectReader<ImageData>::Read>, &MetaData::mImageData)));\r
+  return METADATA_READER;\r
+}\r
 } // namespace\r
 \r
 void LoadSceneMetadata(const std::string& url, SceneMetadata& sceneMetadata)\r
@@ -101,13 +112,13 @@ void LoadSceneMetadata(const std::string& url, SceneMetadata& sceneMetadata)
   static bool setObjectReaders = true;\r
   if(setObjectReaders)\r
   {\r
-    js::SetObjectReader(IMAGE_METADATA_READER);\r
+    js::SetObjectReader(GetImageMetaDataReader());\r
 \r
     setObjectReaders = false;\r
   }\r
 \r
   MetaData metaData;\r
-  METADATA_READER.Read(rootObj, metaData);\r
+  GetMetaDataReader().Read(rootObj, metaData);\r
 \r
   sceneMetadata.mImageMetadata.reserve(metaData.mImageData.size() + metaData.mImageData.size());\r
   for(auto&& data : metaData.mImageData)\r