@amazon-devices/expo-image 2.0.9000000000 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CMakeLists.txt +80 -58
  2. package/README.kepler.md +1 -0
  3. package/build/ExpoImageModules.kepler.d.ts.map +1 -1
  4. package/build/ExpoImageModules.kepler.js +1 -1
  5. package/build/ExpoImageModules.kepler.js.map +1 -1
  6. package/build/ImageViewNativeComponent.js +1 -1
  7. package/build/ImageViewNativeComponent.js.map +1 -1
  8. package/kepler/AutoLinkInit.cpp +17 -0
  9. package/kepler/Constants.h +21 -0
  10. package/kepler/ExpoImage/ExpoImageTurboModule.cpp +148 -0
  11. package/kepler/ExpoImage/ExpoImageTurboModule.h +41 -0
  12. package/kepler/ExpoImage/PrefetchListener.cpp +45 -0
  13. package/kepler/ExpoImage/PrefetchListener.h +40 -0
  14. package/kepler/ExpoImageComponent/AccessibilityDelegate.cpp +33 -0
  15. package/kepler/ExpoImageComponent/AccessibilityDelegate.h +30 -0
  16. package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.cpp +132 -0
  17. package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.h +66 -0
  18. package/kepler/ExpoImageComponent/ExpoImageStore.h +102 -0
  19. package/kepler/ExpoImageComponent/ExpoImageView.cpp +556 -0
  20. package/kepler/ExpoImageComponent/ExpoImageView.h +186 -0
  21. package/kepler/ExpoImageComponent/HashImageDecoder.cpp +91 -0
  22. package/kepler/ExpoImageComponent/HashImageDecoder.h +18 -0
  23. package/kepler/ExpoImageComponent/ImageLoadListeners.cpp +73 -0
  24. package/kepler/ExpoImageComponent/ImageLoadListeners.h +52 -0
  25. package/kepler/ExpoImageComponent/TransitionAnimator.cpp +113 -0
  26. package/kepler/ExpoImageComponent/TransitionAnimator.h +32 -0
  27. package/kepler/ImageSourceInfo.h +21 -0
  28. package/kepler/ViewManagerFactory.cpp +36 -0
  29. package/kepler/ViewManagerFactory.h +29 -0
  30. package/kepler/blurhash/common.h +11 -10
  31. package/kepler/blurhash/decode.cpp +26 -16
  32. package/kepler/contentposition/ImageContentPosition.cpp +50 -69
  33. package/kepler/contentposition/ImageContentPosition.h +36 -26
  34. package/kepler/enums/ImageContentFit.h +47 -15
  35. package/kepler/enums/ImagePriority.h +12 -13
  36. package/kepler/test/blurhash/decode.cpp +36 -27
  37. package/kepler/test/contentposition/ImageContentPosition.cpp +70 -76
  38. package/kepler/test/thumbhash/decode.cpp +18 -10
  39. package/kepler/test/utils/Base64.cpp +73 -0
  40. package/kepler/test/utils/ColorUtils.cpp +53 -0
  41. package/kepler/test/utils/SourceSelection.cpp +87 -0
  42. package/kepler/thumbhash/decode.cpp +7 -16
  43. package/kepler/utils/Base64.cpp +85 -0
  44. package/kepler/utils/Base64.h +36 -0
  45. package/kepler/utils/ColorUtils.h +26 -0
  46. package/kepler/utils/SourceSelection.cpp +80 -0
  47. package/kepler/utils/SourceSelection.h +65 -0
  48. package/lib/aarch64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
  49. package/lib/aarch64-release/libexpo-image-3.so +0 -0
  50. package/lib/armv7-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
  51. package/lib/armv7-release/libexpo-image-3.so +0 -0
  52. package/lib/x86_64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
  53. package/lib/x86_64-release/libexpo-image-3.so +0 -0
  54. package/package.json +1 -1
  55. package/react-native.config.js +12 -4
  56. package/src/ExpoImageModules.kepler.ts +1 -1
  57. package/src/ImageViewNativeComponent.ts +1 -1
  58. package/kepler/ExpoImage.cpp +0 -63
  59. package/kepler/ExpoImage.h +0 -29
  60. package/kepler/ExpoImageCacheHandler.cpp +0 -15
  61. package/kepler/ExpoImageCacheHandler.h +0 -28
  62. package/kepler/ExpoImageComponent.cpp +0 -579
  63. package/kepler/ExpoImageComponent.h +0 -199
  64. package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +0 -37
  65. package/kepler/ImageWrapper/BlurhashImageWrapper.h +0 -23
  66. package/kepler/ImageWrapper/CacheImageWrapper.cpp +0 -80
  67. package/kepler/ImageWrapper/CacheImageWrapper.h +0 -41
  68. package/kepler/ImageWrapper/HashImageWrapper.cpp +0 -21
  69. package/kepler/ImageWrapper/HashImageWrapper.h +0 -22
  70. package/kepler/ImageWrapper/ImageWrapper.cpp +0 -24
  71. package/kepler/ImageWrapper/ImageWrapper.h +0 -33
  72. package/kepler/ImageWrapper/LocalImageWrapper.cpp +0 -61
  73. package/kepler/ImageWrapper/LocalImageWrapper.h +0 -30
  74. package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +0 -33
  75. package/kepler/ImageWrapper/ThumbhashImageWrapper.h +0 -20
  76. package/kepler/KeplerImageViewComponent.h +0 -64
  77. package/kepler/KeplerInit.cpp +0 -35
  78. package/kepler/managers/ExpoImageManager.cpp +0 -38
  79. package/kepler/managers/ExpoImageManager.h +0 -23
  80. package/kepler/test/ExpoImage.cpp +0 -73
  81. package/kepler/test/ExpoImageComponent.cpp +0 -304
  82. package/kepler/test/managers/ExpoImageManager.cpp +0 -39
  83. package/kepler/test/utils/LoaderDelegate.cpp +0 -130
  84. package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +0 -26
  85. package/kepler/utils/PlaceholderImageLoaderDelegate.h +0 -26
  86. package/kepler/utils/PrefetchImageLoaderDelegate.cpp +0 -55
  87. package/kepler/utils/PrefetchImageLoaderDelegate.h +0 -35
  88. package/kepler/utils/SourceImageLoaderDelegate.cpp +0 -47
  89. package/kepler/utils/SourceImageLoaderDelegate.h +0 -42
  90. package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +0 -15
  91. package/kepler/utils/SourceImageLoaderDelegateEmitter.h +0 -28
  92. package/kepler-compatibility.json +0 -6
