feat: add FeOffset filter (#2361)

# Summary

Continuation of #2316
Introducing new filter `FeOffset`.

## Test Plan

Example app -> Filters -> FeOffset

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |         |
| Android |         |

## Checklist

- [x] I have tested this on a device and a simulator
- [x] I added documentation in `README.md`
- [x] I updated the typed files (typescript)
This commit is contained in:
Jakub Grzywacz
2024-07-25 12:32:15 +02:00
committed by GitHub
parent 44254df9fb
commit 5807f2c1a6
23 changed files with 507 additions and 5 deletions

View File

@@ -0,0 +1,9 @@
#import "RNSVGFilterPrimitive.h"
@interface RNSVGFeOffset : RNSVGFilterPrimitive
@property (nonatomic, strong) NSString *in1;
@property (nonatomic, strong) RNSVGLength *dx;
@property (nonatomic, strong) RNSVGLength *dy;
@end

View File

@@ -0,0 +1,126 @@
#import "RNSVGFeOffset.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 "RNSVGFabricConversions.h"
#endif // RCT_NEW_ARCH_ENABLED
@implementation RNSVGFeOffset
#ifdef RCT_NEW_ARCH_ENABLED
using namespace facebook::react;
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const RNSVGFeOffsetProps>();
_props = defaultProps;
}
return self;
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNSVGFeOffsetComponentDescriptor>();
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = static_cast<const RNSVGFeOffsetProps &>(*props);
self.in1 = RCTNSStringFromStringNilIfEmpty(newProps.in1);
id dx = RNSVGConvertFollyDynamicToId(newProps.dx);
if (dx != nil) {
self.dx = [RCTConvert RNSVGLength:dx];
}
id dy = RNSVGConvertFollyDynamicToId(newProps.dy);
if (dy != nil) {
self.dy = [RCTConvert RNSVGLength:dy];
}
setCommonFilterProps(newProps, self);
_props = std::static_pointer_cast<RNSVGFeOffsetProps const>(props);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_in1 = nil;
_dx = nil;
_dy = nil;
}
#endif // RCT_NEW_ARCH_ENABLED
- (void)setIn1:(NSString *)in1
{
if ([in1 isEqualToString:_in1]) {
return;
}
_in1 = in1;
[self invalidate];
}
- (void)setDx:(RNSVGLength *)dx
{
if ([dx isEqualTo:_dx]) {
return;
}
_dx = dx;
[self invalidate];
}
- (void)setDy:(RNSVGLength *)dy
{
if ([dy isEqualTo:_dy]) {
return;
}
_dy = dy;
[self invalidate];
}
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results
previousFilterResult:(CIImage *)previous
ctm:(CGAffineTransform)ctm
{
CIImage *inResults = self.in1 ? [results objectForKey:self.in1] : nil;
CIImage *inputImage = inResults ? inResults : previous;
if (!inputImage) {
return nil;
}
CIFilter *filter = [CIFilter filterWithName:@"CIAffineTransform"];
[filter setDefaults];
[filter setValue:inputImage forKey:@"inputImage"];
CGFloat dx = [self relativeOnWidth:self.dx];
CGFloat dy = [self relativeOnWidth:self.dy];
// reset ctm translation
CGAffineTransform contextTransform = CGAffineTransformConcat(ctm, CGAffineTransformMakeTranslation(-ctm.tx, -ctm.ty));
CGPoint translate = CGPointMake(dx, dy);
translate = CGPointApplyAffineTransform(translate, contextTransform);
CGAffineTransform transform = CGAffineTransformMakeTranslation(translate.x, translate.y);
[filter setValue:[NSValue valueWithCGAffineTransform:transform] forKey:@"inputTransform"];
return [filter valueForKey:@"outputImage"];
}
#ifdef RCT_NEW_ARCH_ENABLED
Class<RCTComponentViewProtocol> RNSVGFeOffsetCls(void)
{
return RNSVGFeOffset.class;
}
#endif // RCT_NEW_ARCH_ENABLED
@end

View File

@@ -99,7 +99,8 @@ using namespace facebook::react;
if ([node isKindOfClass:[RNSVGFilterPrimitive class]]) {
currentFilter = (RNSVGFilterPrimitive *)node;
CGImageRef cgResult = [[RNSVGRenderUtils sharedCIContext] createCGImage:[currentFilter applyFilter:resultsMap
previousFilterResult:result]
previousFilterResult:result
ctm:ctm]
fromRect:[result extent]];
result = [CIImage imageWithCGImage:cgResult];
CGImageRelease(cgResult);

View File

@@ -9,6 +9,9 @@
@property (nonatomic, strong) NSString *result;
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous;
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results
previousFilterResult:(CIImage *)previous
ctm:(CGAffineTransform)ctm;
- (CIImage *)cropResult:(CIImage *)result;
@end

View File

@@ -93,6 +93,13 @@
return previous;
}
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results
previousFilterResult:(CIImage *)previous
ctm:(CGAffineTransform)ctm
{
return [self applyFilter:results previousFilterResult:previous];
}
- (CIImage *)cropResult:(CIImage *)result
{
CIFilter *filter = [CIFilter filterWithName:@"CICrop"];

View File

@@ -0,0 +1,5 @@
#import "RNSVGFilterPrimitiveManager.h"
@interface RNSVGFeOffsetManager : RNSVGFilterPrimitiveManager
@end

View File

@@ -0,0 +1,17 @@
#import "RNSVGFeOffsetManager.h"
#import "RNSVGFeOffset.h"
@implementation RNSVGFeOffsetManager
RCT_EXPORT_MODULE()
- (RNSVGFeOffset *)node
{
return [RNSVGFeOffset new];
}
RCT_EXPORT_VIEW_PROPERTY(in1, NSString)
RCT_EXPORT_VIEW_PROPERTY(dx, RNSVGLength *)
RCT_EXPORT_VIEW_PROPERTY(dy, RNSVGLength *)
@end