Skip to content

Commit

Permalink
Add mix-blend-mode effects to iOS (#45304)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #45304

Add support for most keyword values of mix-blend-mode on iOS and added RNTester Example
Missing compositing operators and global values

Changelog: [Internal]

Differential Revision: D59402969
  • Loading branch information
jorge-cab authored and facebook-github-bot committed Jul 5, 2024
1 parent 820a884 commit d1fc5d2
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import <react/renderer/components/view/ViewEventEmitter.h>
#import <react/renderer/components/view/ViewProps.h>
#import <react/renderer/components/view/accessibilityPropsConversions.h>
#import <react/renderer/graphics/MixBlendMode.h>

#ifdef RCT_DYNAMIC_FRAMEWORKS
#import <React/RCTComponentViewFactory.h>
Expand Down Expand Up @@ -396,6 +397,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
_needsInvalidateLayer = YES;
}

// `mixBlendMode`
if (oldViewProps.mixBlendMode != newViewProps.mixBlendMode) {
_needsInvalidateLayer = YES;
}

// `boxShadow`
if (oldViewProps.boxShadow != newViewProps.boxShadow) {
_needsInvalidateLayer = YES;
Expand Down Expand Up @@ -769,6 +775,57 @@ - (void)invalidateLayer
[self.layer addSublayer:_filterLayer];
}

switch (_props->mixBlendMode) {
case MixBlendMode::Multiply:
layer.compositingFilter = @"multiplyBlendMode";
break;
case MixBlendMode::Screen:
layer.compositingFilter = @"screenBlendMode";
break;
case MixBlendMode::Overlay:
layer.compositingFilter = @"overlayBlendMode";
break;
case MixBlendMode::Darken:
layer.compositingFilter = @"darkenBlendMode";
break;
case MixBlendMode::Lighten:
layer.compositingFilter = @"lightenBlendMode";
break;
case MixBlendMode::ColorDodge:
layer.compositingFilter = @"colorDodgeBlendMode";
break;
case MixBlendMode::ColorBurn:
layer.compositingFilter = @"colorBurnBlendMode";
break;
case MixBlendMode::HardLight:
layer.compositingFilter = @"hardLightBlendMode";
break;
case MixBlendMode::SoftLight:
layer.compositingFilter = @"softLightBlendMode";
break;
case MixBlendMode::Difference:
layer.compositingFilter = @"differenceBlendMode";
break;
case MixBlendMode::Exclusion:
layer.compositingFilter = @"exclusionBlendMode";
break;
case MixBlendMode::Hue:
layer.compositingFilter = @"hueBlendMode";
break;
case MixBlendMode::Saturation:
layer.compositingFilter = @"saturationBlendMode";
break;
case MixBlendMode::Color:
layer.compositingFilter = @"colorBlendMode";
break;
case MixBlendMode::Luminosity:
layer.compositingFilter = @"luminosityBlendMode";
break;
case MixBlendMode::Normal:
layer.compositingFilter = nil;
break;
}

_boxShadowLayer = nil;
if (!_props->boxShadow.empty()) {
_boxShadowLayer = [CALayer layer];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Filter.h>
#include <react/renderer/graphics/MixBlendMode.h>
#include <react/renderer/graphics/Transform.h>

#include <optional>
Expand Down Expand Up @@ -63,7 +64,7 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
std::vector<FilterPrimitive> filter{};

// MixBlendMode
std::string mixBlendMode;
MixBlendMode mixBlendMode;

// Transform
Transform transform{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ void ViewShadowNode::initialize() noexcept {
viewProps.accessibilityViewIsModal ||
viewProps.importantForAccessibility != ImportantForAccessibility::Auto ||
viewProps.removeClippedSubviews || viewProps.cursor != Cursor::Auto ||
!viewProps.filter.empty() || !viewProps.mixBlendMode.empty() ||
!viewProps.filter.empty() ||
viewProps.mixBlendMode != MixBlendMode::Normal ||
HostPlatformViewTraitsInitializer::formsStackingContext(viewProps);

bool formsView = formsStackingContext ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <react/renderer/core/RawProps.h>
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/Filter.h>
#include <react/renderer/graphics/MixBlendMode.h>
#include <react/renderer/graphics/PlatformColorParser.h>
#include <react/renderer/graphics/Transform.h>
#include <react/renderer/graphics/ValueUnit.h>
Expand Down Expand Up @@ -1069,6 +1070,22 @@ inline void fromRawValue(
result = filter;
}

inline void fromRawValue(
const PropsParserContext& /*context*/,
const RawValue& value,
MixBlendMode& result) {
react_native_expect(value.hasType<std::string>());
result = MixBlendMode::Normal;
if (!value.hasType<std::string>()) {
return;
}

auto rawMixBlendMode = static_cast<std::string>(value);
MixBlendMode mixBlendMode = mixBlendModeFromString(rawMixBlendMode);

result = mixBlendMode;
}

template <size_t N>
inline std::string toString(const std::array<float, N> vec) {
std::string s;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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>

#include <string>
#include <string_view>
#include <vector>

namespace facebook::react {

enum class MixBlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
};

inline MixBlendMode mixBlendModeFromString(std::string_view mixBlendModeName) {
if (mixBlendModeName == "normal") {
return MixBlendMode::Normal;
} else if (mixBlendModeName == "multiply") {
return MixBlendMode::Multiply;
} else if (mixBlendModeName == "screen") {
return MixBlendMode::Screen;
} else if (mixBlendModeName == "overlay") {
return MixBlendMode::Overlay;
} else if (mixBlendModeName == "darken") {
return MixBlendMode::Darken;
} else if (mixBlendModeName == "lighten") {
return MixBlendMode::Lighten;
} else if (mixBlendModeName == "color-dodge") {
return MixBlendMode::ColorDodge;
} else if (mixBlendModeName == "color-burn") {
return MixBlendMode::ColorBurn;
} else if (mixBlendModeName == "hard-light") {
return MixBlendMode::HardLight;
} else if (mixBlendModeName == "soft-light") {
return MixBlendMode::SoftLight;
} else if (mixBlendModeName == "difference") {
return MixBlendMode::Difference;
} else if (mixBlendModeName == "exclusion") {
return MixBlendMode::Exclusion;
} else if (mixBlendModeName == "hue") {
return MixBlendMode::Hue;
} else if (mixBlendModeName == "saturation") {
return MixBlendMode::Saturation;
} else if (mixBlendModeName == "color") {
return MixBlendMode::Color;
} else if (mixBlendModeName == "luminosity") {
return MixBlendMode::Luminosity;
} else {
throw std::invalid_argument(std::string(mixBlendModeName));
}
}
} // namespace facebook::react
4 changes: 4 additions & 0 deletions packages/rn-tester/js/utils/RNTesterList.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ const APIs: Array<RNTesterModuleInfo> = ([
key: 'FilterExample',
module: require('../examples/Filter/FilterExample'),
},
{
key: 'MixBlendModeExample',
module: require('../examples/MixBlendMode/MixBlendModeExample'),
},
{
key: 'TurboModuleExample',
module: require('../examples/TurboModule/TurboModuleExample'),
Expand Down

0 comments on commit d1fc5d2

Please sign in to comment.