@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.
- package/CMakeLists.txt +80 -58
- package/README.kepler.md +1 -0
- package/build/ExpoImageModules.kepler.d.ts.map +1 -1
- package/build/ExpoImageModules.kepler.js +1 -1
- package/build/ExpoImageModules.kepler.js.map +1 -1
- package/build/ImageViewNativeComponent.js +1 -1
- package/build/ImageViewNativeComponent.js.map +1 -1
- package/kepler/AutoLinkInit.cpp +17 -0
- package/kepler/Constants.h +21 -0
- package/kepler/ExpoImage/ExpoImageTurboModule.cpp +148 -0
- package/kepler/ExpoImage/ExpoImageTurboModule.h +41 -0
- package/kepler/ExpoImage/PrefetchListener.cpp +45 -0
- package/kepler/ExpoImage/PrefetchListener.h +40 -0
- package/kepler/ExpoImageComponent/AccessibilityDelegate.cpp +33 -0
- package/kepler/ExpoImageComponent/AccessibilityDelegate.h +30 -0
- package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.cpp +132 -0
- package/kepler/ExpoImageComponent/ExpoImageComponentViewManager.h +66 -0
- package/kepler/ExpoImageComponent/ExpoImageStore.h +102 -0
- package/kepler/ExpoImageComponent/ExpoImageView.cpp +556 -0
- package/kepler/ExpoImageComponent/ExpoImageView.h +186 -0
- package/kepler/ExpoImageComponent/HashImageDecoder.cpp +91 -0
- package/kepler/ExpoImageComponent/HashImageDecoder.h +18 -0
- package/kepler/ExpoImageComponent/ImageLoadListeners.cpp +73 -0
- package/kepler/ExpoImageComponent/ImageLoadListeners.h +52 -0
- package/kepler/ExpoImageComponent/TransitionAnimator.cpp +113 -0
- package/kepler/ExpoImageComponent/TransitionAnimator.h +32 -0
- package/kepler/ImageSourceInfo.h +21 -0
- package/kepler/ViewManagerFactory.cpp +36 -0
- package/kepler/ViewManagerFactory.h +29 -0
- package/kepler/blurhash/common.h +11 -10
- package/kepler/blurhash/decode.cpp +26 -16
- package/kepler/contentposition/ImageContentPosition.cpp +50 -69
- package/kepler/contentposition/ImageContentPosition.h +36 -26
- package/kepler/enums/ImageContentFit.h +47 -15
- package/kepler/enums/ImagePriority.h +12 -13
- package/kepler/test/blurhash/decode.cpp +36 -27
- package/kepler/test/contentposition/ImageContentPosition.cpp +70 -76
- package/kepler/test/thumbhash/decode.cpp +18 -10
- package/kepler/test/utils/Base64.cpp +73 -0
- package/kepler/test/utils/ColorUtils.cpp +53 -0
- package/kepler/test/utils/SourceSelection.cpp +87 -0
- package/kepler/thumbhash/decode.cpp +7 -16
- package/kepler/utils/Base64.cpp +85 -0
- package/kepler/utils/Base64.h +36 -0
- package/kepler/utils/ColorUtils.h +26 -0
- package/kepler/utils/SourceSelection.cpp +80 -0
- package/kepler/utils/SourceSelection.h +65 -0
- package/lib/aarch64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/aarch64-release/libexpo-image-3.so +0 -0
- package/lib/armv7-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/armv7-release/libexpo-image-3.so +0 -0
- package/lib/x86_64-release/com.amazon.kepler.uitoolkit.react.expoimageapp.so +0 -0
- package/lib/x86_64-release/libexpo-image-3.so +0 -0
- package/package.json +1 -1
- package/react-native.config.js +12 -4
- package/src/ExpoImageModules.kepler.ts +1 -1
- package/src/ImageViewNativeComponent.ts +1 -1
- package/kepler/ExpoImage.cpp +0 -63
- package/kepler/ExpoImage.h +0 -29
- package/kepler/ExpoImageCacheHandler.cpp +0 -15
- package/kepler/ExpoImageCacheHandler.h +0 -28
- package/kepler/ExpoImageComponent.cpp +0 -579
- package/kepler/ExpoImageComponent.h +0 -199
- package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +0 -37
- package/kepler/ImageWrapper/BlurhashImageWrapper.h +0 -23
- package/kepler/ImageWrapper/CacheImageWrapper.cpp +0 -80
- package/kepler/ImageWrapper/CacheImageWrapper.h +0 -41
- package/kepler/ImageWrapper/HashImageWrapper.cpp +0 -21
- package/kepler/ImageWrapper/HashImageWrapper.h +0 -22
- package/kepler/ImageWrapper/ImageWrapper.cpp +0 -24
- package/kepler/ImageWrapper/ImageWrapper.h +0 -33
- package/kepler/ImageWrapper/LocalImageWrapper.cpp +0 -61
- package/kepler/ImageWrapper/LocalImageWrapper.h +0 -30
- package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +0 -33
- package/kepler/ImageWrapper/ThumbhashImageWrapper.h +0 -20
- package/kepler/KeplerImageViewComponent.h +0 -64
- package/kepler/KeplerInit.cpp +0 -35
- package/kepler/managers/ExpoImageManager.cpp +0 -38
- package/kepler/managers/ExpoImageManager.h +0 -23
- package/kepler/test/ExpoImage.cpp +0 -73
- package/kepler/test/ExpoImageComponent.cpp +0 -304
- package/kepler/test/managers/ExpoImageManager.cpp +0 -39
- package/kepler/test/utils/LoaderDelegate.cpp +0 -130
- package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +0 -26
- package/kepler/utils/PlaceholderImageLoaderDelegate.h +0 -26
- package/kepler/utils/PrefetchImageLoaderDelegate.cpp +0 -55
- package/kepler/utils/PrefetchImageLoaderDelegate.h +0 -35
- package/kepler/utils/SourceImageLoaderDelegate.cpp +0 -47
- package/kepler/utils/SourceImageLoaderDelegate.h +0 -42
- package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +0 -15
- package/kepler/utils/SourceImageLoaderDelegateEmitter.h +0 -28
- package/kepler-compatibility.json +0 -6
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <apmf/array.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/apmf/ISubscription.h>
|
|
5
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/Size.h>
|
|
6
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IContext.h>
|
|
7
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IJsArray.h>
|
|
8
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IJsObject.h>
|
|
9
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/ILayout.h>
|
|
10
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAccessibilityService.h>
|
|
11
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImage.h>
|
|
12
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImageLoader.h>
|
|
13
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImageView3.h>
|
|
14
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IView.h>
|
|
15
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IViewModule.h>
|
|
16
|
+
#include <apmf/ptr.h>
|
|
17
|
+
#include <apmf/string.h>
|
|
18
|
+
|
|
19
|
+
#include "../ImageSourceInfo.h"
|
|
20
|
+
#include "../contentposition/ImageContentPosition.h"
|
|
21
|
+
#include "../enums/ImageContentFit.h"
|
|
22
|
+
#include "../enums/ImagePriority.h"
|
|
23
|
+
#include "AccessibilityDelegate.h"
|
|
24
|
+
#include "ExpoImageStore.h"
|
|
25
|
+
|
|
26
|
+
#include <deque>
|
|
27
|
+
#include <limits>
|
|
28
|
+
#include <memory>
|
|
29
|
+
#include <optional>
|
|
30
|
+
#include <string>
|
|
31
|
+
#include <unordered_map>
|
|
32
|
+
#include <vector>
|
|
33
|
+
|
|
34
|
+
namespace expoimage {
|
|
35
|
+
|
|
36
|
+
using apmf::Ptr;
|
|
37
|
+
using apmf::String;
|
|
38
|
+
using apmf::View;
|
|
39
|
+
template <typename T>
|
|
40
|
+
using Array = apmf::Array<T>;
|
|
41
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IContext;
|
|
42
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IJsArray;
|
|
43
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IJsObject;
|
|
44
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::ILayout;
|
|
45
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAccessibilityService;
|
|
46
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IImageLoader;
|
|
47
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IImageView3;
|
|
48
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IView;
|
|
49
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IViewModule;
|
|
50
|
+
|
|
51
|
+
namespace gfx = apmf::iface::com::amazon::kepler::uitoolkit::graphics;
|
|
52
|
+
namespace vi = apmf::iface::com::amazon::kepler::uitoolkit::view;
|
|
53
|
+
|
|
54
|
+
// ExpoImageStore (the process-wide shared loader + decoded-image cache) is defined
|
|
55
|
+
// in ExpoImageStore.h.
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Per-view state and logic for a single <Image> element.
|
|
59
|
+
* Created by ExpoImageComponentViewManager::makeView(), one instance per native view.
|
|
60
|
+
*/
|
|
61
|
+
class ExpoImageView {
|
|
62
|
+
public:
|
|
63
|
+
ExpoImageView(
|
|
64
|
+
Ptr<IViewModule> viewModule,
|
|
65
|
+
Ptr<IView> container,
|
|
66
|
+
Ptr<IView> clipView,
|
|
67
|
+
Ptr<IImageView3> imageView,
|
|
68
|
+
std::shared_ptr<ExpoImageStore> store);
|
|
69
|
+
|
|
70
|
+
void updateProps(View<IJsObject> newProps, View<IContext> context);
|
|
71
|
+
void updateLayout(View<ILayout> newLayout);
|
|
72
|
+
|
|
73
|
+
Ptr<apmf::iface::com::amazon::apmf::ISubscription> lifecycleSub_;
|
|
74
|
+
|
|
75
|
+
private:
|
|
76
|
+
void setContentFit(const std::string& value);
|
|
77
|
+
void setPlaceholderContentFit(const std::string& value);
|
|
78
|
+
void setAllowDownscaling(bool allow);
|
|
79
|
+
void setContentPosition(View<IJsObject> posObj);
|
|
80
|
+
void setTintColor(int color);
|
|
81
|
+
void setOverlayColor(int color);
|
|
82
|
+
void setBlurRadius(double radius);
|
|
83
|
+
void setSource(View<IJsArray> sourceArr);
|
|
84
|
+
void setPlaceholder(View<IJsArray> phArr);
|
|
85
|
+
void setPriority(const std::string& value);
|
|
86
|
+
void setTransition(View<IJsObject> transObj);
|
|
87
|
+
void setRecyclingKey(const std::string& value);
|
|
88
|
+
void setAccessibilityLabel(const std::string& value);
|
|
89
|
+
void setAlt(const std::string& value);
|
|
90
|
+
void setAccessible(bool value);
|
|
91
|
+
|
|
92
|
+
void loadSource(bool animate);
|
|
93
|
+
void loadPlaceholder();
|
|
94
|
+
bool loadHashImage(const std::string& uri, float width, float height, bool animate);
|
|
95
|
+
void animateTransition();
|
|
96
|
+
void applyContentPositionOffset();
|
|
97
|
+
|
|
98
|
+
bool shouldDeferRemoteLoad(const ImageSourceInfo& best) const;
|
|
99
|
+
std::optional<apmf::iface::com::amazon::kepler::uitoolkit::graphics::Size>
|
|
100
|
+
computePreferredSize(const ImageSourceInfo& best) const;
|
|
101
|
+
void loadHashSource(const ImageSourceInfo& best, bool animate);
|
|
102
|
+
void loadRemoteSource(const ImageSourceInfo& best, bool animate);
|
|
103
|
+
void loadHashPlaceholder(const ImageSourceInfo& best);
|
|
104
|
+
void loadRemotePlaceholder(const ImageSourceInfo& best);
|
|
105
|
+
|
|
106
|
+
void applyContentPositionOffset(ImageContentFit fit, bool allowDownscaling);
|
|
107
|
+
static void layoutImageView(
|
|
108
|
+
Ptr<IImageView3> imageView,
|
|
109
|
+
ImageContentFit fit,
|
|
110
|
+
bool allowDownscaling,
|
|
111
|
+
gfx::Size imageSize,
|
|
112
|
+
gfx::Size viewSize,
|
|
113
|
+
const ImageContentPosition& contentPosition);
|
|
114
|
+
|
|
115
|
+
struct DisplayState {
|
|
116
|
+
bool sourceLoaded{false};
|
|
117
|
+
// true once this view has displayed ANY real image
|
|
118
|
+
// (source or placeholder). Persists across source changes / recycling
|
|
119
|
+
// (it is NOT reset by setSource). Used to suppress placeholder re-show
|
|
120
|
+
// and the fade-from-blank transition on a view that already shows an
|
|
121
|
+
// image, so a recycled cell swaps atomically instead of flickering.
|
|
122
|
+
bool hasDisplayedImage{false};
|
|
123
|
+
gfx::Size loadedImageSize{0, 0};
|
|
124
|
+
ImageContentFit activeFit{ImageContentFit::Cover};
|
|
125
|
+
bool activeAllowDownscaling{true};
|
|
126
|
+
gfx::Size viewSize{0, 0};
|
|
127
|
+
ImageContentPosition contentPosition;
|
|
128
|
+
// URI of the source currently displayed, and the URI of an
|
|
129
|
+
// in-flight load. Kept in DisplayState (a shared_ptr captured by the load
|
|
130
|
+
// callback) rather than on ExpoImageView so that the async failure path can
|
|
131
|
+
// clear `pendingUri` and allow a retry even after the originating cell is
|
|
132
|
+
// gone. loadedUri short-circuits redundant reloads; pendingUri de-dups
|
|
133
|
+
// concurrent loads of the same URI.
|
|
134
|
+
std::string loadedUri;
|
|
135
|
+
std::string pendingUri;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
struct SourceLoadCaptures {
|
|
139
|
+
Ptr<vi::IView> view;
|
|
140
|
+
Ptr<vi::IImageView3> imageView;
|
|
141
|
+
Ptr<vi::IViewModule> viewModule;
|
|
142
|
+
std::shared_ptr<DisplayState> state;
|
|
143
|
+
ImageContentFit contentFit;
|
|
144
|
+
bool allowDownscaling;
|
|
145
|
+
bool shouldAnimate;
|
|
146
|
+
float duration;
|
|
147
|
+
std::string timing;
|
|
148
|
+
std::shared_ptr<ExpoImageStore> store; // cache decoded image on success
|
|
149
|
+
std::string uri; // raw source URI (load-state tracking)
|
|
150
|
+
std::string cacheKey; // cache key = uri + decode size
|
|
151
|
+
};
|
|
152
|
+
static void applySourceImage(const SourceLoadCaptures& captures, Ptr<vi::IImage> img);
|
|
153
|
+
|
|
154
|
+
Ptr<IViewModule> viewModule_;
|
|
155
|
+
Ptr<IView> view_; // container passed back to Fabric (also accessibility root)
|
|
156
|
+
Ptr<IView> clipView_; // plain IView wrapper with setContentClipping(true); clips descendants to cell bounds
|
|
157
|
+
Ptr<IImageView3> imageView_; // IImageView child — setImage/setFit and setOffset/setSize (IImageView3 extends IView)
|
|
158
|
+
Ptr<IImageLoader> imageLoader_;
|
|
159
|
+
std::shared_ptr<ExpoImageStore> store_; // shared loader + decoded-image cache
|
|
160
|
+
Ptr<IContext> context_;
|
|
161
|
+
|
|
162
|
+
std::vector<ImageSourceInfo> sources_;
|
|
163
|
+
std::vector<ImageSourceInfo> placeholders_;
|
|
164
|
+
ImageContentFit contentFit_{ImageContentFit::Cover};
|
|
165
|
+
ImageContentFit placeholderContentFit_{ImageContentFit::ScaleDown};
|
|
166
|
+
ImageContentPosition contentPosition_;
|
|
167
|
+
ImagePriority priority_{ImagePriority::Normal};
|
|
168
|
+
int tintColor_{std::numeric_limits<int>::max()};
|
|
169
|
+
int overlayColor_{std::numeric_limits<int>::max()};
|
|
170
|
+
double blurRadius_{0.0};
|
|
171
|
+
bool allowDownscaling_{true};
|
|
172
|
+
std::string recyclingKey_;
|
|
173
|
+
bool shouldClearOnSourceChange_{false};
|
|
174
|
+
float transitionDuration_{0.0f};
|
|
175
|
+
std::string transitionTiming_;
|
|
176
|
+
bool shouldAnimate_{false};
|
|
177
|
+
std::shared_ptr<DisplayState> displayState_ = std::make_shared<DisplayState>();
|
|
178
|
+
gfx::Size viewSize_{0, 0};
|
|
179
|
+
|
|
180
|
+
Ptr<ExpoImageAccessibilityDelegate> accessibilityDelegate_;
|
|
181
|
+
Ptr<IAccessibilityService> accessibilityService_;
|
|
182
|
+
std::string accessibilityLabel_;
|
|
183
|
+
std::string alt_;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#include "HashImageDecoder.h"
|
|
2
|
+
|
|
3
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/Color.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/ICanvas.h>
|
|
5
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/IGraphicsModule2.h>
|
|
6
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/IPaint.h>
|
|
7
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/ISurface.h>
|
|
8
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/Rect.h>
|
|
9
|
+
#include <apmf/process.h>
|
|
10
|
+
|
|
11
|
+
#include "../blurhash/decode.h"
|
|
12
|
+
#include "../thumbhash/decode.h"
|
|
13
|
+
#include "../utils/SourceSelection.h"
|
|
14
|
+
|
|
15
|
+
#include <algorithm>
|
|
16
|
+
#include <cmath>
|
|
17
|
+
|
|
18
|
+
namespace expoimage {
|
|
19
|
+
|
|
20
|
+
namespace gfx = apmf::iface::com::amazon::kepler::uitoolkit::graphics;
|
|
21
|
+
|
|
22
|
+
namespace {
|
|
23
|
+
|
|
24
|
+
constexpr int kBlurhashMaxInternalDim = 32;
|
|
25
|
+
|
|
26
|
+
/** Creates a graphics IImage from raw RGBA pixel data using ISurface. */
|
|
27
|
+
apmf::Ptr<gfx::IImage> createImageFromPixels(const uint8_t* pixels, int width, int height) {
|
|
28
|
+
auto graphicsModule = apmf::GetProcessObject()
|
|
29
|
+
->getComponent("/com.amazon.kepler.uitoolkit.graphics")
|
|
30
|
+
.TryQueryInterface<gfx::IGraphicsModule2>();
|
|
31
|
+
if (!graphicsModule) return nullptr;
|
|
32
|
+
|
|
33
|
+
auto surface = graphicsModule->makeSurface(gfx::SizeI{width, height});
|
|
34
|
+
if (!surface) return nullptr;
|
|
35
|
+
|
|
36
|
+
auto canvas = surface->getCanvas();
|
|
37
|
+
auto paint = graphicsModule->makePaint();
|
|
38
|
+
|
|
39
|
+
for (int y = 0; y < height; ++y) {
|
|
40
|
+
for (int x = 0; x < width; ++x) {
|
|
41
|
+
int idx = (y * width + x) * 4;
|
|
42
|
+
float r = pixels[idx] / 255.0f;
|
|
43
|
+
float g = pixels[idx + 1] / 255.0f;
|
|
44
|
+
float b = pixels[idx + 2] / 255.0f;
|
|
45
|
+
float a = pixels[idx + 3] / 255.0f;
|
|
46
|
+
paint->setColor(gfx::Color{r, g, b, a});
|
|
47
|
+
canvas->drawRect(
|
|
48
|
+
gfx::Rect{static_cast<float>(x), static_cast<float>(y),
|
|
49
|
+
static_cast<float>(x + 1), static_cast<float>(y + 1)},
|
|
50
|
+
paint);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return surface->createSnapshot();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
} // anonymous namespace
|
|
57
|
+
|
|
58
|
+
apmf::Ptr<gfx::IImage> decodeHashToImage(const std::string& uri, float width, float height) {
|
|
59
|
+
if (!std::isfinite(width) || !std::isfinite(height)) {
|
|
60
|
+
return nullptr;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
auto delimPos = uri.find('/');
|
|
64
|
+
if (delimPos == std::string::npos) return nullptr;
|
|
65
|
+
auto hash = uriDecode(uri.substr(delimPos + 1));
|
|
66
|
+
|
|
67
|
+
int w = std::max(1, static_cast<int>(width));
|
|
68
|
+
int h = std::max(1, static_cast<int>(height));
|
|
69
|
+
const int channels = 4;
|
|
70
|
+
std::unique_ptr<uint8_t[]> pixels;
|
|
71
|
+
|
|
72
|
+
if (startsWith(uri, "blurhash")) {
|
|
73
|
+
int longest = std::max(w, h);
|
|
74
|
+
if (longest > kBlurhashMaxInternalDim) {
|
|
75
|
+
float scale = static_cast<float>(kBlurhashMaxInternalDim) / static_cast<float>(longest);
|
|
76
|
+
w = std::max(1, static_cast<int>(std::round(static_cast<float>(w) * scale)));
|
|
77
|
+
h = std::max(1, static_cast<int>(std::round(static_cast<float>(h) * scale)));
|
|
78
|
+
}
|
|
79
|
+
pixels = decodeBlurhash(hash.c_str(), w, h, 1, channels);
|
|
80
|
+
} else if (startsWith(uri, "thumbhash")) {
|
|
81
|
+
auto result = decodeThumbhash(hash);
|
|
82
|
+
w = result.width;
|
|
83
|
+
h = result.height;
|
|
84
|
+
pixels = std::move(result.pixelArray);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!pixels) return nullptr;
|
|
88
|
+
return createImageFromPixels(pixels.get(), w, h);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/graphics/IImage.h>
|
|
4
|
+
#include <apmf/ptr.h>
|
|
5
|
+
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
namespace expoimage {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Decodes a blurhash or thumbhash URI into a graphics IImage.
|
|
12
|
+
* The URI format is "blurhash/<hash>" or "thumbhash:/<hash>".
|
|
13
|
+
* Returns nullptr if decoding fails.
|
|
14
|
+
*/
|
|
15
|
+
apmf::Ptr<apmf::iface::com::amazon::kepler::uitoolkit::graphics::IImage>
|
|
16
|
+
decodeHashToImage(const std::string& uri, float width, float height);
|
|
17
|
+
|
|
18
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#include "ImageLoadListeners.h"
|
|
2
|
+
|
|
3
|
+
#include <apmf/fn.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IJsObjectBuilder.h>
|
|
5
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IPayloadDelegate.h>
|
|
6
|
+
#include <apmf/log.h>
|
|
7
|
+
|
|
8
|
+
namespace expoimage {
|
|
9
|
+
|
|
10
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IJsObjectBuilder;
|
|
11
|
+
|
|
12
|
+
APMF_DEFINE_LOGGER(ImageLoadListenerLogger,
|
|
13
|
+
"com.amazon.kepler.uitoolkit.react.expoimage.imageload");
|
|
14
|
+
using namespace apmf::log_levels;
|
|
15
|
+
|
|
16
|
+
// --- SourceImageLoadListener ---
|
|
17
|
+
|
|
18
|
+
SourceImageLoadListener::SourceImageLoadListener(
|
|
19
|
+
View<IEventEmitter> emitter, View<IView> view, std::function<void(Ptr<IImage>)> onLoad)
|
|
20
|
+
: emitter_(emitter), view_(view), onLoad_(std::move(onLoad)) {}
|
|
21
|
+
|
|
22
|
+
void SourceImageLoadListener::onStart(View<IImageLoadRequest>) { emitter_->emit(view_, "loadStart"); }
|
|
23
|
+
|
|
24
|
+
void SourceImageLoadListener::onProgress(View<IImageLoadRequest>, float progress) {
|
|
25
|
+
constexpr int32_t total = 10000;
|
|
26
|
+
emitter_->emitWithPayload(
|
|
27
|
+
view_, "progress",
|
|
28
|
+
apmf::Fn([progress, total](apmf::method::getPayload, View<IJsObjectBuilder> builder) {
|
|
29
|
+
builder->setInt("loaded", static_cast<int>(progress * total))->setInt("total", total);
|
|
30
|
+
return builder->build();
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void SourceImageLoadListener::onSuccess(View<IImageLoadRequest>, View<IImage> image) {
|
|
35
|
+
emitter_->emit(view_, "load");
|
|
36
|
+
if (onLoad_) onLoad_(image);
|
|
37
|
+
emitter_->emit(view_, "loadEnd");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void SourceImageLoadListener::onFailure(View<IImageLoadRequest>, ImageLoadError const&) {
|
|
41
|
+
APMF_LOG(ImageLoadListenerLogger, WARNING)
|
|
42
|
+
<< "SourceImageLoadListener::onFailure: emitting 'error' + 'loadEnd'";
|
|
43
|
+
if (onLoad_) onLoad_(nullptr);
|
|
44
|
+
emitter_->emit(view_, "error");
|
|
45
|
+
emitter_->emit(view_, "loadEnd");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void SourceImageLoadListener::onCanceled(View<IImageLoadRequest>) {
|
|
49
|
+
APMF_LOG(ImageLoadListenerLogger, DEBUG)
|
|
50
|
+
<< "SourceImageLoadListener::onCanceled: emitting 'loadEnd'";
|
|
51
|
+
emitter_->emit(view_, "loadEnd");
|
|
52
|
+
}
|
|
53
|
+
void SourceImageLoadListener::onPrefetchComplete(View<IImageLoadRequest>) {}
|
|
54
|
+
|
|
55
|
+
// --- PlaceholderImageLoadListener ---
|
|
56
|
+
|
|
57
|
+
PlaceholderImageLoadListener::PlaceholderImageLoadListener(std::function<void(Ptr<IImage>)> onLoad)
|
|
58
|
+
: onLoad_(std::move(onLoad)) {}
|
|
59
|
+
|
|
60
|
+
void PlaceholderImageLoadListener::onStart(View<IImageLoadRequest>) {}
|
|
61
|
+
void PlaceholderImageLoadListener::onProgress(View<IImageLoadRequest>, float) {}
|
|
62
|
+
void PlaceholderImageLoadListener::onSuccess(View<IImageLoadRequest>, View<IImage> image) {
|
|
63
|
+
if (!image) return;
|
|
64
|
+
if (onLoad_) onLoad_(image);
|
|
65
|
+
}
|
|
66
|
+
void PlaceholderImageLoadListener::onFailure(View<IImageLoadRequest>, ImageLoadError const&) {
|
|
67
|
+
APMF_LOG(ImageLoadListenerLogger, WARNING)
|
|
68
|
+
<< "PlaceholderImageLoadListener::onFailure: placeholder load failed (no event)";
|
|
69
|
+
}
|
|
70
|
+
void PlaceholderImageLoadListener::onCanceled(View<IImageLoadRequest>) {}
|
|
71
|
+
void PlaceholderImageLoadListener::onPrefetchComplete(View<IImageLoadRequest>) {}
|
|
72
|
+
|
|
73
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <apmf/apmf_base.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IEventEmitter.h>
|
|
5
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImage.h>
|
|
6
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImageLoadListener.h>
|
|
7
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IImageLoadRequest.h>
|
|
8
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IView.h>
|
|
9
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/ImageLoadError.h>
|
|
10
|
+
#include <apmf/ptr.h>
|
|
11
|
+
|
|
12
|
+
#include <functional>
|
|
13
|
+
|
|
14
|
+
namespace expoimage {
|
|
15
|
+
|
|
16
|
+
using apmf::ApmfBase;
|
|
17
|
+
using apmf::Ptr;
|
|
18
|
+
using apmf::View;
|
|
19
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IEventEmitter;
|
|
20
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IImage;
|
|
21
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IImageLoadListener;
|
|
22
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IImageLoadRequest;
|
|
23
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::ImageLoadError;
|
|
24
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IView;
|
|
25
|
+
|
|
26
|
+
struct SourceImageLoadListener : ApmfBase<SourceImageLoadListener, IImageLoadListener> {
|
|
27
|
+
SourceImageLoadListener(View<IEventEmitter> emitter, View<IView> view, std::function<void(Ptr<IImage>)> onLoad);
|
|
28
|
+
void onStart(View<IImageLoadRequest> request);
|
|
29
|
+
void onProgress(View<IImageLoadRequest> request, float progress);
|
|
30
|
+
void onSuccess(View<IImageLoadRequest> request, View<IImage> image);
|
|
31
|
+
void onFailure(View<IImageLoadRequest> request, ImageLoadError const& error);
|
|
32
|
+
void onCanceled(View<IImageLoadRequest> request);
|
|
33
|
+
void onPrefetchComplete(View<IImageLoadRequest> request);
|
|
34
|
+
private:
|
|
35
|
+
Ptr<IEventEmitter> emitter_;
|
|
36
|
+
Ptr<IView> view_;
|
|
37
|
+
std::function<void(Ptr<IImage>)> onLoad_;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
struct PlaceholderImageLoadListener : ApmfBase<PlaceholderImageLoadListener, IImageLoadListener> {
|
|
41
|
+
explicit PlaceholderImageLoadListener(std::function<void(Ptr<IImage>)> onLoad);
|
|
42
|
+
void onStart(View<IImageLoadRequest> request);
|
|
43
|
+
void onProgress(View<IImageLoadRequest> request, float progress);
|
|
44
|
+
void onSuccess(View<IImageLoadRequest> request, View<IImage> image);
|
|
45
|
+
void onFailure(View<IImageLoadRequest> request, ImageLoadError const& error);
|
|
46
|
+
void onCanceled(View<IImageLoadRequest> request);
|
|
47
|
+
void onPrefetchComplete(View<IImageLoadRequest> request);
|
|
48
|
+
private:
|
|
49
|
+
std::function<void(Ptr<IImage>)> onLoad_;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#include "TransitionAnimator.h"
|
|
2
|
+
|
|
3
|
+
#include <apmf/apmf_base.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/apmf/MonotonicDuration.h>
|
|
5
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAnimationValue.h>
|
|
6
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAnimationValueBuilder.h>
|
|
7
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAnimator.h>
|
|
8
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAnimatorBuilder.h>
|
|
9
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IAnimatorListener.h>
|
|
10
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IInterpolator.h>
|
|
11
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IInterpolatorModule.h>
|
|
12
|
+
#include <apmf/log.h>
|
|
13
|
+
|
|
14
|
+
namespace expoimage {
|
|
15
|
+
|
|
16
|
+
using apmf::ApmfBase;
|
|
17
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAnimationValue;
|
|
18
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAnimationValueBuilder;
|
|
19
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAnimator;
|
|
20
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAnimatorBuilder;
|
|
21
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IAnimatorListener;
|
|
22
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IInterpolator;
|
|
23
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IInterpolatorModule;
|
|
24
|
+
|
|
25
|
+
APMF_DEFINE_LOGGER(TransitionAnimatorLogger,
|
|
26
|
+
"com.amazon.kepler.uitoolkit.react.expoimage.transition");
|
|
27
|
+
using namespace apmf::log_levels;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Listener that updates view opacity on each animation frame.
|
|
31
|
+
* Holds a Ptr<IAnimator> to prevent the animator from being garbage collected
|
|
32
|
+
* before the animation completes.
|
|
33
|
+
*/
|
|
34
|
+
struct TransitionListener : ApmfBase<TransitionListener, IAnimatorListener> {
|
|
35
|
+
TransitionListener(Ptr<IView> view) : view_(view) {}
|
|
36
|
+
|
|
37
|
+
/** Stores the animator to prevent GC during animation. */
|
|
38
|
+
void setAnimator(Ptr<IAnimator> animator) { animator_ = animator; }
|
|
39
|
+
|
|
40
|
+
void onStart(View<IAnimator>) {}
|
|
41
|
+
void onEnd(View<IAnimator>) { if (view_) view_->setOpacity(1.0f); }
|
|
42
|
+
void onCancel(View<IAnimator>) { if (view_) view_->setOpacity(1.0f); }
|
|
43
|
+
void onPause(View<IAnimator>) {}
|
|
44
|
+
void onResume(View<IAnimator>) {}
|
|
45
|
+
|
|
46
|
+
void onUpdate(View<IAnimator> animator) {
|
|
47
|
+
if (!view_) return;
|
|
48
|
+
auto val = animator->getCurrentAnimationValue();
|
|
49
|
+
if (val) {
|
|
50
|
+
auto f = val->getFloatValue();
|
|
51
|
+
if (f) view_->setOpacity(*f);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private:
|
|
56
|
+
Ptr<IView> view_;
|
|
57
|
+
Ptr<IAnimator> animator_;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
namespace {
|
|
61
|
+
|
|
62
|
+
Ptr<IInterpolator> makeInterpolator(View<IInterpolatorModule> module, const std::string& timing) {
|
|
63
|
+
if (timing == "ease-in") return module->makeEaseInCubicInterpolator();
|
|
64
|
+
if (timing == "ease-out") return module->makeEaseOutCubicInterpolator();
|
|
65
|
+
if (timing == "linear") return module->makeLinearInterpolator();
|
|
66
|
+
// "ease-in-out" and any unknown value fall through to the default.
|
|
67
|
+
return module->makeEaseInOutCubicInterpolator();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Ptr<IAnimationValue> makeFloatValue(View<IViewModule> vm, float v) {
|
|
71
|
+
auto b = vm->makeAnimationValueBuilder();
|
|
72
|
+
b->floatValue(v);
|
|
73
|
+
return b->build();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
apmf::iface::com::amazon::apmf::MonotonicDuration toMonotonicDuration(float seconds) {
|
|
77
|
+
auto sec = static_cast<int64_t>(seconds);
|
|
78
|
+
auto nsFrac = static_cast<int32_t>((seconds - sec) * 1e9);
|
|
79
|
+
return apmf::iface::com::amazon::apmf::MonotonicDuration{{sec, nsFrac}};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
} // anonymous namespace
|
|
83
|
+
|
|
84
|
+
void startFadeInTransition(
|
|
85
|
+
View<IViewModule> viewModule,
|
|
86
|
+
Ptr<IView> view,
|
|
87
|
+
float duration,
|
|
88
|
+
const std::string& timing) {
|
|
89
|
+
if (!view || duration <= 0.0f) {
|
|
90
|
+
APMF_LOG(TransitionAnimatorLogger, DEBUG)
|
|
91
|
+
<< "startFadeInTransition: skipped (view=" << (view ? "ok" : "null")
|
|
92
|
+
<< ", duration=" << duration << ")";
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
APMF_LOG(TransitionAnimatorLogger, DEBUG)
|
|
96
|
+
<< "startFadeInTransition: duration=" << duration << " timing=" << timing;
|
|
97
|
+
|
|
98
|
+
view->setOpacity(0.0f);
|
|
99
|
+
|
|
100
|
+
auto ab = viewModule->makeAnimatorBuilder();
|
|
101
|
+
ab->duration(toMonotonicDuration(duration));
|
|
102
|
+
ab->interpolator(makeInterpolator(viewModule->makeInterpolatorModule(), timing));
|
|
103
|
+
ab->animationBounds(makeFloatValue(viewModule, 0.0f), makeFloatValue(viewModule, 1.0f));
|
|
104
|
+
auto animator = ab->build();
|
|
105
|
+
|
|
106
|
+
// Listener keeps animator alive via setAnimator
|
|
107
|
+
auto listener = Ptr<TransitionListener>::Make(view);
|
|
108
|
+
animator->addAnimationListener(listener);
|
|
109
|
+
listener->setAnimator(animator);
|
|
110
|
+
animator->start();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IView.h>
|
|
4
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IViewModule.h>
|
|
5
|
+
#include <apmf/ptr.h>
|
|
6
|
+
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
namespace expoimage {
|
|
10
|
+
|
|
11
|
+
using apmf::Ptr;
|
|
12
|
+
using apmf::View;
|
|
13
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IView;
|
|
14
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::view::IViewModule;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Starts a fade-in transition animation on the given view.
|
|
18
|
+
* Sets opacity to 0, then animates from 0 to 1 over the given duration.
|
|
19
|
+
* The animator is kept alive by the listener until the animation completes.
|
|
20
|
+
*
|
|
21
|
+
* @param viewModule Used to create animator, interpolator, and animation values.
|
|
22
|
+
* @param view The view whose opacity will be animated.
|
|
23
|
+
* @param duration Duration in seconds.
|
|
24
|
+
* @param timing Timing function: "ease-in", "ease-out", "ease-in-out", or "linear".
|
|
25
|
+
*/
|
|
26
|
+
void startFadeInTransition(
|
|
27
|
+
View<IViewModule> viewModule,
|
|
28
|
+
Ptr<IView> view,
|
|
29
|
+
float duration,
|
|
30
|
+
const std::string& timing);
|
|
31
|
+
|
|
32
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
* Use is subject to license terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
|
|
12
|
+
namespace expoimage {
|
|
13
|
+
|
|
14
|
+
/** Parsed image source from JS props (uri, dimensions, scale). */
|
|
15
|
+
struct ImageSourceInfo {
|
|
16
|
+
std::string uri;
|
|
17
|
+
float width{0};
|
|
18
|
+
float height{0};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
} // namespace expoimage
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
* Use is subject to license terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "ViewManagerFactory.h"
|
|
9
|
+
|
|
10
|
+
#include <apmf/component.h>
|
|
11
|
+
#include <apmf/iface/com/amazon/apmf/InvalidArgumentError.h>
|
|
12
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/view/IViewModule.h>
|
|
13
|
+
|
|
14
|
+
#include "ExpoImageComponent/ExpoImageComponentViewManager.h"
|
|
15
|
+
|
|
16
|
+
namespace expoimage {
|
|
17
|
+
|
|
18
|
+
namespace view = apmf::iface::com::amazon::kepler::uitoolkit::view;
|
|
19
|
+
|
|
20
|
+
Ptr<IViewManager> ViewManagerFactory::makeViewManager(StringView name) {
|
|
21
|
+
auto viewModule = apmf::GetProcessObject()
|
|
22
|
+
->getComponent("/com.amazon.kepler.uitoolkit.view")
|
|
23
|
+
.TryQueryInterface<view::IViewModule>();
|
|
24
|
+
if (!viewModule) {
|
|
25
|
+
throw apmf::InvalidArgumentError("ExpoImage: failed to acquire IViewModule");
|
|
26
|
+
}
|
|
27
|
+
if (name == "ExpoImageAppComponent") {
|
|
28
|
+
return Ptr<ExpoImageComponentViewManager>::Make(viewModule);
|
|
29
|
+
}
|
|
30
|
+
throw apmf::InvalidArgumentError(
|
|
31
|
+
"ExpoImage: cannot make UI component '" + std::string(name) + "'");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
} // namespace expoimage
|
|
35
|
+
|
|
36
|
+
APMF_COMPONENT("/com.amazon.kepler.uitoolkit.react.expoimageapp", expoimage::ViewManagerFactory);
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <apmf/apmf_base.h>
|
|
12
|
+
#include <apmf/iface/com/amazon/kepler/uitoolkit/react/IViewManagerFactory.h>
|
|
13
|
+
#include <apmf/process.h>
|
|
14
|
+
#include <apmf/ptr.h>
|
|
15
|
+
#include <apmf/string_view.h>
|
|
16
|
+
|
|
17
|
+
namespace expoimage {
|
|
18
|
+
|
|
19
|
+
using apmf::ApmfBase;
|
|
20
|
+
using apmf::Ptr;
|
|
21
|
+
using apmf::StringView;
|
|
22
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IViewManager;
|
|
23
|
+
using apmf::iface::com::amazon::kepler::uitoolkit::react::IViewManagerFactory;
|
|
24
|
+
|
|
25
|
+
struct ViewManagerFactory : ApmfBase<ViewManagerFactory, IViewManagerFactory> {
|
|
26
|
+
Ptr<IViewManager> makeViewManager(StringView name);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
} // namespace expoimage
|