feat: FeBlend (#2489)

# Summary

Continuation of #2362 implementing `FeBlend` filter
https://www.w3.org/TR/SVG11/filters.html#feBlendElement

## Test Plan

Example app → Filters → `FeBlend`

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |          |
| macOS   |     _*_      |
| Android |          |
| Web     |          |
This commit is contained in:
Jakub Grzywacz
2024-10-16 11:45:44 +02:00
committed by GitHub
parent cb30bd66d5
commit 096fdc22a5
24 changed files with 736 additions and 4 deletions
+8
View File
@@ -0,0 +1,8 @@
typedef CF_ENUM(int32_t, RNSVGBlendMode) {
SVG_FEBLEND_MODE_UNKNOWN,
SVG_FEBLEND_MODE_NORMAL,
SVG_FEBLEND_MODE_MULTIPLY,
SVG_FEBLEND_MODE_SCREEN,
SVG_FEBLEND_MODE_DARKEN,
SVG_FEBLEND_MODE_LIGHTEN,
};
+10
View File
@@ -0,0 +1,10 @@
#import "RNSVGBlendMode.h"
#import "RNSVGFilterPrimitive.h"
@interface RNSVGFeBlend : RNSVGFilterPrimitive
@property (nonatomic, strong) NSString *in1;
@property (nonatomic, strong) NSString *in2;
@property (nonatomic, assign) RNSVGBlendMode mode;
@end
+132
View File
@@ -0,0 +1,132 @@
#import "RNSVGFeBlend.h"
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import <react/renderer/components/view/conversions.h>
#import "RNSVGConvert.h"
#import "RNSVGFabricConversions.h"
#endif // RCT_NEW_ARCH_ENABLED
@implementation RNSVGFeBlend
#ifdef RCT_NEW_ARCH_ENABLED
using namespace facebook::react;
// Needed because of this: https://github.com/facebook/react-native/pull/37274
+ (void)load
{
[super load];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const RNSVGFeBlendProps>();
_props = defaultProps;
}
return self;
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNSVGFeBlendComponentDescriptor>();
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = static_cast<const RNSVGFeBlendProps &>(*props);
self.in1 = RCTNSStringFromStringNilIfEmpty(newProps.in1);
self.in2 = RCTNSStringFromStringNilIfEmpty(newProps.in2);
self.mode = [RNSVGConvert RNSVGBlendModeFromCppEquivalent:newProps.mode];
setCommonFilterProps(newProps, self);
_props = std::static_pointer_cast<RNSVGFeBlendProps const>(props);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_in1 = nil;
_in2 = nil;
_mode = RNSVGBlendMode::SVG_FEBLEND_MODE_NORMAL;
}
#endif // RCT_NEW_ARCH_ENABLED
- (void)setIn1:(NSString *)in1
{
if ([in1 isEqualToString:_in1]) {
return;
}
_in1 = in1;
[self invalidate];
}
- (void)setIn2:(NSString *)in2
{
if ([in2 isEqualToString:_in2]) {
return;
}
_in2 = in2;
[self invalidate];
}
- (void)setMode:(RNSVGBlendMode)mode
{
if (mode == _mode) {
return;
}
_mode = mode;
[self invalidate];
}
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
{
CIImage *inResults1 = self.in1 ? [results objectForKey:self.in1] : nil;
CIImage *inResults2 = self.in2 ? [results objectForKey:self.in2] : nil;
CIImage *inputImage1 = inResults1 ? inResults1 : previous;
CIImage *inputImage2 = inResults2 ? inResults2 : previous;
CIFilter *filter = nil;
switch (self.mode) {
case SVG_FEBLEND_MODE_NORMAL:
filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
break;
case SVG_FEBLEND_MODE_MULTIPLY:
filter = [CIFilter filterWithName:@"CIMultiplyBlendMode"];
break;
case SVG_FEBLEND_MODE_SCREEN:
filter = [CIFilter filterWithName:@"CIScreenBlendMode"];
break;
case SVG_FEBLEND_MODE_DARKEN:
filter = [CIFilter filterWithName:@"CIDarkenBlendMode"];
break;
case SVG_FEBLEND_MODE_LIGHTEN:
filter = [CIFilter filterWithName:@"CILightenBlendMode"];
break;
default:
return nil;
}
[filter setDefaults];
[filter setValue:inputImage1 forKey:@"inputImage"];
[filter setValue:inputImage2 forKey:@"inputBackgroundImage"];
return [filter valueForKey:@"outputImage"];
}
#ifdef RCT_NEW_ARCH_ENABLED
Class<RCTComponentViewProtocol> RNSVGFeBlendCls(void)
{
return RNSVGFeBlend.class;
}
#endif // RCT_NEW_ARCH_ENABLED
@end
+1
View File
@@ -10,6 +10,7 @@
#import <QuartzCore/QuartzCore.h>
#import <React/RCTConvert.h>
#import "RCTConvert+RNSVG.h"
#import "RNSVGBlendMode.h"
#import "RNSVGCGFCRule.h"
#import "RNSVGColorMatrixType.h"
#import "RNSVGEdgeMode.h"
+13
View File
@@ -72,6 +72,19 @@ RCT_ENUM_CONVERTER(
SVG_FECOLORMATRIX_TYPE_UNKNOWN,
intValue)
RCT_ENUM_CONVERTER(
RNSVGBlendMode,
(@{
@"unknown" : @(SVG_FEBLEND_MODE_UNKNOWN),
@"normal" : @(SVG_FEBLEND_MODE_NORMAL),
@"multiply" : @(SVG_FEBLEND_MODE_MULTIPLY),
@"screen" : @(SVG_FEBLEND_MODE_SCREEN),
@"darken" : @(SVG_FEBLEND_MODE_DARKEN),
@"lighten" : @(SVG_FEBLEND_MODE_LIGHTEN),
}),
SVG_FEBLEND_MODE_UNKNOWN,
intValue)
+ (RNSVGBrush *)RNSVGBrush:(id)json
{
if ([json isKindOfClass:[NSNumber class]]) {
+2
View File
@@ -1,5 +1,6 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import <react/renderer/components/rnsvg/Props.h>
#import "RNSVGBlendMode.h"
#import "RNSVGColorMatrixType.h"
#import "RNSVGEdgeMode.h"
#import "RNSVGUnits.h"
@@ -10,6 +11,7 @@ namespace react = facebook::react;
+ (RNSVGUnits)RNSVGUnitsFromFilterUnitsCppEquivalent:(react::RNSVGFilterFilterUnits)svgUnits;
+ (RNSVGUnits)RNSVGUnitsFromPrimitiveUnitsCppEquivalent:(react::RNSVGFilterPrimitiveUnits)svgUnits;
+ (RNSVGBlendMode)RNSVGBlendModeFromCppEquivalent:(react::RNSVGFeBlendMode)mode;
+ (RNSVGColorMatrixType)RNSVGColorMatrixTypeFromCppEquivalent:(react::RNSVGFeColorMatrixType)type;
+ (RNSVGEdgeMode)RNSVGEdgeModeFromCppEquivalent:(react::RNSVGFeGaussianBlurEdgeMode)edgeMode;
+18
View File
@@ -23,6 +23,24 @@
}
}
+ (RNSVGBlendMode)RNSVGBlendModeFromCppEquivalent:(react::RNSVGFeBlendMode)mode
{
switch (mode) {
case react::RNSVGFeBlendMode::Unknown:
return SVG_FEBLEND_MODE_UNKNOWN;
case react::RNSVGFeBlendMode::Normal:
return SVG_FEBLEND_MODE_NORMAL;
case react::RNSVGFeBlendMode::Multiply:
return SVG_FEBLEND_MODE_MULTIPLY;
case react::RNSVGFeBlendMode::Screen:
return SVG_FEBLEND_MODE_SCREEN;
case react::RNSVGFeBlendMode::Darken:
return SVG_FEBLEND_MODE_DARKEN;
case react::RNSVGFeBlendMode::Lighten:
return SVG_FEBLEND_MODE_LIGHTEN;
}
}
+ (RNSVGColorMatrixType)RNSVGColorMatrixTypeFromCppEquivalent:(react::RNSVGFeColorMatrixType)type
{
switch (type) {
+5
View File
@@ -0,0 +1,5 @@
#import "RNSVGFilterPrimitiveManager.h"
@interface RNSVGFeBlendManager : RNSVGFilterPrimitiveManager
@end
+18
View File
@@ -0,0 +1,18 @@
#import "RNSVGFeBlendManager.h"
#import "RNSVGBlendMode.h"
#import "RNSVGFeBlend.h"
@implementation RNSVGFeBlendManager
RCT_EXPORT_MODULE()
- (RNSVGFeBlend *)node
{
return [RNSVGFeBlend new];
}
RCT_EXPORT_VIEW_PROPERTY(in1, NSString)
RCT_EXPORT_VIEW_PROPERTY(in2, NSString)
RCT_EXPORT_VIEW_PROPERTY(mode, RNSVGBlendMode)
@end