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,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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_image_SRC CONFIGURE_DEPENDS *.cpp)
add_library(rrc_image OBJECT ${rrc_image_SRC})
target_include_directories(rrc_image PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(rrc_image
glog
folly_runtime
glog_init
jsi
react_debug
react_utils
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_imagemanager
react_renderer_mapbuffer
rrc_view
yoga
)
target_compile_reactnative_options(rrc_image PRIVATE)
target_compile_options(rrc_image PRIVATE -Wpedantic)

View File

@@ -0,0 +1,31 @@
/*
* 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 "ImageComponentDescriptor.h"
#include <react/renderer/imagemanager/ImageManager.h>
namespace facebook::react {
extern const char ImageManagerKey[] = "ImageManager";
ImageComponentDescriptor::ImageComponentDescriptor(
const ComponentDescriptorParameters& parameters)
: ConcreteComponentDescriptor(parameters),
imageManager_(
getManagerByName<ImageManager>(contextContainer_, ImageManagerKey)) {
};
void ImageComponentDescriptor::adopt(ShadowNode& shadowNode) const {
ConcreteComponentDescriptor::adopt(shadowNode);
auto& imageShadowNode = static_cast<ImageShadowNode&>(shadowNode);
// `ImageShadowNode` uses `ImageManager` to initiate image loading and
// communicate the loading state and results to mounting layer.
imageShadowNode.setImageManager(imageManager_);
}
} // 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/components/image/ImageShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
class ImageManager;
extern const char ImageManagerKey[];
/*
* Descriptor for <Image> component.
*/
class ImageComponentDescriptor final : public ConcreteComponentDescriptor<ImageShadowNode> {
public:
explicit ImageComponentDescriptor(const ComponentDescriptorParameters &parameters);
void adopt(ShadowNode &shadowNode) const override;
private:
const std::shared_ptr<ImageManager> imageManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,69 @@
/*
* 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 "ImageEventEmitter.h"
namespace facebook::react {
void ImageEventEmitter::onLoadStart() const {
dispatchEvent("loadStart");
}
void ImageEventEmitter::onLoad(const ImageSource& source) const {
dispatchEvent("load", [source](jsi::Runtime& runtime) {
auto src = jsi::Object(runtime);
src.setProperty(runtime, "uri", source.uri);
src.setProperty(runtime, "width", source.size.width * source.scale);
src.setProperty(runtime, "height", source.size.height * source.scale);
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "source", src);
return payload;
});
}
void ImageEventEmitter::onLoadEnd() const {
dispatchEvent("loadEnd");
}
void ImageEventEmitter::onProgress(
double progress,
int64_t loaded,
int64_t total) const {
dispatchEvent("progress", [progress, loaded, total](jsi::Runtime& runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "progress", progress);
payload.setProperty(runtime, "loaded", (double)loaded);
payload.setProperty(runtime, "total", (double)total);
return payload;
});
}
void ImageEventEmitter::onError(const ImageErrorInfo& error) const {
dispatchEvent("error", [error](jsi::Runtime& runtime) {
auto payload = jsi::Object(runtime);
if (!error.error.empty()) {
payload.setProperty(runtime, "error", error.error);
}
if (error.responseCode != 0) {
payload.setProperty(runtime, "responseCode", error.responseCode);
}
if (!error.httpResponseHeaders.empty()) {
auto headers = jsi::Object(runtime);
for (const auto& x : error.httpResponseHeaders) {
headers.setProperty(runtime, x.first.c_str(), x.second);
}
payload.setProperty(runtime, "httpResponseHeaders", headers);
}
return payload;
});
}
void ImageEventEmitter::onPartialLoad() const {
dispatchEvent("partialLoad");
}
} // 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/components/view/ViewEventEmitter.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
class ImageEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
void onLoadStart() const;
void onLoad(const ImageSource &source) const;
void onLoadEnd() const;
void onProgress(double progress, int64_t loaded, int64_t total) const;
void onError(const ImageErrorInfo &error) const;
void onPartialLoad() const;
};
} // namespace facebook::react

View File

@@ -0,0 +1,335 @@
/*
* 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/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/image/ImageProps.h>
#include <react/renderer/components/image/conversions.h>
#include <react/renderer/core/propsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
namespace facebook::react {
ImageProps::ImageProps(
const PropsParserContext& context,
const ImageProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
sources(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.sources
: convertRawProp(
context,
rawProps,
"source",
sourceProps.sources,
{})),
defaultSource(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.defaultSource
: convertRawProp(
context,
rawProps,
"defaultSource",
sourceProps.defaultSource,
{})),
loadingIndicatorSource(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.loadingIndicatorSource
: convertRawProp(
context,
rawProps,
"loadingIndicatorSource",
sourceProps.loadingIndicatorSource,
{})),
resizeMode(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMode
: convertRawProp(
context,
rawProps,
"resizeMode",
sourceProps.resizeMode,
ImageResizeMode::Stretch)),
blurRadius(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.blurRadius
: convertRawProp(
context,
rawProps,
"blurRadius",
sourceProps.blurRadius,
{})),
capInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.capInsets
: convertRawProp(
context,
rawProps,
"capInsets",
sourceProps.capInsets,
{})),
tintColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.tintColor
: convertRawProp(
context,
rawProps,
"tintColor",
sourceProps.tintColor,
{})),
internal_analyticTag(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.internal_analyticTag
: convertRawProp(
context,
rawProps,
"internal_analyticTag",
sourceProps.internal_analyticTag,
{})),
resizeMethod(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMethod
: convertRawProp(
context,
rawProps,
"resizeMethod",
sourceProps.resizeMethod,
{})),
resizeMultiplier(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMultiplier
: convertRawProp(
context,
rawProps,
"resizeMultiplier",
sourceProps.resizeMultiplier,
{})),
shouldNotifyLoadEvents(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.shouldNotifyLoadEvents
: convertRawProp(
context,
rawProps,
"shouldNotifyLoadEvents",
sourceProps.shouldNotifyLoadEvents,
{})),
overlayColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.overlayColor
: convertRawProp(
context,
rawProps,
"overlayColor",
sourceProps.overlayColor,
{})),
fadeDuration(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.fadeDuration
: convertRawProp(
context,
rawProps,
"fadeDuration",
sourceProps.fadeDuration,
{})),
progressiveRenderingEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.progressiveRenderingEnabled
: convertRawProp(
context,
rawProps,
"progressiveRenderingEnabled",
sourceProps.progressiveRenderingEnabled,
{})) {}
void ImageProps::setProp(
const PropsParserContext& context,
RawPropsPropNameHash hash,
const char* propName,
const RawValue& value) {
// All Props structs setProp methods must always, unconditionally,
// call all super::setProp methods, since multiple structs may
// reuse the same values.
ViewProps::setProp(context, hash, propName, value);
static auto defaults = ImageProps{};
switch (hash) {
RAW_SET_PROP_SWITCH_CASE(sources, "source");
RAW_SET_PROP_SWITCH_CASE(defaultSource, "defaultSource");
RAW_SET_PROP_SWITCH_CASE(loadingIndicatorSource, "loadingIndicatorSource");
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMode);
RAW_SET_PROP_SWITCH_CASE_BASIC(blurRadius);
RAW_SET_PROP_SWITCH_CASE_BASIC(capInsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(tintColor);
RAW_SET_PROP_SWITCH_CASE_BASIC(internal_analyticTag);
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMethod);
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMultiplier);
RAW_SET_PROP_SWITCH_CASE_BASIC(shouldNotifyLoadEvents);
RAW_SET_PROP_SWITCH_CASE_BASIC(overlayColor);
RAW_SET_PROP_SWITCH_CASE_BASIC(fadeDuration);
RAW_SET_PROP_SWITCH_CASE_BASIC(progressiveRenderingEnabled);
}
}
#ifdef RN_SERIALIZABLE_STATE
static folly::dynamic convertEdgeInsets(const EdgeInsets& edgeInsets) {
folly::dynamic edgeInsetsResult = folly::dynamic::object();
edgeInsetsResult["left"] = edgeInsets.left;
edgeInsetsResult["top"] = edgeInsets.top;
edgeInsetsResult["right"] = edgeInsets.right;
edgeInsetsResult["bottom"] = edgeInsets.bottom;
return edgeInsetsResult;
}
ComponentName ImageProps::getDiffPropsImplementationTarget() const {
return "Image";
}
folly::dynamic ImageProps::getDiffProps(const Props* prevProps) const {
static const auto defaultProps = ImageProps();
const ImageProps* oldProps = prevProps == nullptr
? &defaultProps
: static_cast<const ImageProps*>(prevProps);
folly::dynamic result = ViewProps::getDiffProps(oldProps);
if (sources != oldProps->sources) {
auto sourcesArray = folly::dynamic::array();
for (const auto& source : sources) {
sourcesArray.push_back(toDynamic(source));
}
result["source"] = sourcesArray;
}
if (defaultSource != oldProps->defaultSource) {
result["defaultSource"] = toDynamic(defaultSource);
}
if (loadingIndicatorSource != oldProps->loadingIndicatorSource) {
result["loadingIndicatorSource"] = toDynamic(loadingIndicatorSource);
}
if (resizeMode != oldProps->resizeMode) {
switch (resizeMode) {
case ImageResizeMode::Cover:
result["resizeMode"] = "cover";
break;
case ImageResizeMode::Contain:
result["resizeMode"] = "contain";
break;
case ImageResizeMode::Stretch:
result["resizeMode"] = "stretch";
break;
case ImageResizeMode::Center:
result["resizeMode"] = "center";
break;
case ImageResizeMode::Repeat:
result["resizeMode"] = "repeat";
break;
case ImageResizeMode::None:
result["resizeMode"] = "none";
break;
}
}
if (blurRadius != oldProps->blurRadius) {
result["blurRadius"] = blurRadius;
}
if (capInsets != oldProps->capInsets) {
result["capInsets"] = convertEdgeInsets(capInsets);
}
if (tintColor != oldProps->tintColor) {
result["tintColor"] = *tintColor;
}
if (internal_analyticTag != oldProps->internal_analyticTag) {
result["internal_analyticTag"] = internal_analyticTag;
}
if (resizeMethod != oldProps->resizeMethod) {
result["resizeMethod"] = resizeMethod;
}
if (resizeMultiplier != oldProps->resizeMultiplier) {
result["resizeMultiplier"] = resizeMultiplier;
}
if (shouldNotifyLoadEvents != oldProps->shouldNotifyLoadEvents) {
result["shouldNotifyLoadEvents"] = shouldNotifyLoadEvents;
}
if (overlayColor != oldProps->overlayColor) {
result["overlayColor"] = *overlayColor;
}
if (fadeDuration != oldProps->fadeDuration) {
result["fadeDuration"] = fadeDuration;
}
if (progressiveRenderingEnabled != oldProps->progressiveRenderingEnabled) {
result["progressiveRenderingEnabled"] = progressiveRenderingEnabled;
}
return result;
}
#endif
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList ImageProps::getDebugProps() const {
const auto& imageProps = ImageProps();
auto sourcesList = SharedDebugStringConvertibleList{};
if (sources.size() == 1) {
sourcesList = sources[0].getDebugProps("source");
} else if (sources.size() > 1) {
for (const auto& source : sources) {
std::string sourceName = "source-" + react::toString(source.scale) + "x";
auto debugProps = source.getDebugProps(sourceName);
sourcesList.insert(
sourcesList.end(), debugProps.begin(), debugProps.end());
}
}
return ViewProps::getDebugProps() +
defaultSource.getDebugProps("defaultSource") + sourcesList +
SharedDebugStringConvertibleList{
debugStringConvertibleItem(
"blurRadius", blurRadius, imageProps.blurRadius),
debugStringConvertibleItem(
"resizeMode",
toString(resizeMode),
toString(imageProps.resizeMode)),
debugStringConvertibleItem(
"tintColor", toString(tintColor), toString(imageProps.tintColor)),
};
}
inline std::string toString(ImageResizeMode resizeMode) {
switch (resizeMode) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,53 @@
/*
* 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/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
// TODO (T28334063): Consider for codegen.
class ImageProps final : public ViewProps {
public:
ImageProps() = default;
ImageProps(const PropsParserContext &context, const ImageProps &sourceProps, const RawProps &rawProps);
void
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);
#pragma mark - Props
ImageSources sources{};
ImageSource defaultSource{};
ImageSource loadingIndicatorSource{};
ImageResizeMode resizeMode{ImageResizeMode::Stretch};
Float blurRadius{};
EdgeInsets capInsets{};
SharedColor tintColor{};
std::string internal_analyticTag{};
std::string resizeMethod{};
Float resizeMultiplier{};
bool shouldNotifyLoadEvents{};
SharedColor overlayColor{};
Float fadeDuration{};
bool progressiveRenderingEnabled{};
#ifdef RN_SERIALIZABLE_STATE
ComponentName getDiffPropsImplementationTarget() const override;
folly::dynamic getDiffProps(const Props *prevProps) const override;
#endif
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace facebook::react

View File

@@ -0,0 +1,155 @@
/*
* 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 "ImageState.h"
#include <cstdlib>
#include <limits>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/image/ImageShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
namespace facebook::react {
const char ImageComponentName[] = "Image";
void ImageShadowNode::setImageManager(
const std::shared_ptr<ImageManager>& imageManager) {
ensureUnsealed();
imageManager_ = imageManager;
// TODO: T226624691 Improve image request creation to avoid double requests
// The image manager is set when the component descriptor adopts the shadow
// node. For instances where the shadow node was cloned without dirtying the
// layout, if the image source was changed we have to initiate the image
// request now since there is no guarantee that layout will run for the shadow
// node at a later time.
if (getIsLayoutClean() ||
ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
auto sources = getConcreteProps().sources;
auto layoutMetric = getLayoutMetrics();
if (sources.size() <= 1 ||
(layoutMetric.frame.size.width > 0 &&
layoutMetric.frame.size.height > 0)) {
updateStateIfNeeded();
}
}
}
void ImageShadowNode::updateStateIfNeeded() {
ensureUnsealed();
const auto& savedState = getStateData();
const auto& oldImageSource = savedState.getImageSource();
auto newImageSource = getImageSource();
const auto& oldImageRequestParams = savedState.getImageRequestParams();
const auto& imageProps = getConcreteProps();
const auto& newImageRequestParams = ImageRequestParams(
imageProps.blurRadius
#ifdef ANDROID
,
imageProps.defaultSource,
imageProps.resizeMode,
imageProps.resizeMethod,
// TODO: should we resizeMultiplier * imageSource.scale ?
imageProps.resizeMultiplier,
imageProps.shouldNotifyLoadEvents,
imageProps.overlayColor,
imageProps.tintColor,
imageProps.fadeDuration,
imageProps.progressiveRenderingEnabled,
imageProps.loadingIndicatorSource,
imageProps.internal_analyticTag
#endif
);
if (oldImageSource == newImageSource &&
oldImageRequestParams == newImageRequestParams) {
return;
}
#ifdef ANDROID
// Check if we should skip prefetching based on shouldResize logic
if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
const auto& resizeMethod = imageProps.resizeMethod;
const auto& uri = newImageSource.uri;
bool shouldResize = (resizeMethod == "resize") ||
// Only resize for local content/file URIs
(resizeMethod == "auto" &&
(uri.starts_with("content://") || uri.starts_with("file://")));
// If we would resize but have no dimensions, skip creating the request
if (shouldResize &&
(newImageSource.size.width == 0 || newImageSource.size.height == 0)) {
// Keep the old state - don't create a new image request
return;
}
}
#endif
ImageState state{
newImageSource,
imageManager_->requestImage(
newImageSource, getSurfaceId(), newImageRequestParams, getTag()),
newImageRequestParams};
setStateData(std::move(state));
}
ImageSource ImageShadowNode::getImageSource() const {
auto sources = getConcreteProps().sources;
if (sources.empty()) {
return {
/* .type = */ ImageSource::Type::Invalid,
};
}
auto layoutMetrics = getLayoutMetrics();
auto size = layoutMetrics.getContentFrame().size;
auto scale = layoutMetrics.pointScaleFactor;
if (sources.size() == 1) {
auto source = sources[0];
source.size = size;
source.scale = scale;
return source;
}
auto targetImageArea = size.width * size.height * scale * scale;
auto bestFit = std::numeric_limits<Float>::infinity();
auto bestSource = ImageSource{};
for (const auto& source : sources) {
auto sourceSize = source.size;
auto sourceScale = source.scale == 0 ? scale : source.scale;
auto sourceArea =
sourceSize.width * sourceSize.height * sourceScale * sourceScale;
auto fit = std::abs(1 - (sourceArea / targetImageArea));
if (fit < bestFit) {
bestFit = fit;
bestSource = source;
}
}
bestSource.size = size;
bestSource.scale = scale;
return bestSource;
}
#pragma mark - LayoutableShadowNode
void ImageShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded();
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace facebook::react

