/*
- * Copyright (c) 2023 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.
END_TEST;
}
+
+int UtcDaliLoadFacialAnimationFailed01(void)
+{
+ TestApplication application;
+
+ AnimationDefinition animDef = LoadFacialAnimation("/nothing.json");
+ DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
+ END_TEST;
+}
+
+int UtcDaliLoadFacialAnimationFailed02(void)
+{
+ TestApplication application;
+
+ AnimationDefinition animDef = LoadFacialAnimationFromBuffer(nullptr, 0);
+ DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
+ END_TEST;
+}
+
+int UtcDaliLoadFacialAnimationFailed03(void)
+{
+ TestApplication application;
+
+ tet_infoline("Parse error for invalid json");
+ uint32_t caseCount = 6;
+ for(uint32_t tc = 0; tc < caseCount; ++tc)
+ {
+ tet_printf("Parse error for json %u\n", tc);
+ std::ostringstream oss;
+ oss << TEST_RESOURCE_DIR << "/test-invalid-facial-animation" << tc << ".json";
+ AnimationDefinition animDef = LoadFacialAnimation(oss.str());
+ DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
+ }
+ END_TEST;
+}
\ No newline at end of file
\r
// EXTERNAL INCLUDES\r
#include <dali/devel-api/animation/key-frames-devel.h>\r
+#include <dali/integration-api/debug.h>\r
#include <sstream>\r
\r
// INTERNAL INCLUDES\r
\r
struct BlendShape\r
{\r
- std::vector<std::vector<float>> mKeys;\r
- std::string_view mNodeName;\r
- uint32_t mNumberOfMorphTarget;\r
- std::string_view mVersion;\r
- std::string_view mFullName;\r
- std::vector<std::string_view> mMorphNames;\r
+ std::vector<std::vector<float>> mKeys{{}};\r
+ std::string_view mNodeName{};\r
+ uint32_t mNumberOfMorphTarget{0u};\r
+ std::string_view mVersion{};\r
+ std::string_view mFullName{};\r
+ std::vector<std::string_view> mMorphNames{};\r
};\r
\r
struct FacialAnimation\r
{\r
- std::string_view mName;\r
- std::vector<BlendShape> mBlendShapes;\r
- std::string_view mVersion;\r
- uint32_t mNumberOfShapes;\r
- std::vector<uint32_t> mTime;\r
- uint32_t mNumberOfFrames;\r
+ std::string_view mName{};\r
+ std::vector<BlendShape> mBlendShapes{{}};\r
+ std::string_view mVersion{};\r
+ uint32_t mNumberOfShapes{0u};\r
+ std::vector<uint32_t> mTime{};\r
+ uint32_t mNumberOfFrames{0u};\r
};\r
\r
std::vector<std::vector<float>> ReadBlendShapeKeys(const json_value_s& j)\r
GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
\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
+ // Check validation of the facial animation data.\r
+ if(facialAnimation.mNumberOfFrames == 0u)\r
+ {\r
+ DALI_LOG_ERROR("The number of frames is zero! return empty animation.\n");\r
+ return animationDefinition;\r
+ }\r
+ if(facialAnimation.mTime.size() != static_cast<size_t>(facialAnimation.mNumberOfFrames))\r
+ {\r
+ DALI_LOG_ERROR("The number of frames does not match the number of time values.\n");\r
+ return animationDefinition;\r
+ }\r
\r
// Calculate the number of animated properties.\r
uint32_t numberOfAnimatedProperties = 0u;\r
for(const auto& blendShape : facialAnimation.mBlendShapes)\r
{\r
numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
+\r
+ if(blendShape.mKeys.size() != static_cast<uint32_t>(facialAnimation.mNumberOfFrames))\r
+ {\r
+ DALI_LOG_ERROR("The number of frames does not match the number of node[%s]'s keys.\n", std::string(blendShape.mNodeName).c_str());\r
+ return animationDefinition;\r
+ }\r
+\r
+ for(const auto& keyframes : blendShape.mKeys)\r
+ {\r
+ if(keyframes.size() != static_cast<size_t>(blendShape.mNumberOfMorphTarget))\r
+ {\r
+ DALI_LOG_ERROR("The number of keys does not match the number of node[%s]'s values.\n", std::string(blendShape.mNodeName).c_str());\r
+ return animationDefinition;\r
+ }\r
+ }\r
}\r
+\r
+ animationDefinition.SetName(facialAnimation.mName.data());\r
+ animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
animationDefinition.ReserveSize(numberOfAnimatedProperties);\r
\r
uint32_t targets = 0u;\r
animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
{\r
- const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
+ const float progress = Dali::EqualsZero(animationDefinition.GetDuration()) ? 0.0f : MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
}\r
// Optimize keyframes, for heuristic!\r
auto jsonData = LoadTextFile(url.c_str(), &failed);\r
if(failed)\r
{\r
- ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
+ DALI_LOG_ERROR("Failed to load file. url : %s\n", url.c_str());\r
+ AnimationDefinition animationDefinition;\r
+ return animationDefinition;\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
+ DALI_LOG_ERROR("Failed to parse json. url : %s\n", url.c_str());\r
+ AnimationDefinition animationDefinition;\r
+ return animationDefinition;\r
}\r
\r
return LoadFacialAnimationInternal(root);\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
+ DALI_LOG_ERROR("Failed to parse json from buffer.\n");\r
+ AnimationDefinition animationDefinition;\r
+ return animationDefinition;\r
}\r
\r
return LoadFacialAnimationInternal(root);\r