first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/internal/react-native-platform-selector.cmake)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
react_native_android_selector(platform_SRC
platform/android/react/renderer/imagemanager/*.cpp
platform/cxx/react/renderer/imagemanager/*.cpp)
file(GLOB react_renderer_imagemanager_SRC CONFIGURE_DEPENDS
*.cpp
${platform_SRC})
add_library(react_renderer_imagemanager
OBJECT
${react_renderer_imagemanager_SRC})
react_native_android_selector(platform_DIR
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/)
target_include_directories(react_renderer_imagemanager
PUBLIC
${REACT_COMMON_DIR}
${platform_DIR}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
react_native_android_selector(mapbufferjni mapbufferjni "")
react_native_android_selector(reactnativejni reactnativejni "")
target_link_libraries(react_renderer_imagemanager
folly_runtime
${mapbufferjni}
react_debug
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_mounting
${reactnativejni}
yoga)
target_compile_reactnative_options(react_renderer_imagemanager PRIVATE)
target_compile_options(react_renderer_imagemanager PRIVATE -Wpedantic)

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/renderer/imagemanager/ImageRequest.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
class ImageManager;
using SharedImageManager [[deprecated("Use std::shared_ptr<ImageManager> instead.")]] = std::shared_ptr<ImageManager>;
/*
* Cross platform facade for image management (e.g. iOS-specific
* RCTImageManager)
*/
class ImageManager {
public:
ImageManager(const std::shared_ptr<const ContextContainer> &contextContainer);
virtual ~ImageManager();
virtual ImageRequest requestImage(
const ImageSource &imageSource,
SurfaceId surfaceId,
const ImageRequestParams &imageRequestParams = {},
Tag tag = {}) const;
private:
void *self_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/renderer/imagemanager/ImageRequest.h>
namespace facebook::react {
ImageRequest::ImageRequest(
ImageSource imageSource,
std::shared_ptr<const ImageTelemetry> telemetry,
SharedFunction<> resumeFunction,
SharedFunction<> cancelationFunction)
: imageSource_(std::move(imageSource)), telemetry_(std::move(telemetry)) {
coordinator_ = std::make_shared<ImageResponseObserverCoordinator>(
std::move(resumeFunction), std::move(cancelationFunction));
}
const ImageSource& ImageRequest::getImageSource() const {
return imageSource_;
}
const std::shared_ptr<const ImageTelemetry>& ImageRequest::getSharedTelemetry()
const {
return telemetry_;
}
const ImageResponseObserverCoordinator& ImageRequest::getObserverCoordinator()
const {
return *coordinator_;
}
const std::shared_ptr<const ImageResponseObserverCoordinator>&
ImageRequest::getSharedObserverCoordinator() const {
return coordinator_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/imagemanager/ImageResponse.h>
#include <react/renderer/imagemanager/ImageResponseObserver.h>
#include <react/renderer/imagemanager/ImageResponseObserverCoordinator.h>
#include <react/renderer/imagemanager/ImageTelemetry.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/utils/SharedFunction.h>
namespace facebook::react {
/*
* Represents ongoing request for an image resource.
* The separate object must be constructed for every single separate
* image request. The object cannot be copied because it would make managing of
* event listeners hard and inefficient; the object can be moved though.
* Destroy to cancel the underlying request.
*/
class ImageRequest final {
public:
/*
* The default constructor
*/
ImageRequest(
ImageSource imageSource,
std::shared_ptr<const ImageTelemetry> telemetry,
SharedFunction<> resumeFunction = {},
SharedFunction<> cancelationFunction = {});
/*
* The move constructor.
*/
ImageRequest(ImageRequest &&other) noexcept = default;
/*
* Move assignment operator.
*/
ImageRequest &operator=(ImageRequest &&other) noexcept = default;
/*
* `ImageRequest` does not support copying by design.
*/
ImageRequest(const ImageRequest &other) = delete;
ImageRequest &operator=(const ImageRequest &other) = delete;
/*
* Destructor
*/
~ImageRequest() = default;
/*
* Returns the Image Source associated with the request.
*/
const ImageSource &getImageSource() const;
/*
* Returns stored observer coordinator as a shared pointer.
* Retain this *or* `ImageRequest` to ensure a correct lifetime of the object.
*/
const std::shared_ptr<const ImageResponseObserverCoordinator> &getSharedObserverCoordinator() const;
/*
* Returns stored observer coordinator as a reference.
* Use this if a correct lifetime of the object is ensured in some other way
* (e.g. by retaining an `ImageRequest`).
*/
const ImageResponseObserverCoordinator &getObserverCoordinator() const;
/*
* Returns stored image telemetry object as a shared pointer.
* Retain this *or* `ImageRequest` to ensure a correct lifetime of the object.
*/
const std::shared_ptr<const ImageTelemetry> &getSharedTelemetry() const;
private:
/*
* Image source associated with the request.
*/
ImageSource imageSource_;
/*
* Image telemetry associated with the request.
*/
std::shared_ptr<const ImageTelemetry> telemetry_{};
/*
* Event coordinator associated with the request.
*/
std::shared_ptr<const ImageResponseObserverCoordinator> coordinator_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageResponse.h"
#include <utility>
namespace facebook::react {
ImageResponse::ImageResponse(
std::shared_ptr<void> image,
std::shared_ptr<void> metadata)
: image_(std::move(image)), metadata_(std::move(metadata)) {}
std::shared_ptr<void> ImageResponse::getImage() const {
return image_;
}
std::shared_ptr<void> ImageResponse::getMetadata() const {
return metadata_;
}
ImageLoadError::ImageLoadError(std::shared_ptr<void> error)
: error_(std::move(error)) {}
std::shared_ptr<void> ImageLoadError::getError() const {
return error_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
namespace facebook::react {
/*
* Represents retrieved image bitmap and any associated platform-specific info.
*/
class ImageResponse final {
public:
enum class Status {
Loading,
Completed,
Failed,
Cancelled,
Consumed,
};
ImageResponse(std::shared_ptr<void> image, std::shared_ptr<void> metadata);
std::shared_ptr<void> getImage() const;
std::shared_ptr<void> getMetadata() const;
private:
std::shared_ptr<void> image_{};
std::shared_ptr<void> metadata_{};
};
class ImageLoadError {
public:
explicit ImageLoadError(std::shared_ptr<void> error);
std::shared_ptr<void> getError() const;
private:
std::shared_ptr<void> error_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/imagemanager/ImageResponse.h>
namespace facebook::react {
/*
* Represents any observer of ImageResponse progression, completion, or failure.
* All methods must be thread-safe.
*/
class ImageResponseObserver {
public:
virtual ~ImageResponseObserver() noexcept = default;
virtual void didReceiveProgress(float progress, int64_t loaded, int64_t total) const = 0;
virtual void didReceiveImage(const ImageResponse &imageResponse) const = 0;
virtual void didReceiveFailure(const ImageLoadError &error) const = 0;
};
} // namespace facebook::react

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageResponseObserverCoordinator.h"
#include <algorithm>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
namespace facebook::react {
ImageResponseObserverCoordinator::ImageResponseObserverCoordinator(
SharedFunction<> resumeFunction,
SharedFunction<> cancelationFunction)
: resumeRequest_(std::move(resumeFunction)),
cancelRequest_(std::move(cancelationFunction)) {}
void ImageResponseObserverCoordinator::addObserver(
const ImageResponseObserver& observer) const {
mutex_.lock();
switch (status_) {
case ImageResponse::Status::Loading: {
observers_.push_back(&observer);
mutex_.unlock();
break;
}
case ImageResponse::Status::Completed: {
auto imageData = imageData_;
auto imageMetadata = imageMetadata_;
consumeResponse();
mutex_.unlock();
observer.didReceiveImage(ImageResponse{imageData, imageMetadata});
break;
}
case ImageResponse::Status::Failed: {
auto imageErrorData = imageErrorData_;
mutex_.unlock();
observer.didReceiveFailure(ImageLoadError{imageErrorData});
break;
}
case ImageResponse::Status::Cancelled:
case ImageResponse::Status::Consumed: {
observers_.push_back(&observer);
status_ = ImageResponse::Status::Loading;
mutex_.unlock();
resumeRequest_();
break;
}
}
}
void ImageResponseObserverCoordinator::removeObserver(
const ImageResponseObserver& observer) const {
std::scoped_lock lock(mutex_);
// We remove only one element to maintain a balance between add/remove calls.
auto position = std::find(observers_.begin(), observers_.end(), &observer);
if (position != observers_.end()) {
observers_.erase(position, observers_.end());
if (observers_.empty() && status_ == ImageResponse::Status::Loading) {
status_ = ImageResponse::Status::Cancelled;
cancelRequest_();
}
}
}
void ImageResponseObserverCoordinator::nativeImageResponseProgress(
float progress,
int64_t loaded,
int64_t total) const {
mutex_.lock();
auto observers = observers_;
react_native_assert(
status_ == ImageResponse::Status::Loading ||
status_ == ImageResponse::Status::Cancelled);
mutex_.unlock();
for (auto observer : observers) {
observer->didReceiveProgress(progress, loaded, total);
}
}
void ImageResponseObserverCoordinator::nativeImageResponseComplete(
const ImageResponse& imageResponse) const {
mutex_.lock();
imageData_ = imageResponse.getImage();
imageMetadata_ = imageResponse.getMetadata();
react_native_assert(
status_ == ImageResponse::Status::Loading ||
status_ == ImageResponse::Status::Cancelled);
status_ = ImageResponse::Status::Completed;
auto observers = observers_;
if (!observers.empty()) {
consumeResponse();
}
mutex_.unlock();
for (auto observer : observers) {
observer->didReceiveImage(imageResponse);
}
}
void ImageResponseObserverCoordinator::nativeImageResponseFailed(
const ImageLoadError& loadError) const {
mutex_.lock();
react_native_assert(
status_ == ImageResponse::Status::Loading ||
status_ == ImageResponse::Status::Cancelled);
status_ = ImageResponse::Status::Failed;
imageErrorData_ = loadError.getError();
auto observers = observers_;
mutex_.unlock();
for (auto observer : observers) {
observer->didReceiveFailure(loadError);
}
}
void ImageResponseObserverCoordinator::consumeResponse() const {
status_ = ImageResponse::Status::Consumed;
imageData_.reset();
imageMetadata_.reset();
}
} // namespace facebook::react

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/imagemanager/ImageResponse.h>
#include <react/renderer/imagemanager/ImageResponseObserver.h>
#include <react/utils/SharedFunction.h>
#include <mutex>
#include <vector>
namespace facebook::react {
/*
* The ImageResponseObserverCoordinator receives events and completed image
* data from native image loaders and sends events to any observers attached
* to the coordinator. The Coordinator also keeps track of response status
* and caches completed images.
*/
class ImageResponseObserverCoordinator {
public:
ImageResponseObserverCoordinator(SharedFunction<> resumeFunction, SharedFunction<> cancelationFunction);
/*
* Interested parties may observe the image response.
* If the current image request status is not equal to `Loading`, the observer
* will be called immediately.
*/
void addObserver(const ImageResponseObserver &observer) const;
/*
* Interested parties may stop observing the image response.
*/
void removeObserver(const ImageResponseObserver &observer) const;
/*
* Platform-specific image loader will call this method with progress updates.
*/
void nativeImageResponseProgress(float progress, int64_t loaded, int64_t total) const;
/*
* Platform-specific image loader will call this method with a completed image
* response.
*/
void nativeImageResponseComplete(const ImageResponse &imageResponse) const;
/*
* Platform-specific image loader will call this method in case of any
* failures.
*/
void nativeImageResponseFailed(const ImageLoadError &loadError) const;
private:
/*
* Resets the cached image data pointers. Needs to be protected by mutex_.
*/
void consumeResponse() const;
/*
* List of observers.
* Mutable: protected by mutex_.
*/
mutable std::vector<const ImageResponseObserver *> observers_;
/*
* Current status of image loading.
* Mutable: protected by mutex_.
*/
mutable ImageResponse::Status status_{ImageResponse::Status::Loading};
/*
* Cache image data.
* Mutable: protected by mutex_.
*/
mutable std::shared_ptr<void> imageData_;
/*
* Cache image metadata.
* Mutable: protected by mutex_.
*/
mutable std::shared_ptr<void> imageMetadata_;
/*
* Cache image error Data.
* Mutable: protected by mutex_.
*/
mutable std::shared_ptr<void> imageErrorData_;
/*
* Observer and data mutex.
*/
mutable std::mutex mutex_;
/*
* Function we can call to resume image request.
*/
SharedFunction<> resumeRequest_;
/*
* Function we can call to cancel image request.
*/
SharedFunction<> cancelRequest_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageTelemetry.h"
namespace facebook::react {
SurfaceId ImageTelemetry::getSurfaceId() const {
return surfaceId_;
}
TelemetryTimePoint ImageTelemetry::getWillRequestUrlTime() const {
return willRequestUrlTime_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/core/ReactPrimitives.h>
#include <react/utils/Telemetry.h>
namespace facebook::react {
/*
* Represents telemetry data associated with a image request
* where the willRequestUrlTime is the time at ImageTelemetry's creation.
*/
class ImageTelemetry final {
public:
ImageTelemetry(const SurfaceId surfaceId) : surfaceId_(surfaceId)
{
willRequestUrlTime_ = telemetryTimePointNow();
}
TelemetryTimePoint getWillRequestUrlTime() const;
SurfaceId getSurfaceId() const;
private:
TelemetryTimePoint willRequestUrlTime_;
const SurfaceId surfaceId_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageFetcher.h"
#include <react/common/mapbuffer/JReadableMapBuffer.h>
#include <react/renderer/imagemanager/conversions.h>
namespace facebook::react {
ImageFetcher::ImageFetcher(
std::shared_ptr<const ContextContainer> contextContainer)
: contextContainer_(std::move(contextContainer)) {}
ImageRequest ImageFetcher::requestImage(
const ImageSource& imageSource,
SurfaceId surfaceId,
const ImageRequestParams& imageRequestParams,
Tag tag) {
items_[surfaceId].emplace_back(
ImageRequestItem{
.imageSource = imageSource,
.imageRequestParams = imageRequestParams,
.tag = tag});
auto telemetry = std::make_shared<ImageTelemetry>(surfaceId);
flushImageRequests();
return {imageSource, telemetry};
}
void ImageFetcher::flushImageRequests() {
if (items_.empty()) {
return;
}
auto fabricUIManager_ =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto prefetchResources =
fabricUIManager_->getClass()
->getMethod<void(
SurfaceId, std::string, JReadableMapBuffer::javaobject)>(
"experimental_prefetchResources");
for (auto& [surfaceId, surfaceImageRequests] : items_) {
auto readableMapBuffer = JReadableMapBuffer::createWithContents(
serializeImageRequests(surfaceImageRequests));
prefetchResources(
fabricUIManager_, surfaceId, "RCTImageView", readableMapBuffer.get());
}
items_.clear();
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/imagemanager/ImageRequest.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/utils/ContextContainer.h>
#include <memory>
#include <unordered_map>
#include <vector>
namespace facebook::react {
class ImageFetcher {
public:
ImageFetcher(std::shared_ptr<const ContextContainer> contextContainer);
~ImageFetcher() = default;
ImageFetcher(const ImageFetcher &) = delete;
ImageFetcher &operator=(const ImageFetcher &) = delete;
ImageFetcher(ImageFetcher &&) = delete;
ImageFetcher &operator=(ImageFetcher &&) = delete;
ImageRequest requestImage(
const ImageSource &imageSource,
SurfaceId surfaceId,
const ImageRequestParams &imageRequestParams,
Tag tag);
private:
void flushImageRequests();
std::unordered_map<SurfaceId, std::vector<ImageRequestItem>> items_;
std::shared_ptr<const ContextContainer> contextContainer_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageManager.h"
#include "ImageFetcher.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
namespace facebook::react {
namespace {
constexpr inline bool isInteger(const std::string& str) {
return str.find_first_not_of("0123456789") == std::string::npos;
}
} // namespace
ImageManager::ImageManager(
const std::shared_ptr<const ContextContainer>& contextContainer)
: self_(new ImageFetcher(contextContainer)) {}
ImageManager::~ImageManager() {
delete static_cast<ImageFetcher*>(self_);
}
ImageRequest ImageManager::requestImage(
const ImageSource& imageSource,
SurfaceId surfaceId,
const ImageRequestParams& imageRequestParams,
Tag tag) const {
if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
if (!isInteger(imageSource.uri)) {
return static_cast<ImageFetcher*>(self_)->requestImage(
imageSource, surfaceId, imageRequestParams, tag);
}
}
return {imageSource, nullptr, {}};
}
} // namespace facebook::react

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <utility>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
class ImageRequestParams {
public:
ImageRequestParams() = default;
ImageRequestParams(
Float blurRadius,
ImageSource defaultSource,
ImageResizeMode resizeMode,
std::string resizeMethod,
Float resizeMultiplier,
bool shouldNotifyLoadEvents,
SharedColor overlayColor,
SharedColor tintColor,
Float fadeDuration,
bool progressiveRenderingEnabled,
ImageSource loadingIndicatorSource,
std::string analyticTag)
: blurRadius(blurRadius),
defaultSource(std::move(defaultSource)),
resizeMode(resizeMode),
resizeMethod(std::move(resizeMethod)),
resizeMultiplier(resizeMultiplier),
shouldNotifyLoadEvents(shouldNotifyLoadEvents),
overlayColor(overlayColor),
tintColor(tintColor),
fadeDuration(fadeDuration),
progressiveRenderingEnabled(progressiveRenderingEnabled),
loadingIndicatorSource(std::move(loadingIndicatorSource)),
analyticTag(std::move(analyticTag))
{
}
Float blurRadius{};
ImageSource defaultSource{};
ImageResizeMode resizeMode{ImageResizeMode::Stretch};
std::string resizeMethod{};
Float resizeMultiplier{};
bool shouldNotifyLoadEvents{};
SharedColor overlayColor{};
SharedColor tintColor{};
Float fadeDuration{};
bool progressiveRenderingEnabled{};
ImageSource loadingIndicatorSource{};
std::string analyticTag{};
bool operator==(const ImageRequestParams &rhs) const
{
return std::tie(
this->blurRadius,
this->defaultSource,
this->resizeMode,
this->resizeMethod,
this->resizeMultiplier,
this->shouldNotifyLoadEvents,
this->overlayColor,
this->tintColor,
this->fadeDuration,
this->progressiveRenderingEnabled,
this->loadingIndicatorSource,
this->analyticTag) ==
std::tie(
rhs.blurRadius,
rhs.defaultSource,
rhs.resizeMode,
rhs.resizeMethod,
rhs.resizeMultiplier,
rhs.shouldNotifyLoadEvents,
rhs.overlayColor,
rhs.tintColor,
rhs.fadeDuration,
rhs.progressiveRenderingEnabled,
rhs.loadingIndicatorSource,
rhs.analyticTag);
}
bool operator!=(const ImageRequestParams &rhs) const
{
return !(*this == rhs);
}
};
struct ImageRequestItem {
ImageSource imageSource;
ImageRequestParams imageRequestParams;
Tag tag{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
#include <react/utils/to_underlying.h>
#include <vector>
namespace facebook::react {
namespace {
constexpr MapBuffer::Key IS_KEY_URI = 0;
constexpr MapBuffer::Key IS_KEY_DEFAULT_SRC = 1;
constexpr MapBuffer::Key IS_KEY_RESIZE_MODE = 2;
constexpr MapBuffer::Key IS_KEY_RESIZE_METHOD = 3;
constexpr MapBuffer::Key IS_KEY_BLUR_RADIUS = 4;
constexpr MapBuffer::Key IS_KEY_VIEW_WIDTH = 5;
constexpr MapBuffer::Key IS_KEY_VIEW_HEIGHT = 6;
constexpr MapBuffer::Key IS_KEY_RESIZE_MULTIPLIER = 7;
constexpr MapBuffer::Key IS_KEY_SHOULD_NOTIFY_LOAD_EVENTS = 8;
constexpr MapBuffer::Key IS_KEY_OVERLAY_COLOR = 9;
constexpr MapBuffer::Key IS_KEY_TINT_COLOR = 10;
constexpr MapBuffer::Key IS_KEY_FADE_DURATION = 11;
constexpr MapBuffer::Key IS_KEY_PROGRESSIVE_RENDERING_ENABLED = 12;
constexpr MapBuffer::Key IS_KEY_LOADING_INDICATOR_SRC = 13;
constexpr MapBuffer::Key IS_KEY_ANALYTIC_TAG = 14;
constexpr MapBuffer::Key IS_KEY_TAG = 15;
inline void serializeImageSource(MapBufferBuilder &builder, const ImageSource &imageSource)
{
builder.putString(IS_KEY_URI, imageSource.uri);
builder.putInt(IS_KEY_VIEW_WIDTH, static_cast<int32_t>(imageSource.size.width));
builder.putInt(IS_KEY_VIEW_HEIGHT, static_cast<int32_t>(imageSource.size.height));
}
inline void serializeImageRequestParams(MapBufferBuilder &builder, const ImageRequestParams &imageRequestParams)
{
builder.putString(IS_KEY_DEFAULT_SRC, imageRequestParams.defaultSource.uri);
builder.putInt(IS_KEY_RESIZE_MODE, to_underlying(imageRequestParams.resizeMode));
builder.putString(IS_KEY_RESIZE_METHOD, imageRequestParams.resizeMethod);
builder.putDouble(IS_KEY_BLUR_RADIUS, imageRequestParams.blurRadius);
builder.putDouble(IS_KEY_RESIZE_MULTIPLIER, imageRequestParams.resizeMultiplier);
builder.putBool(IS_KEY_SHOULD_NOTIFY_LOAD_EVENTS, imageRequestParams.shouldNotifyLoadEvents);
if (isColorMeaningful(imageRequestParams.overlayColor)) {
builder.putInt(IS_KEY_OVERLAY_COLOR, toAndroidRepr(imageRequestParams.overlayColor));
}
if (isColorMeaningful(imageRequestParams.tintColor)) {
builder.putInt(IS_KEY_TINT_COLOR, toAndroidRepr(imageRequestParams.tintColor));
}
builder.putInt(IS_KEY_FADE_DURATION, static_cast<int32_t>(imageRequestParams.fadeDuration));
builder.putBool(IS_KEY_PROGRESSIVE_RENDERING_ENABLED, imageRequestParams.progressiveRenderingEnabled);
builder.putString(IS_KEY_LOADING_INDICATOR_SRC, imageRequestParams.loadingIndicatorSource.uri);
builder.putString(IS_KEY_ANALYTIC_TAG, imageRequestParams.analyticTag);
}
inline MapBuffer serializeImageRequest(const ImageRequestItem &item)
{
auto builder = MapBufferBuilder();
serializeImageSource(builder, item.imageSource);
serializeImageRequestParams(builder, item.imageRequestParams);
builder.putInt(IS_KEY_TAG, item.tag);
return builder.build();
}
} // namespace
inline MapBuffer serializeImageRequests(const std::vector<ImageRequestItem> &items)
{
std::vector<MapBuffer> mapBufferList;
mapBufferList.reserve(items.size());
for (const auto &item : items) {
mapBufferList.emplace_back(serializeImageRequest(item));
}
MapBufferBuilder builder;
builder.putMapBufferList(0, mapBufferList);
return builder.build();
}
} // namespace facebook::react

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageManager.h"
namespace facebook::react {
ImageManager::ImageManager(
const std::shared_ptr<const ContextContainer>& /*contextContainer*/) {
// Silence unused-private-field warning.
(void)self_;
// Not implemented.
}
ImageManager::~ImageManager() {
// Not implemented.
}
ImageRequest ImageManager::requestImage(
const ImageSource& imageSource,
SurfaceId /*surfaceId*/,
const ImageRequestParams& /*imageRequestParams*/,
Tag /*tag*/) const {
// Not implemented.
return {imageSource, nullptr, {}};
}
} // namespace facebook::react

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
class ImageRequestParams {
public:
ImageRequestParams() = default;
explicit ImageRequestParams(Float blurRadius) : blurRadius(blurRadius) {}
Float blurRadius{};
bool operator==(const ImageRequestParams &rhs) const
{
return this->blurRadius == rhs.blurRadius;
}
bool operator!=(const ImageRequestParams &rhs) const
{
return !(*this == rhs);
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,56 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |s|
source_files = "**/*.{m,mm,cpp,h}"
header_search_paths = [
"\"$(PODS_TARGET_SRCROOT)/../../../\"",
"\"$(PODS_TARGET_SRCROOT)\"",
].join(" ")
s.name = "React-ImageManager"
s.version = version
s.summary = "Fabric for React Native."
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = podspec_sources(source_files, "**/*.h")
s.header_dir = "react/renderer/imagemanager"
resolve_use_frameworks(s, header_mappings_dir: "./", module_name: "React_ImageManager")
s.pod_target_xcconfig = {
"USE_HEADERMAP" => "NO",
"HEADER_SEARCH_PATHS" => header_search_paths,
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"DEFINES_MODULE" => "YES",
}
s.dependency "React-Core/Default"
add_dependency(s, "React-Fabric")
add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"])
add_dependency(s, "React-debug")
add_dependency(s, "React-utils")
add_dependency(s, "React-rendererdebug")
add_rn_third_party_dependencies(s)
add_rncore_dependency(s)
end

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <react/renderer/imagemanager/ImageManager.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <React/RCTUtils.h>
#import <react/utils/ManagedObjectWrapper.h>
#import "RCTImageManager.h"
#import "RCTSyncImageManager.h"
namespace facebook::react {
ImageManager::ImageManager(const std::shared_ptr<const ContextContainer> &contextContainer)
{
id<RCTImageLoaderWithAttributionProtocol> imageLoader =
(id<RCTImageLoaderWithAttributionProtocol>)unwrapManagedObject(
contextContainer->at<std::shared_ptr<void>>("RCTImageLoader"));
if (RCTRunningInTestEnvironment()) {
self_ = (__bridge_retained void *)[[RCTSyncImageManager alloc] initWithImageLoader:imageLoader];
} else {
self_ = (__bridge_retained void *)[[RCTImageManager alloc] initWithImageLoader:imageLoader];
}
}
ImageManager::~ImageManager()
{
CFRelease(self_);
self_ = nullptr;
}
ImageRequest ImageManager::requestImage(
const ImageSource &imageSource,
SurfaceId surfaceId,
const ImageRequestParams & /*imageRequestParams*/,
Tag /*tag*/) const
{
RCTImageManager *imageManager = (__bridge RCTImageManager *)self_;
return [imageManager requestImage:imageSource surfaceId:surfaceId];
}
} // namespace facebook::react

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
class ImageRequestParams {
public:
ImageRequestParams() {}
ImageRequestParams(Float blurRadius) : blurRadius(blurRadius) {}
Float blurRadius{};
bool operator==(const ImageRequestParams &rhs) const
{
return this->blurRadius == rhs.blurRadius;
}
bool operator!=(const ImageRequestParams &rhs) const
{
return !(*this == rhs);
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "RCTImageManagerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@protocol RCTImageLoaderWithAttributionProtocol;
/**
* iOS-specific ImageManager.
*/
@interface RCTImageManager : NSObject <RCTImageManagerProtocol>
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader;
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTImageManager.h"
#import <cxxreact/TraceSection.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <react/utils/SharedFunction.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <react/renderer/imagemanager/ImageResponse.h>
#import <react/renderer/imagemanager/ImageResponseObserver.h>
#import "RCTImagePrimitivesConversions.h"
using namespace facebook::react;
@implementation RCTImageManager {
id<RCTImageLoaderWithAttributionProtocol> _imageLoader;
dispatch_queue_t _backgroundSerialQueue;
}
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader
{
if (self = [super init]) {
_imageLoader = imageLoader;
_backgroundSerialQueue =
dispatch_queue_create("com.facebook.react-native.image-manager-queue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId
{
TraceSection s("RCTImageManager::requestImage");
NSURLRequest *request = NSURLRequestFromImageSource(imageSource);
std::shared_ptr<ImageTelemetry> telemetry;
if ([self->_imageLoader shouldEnablePerfLoggingForRequestUrl:request.URL]) {
telemetry = std::make_shared<ImageTelemetry>(surfaceId);
} else {
telemetry = nullptr;
}
auto sharedResumeFunction = SharedFunction<>();
auto sharedCancelationFunction = SharedFunction<>();
auto imageRequest = ImageRequest(imageSource, telemetry, sharedResumeFunction, sharedCancelationFunction);
auto weakObserverCoordinator =
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
/*
* Even if an image is being loaded asynchronously on some other background thread, some other preparation
* work (such as creating an `NSURLRequest` object and some obscure logic inside `RCTImageLoader`) can take a couple
* of milliseconds, so we have to offload this to a separate thread. `ImageRequest` can be created as part of the
* layout process, so it must be highly performant.
*
* Technically, we don't need to dispatch this to *serial* queue. The interface of `RCTImageLoader` promises to be
* fully thread-safe. However, in reality, it crashes when we request images on concurrently on different threads. See
* T46024425 for more details.
*/
dispatch_async(_backgroundSerialQueue, ^{
auto completionBlock = ^(NSError *error, UIImage *image, id metadata) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
if (image && !error) {
auto wrappedMetadata = metadata ? wrapManagedObject(metadata) : nullptr;
observerCoordinator->nativeImageResponseComplete(ImageResponse(wrapManagedObject(image), wrappedMetadata));
} else {
auto wrappedError = error ? wrapManagedObject(error) : nullptr;
observerCoordinator->nativeImageResponseFailed(ImageLoadError(wrappedError));
}
};
auto progressBlock = ^(int64_t progress, int64_t total) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
observerCoordinator->nativeImageResponseProgress((float)progress / (float)total, progress, total);
};
void (^startRequest)() = ^() {
RCTImageURLLoaderRequest *loaderRequest =
[self->_imageLoader loadImageWithURLRequest:request
size:CGSizeMake(imageSource.size.width, imageSource.size.height)
scale:imageSource.scale
clipped:NO
resizeMode:RCTResizeModeStretch
priority:RCTImageLoaderPriorityImmediate
attribution:{
.surfaceId = surfaceId,
}
progressBlock:progressBlock
partialLoadBlock:nil
completionBlock:completionBlock];
RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock;
sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); });
};
startRequest();
sharedResumeFunction.assign([startRequest]() { startRequest(); });
});
return imageRequest;
}
@end

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <react/renderer/core/ReactPrimitives.h>
#import <react/renderer/imagemanager/ImageRequest.h>
@protocol RCTImageManagerProtocol <NSObject>
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTImageLoader.h>
#import <react/renderer/imagemanager/primitives.h>
inline static UIViewContentMode RCTContentModeFromImageResizeMode(facebook::react::ImageResizeMode imageResizeMode)
{
switch (imageResizeMode) {
case facebook::react::ImageResizeMode::Cover:
return UIViewContentModeScaleAspectFill;
case facebook::react::ImageResizeMode::Contain:
return UIViewContentModeScaleAspectFit;
case facebook::react::ImageResizeMode::Stretch:
return UIViewContentModeScaleToFill;
case facebook::react::ImageResizeMode::Center:
return UIViewContentModeCenter;
case facebook::react::ImageResizeMode::Repeat:
// Repeat resize mode is handled by the UIImage. Use scale to fill
// so the repeated image fills the UIImageView.
return UIViewContentModeScaleToFill;
case facebook::react::ImageResizeMode::None:
return UIViewContentModeTopLeft;
}
}
inline std::string toString(const facebook::react::ImageResizeMode &value)
{
switch (value) {
case facebook::react::ImageResizeMode::Cover:
return "cover";
case facebook::react::ImageResizeMode::Contain:
return "contain";
case facebook::react::ImageResizeMode::Stretch:
return "stretch";
case facebook::react::ImageResizeMode::Center:
return "center";
case facebook::react::ImageResizeMode::Repeat:
return "repeat";
case facebook::react::ImageResizeMode::None:
return "none";
}
}
inline static NSURLRequestCachePolicy NSURLRequestCachePolicyFromImageSource(
const facebook::react::ImageSource &imageSource)
{
switch (imageSource.cache) {
case facebook::react::ImageSource::CacheStategy::Reload:
return NSURLRequestReloadIgnoringLocalCacheData;
break;
case facebook::react::ImageSource::CacheStategy::ForceCache:
return NSURLRequestReturnCacheDataElseLoad;
break;
case facebook::react::ImageSource::CacheStategy::OnlyIfCached:
return NSURLRequestReturnCacheDataDontLoad;
break;
default:
return NSURLRequestUseProtocolCachePolicy;
break;
}
}
inline static NSURL *NSURLFromImageSource(const facebook::react::ImageSource &imageSource)
{
// `NSURL` has a history of crashing with bad input, so let's be safe.
@try {
NSString *urlString = [NSString stringWithUTF8String:imageSource.uri.c_str()];
if (!imageSource.bundle.empty()) {
NSString *bundle = [NSString stringWithUTF8String:imageSource.bundle.c_str()];
urlString = [NSString stringWithFormat:@"%@.bundle/%@", bundle, urlString];
}
NSURL *url = [[NSURL alloc] initWithString:urlString];
if (url.scheme != nullptr) {
// Well-formed absolute URL.
return url;
}
if ([urlString rangeOfString:@":"].location != NSNotFound) {
// The URL has a scheme.
urlString =
[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
url = [NSURL URLWithString:urlString];
return url;
}
// Assume that it's a local path.
urlString = [urlString stringByRemovingPercentEncoding];
if ([urlString hasPrefix:@"~"]) {
// Path is inside user directory.
urlString = [urlString stringByExpandingTildeInPath];
} else {
if (![urlString isAbsolutePath]) {
// Assume it's a resource path.
urlString = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:urlString];
}
}
url = [NSURL fileURLWithPath:urlString];
return url;
} @catch (__unused NSException *exception) {
return nil;
}
}
inline static NSURLRequest *NSURLRequestFromImageSource(const facebook::react::ImageSource &imageSource)
{
NSURL *url = NSURLFromImageSource(imageSource);
if (url == nullptr) {
RCTLogError(@"URI parsing error.");
return nil;
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSString *method = @"GET";
if (!imageSource.method.empty()) {
method = [[NSString alloc] initWithUTF8String:imageSource.method.c_str()].uppercaseString;
}
NSData *body = nil;
if (!imageSource.body.empty()) {
body = [NSData dataWithBytes:imageSource.body.c_str() length:imageSource.body.size()];
}
NSURLRequestCachePolicy cachePolicy = NSURLRequestCachePolicyFromImageSource(imageSource);
if ([method isEqualToString:@"GET"] && imageSource.headers.empty() && body == nil &&
cachePolicy == NSURLRequestUseProtocolCachePolicy) {
return request;
}
for (const auto &header : imageSource.headers) {
NSString *key = [NSString stringWithUTF8String:header.first.c_str()];
NSString *value = [NSString stringWithUTF8String:header.second.c_str()];
if (key != nullptr && value != nullptr) {
[request setValue:value forHTTPHeaderField:key];
}
}
request.HTTPBody = body;
request.HTTPMethod = method;
request.cachePolicy = cachePolicy;
return request;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "RCTImageManagerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@protocol RCTImageLoaderWithAttributionProtocol;
/**
* iOS-specific ImageManager.
*/
@interface RCTSyncImageManager : NSObject <RCTImageManagerProtocol>
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader;
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSyncImageManager.h"
#import <react/utils/ManagedObjectWrapper.h>
#import <react/utils/SharedFunction.h>
#import <React/RCTAssert.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <React/RCTLog.h>
#import <react/renderer/imagemanager/ImageResponse.h>
#import <react/renderer/imagemanager/ImageResponseObserver.h>
#import "RCTImagePrimitivesConversions.h"
using namespace facebook::react;
@implementation RCTSyncImageManager {
id<RCTImageLoaderWithAttributionProtocol> _imageLoader;
}
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader
{
if (self = [super init]) {
RCTAssert(RCTRunningInTestEnvironment(), @"This class is only meant to be used in test environment");
_imageLoader = imageLoader;
}
return self;
}
- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId
{
auto telemetry = std::make_shared<ImageTelemetry>(surfaceId);
auto sharedCancelationFunction = SharedFunction<>();
// Sync image request is not cancellable so it does not need to be resumed.
auto sharedResumeFunction = SharedFunction<>();
auto imageRequest = ImageRequest(imageSource, telemetry, sharedResumeFunction, sharedCancelationFunction);
auto weakObserverCoordinator =
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
dispatch_group_t imageWaitGroup = dispatch_group_create();
dispatch_group_enter(imageWaitGroup);
NSURLRequest *request = NSURLRequestFromImageSource(imageSource);
auto completionBlock = ^(NSError *error, UIImage *image, id metadata) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
if (image && !error) {
auto wrappedMetadata = metadata ? wrapManagedObject(metadata) : nullptr;
observerCoordinator->nativeImageResponseComplete(ImageResponse(wrapManagedObject(image), wrappedMetadata));
} else {
auto wrappedError = error ? wrapManagedObject(error) : nullptr;
observerCoordinator->nativeImageResponseFailed(ImageLoadError(wrappedError));
}
dispatch_group_leave(imageWaitGroup);
};
auto progressBlock = ^(int64_t progress, int64_t total) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
observerCoordinator->nativeImageResponseProgress((float)progress / (float)total, progress, total);
};
RCTImageURLLoaderRequest *loaderRequest =
[self->_imageLoader loadImageWithURLRequest:request
size:CGSizeMake(imageSource.size.width, imageSource.size.height)
scale:imageSource.scale
clipped:YES
resizeMode:RCTResizeModeStretch
priority:RCTImageLoaderPriorityImmediate
attribution:{
.surfaceId = surfaceId,
}
progressBlock:progressBlock
partialLoadBlock:nil
completionBlock:completionBlock];
auto result = dispatch_group_wait(imageWaitGroup, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
if (result != 0) {
RCTLogError(@"Image timed out in test environment for url: %@", loaderRequest.imageURL);
}
return imageRequest;
}
@end

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string>
#include <vector>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/core/propsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Size.h>
namespace facebook::react {
class ImageSource {
public:
enum class Type { Invalid, Remote, Local };
enum class CacheStategy { Default, Reload, ForceCache, OnlyIfCached };
Type type{};
std::string uri{};
std::string bundle{};
Float scale{3};
Size size{.width = 0};
std::string body{};
std::string method{};
CacheStategy cache = CacheStategy::Default;
std::vector<std::pair<std::string, std::string>> headers{};
bool operator==(const ImageSource &rhs) const
{
return std::tie(this->type, this->uri) == std::tie(rhs.type, rhs.uri);
}
bool operator!=(const ImageSource &rhs) const
{
return !(*this == rhs);
}
#ifdef RN_SERIALIZABLE_STATE
folly::dynamic toDynamic() const
{
folly::dynamic imageSourceResult = folly::dynamic::object();
switch (type) {
case ImageSource::Type::Invalid:
imageSourceResult["type"] = "invalid";
break;
case ImageSource::Type::Remote:
imageSourceResult["type"] = "remote";
break;
case ImageSource::Type::Local:
imageSourceResult["type"] = "local";
break;
}
imageSourceResult["uri"] = uri;
imageSourceResult["bundle"] = bundle;
imageSourceResult["scale"] = scale;
folly::dynamic sizeResult = folly::dynamic::object();
sizeResult["width"] = size.width;
sizeResult["height"] = size.height;
imageSourceResult["size"] = sizeResult;
imageSourceResult["body"] = body;
imageSourceResult["method"] = method;
switch (cache) {
case ImageSource::CacheStategy::Default:
imageSourceResult["cache"] = "default";
break;
case ImageSource::CacheStategy::Reload:
imageSourceResult["cache"] = "reload";
break;
case ImageSource::CacheStategy::ForceCache:
imageSourceResult["cache"] = "force-cache";
break;
case ImageSource::CacheStategy::OnlyIfCached:
imageSourceResult["cache"] = "only-if-cached";
break;
}
folly::dynamic headersObject = folly::dynamic::object();
for (const auto &header : headers) {
headersObject[header.first] = header.second;
}
imageSourceResult["headers"] = headersObject;
return imageSourceResult;
}
#endif
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps(const std::string &prefix) const
{
ImageSource imageSource{};
SharedDebugStringConvertibleList headersList;
for (const auto &header : headers) {
headersList.push_back(debugStringConvertibleItem(prefix + "-header-" + header.first, header.second));
}
return headersList +
SharedDebugStringConvertibleList{
debugStringConvertibleItem(prefix + "-type", toString(type), toString(imageSource.type)),
debugStringConvertibleItem(prefix + "-uri", uri, imageSource.uri),
debugStringConvertibleItem(prefix + "-bundle", bundle, imageSource.bundle),
debugStringConvertibleItem(prefix + "-scale", scale, imageSource.scale),
debugStringConvertibleItem(prefix + "-size", react::toString(size), react::toString(imageSource.size)),
debugStringConvertibleItem(prefix + "-body", body, imageSource.body),
debugStringConvertibleItem(prefix + "-method", method, imageSource.method),
debugStringConvertibleItem(prefix + "-cache", toString(cache), toString(imageSource.cache)),
};
}
std::string toString(const Type &typeValue) const
{
switch (typeValue) {
case ImageSource::Type::Invalid:
return "invalid";
case ImageSource::Type::Remote:
return "remote";
case ImageSource::Type::Local:
return "local";
}
}
std::string toString(const CacheStategy &cacheValue) const
{
switch (cacheValue) {
case ImageSource::CacheStategy::Default:
return "default";
case ImageSource::CacheStategy::Reload:
return "reload";
case ImageSource::CacheStategy::ForceCache:
return "force-cache";
case ImageSource::CacheStategy::OnlyIfCached:
return "only-if-cached";
}
}
#endif
};
#ifdef RN_SERIALIZABLE_STATE
inline folly::dynamic toDynamic(const ImageSource &imageSource)
{
return imageSource.toDynamic();
}
#endif
using ImageSources = std::vector<ImageSource>;
enum class ImageResizeMode : int8_t {
Cover = 0,
Contain = 1,
Stretch = 2,
Center = 3,
Repeat = 4,
None = 5,
};
class ImageErrorInfo {
public:
std::string error{};
int responseCode{};
std::vector<std::pair<std::string, std::string>> httpResponseHeaders{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <gtest/gtest.h>
#include <react/renderer/imagemanager/ImageManager.h>
using namespace facebook::react;
TEST(ImageManagerTest, testSomething) {
// TODO:
}