View File

@@ -0,0 +1,63 @@
/*
* 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/components/image/ImageEventEmitter.h>
#include <react/renderer/components/image/ImageProps.h>
#include <react/renderer/components/image/ImageState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/ShadowNodeFamily.h>
#include <react/renderer/imagemanager/ImageManager.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
extern const char ImageComponentName[];
/*
* `ShadowNode` for <Image> component.
*/
class ImageShadowNode final
: public ConcreteViewShadowNode<ImageComponentName, ImageProps, ImageEventEmitter, ImageState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
return traits;
}
/*
* Associates a shared `ImageManager` with the node.
*/
void setImageManager(const std::shared_ptr<ImageManager> &imageManager);
static ImageState initialStateData(
const Props::Shared &props,
const ShadowNodeFamily::Shared & /*family*/,
const ComponentDescriptor &componentDescriptor)
{
auto imageSource = ImageSource{ImageSource::Type::Invalid};
return {imageSource, {imageSource, nullptr}, {}};
}
#pragma mark - LayoutableShadowNode
void layout(LayoutContext layoutContext) override;
private:
ImageSource getImageSource() const;
std::shared_ptr<ImageManager> imageManager_;
void updateStateIfNeeded();
};
} // namespace facebook::react

View File

@@ -0,0 +1,23 @@
/*
* 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 "ImageState.h"
namespace facebook::react {
ImageSource ImageState::getImageSource() const {
return imageSource_;
}
const ImageRequest& ImageState::getImageRequest() const {
return *imageRequest_;
}
const ImageRequestParams& ImageState::getImageRequestParams() const {
return imageRequestParams_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,67 @@
/*
* 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/renderer/imagemanager/primitives.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
#endif
namespace facebook::react {
/*
* State for <Image> component.
*/
class ImageState final {
public:
ImageState(const ImageSource &imageSource, ImageRequest imageRequest, const ImageRequestParams &imageRequestParams)
: imageSource_(imageSource),
imageRequest_(std::make_shared<ImageRequest>(std::move(imageRequest))),
imageRequestParams_(imageRequestParams)
{
}
/*
* Returns stored ImageSource object.
*/
ImageSource getImageSource() const;
/*
* Exposes for reading stored `ImageRequest` object.
* `ImageRequest` object cannot be copied or moved from `ImageLocalData`.
*/
const ImageRequest &getImageRequest() const;
/*
* Returns stored ImageRequestParams object.
*/
const ImageRequestParams &getImageRequestParams() const;
#ifdef ANDROID
ImageState(const ImageState &previousState, folly::dynamic data) : imageRequestParams_{} {};
/*
* Empty implementation for Android because it doesn't use this class.
*/
folly::dynamic getDynamic() const
{
return {};
};
#endif
private:
ImageSource imageSource_;
std::shared_ptr<ImageRequest> imageRequest_;
ImageRequestParams imageRequestParams_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,169 @@
/*
* 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 <unordered_map>
#include <folly/dynamic.h>
#include <glog/logging.h>
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ImageSource &result)
{
if (value.hasType<std::string>()) {
result = {
/* .type = */ ImageSource::Type::Remote,
/* .uri = */ (std::string)value,
};
return;
}
if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
auto items = (std::unordered_map<std::string, RawValue>)value;
result = {};
result.type = ImageSource::Type::Remote;
if (items.find("__packager_asset") != items.end()) {
result.type = ImageSource::Type::Local;
}
if (items.find("width") != items.end() && items.find("height") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("width").hasType<Float>() && items.at("height").hasType<Float>()) {
result.size = {(Float)items.at("width"), (Float)items.at("height")};
}
if (items.find("scale") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("scale").hasType<Float>()) {
result.scale = (Float)items.at("scale");
} else {
result.scale = items.find("deprecated") != items.end() ? 0.0f : 1.0f;
}
if (items.find("url") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("url").hasType<std::string>()) {
result.uri = (std::string)items.at("url");
}
if (items.find("uri") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("uri").hasType<std::string>()) {
result.uri = (std::string)items.at("uri");
}
if (items.find("bundle") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("bundle").hasType<std::string>()) {
result.bundle = (std::string)items.at("bundle");
result.type = ImageSource::Type::Local;
}
if (items.find("headers") != items.end() &&
items.at("headers").hasType<std::unordered_map<std::string, std::string>>()) {
auto headers = (std::unordered_map<std::string, std::string>)items.at("headers");
for (const auto &header : headers) {
result.headers.push_back(header);
}
}
if (items.find("body") != items.end() && items.at("body").hasType<std::string>()) {
result.body = (std::string)items.at("body");
}
if (items.find("method") != items.end() && items.at("method").hasType<std::string>()) {
result.method = (std::string)items.at("method");
}
if (items.find("cache") != items.end() && items.at("cache").hasType<std::string>()) {
auto cache = (std::string)items.at("cache");
if (cache == "reload") {
result.cache = ImageSource::CacheStategy::Reload;
} else if (cache == "force-cache") {
result.cache = ImageSource::CacheStategy::ForceCache;
} else if (cache == "only-if-cached") {
result.cache = ImageSource::CacheStategy::OnlyIfCached;
}
}
return;
}
// The following should be removed after codegen is shipped.
// See T45151459.
result = {};
result.type = ImageSource::Type::Invalid;
}
inline std::string toString(const ImageSource &value)
{
return "{uri: " + value.uri + "}";
}
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ImageResizeMode &result)
{
react_native_expect(value.hasType<std::string>());
if (!value.hasType<std::string>()) {
LOG(ERROR) << "Unsupported ImageResizeMode type";
// "cover" is default in non-Fabric web and iOS
result = ImageResizeMode::Cover;
return;
}
auto stringValue = (std::string)value;
if (stringValue == "cover") {
result = ImageResizeMode::Cover;
} else if (stringValue == "contain") {
result = ImageResizeMode::Contain;
} else if (stringValue == "stretch") {
result = ImageResizeMode::Stretch;
} else if (stringValue == "center") {
result = ImageResizeMode::Center;
} else if (stringValue == "repeat") {
result = ImageResizeMode::Repeat;
} else if (stringValue == "none") {
result = ImageResizeMode::None;
} else {
LOG(ERROR) << "Unsupported ImageResizeMode value: " << stringValue;
react_native_expect(false);
// "cover" is default in non-Fabric web and iOS
result = ImageResizeMode::Cover;
}
}
inline std::string toString(const ImageResizeMode &value)
{
switch (value) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,14 @@
/*
* 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>
TEST(ImageTest, testSomething) {
// TODO
}