@@ -1,579 +0,0 @@
1
- /*
2
- * Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
- *
4
- * PROPRIETARY/CONFIDENTIAL
5
- *
6
- * Use is subject to license terms.
7
- */
8
-
9
- #include "ExpoImageComponent.h"
10
-
11
- #include <KeplerScript/logger/KPLog.h>
12
- #include <KeplerScript/utils/image/LocalAssetImageLoader.h>
13
- #include <curl/curl.h>
14
-
15
- #include <unordered_map>
16
-
17
- #include "ImageWrapper/BlurhashImageWrapper.h"
18
- #include "ImageWrapper/CacheImageWrapper.h"
19
- #include "ImageWrapper/LocalImageWrapper.h"
20
- #include "ImageWrapper/ThumbhashImageWrapper.h"
21
- #include "boost/algorithm/string.hpp"
22
- #include "utils/PlaceholderImageLoaderDelegate.h"
23
- #include "utils/SourceImageLoaderDelegate.h"
24
-
25
- using namespace volta::react;
26
-
27
- namespace {
28
- bool startsWith(const std::string& string, const std::string& pattern) {
29
- return boost::algorithm::istarts_with(string, pattern);
30
- }
31
-
32
- std::shared_ptr<ImageWrapper> createImageWrapper(
33
- const facebook::react::ImageSource& source,
34
- const std::weak_ptr<LocalAssetImageLoader>& localImageLoader,
35
- const std::shared_ptr<KeplerScriptImageCacheHandler>& cacheHandler,
36
- const volta::IImageLoaderDelegate::Shared& loaderDelegate,
37
- const ImagePriority priority) {
38
- if (source.uri.empty()) return nullptr;
39
-
40
- if (startsWith(source.uri, "blurhash")) {
41
- return std::make_shared<BlurHashImageWrapper>(source, loaderDelegate);
42
- } else if (startsWith(source.uri, "thumbhash")) {
43
- return std::make_shared<ThumbHashImageWrapper>(source, loaderDelegate);
44
- } else if (startsWith(source.uri, "file:///")) {
45
- return std::make_shared<LocalImageWrapper>(source, localImageLoader, loaderDelegate);
46
- }
47
-
48
- // CacheImageWrapper should handle following cases:
49
- // - Raw data URI starting with `data:`
50
- // - Remote URI
51
- return std::make_shared<CacheImageWrapper>(source, cacheHandler, loaderDelegate, priority);
52
- }
53
-
54
- void paintImage(
55
- const volta::graphics::Image& image,
56
- volta::graphics::Canvas& canvas,
57
- const volta::graphics::Paint& paint,
58
- const volta::graphics::Size& size,
59
- float scale) {
60
- KPLog.debug("ExpoImage :: paintImage");
61
- const auto& content = volta::graphics::Size{image.width() * scale, image.height() * scale};
62
- const volta::graphics::Alignment alignment = volta::graphics::Alignment::center();
63
- const auto& offset = content.align(size, alignment);
64
- canvas.drawImageRect2(image, {offset, content}, volta::graphics::Image::FilterQuality::Medium, &paint);
65
- }
66
-
67
- } // namespace
68
-
69
- ExpoImageDrawableLayer::ExpoImageDrawableLayer() :
70
- tintColor_(volta::graphics::Color(std::numeric_limits<int>::max())),
71
- overlayColor_(volta::graphics::Color(std::numeric_limits<int>::max())),
72
- imageContentFit_(ImageContentFit::Cover),
73
- imagePlaceholderContentFit_(ImageContentFit::ScaleDown) {}
74
-
75
- void ExpoImageDrawableLayer::setSize(const volta::graphics::Size& size) {
76
- KPLog.debug("ExpoImage :: setSize");
77
- size_ = size;
78
- }
79
-
80
- void ExpoImageDrawableLayer::paintImageWithContentFit(
81
- const volta::graphics::Image& image,
82
- volta::graphics::Canvas& canvas,
83
- const volta::graphics::Paint& paint,
84
- const ImageContentFit imageContentFit,
85
- const bool isPlaceholderUsed) {
86
- const auto& scaleWidth = size_.width() / image.width();
87
- const auto& scaleHeight = size_.height() / image.height();
88
-
89
- switch (imageContentFit) {
90
- case ImageContentFit::Cover: {
91
- auto scale = imageAllowDownscaling_ ? std::max(scaleWidth, scaleHeight) : 1;
92
- paintImage(image, canvas, paint, size_, scale);
93
- break;
94
- }
95
- case ImageContentFit::Contain: {
96
- auto scale = imageAllowDownscaling_ ? std::min(scaleWidth, scaleHeight) : 1;
97
- paintImage(image, canvas, paint, size_, scale);
98
- break;
99
- }
100
- case ImageContentFit::Fill: {
101
- canvas.drawImageRect2(image, size_, volta::graphics::Image::FilterQuality::Medium, &paint);
102
- break;
103
- }
104
- case ImageContentFit::None: {
105
- if (isPlaceholderUsed) {
106
- paintImage(image, canvas, paint, size_, 1);
107
- } else {
108
- const auto& content =
109
- volta::graphics::Size{static_cast<float>(image.width()), static_cast<float>(image.height())};
110
- float offsetX = imageContentPosition_.offsetX(content, size_);
111
- float offsetY = imageContentPosition_.offsetY(content, size_);
112
- canvas.drawImageRect2(
113
- image,
114
- {{offsetX, offsetY}, content},
115
- volta::graphics::Image::FilterQuality::Medium,
116
- &paint);
117
- }
118
- break;
119
- }
120
- case ImageContentFit::ScaleDown: {
121
- auto scale = imageAllowDownscaling_ ? std::min<float>(std::min(scaleWidth, scaleHeight), 1) : 1;
122
- paintImage(image, canvas, paint, size_, scale);
123
- break;
124
- }
125
- }
126
- }
127
-
128
- void ExpoImageDrawableLayer::onDraw(volta::graphics::Canvas& canvas) {
129
- KPLog.debug("ExpoImage :: onDraw");
130
- volta::graphics::Image* image = imageWrapper_ ? imageWrapper_->getImage() : nullptr;
131
- ImageContentFit imageContentFit = imageContentFit_;
132
- bool isPlaceholderUsed = false;
133
-
134
- if (image == nullptr && placeholderWrapper_ != nullptr) {
135
- image = placeholderWrapper_->getImage();
136
- imageContentFit = imagePlaceholderContentFit_;
137
- isPlaceholderUsed = true;
138
- }
139
-
140
- // draw overlay color
141
- if (overlayColor_ != volta::graphics::Color(std::numeric_limits<int>::max()) &&
142
- imageContentFit != ImageContentFit::Cover) {
143
- volta::graphics::Paint overlayPaint;
144
- overlayPaint.setAntiAlias(true);
145
- overlayPaint.setColor(overlayColor_);
146
- overlayPaint.setStyle(volta::graphics::Paint::Style::Fill);
147
- auto overlayColorPath = borderCalculatedMetrics_.outerDrawPath();
148
- overlayColorPath.setFillType(volta::graphics::Path::FillType::EvenOdd);
149
- canvas.drawPath(overlayColorPath, overlayPaint);
150
- }
151
-
152
- // clip rectangle based on borders
153
- if (!borderCalculatedMetrics_.innerClipPathForBorderRadius().isEmpty()) {
154
- canvas.clipRRect(borderCalculatedMetrics_.innerClipRRectForBorderRadius());
155
- } else {
156
- canvas.clipRect({size_});
157
- }
158
-
159
- if (image == nullptr) {
160
- return;
161
- }
162
-
163
- // draw tint color
164
- volta::graphics::Paint paint;
165
- if (tintColor_ != volta::graphics::Color(std::numeric_limits<int>::max())) {
166
- paint.setColorFilter(volta::graphics::PorterDuffColorFilter::makePorterDuffColorFilter(
167
- tintColor_.argb(),
168
- volta::graphics::PorterDuffMode::SrcATop));
169
- }
170
-
171
- // Blur Radius
172
- if (blurRadius_ > 0.0) {
173
- auto blurFilter = volta::graphics::BlurImageFilter::makeBlurImageFilter(
174
- blurRadius_,
175
- blurRadius_,
176
- volta::graphics::TileMode::Decal);
177
- paint.setImageFilter(blurFilter);
178
- }
179
-
180
- // Src change animation
181
- volta::graphics::Image* oldImage = oldImageWrapper_ ? oldImageWrapper_->getImage() : nullptr;
182
- if (oldImage != nullptr) {
183
- paint.setAlpha(255 - opacity_);
184
- paintImageWithContentFit(*oldImage, canvas, paint, imageContentFit, isPlaceholderUsed);
185
- }
186
- paint.setAlpha(opacity_);
187
- paintImageWithContentFit(*image, canvas, paint, imageContentFit, isPlaceholderUsed);
188
-
189
- if (shouldInitializeAnimation_) {
190
- onAnimationInit_();
191
- shouldInitializeAnimation_ = false;
192
- }
193
- }
194
-
195
- ExpoImageComponent::ExpoImageComponent(
196
- const std::shared_ptr<volta::react::KeplerScriptImageCacheHandler>& cacheHandler) :
197
- tintColor_(volta::graphics::Color(std::numeric_limits<int>::max())),
198
- overlayColor_(volta::graphics::Color(std::numeric_limits<int>::max())),
199
- animator_(volta::Animator(volta::Duration{0.0f})),
200
- cacheHandler_(cacheHandler) {
201
- opacityLayer_.addChild(imageDrawableLayer_);
202
- animation_ = animator_.animate(0.0f);
203
- }
204
-
205
- void ExpoImageComponent::startImageAnimation() {
206
- imageDrawableLayer_.onAnimationInit_ = [this]() { this->init(); };
207
- imageDrawableLayer_.shouldInitializeAnimation_ = true;
208
- imageDrawableLayer_.opacity_ = 0;
209
- animator_.reset();
210
- animator_.start();
211
- imageDrawableLayer_.invalidate();
212
- }
213
-
214
- void ExpoImageComponent::animateImage() {
215
- if (animator_.animating()) {
216
- imageDrawableLayer_.opacity_ = static_cast<uint8_t>(std::floor((*animation_ / opacityTargetValue_) * 255));
217
- imageDrawableLayer_.invalidate();
218
- }
219
- }
220
-
221
- void ExpoImageComponent::event(volta::EventContext& context) {
222
- if (context.event().type() == volta::Event::Type::Frame) {
223
- if (animator_.animating()) {
224
- animateImage();
225
- context.markNeedFrame();
226
- }
227
- }
228
- }
229
-
230
- void ExpoImageComponent::setTintColor(const int& newValue) {
231
- KPLog.debug("ExpoImage :: setTintcolor");
232
- tintColor_ = volta::graphics::Color(newValue);
233
- imageDrawableLayer_.tintColor_ = tintColor_;
234
- imageDrawableLayer_.invalidate();
235
- }
236
-
237
- void ExpoImageComponent::setBlurRadius(const double& newValue) {
238
- KPLog.debug("ExpoImage :: setBlurRadius");
239
- imageDrawableLayer_.blurRadius_ = newValue;
240
- imageDrawableLayer_.invalidate();
241
- }
242
-
243
- void ExpoImageComponent::setOverlayColor(const int& newValue) {
244
- KPLog.debug("ExpoImage :: setOverlayColor");
245
- overlayColor_ = volta::graphics::Color(newValue);
246
- imageDrawableLayer_.overlayColor_ = overlayColor_;
247
- imageDrawableLayer_.invalidate();
248
- }
249
-
250
- void ExpoImageComponent::setRecyclingKey(const std::string& newValue) {
251
- KPLog.debug("ExpoImage :: setRecyclingKey");
252
- shouldClearOnSourceChange_ = recyclingKey_ != "" && recyclingKey_ != newValue;
253
- recyclingKey_ = newValue;
254
- }
255
-
256
- ImageContentFit parseImageContentFit(
257
- const std::string& contentFit,
258
- ImageContentFit defaultValue = ImageContentFit::Cover) {
259
- KPLog.debug("ExpoImage :: parseImageContentFit");
260
- if (contentFit == "contain") {
261
- return ImageContentFit::Contain;
262
- } else if (contentFit == "fill") {
263
- return ImageContentFit::Fill;
264
- } else if (contentFit == "scale-down") {
265
- return ImageContentFit::ScaleDown;
266
- } else if (contentFit == "none") {
267
- return ImageContentFit::None;
268
- } else if (contentFit == "cover") {
269
- return ImageContentFit::Cover;
270
- }
271
- return defaultValue;
272
- }
273
-
274
- void ExpoImageComponent::setContentFit(const std::string& newValue) {
275
- KPLog.debug("ExpoImage :: setContentFit");
276
- contentFit_ = newValue;
277
- imageDrawableLayer_.imageContentFit_ = parseImageContentFit(newValue);
278
- imageDrawableLayer_.invalidate();
279
- }
280
-
281
- void ExpoImageComponent::setPlaceholderContentFit(const std::string& newValue) {
282
- KPLog.debug("ExpoImage :: setPlaceholderContentFit");
283
- placeholderContentFit_ = newValue;
284
- imageDrawableLayer_.imagePlaceholderContentFit_ = parseImageContentFit(newValue, ImageContentFit::ScaleDown);
285
- imageDrawableLayer_.invalidate();
286
- }
287
-
288
- void ExpoImageComponent::setAllowDownscaling(const bool& newValue) {
289
- KPLog.debug("ExpoImage :: setAllowDownscaling");
290
- imageDrawableLayer_.imageAllowDownscaling_ = newValue;
291
- imageDrawableLayer_.invalidate();
292
- }
293
-
294
- facebook::react::ImageSource parseImageSource(const utils::json::JsonContainer& constNewValue) {
295
- auto newValue = constNewValue;
296
- auto imageSourceJsonContainer = newValue;
297
- // JSON will look akin to:
298
- // {
299
- // "__packager_asset":true,
300
- // "width":200.0,
301
- // "uri":"file:///Volumes/MyHD/workspace/src/somepath/default-loading-image.png",
302
- // "height":200.0,
303
- // "scale":1.0
304
- // }
305
-
306
- facebook::react::ImageSource imageSource;
307
-
308
- std::string decodedUrl;
309
- folly::uriUnescape(imageSourceJsonContainer["uri"].getValue<std::string>(""), decodedUrl);
310
-
311
- imageSource.uri = decodedUrl;
312
- imageSource.size.height = imageSourceJsonContainer["height"].getValue<float>(0.0);
313
- imageSource.size.width = imageSourceJsonContainer["width"].getValue<float>(0.0);
314
- return imageSource;
315
- }
316
-
317
- void ExpoImageComponent::setSource(const utils::json::JsonContainer& constNewValue) {
318
- auto newValue = constNewValue;
319
- KPLog.debug("ExpoImage :: setSource {}", newValue.toJson());
320
- if (newValue.getType() == utils::json::JsonContainer::Type::ARRAY) {
321
- sources_.resize(newValue.size());
322
- for (int i = 0; i < newValue.size(); ++i) {
323
- auto jsonContainer = newValue[i];
324
- sources_[i] = parseImageSource(jsonContainer);
325
- }
326
- rerenderIfNeeded();
327
- }
328
- }
329
-
330
- std::optional<facebook::react::ImageSource> ExpoImageComponent::getBestSource(
331
- const std::vector<facebook::react::ImageSource>& sources) {
332
- if (sources.empty()) return std::nullopt;
333
-
334
- if (sources.size() == 1) {
335
- return sources.front();
336
- }
337
-
338
- int targetPixelCount = imageDrawableLayer_.size_.width() * imageDrawableLayer_.size_.height();
339
- if (targetPixelCount == 0) return std::nullopt;
340
-
341
- facebook::react::ImageSource bestSource;
342
- double bestFit = std::numeric_limits<double>::max();
343
- for (auto& source : sources) {
344
- int pixelCount = source.size.width * source.size.height;
345
- double fit = abs(1 - (pixelCount / targetPixelCount));
346
-
347
- if (fit < bestFit) {
348
- bestSource = source;
349
- bestFit = fit;
350
- }
351
- }
352
-
353
- return bestSource;
354
- }
355
-
356
- void ExpoImageComponent::setPlaceholder(const utils::json::JsonContainer& constNewValue) {
357
- auto newValue = constNewValue;
358
- KPLog.debug("ExpoImage :: setPlaceholder {}", newValue.toJson());
359
- if (newValue.getType() == utils::json::JsonContainer::Type::ARRAY) {
360
- placeholders_.resize(newValue.size());
361
- for (int i = 0; i < newValue.size(); ++i) {
362
- auto jsonContainer = newValue[i];
363
- placeholders_[i] = parseImageSource(jsonContainer);
364
- }
365
- rerenderIfNeeded();
366
- }
367
- }
368
-
369
- ImagePriority parseImagePriority(const std::string& priority) {
370
- KPLog.debug("ExpoImage :: parseImagePriority");
371
- if (priority == "low") {
372
- return ImagePriority::Low;
373
- } else if (priority == "normal") {
374
- return ImagePriority::Normal;
375
- } else if (priority == "high") {
376
- return ImagePriority::High;
377
- }
378
- return ImagePriority::Normal;
379
- }
380
-
381
- void ExpoImageComponent::setPriority(const std::string& newValue) {
382
- KPLog.debug("ExpoImage :: setPriority");
383
- priority_ = parseImagePriority(newValue);
384
- }
385
-
386
- std::optional<float> tryParseStringToFloat(const std::string& value) {
387
- if (!value.empty()) {
388
- try {
389
- return std::stof(value);
390
- } catch (const std::logic_error& e) {
391
- return std::nullopt;
392
- }
393
- }
394
- return std::nullopt;
395
- }
396
-
397
- ImageContentPositionValue parseContentPositionValue(const utils::json::JsonContainer constNewValue) {
398
- auto newValue = constNewValue;
399
- KPLog.debug("ExpoImage :: parseContentPositionValue");
400
- ImageContentPositionValue imageContentPositionValue;
401
- switch (newValue.getType()) {
402
- case utils::json::JsonContainer::Type::STRING: {
403
- std::string value = newValue.getValue<std::string>("");
404
- if (value.back() == '%') {
405
- auto parsedValue = tryParseStringToFloat(value);
406
- imageContentPositionValue.value_ = parsedValue;
407
- imageContentPositionValue.isPercentage_ = true;
408
- }
409
- break;
410
- }
411
- case utils::json::JsonContainer::Type::DOUBLE:
412
- imageContentPositionValue.value_ = static_cast<float>(newValue.getValue<double>(0.0));
413
- imageContentPositionValue.isPercentage_ = false;
414
- break;
415
- case utils::json::JsonContainer::Type::INT64:
416
- imageContentPositionValue.value_ = static_cast<float>(newValue.getValue<int64_t>(0));
417
- imageContentPositionValue.isPercentage_ = false;
418
- break;
419
- }
420
- return imageContentPositionValue;
421
- }
422
-
423
- void ExpoImageComponent::setContentPosition(const utils::json::JsonContainer& constNewValue) {
424
- auto newValue = constNewValue;
425
- KPLog.debug("ExpoImage :: setContentPosition {}", newValue.toJson());
426
-
427
- auto map = newValue.getValuesToMap<utils::json::JsonContainer>();
428
- ImageContentPositionValue top = parseContentPositionValue(map["top"]);
429
- ImageContentPositionValue bottom = parseContentPositionValue(map["bottom"]);
430
- ImageContentPositionValue right = parseContentPositionValue(map["right"]);
431
- ImageContentPositionValue left = parseContentPositionValue(map["left"]);
432
-
433
- imageDrawableLayer_.imageContentPosition_ = ImageContentPosition(top, bottom, right, left);
434
- imageDrawableLayer_.invalidate();
435
- }
436
-
437
- ImageTransition parseTransition(const utils::json::JsonContainer& constNewValue) {
438
- auto newValue = constNewValue;
439
- ImageTransition imageTransition;
440
- imageTransition.duration = newValue["duration"].getValue<float>(0.0f) / 1000.0f;
441
- imageTransition.timing = newValue["timing"].getValue<std::string>("");
442
- return imageTransition;
443
- }
444
-
445
- void ExpoImageComponent::createImageAnimation(ImageTransition& imageTransition) {
446
- animator_ = volta::Animator(volta::Duration{imageTransition.duration});
447
- auto animation = animator_.animate(opacityTargetValue_);
448
- if (imageTransition.timing == "ease-in") {
449
- animation_ = animation.transform(volta::curve::easeInCubic);
450
- } else if (imageTransition.timing == "ease-out") {
451
- animation_ = animation.transform(volta::curve::easeOutCubic);
452
- } else if (imageTransition.timing == "ease-in-out") {
453
- animation_ = animation.transform(volta::curve::easeInOutCubic);
454
- } else if (imageTransition.timing == "linear") {
455
- animation_ = animation;
456
- } else {
457
- animation_ = animation.transform(volta::curve::easeInOutCubic);
458
- }
459
- // we should check that running animation makes sense, otherwise it may lead to unexpected behaviors
460
- shouldAnimateOnSrcChange_ = imageTransition.duration != 0;
461
- }
462
-
463
- void ExpoImageComponent::setTransition(const utils::json::JsonContainer& constNewValue) {
464
- auto newValue = constNewValue;
465
- KPLog.debug("ExpoImage :: setTransition {}", newValue.toJson());
466
- auto imageTransition = parseTransition(newValue);
467
- createImageAnimation(imageTransition);
468
- }
469
-
470
- void ExpoImageComponent::createCurrentSource(facebook::react::ImageSource bestSource) {
471
- currentSource_ = createImageWrapper(
472
- bestSource,
473
- getLocalAssetImageLoader(),
474
- cacheHandler_,
475
- createSourceLoaderDelegate([weakSelf = weak_from_this()]() {
476
- if (auto self = weakSelf.lock()) {
477
- auto strongSelf = std::static_pointer_cast<ExpoImageComponent>(self);
478
- strongSelf->imageDrawableLayer_.imageWrapper_ = strongSelf->currentSource_;
479
- if (strongSelf->shouldAnimateOnSrcChange_) {
480
- strongSelf->imageDrawableLayer_.oldImageWrapper_ = strongSelf->oldSource_;
481
- strongSelf->startImageAnimation();
482
- } else {
483
- strongSelf->imageDrawableLayer_.invalidate();
484
- }
485
- }
486
- }),
487
- priority_);
488
- }
489
-
490
- void ExpoImageComponent::rerenderIfNeeded() {
491
- auto bestSourceA = getBestSource(sources_);
492
- if (bestSourceA != std::nullopt) {
493
- auto bestSource = bestSourceA.value();
494
- volta::react::KPLog.warn("ExpoImage :: currentSource nullptr {} ", currentSource_ == nullptr);
495
- if (currentSource_ != nullptr) {
496
- if (bestSource == currentSource_->getSource() && currentSource_->isLoaded())
497
- return;
498
- else if (bestSource != currentSource_->getSource()) {
499
- oldSource_ = currentSource_;
500
-
501
- createCurrentSource(bestSource);
502
- if (shouldClearOnSourceChange_) {
503
- imageDrawableLayer_.imageWrapper_ = currentSource_;
504
- imageDrawableLayer_.invalidate();
505
- shouldClearOnSourceChange_ = false;
506
- }
507
- }
508
- } else {
509
- createCurrentSource(bestSource);
510
- }
511
- if (currentSource_ != nullptr && !currentSource_->isLoaded()) currentSource_->load();
512
- }
513
-
514
- auto bestPlaceholderA = getBestSource(placeholders_);
515
- if (bestPlaceholderA != std::nullopt) {
516
- auto bestPlaceholder = bestPlaceholderA.value();
517
- if (currentPlaceholder_ != nullptr) {
518
- if (bestPlaceholder == currentPlaceholder_->getSource() && currentPlaceholder_->isLoaded()) return;
519
- } else {
520
- currentPlaceholder_ = createImageWrapper(
521
- bestPlaceholder,
522
- getLocalAssetImageLoader(),
523
- cacheHandler_,
524
- createPlaceholderLoaderDelegate([weakSelf = weak_from_this()]() {
525
- if (auto self = weakSelf.lock()) {
526
- auto strongSelf = std::static_pointer_cast<const ExpoImageComponent>(self);
527
- strongSelf->imageDrawableLayer_.placeholderWrapper_ = strongSelf->currentPlaceholder_;
528
- strongSelf->imageDrawableLayer_.invalidate();
529
- }
530
- }),
531
- priority_);
532
- }
533
- if (currentPlaceholder_ != nullptr && !currentPlaceholder_->isLoaded()) currentPlaceholder_->load();
534
- }
535
- }
536
-
537
- void ExpoImageComponent::updateLayoutMetrics(
538
- const std::shared_ptr<KeplerViewComponentLayoutMetrics>& oldLayoutMetrics,
539
- const std::shared_ptr<KeplerViewComponentLayoutMetrics>& newLayoutMetrics) {
540
- KPLog.debug("ExpoImage :: updateLayoutMetrics");
541
- KeplerViewComponent::updateLayoutMetrics(oldLayoutMetrics, newLayoutMetrics);
542
- if (oldLayoutMetrics != newLayoutMetrics) {
543
- const auto& contentFrame = layoutMetrics_->frame;
544
- imageDrawableLayer_.setSize(volta::graphics::Size(contentFrame.size.width, contentFrame.size.height));
545
- imageDrawableLayer_.setDimensions(imageDrawableLayer_.size_);
546
- imageDrawableLayer_.borderCalculatedMetrics_ = borderDrawableLayer_.getBorderCalculatedMetrics();
547
- rerenderIfNeeded();
548
- }
549
- }
550
-
551
- void ExpoImageComponent::updateState(
552
- const std::shared_ptr<const void>& oldState,
553
- const std::shared_ptr<const void>& newState) {
554
- KPLog.debug("ExpoImage :: updateState");
555
-
556
- auto oldImageState = std::static_pointer_cast<facebook::react::ImageShadowNode::ConcreteState const>(oldState);
557
- auto newImageState = std::static_pointer_cast<facebook::react::ImageShadowNode::ConcreteState const>(newState);
558
- const bool havePreviousData =
559
- oldImageState && oldImageState->getData().getImageSource() != facebook::react::ImageSource{};
560
- if (!havePreviousData ||
561
- (newImageState && newImageState->getData().getImageSource() != oldImageState->getData().getImageSource())) {
562
- }
563
- }
564
-
565
- std::shared_ptr<volta::IImageLoaderDelegate> ExpoImageComponent::createSourceLoaderDelegate(
566
- std::function<void()> onLoad) {
567
- return std::make_shared<SourceImageLoaderDelegate>(eventEmitter_, onLoad);
568
- }
569
-
570
- std::shared_ptr<volta::IImageLoaderDelegate> ExpoImageComponent::createPlaceholderLoaderDelegate(
571
- std::function<void()> onLoad) {
572
- return std::make_shared<PlaceholderImageLoaderDelegate>(onLoad);
573
- }
574
-
575
- void ExpoImageComponent::init() {
576
- if (auto eventController = getEventController().lock()) {
577
- eventController->markNeedFrame(shared_from_this());
578
- }
579
- }