skottie_wrapper_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "cc/paint/skottie_wrapper.h"
  5. #include <cstdint>
  6. #include <string>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/containers/span.h"
  10. #include "base/files/file_path.h"
  11. #include "base/memory/scoped_refptr.h"
  12. #include "cc/paint/skottie_mru_resource_provider.h"
  13. #include "cc/paint/skottie_resource_metadata.h"
  14. #include "cc/test/lottie_test_data.h"
  15. #include "cc/test/skia_common.h"
  16. #include "cc/test/test_skcanvas.h"
  17. #include "testing/gmock/include/gmock/gmock.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. #include "third_party/skia/include/core/SkRect.h"
  20. #include "third_party/skia/include/core/SkSize.h"
  21. #include "ui/gfx/geometry/size.h"
  22. namespace cc {
  23. namespace {
  24. using ::testing::_;
  25. using ::testing::Contains;
  26. using ::testing::Eq;
  27. using ::testing::FieldsAre;
  28. using ::testing::FloatNear;
  29. using ::testing::IsEmpty;
  30. using ::testing::IsSupersetOf;
  31. using ::testing::Key;
  32. using ::testing::Mock;
  33. using ::testing::Ne;
  34. using ::testing::NotNull;
  35. using ::testing::Optional;
  36. using ::testing::Pair;
  37. using ::testing::SizeIs;
  38. using ::testing::UnorderedElementsAre;
  39. constexpr float kMarkerEpsilon = .01f;
  40. class MockFrameDataCallback {
  41. public:
  42. MOCK_METHOD(SkottieWrapper::FrameDataFetchResult,
  43. OnAssetLoaded,
  44. (SkottieResourceIdHash asset_id_hash,
  45. float t,
  46. sk_sp<SkImage>& image_out,
  47. SkSamplingOptions& sampling_out));
  48. SkottieWrapper::FrameDataCallback Get() {
  49. return base::BindRepeating(&MockFrameDataCallback::OnAssetLoaded,
  50. base::Unretained(this));
  51. }
  52. };
  53. TEST(SkottieWrapperTest, LoadsValidLottieFileNonSerializable) {
  54. scoped_refptr<SkottieWrapper> skottie =
  55. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  56. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()),
  57. kLottieDataWithoutAssets1.length()));
  58. EXPECT_TRUE(skottie->is_valid());
  59. }
  60. TEST(SkottieWrapperTest, LoadsValidLottieFileSerializable) {
  61. scoped_refptr<SkottieWrapper> skottie =
  62. SkottieWrapper::CreateSerializable(std::vector<uint8_t>(
  63. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()),
  64. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()) +
  65. kLottieDataWithoutAssets1.length()));
  66. EXPECT_TRUE(skottie->is_valid());
  67. }
  68. TEST(SkottieWrapperTest, DetectsInvalidLottieFile) {
  69. static constexpr base::StringPiece kInvalidJson = "this is invalid json";
  70. scoped_refptr<SkottieWrapper> skottie =
  71. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  72. reinterpret_cast<const uint8_t*>(kInvalidJson.data()),
  73. kInvalidJson.length()));
  74. EXPECT_FALSE(skottie->is_valid());
  75. }
  76. TEST(SkottieWrapperTest, IdMatchesForSameLottieFile) {
  77. scoped_refptr<SkottieWrapper> skottie_1 =
  78. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  79. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()),
  80. kLottieDataWithoutAssets1.length()));
  81. scoped_refptr<SkottieWrapper> skottie_2 =
  82. SkottieWrapper::CreateSerializable(std::vector<uint8_t>(
  83. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()),
  84. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()) +
  85. kLottieDataWithoutAssets1.length()));
  86. ASSERT_TRUE(skottie_1->is_valid());
  87. ASSERT_TRUE(skottie_2->is_valid());
  88. EXPECT_THAT(skottie_1->id(), Eq(skottie_2->id()));
  89. }
  90. TEST(SkottieWrapperTest, IdDoesNotMatchForDifferentLottieFile) {
  91. scoped_refptr<SkottieWrapper> skottie_1 =
  92. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  93. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets1.data()),
  94. kLottieDataWithoutAssets1.length()));
  95. scoped_refptr<SkottieWrapper> skottie_2 =
  96. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  97. reinterpret_cast<const uint8_t*>(kLottieDataWithoutAssets2.data()),
  98. kLottieDataWithoutAssets2.length()));
  99. ASSERT_TRUE(skottie_1->is_valid());
  100. ASSERT_TRUE(skottie_2->is_valid());
  101. EXPECT_THAT(skottie_1->id(), Ne(skottie_2->id()));
  102. }
  103. TEST(SkottieWrapperTest, LoadsImageAssetsMetadata) {
  104. scoped_refptr<SkottieWrapper> skottie =
  105. SkottieWrapper::CreateNonSerializable(base::span<const uint8_t>(
  106. reinterpret_cast<const uint8_t*>(kLottieDataWith2Assets.data()),
  107. kLottieDataWith2Assets.length()));
  108. ASSERT_TRUE(skottie->is_valid());
  109. SkottieResourceMetadataMap metadata = skottie->GetImageAssetMetadata();
  110. EXPECT_THAT(
  111. metadata.asset_storage(),
  112. UnorderedElementsAre(
  113. Pair("image_0",
  114. FieldsAre(base::FilePath(FILE_PATH_LITERAL("images/img_0.jpg"))
  115. .NormalizePathSeparators(),
  116. Optional(gfx::Size(kLottieDataWith2AssetsWidth,
  117. kLottieDataWith2AssetsHeight)))),
  118. Pair("image_1",
  119. FieldsAre(base::FilePath(FILE_PATH_LITERAL("images/img_1.jpg"))
  120. .NormalizePathSeparators(),
  121. Optional(gfx::Size(kLottieDataWith2AssetsWidth,
  122. kLottieDataWith2AssetsHeight))))));
  123. }
  124. TEST(SkottieWrapperTest, LoadsCorrectAssetsForDraw) {
  125. scoped_refptr<SkottieWrapper> skottie =
  126. CreateSkottieFromString(kLottieDataWith2Assets);
  127. ASSERT_TRUE(skottie->is_valid());
  128. ::testing::NiceMock<MockCanvas> canvas;
  129. MockFrameDataCallback mock_callback;
  130. EXPECT_CALL(mock_callback,
  131. OnAssetLoaded(HashSkottieResourceId("image_0"), _, _, _));
  132. skottie->Draw(&canvas, /*t=*/0.25, SkRect::MakeWH(500, 500),
  133. mock_callback.Get(), SkottieColorMap(),
  134. SkottieTextPropertyValueMap());
  135. Mock::VerifyAndClearExpectations(&mock_callback);
  136. EXPECT_CALL(mock_callback,
  137. OnAssetLoaded(HashSkottieResourceId("image_1"), _, _, _));
  138. skottie->Draw(&canvas, /*t=*/0.75, SkRect::MakeWH(500, 500),
  139. mock_callback.Get(), SkottieColorMap(),
  140. SkottieTextPropertyValueMap());
  141. Mock::VerifyAndClearExpectations(&mock_callback);
  142. }
  143. TEST(SkottieWrapperTest, AllowsNullFrameDataCallbackForDraw) {
  144. scoped_refptr<SkottieWrapper> skottie =
  145. CreateSkottieFromString(kLottieDataWithoutAssets1);
  146. ASSERT_TRUE(skottie->is_valid());
  147. // Just verify that this call does not cause a CHECK failure.
  148. ::testing::NiceMock<MockCanvas> canvas;
  149. skottie->Draw(&canvas, /*t=*/0, SkRect::MakeWH(500, 500),
  150. SkottieWrapper::FrameDataCallback(), SkottieColorMap(),
  151. SkottieTextPropertyValueMap());
  152. }
  153. TEST(SkottieWrapperTest, LoadsCorrectAssetsForSeek) {
  154. scoped_refptr<SkottieWrapper> skottie =
  155. CreateSkottieFromString(kLottieDataWith2Assets);
  156. ASSERT_TRUE(skottie->is_valid());
  157. ::testing::NiceMock<MockCanvas> canvas;
  158. MockFrameDataCallback mock_callback;
  159. EXPECT_CALL(mock_callback,
  160. OnAssetLoaded(HashSkottieResourceId("image_0"), _, _, _));
  161. skottie->Seek(/*t=*/0.25, mock_callback.Get());
  162. Mock::VerifyAndClearExpectations(&mock_callback);
  163. EXPECT_CALL(mock_callback,
  164. OnAssetLoaded(HashSkottieResourceId("image_1"), _, _, _));
  165. skottie->Seek(/*t=*/0.75, mock_callback.Get());
  166. Mock::VerifyAndClearExpectations(&mock_callback);
  167. }
  168. TEST(SkottieWrapperTest, LoadsColorNodes) {
  169. auto skottie = CreateSkottieFromString(kLottieDataWithoutAssets1);
  170. ASSERT_TRUE(skottie->is_valid());
  171. EXPECT_THAT(
  172. skottie->GetCurrentColorPropertyValues(),
  173. UnorderedElementsAre(
  174. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color1Node),
  175. kLottieDataWithoutAssets1Color1),
  176. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color2Node),
  177. kLottieDataWithoutAssets1Color2)));
  178. }
  179. TEST(SkottieWrapperTest, SetsColorNodesWithDraw) {
  180. auto skottie = CreateSkottieFromString(kLottieDataWithoutAssets1);
  181. ASSERT_TRUE(skottie->is_valid());
  182. ::testing::NiceMock<MockCanvas> canvas;
  183. SkottieColorMap color_map = {
  184. {HashSkottieResourceId(kLottieDataWithoutAssets1Color1Node),
  185. SK_ColorYELLOW},
  186. {HashSkottieResourceId(kLottieDataWithoutAssets1Color2Node),
  187. SK_ColorCYAN}};
  188. skottie->Draw(&canvas, /*t=*/0, SkRect::MakeWH(500, 500),
  189. SkottieWrapper::FrameDataCallback(), color_map,
  190. SkottieTextPropertyValueMap());
  191. EXPECT_THAT(
  192. skottie->GetCurrentColorPropertyValues(),
  193. UnorderedElementsAre(
  194. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color1Node),
  195. SK_ColorYELLOW),
  196. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color2Node),
  197. SK_ColorCYAN)));
  198. color_map = {{HashSkottieResourceId(kLottieDataWithoutAssets1Color2Node),
  199. SK_ColorMAGENTA}};
  200. skottie->Draw(&canvas, /*t=*/0, SkRect::MakeWH(500, 500),
  201. SkottieWrapper::FrameDataCallback(), color_map,
  202. SkottieTextPropertyValueMap());
  203. EXPECT_THAT(
  204. skottie->GetCurrentColorPropertyValues(),
  205. UnorderedElementsAre(
  206. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color1Node),
  207. SK_ColorYELLOW),
  208. Pair(HashSkottieResourceId(kLottieDataWithoutAssets1Color2Node),
  209. SK_ColorMAGENTA)));
  210. }
  211. TEST(SkottieWrapperTest, LoadsTextNodes) {
  212. auto skottie = CreateSkottieFromTestDataDir(kLottieDataWith2TextFileName);
  213. ASSERT_TRUE(skottie->is_valid());
  214. EXPECT_THAT(skottie->GetTextNodeNames(),
  215. UnorderedElementsAre(kLottieDataWith2TextNode1,
  216. kLottieDataWith2TextNode2));
  217. EXPECT_THAT(skottie->GetCurrentTextPropertyValues(),
  218. UnorderedElementsAre(
  219. Pair(HashSkottieResourceId(kLottieDataWith2TextNode1),
  220. SkottieTextPropertyValue(
  221. std::string(kLottieDataWith2TextNode1Text),
  222. kLottieDataWith2TextNode1Box)),
  223. Pair(HashSkottieResourceId(kLottieDataWith2TextNode2),
  224. SkottieTextPropertyValue(
  225. std::string(kLottieDataWith2TextNode2Text),
  226. kLottieDataWith2TextNode2Box))));
  227. }
  228. TEST(SkottieWrapperTest, SetsTextNodesWithDraw) {
  229. auto skottie = CreateSkottieFromTestDataDir(kLottieDataWith2TextFileName);
  230. ASSERT_TRUE(skottie->is_valid());
  231. ::testing::NiceMock<MockCanvas> canvas;
  232. SkottieTextPropertyValueMap text_map = {
  233. {HashSkottieResourceId(kLottieDataWith2TextNode1),
  234. SkottieTextPropertyValue("new-test-text-1", gfx::RectF(1, 1, 1, 1))},
  235. {HashSkottieResourceId(kLottieDataWith2TextNode2),
  236. SkottieTextPropertyValue("new-test-text-2", gfx::RectF(2, 2, 2, 2))}};
  237. skottie->Draw(&canvas, /*t=*/0, SkRect::MakeWH(500, 500),
  238. SkottieWrapper::FrameDataCallback(), SkottieColorMap(),
  239. text_map);
  240. EXPECT_THAT(skottie->GetCurrentTextPropertyValues(),
  241. UnorderedElementsAre(
  242. Pair(HashSkottieResourceId(kLottieDataWith2TextNode1),
  243. SkottieTextPropertyValue("new-test-text-1",
  244. gfx::RectF(1, 1, 1, 1))),
  245. Pair(HashSkottieResourceId(kLottieDataWith2TextNode2),
  246. SkottieTextPropertyValue("new-test-text-2",
  247. gfx::RectF(2, 2, 2, 2)))));
  248. text_map = {
  249. {HashSkottieResourceId(kLottieDataWith2TextNode2),
  250. SkottieTextPropertyValue("new-test-text-2b", gfx::RectF(3, 3, 3, 3))}};
  251. skottie->Draw(&canvas, /*t=*/0.1, SkRect::MakeWH(500, 500),
  252. SkottieWrapper::FrameDataCallback(), SkottieColorMap(),
  253. text_map);
  254. EXPECT_THAT(skottie->GetCurrentTextPropertyValues(),
  255. UnorderedElementsAre(
  256. Pair(HashSkottieResourceId(kLottieDataWith2TextNode1),
  257. SkottieTextPropertyValue("new-test-text-1",
  258. gfx::RectF(1, 1, 1, 1))),
  259. Pair(HashSkottieResourceId(kLottieDataWith2TextNode2),
  260. SkottieTextPropertyValue("new-test-text-2b",
  261. gfx::RectF(3, 3, 3, 3)))));
  262. }
  263. TEST(SkottieWrapperTest, Marker) {
  264. auto skottie = CreateSkottieFromString(kLottieDataWith2Markers);
  265. ASSERT_TRUE(skottie->is_valid());
  266. EXPECT_THAT(
  267. skottie->GetAllMarkers(),
  268. UnorderedElementsAre(
  269. FieldsAre(
  270. kLottieDataWith2MarkersMarker1,
  271. FloatNear(kLottieDataWith2MarkersMarker1Time, kMarkerEpsilon),
  272. FloatNear(kLottieDataWith2MarkersMarker1Time, kMarkerEpsilon)),
  273. FieldsAre(
  274. kLottieDataWith2MarkersMarker2,
  275. FloatNear(kLottieDataWith2MarkersMarker2Time, kMarkerEpsilon),
  276. FloatNear(kLottieDataWith2MarkersMarker2Time, kMarkerEpsilon))));
  277. }
  278. TEST(SkottieWrapperTest, LoadsTransformNodes) {
  279. auto skottie = CreateSkottieFromTestDataDir(kLottieDataWith2TextFileName);
  280. ASSERT_TRUE(skottie->is_valid());
  281. EXPECT_THAT(skottie->GetTextNodeNames(),
  282. UnorderedElementsAre(kLottieDataWith2TextNode1,
  283. kLottieDataWith2TextNode2));
  284. EXPECT_THAT(
  285. skottie->GetCurrentTransformPropertyValues(),
  286. IsSupersetOf({Pair(HashSkottieResourceId(kLottieDataWith2TextNode1),
  287. SkottieTransformPropertyValue(
  288. {kLottieDataWith2TextNode1Position})),
  289. Pair(HashSkottieResourceId(kLottieDataWith2TextNode2),
  290. SkottieTransformPropertyValue(
  291. {kLottieDataWith2TextNode2Position}))}));
  292. }
  293. } // namespace
  294. } // namespace cc