mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-03 23:16:13 +00:00
feat: filters support FeColorMatrix and FilterImage (#2316)
# Summary Introducing the long-awaited **Filters** in `react-native-svg` 🎉 ### Motivation This PR is the beginning of bringing support of SVG Filters into `react-native-svg`. * **related issues**: This PR series will address the following issues: #150, #176, #635, #883, #994, #996, #1216 * **feature overview**: This PR is a boilerplate for Filters * introducing `Filter` component and `FeColorMatrix` as a start. * It also introduces a new subdirectory called `react-native-svg/filter-image` with a `FilterImage` component. # Usage ## Filter and Fe... Filters are compatible with the web familiar standard, so most things should be compatible out-of-the-box and changes will be limited to using a capital letter as it's component. ### Example ```tsx import React from 'react'; import { FeColorMatrix, Filter, Rect, Svg } from 'react-native-svg'; export default () => { return ( <Svg height="300" width="300"> <Filter id="myFilter"> <FeColorMatrix type="saturate" values="0.2" /> </Filter> <Rect x="0" y="0" width="300" height="300" fill="red" filter="url(#myFilter)" /> </Svg> ); }; ```  ## Filter Image `FilterImage` is a new component that is not strictly related to SVG. Its behavior should be the same as a regular `Image` component from React Native with one exception - the additional prop `filters`, which accepts an array of filters to apply to the image. ### Example ```tsx import React from 'react'; import { StyleSheet } from 'react-native'; import { FilterImage } from 'react-native-svg/filter-image'; const myImage = require('./myImage.jpg'); export default () => { return ( <FilterImage style={styles.image} source={myImage} filters={[ { name: 'colorMatrix', type: 'saturate', values: 0.2 }, { name: 'colorMatrix', type: 'matrix', values: [ 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 1, 0, ], }, ]} /> ); }; const styles = StyleSheet.create({ image: { width: 200, height: 200, }, }); ```  ## Test Plan **Example App**: Updated the example app with various filter effects, showcasing real-world usage. ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | ## Checklist - [x] I have tested this on a device and a simulator - [x] I added documentation in `README.md` and `USAGE.md` - [x] I updated the typed files (typescript)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
typedef CF_ENUM(int32_t, RNSVGColorMatrixType) {
|
||||
SVG_FECOLORMATRIX_TYPE_UNKNOWN,
|
||||
SVG_FECOLORMATRIX_TYPE_MATRIX,
|
||||
SVG_FECOLORMATRIX_TYPE_SATURATE,
|
||||
SVG_FECOLORMATRIX_TYPE_HUEROTATE,
|
||||
SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
typedef CF_ENUM(int32_t, RNSVGEdgeModeTypes) {
|
||||
SVG_EDGEMODE_UNKNOWN,
|
||||
SVG_EDGEMODE_DUPLICATE,
|
||||
SVG_EDGEMODE_WRAP,
|
||||
SVG_EDGEMODE_NONE
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "RNSVGColorMatrixType.h"
|
||||
#import "RNSVGFilterPrimitive.h"
|
||||
|
||||
@interface RNSVGFeColorMatrix : RNSVGFilterPrimitive
|
||||
|
||||
@property (nonatomic, strong) NSString *in1;
|
||||
@property (nonatomic, assign) RNSVGColorMatrixType type;
|
||||
@property (nonatomic, strong) NSArray<NSNumber *> *values;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,174 @@
|
||||
#import "RNSVGFeColorMatrix.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 RNSVGFeColorMatrix
|
||||
|
||||
#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 RNSVGFeColorMatrixProps>();
|
||||
_props = defaultProps;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<RNSVGFeColorMatrixComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = static_cast<const RNSVGFeColorMatrixProps &>(*props);
|
||||
|
||||
self.in1 = RCTNSStringFromStringNilIfEmpty(newProps.in1);
|
||||
if (newProps.values.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *valuesArray = [NSMutableArray new];
|
||||
for (auto number : newProps.values) {
|
||||
[valuesArray addObject:[NSNumber numberWithFloat:number]];
|
||||
}
|
||||
self.values = valuesArray;
|
||||
}
|
||||
self.type = [RNSVGConvert RNSVGColorMatrixTypeFromCppEquivalent:newProps.type];
|
||||
|
||||
setCommonFilterProps(newProps, self);
|
||||
_props = std::static_pointer_cast<RNSVGFeColorMatrixProps const>(props);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_in1 = nil;
|
||||
_values = nil;
|
||||
_type = RNSVGColorMatrixType::SVG_FECOLORMATRIX_TYPE_MATRIX;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (void)setIn1:(NSString *)in1
|
||||
{
|
||||
if ([in1 isEqualToString:_in1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_in1 = in1;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setValues:(NSArray<NSNumber *> *)values
|
||||
{
|
||||
if (values == _values) {
|
||||
return;
|
||||
}
|
||||
|
||||
_values = values;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setType:(RNSVGColorMatrixType)type
|
||||
{
|
||||
if (type == _type) {
|
||||
return;
|
||||
}
|
||||
_type = type;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
#define deg2rad(degrees) ((M_PI * degrees) / 180)
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
|
||||
{
|
||||
CIImage *inResults = self.in1 ? [results objectForKey:self.in1] : nil;
|
||||
CIImage *inputImage = inResults ? inResults : previous;
|
||||
|
||||
CIFilter *filter = nil;
|
||||
NSArray<NSNumber *> *array = self.values;
|
||||
NSUInteger count = [array count];
|
||||
|
||||
switch (self.type) {
|
||||
case SVG_FECOLORMATRIX_TYPE_UNKNOWN:
|
||||
return nil;
|
||||
case SVG_FECOLORMATRIX_TYPE_MATRIX: {
|
||||
if (count != 20) {
|
||||
return nil;
|
||||
}
|
||||
CGFloat v[20] = {0};
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
v[i] = (CGFloat)[array[i] doubleValue];
|
||||
}
|
||||
filter = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:[CIVector vectorWithX:v[0] Y:v[1] Z:v[2] W:v[3]] forKey:@"inputRVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[5] Y:v[6] Z:v[7] W:v[8]] forKey:@"inputGVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[10] Y:v[11] Z:v[12] W:v[13]] forKey:@"inputBVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[15] Y:v[16] Z:v[17] W:v[18]] forKey:@"inputAVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[4] Y:v[9] Z:v[14] W:v[19]] forKey:@"inputBiasVector"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_SATURATE: {
|
||||
if (count != 1) {
|
||||
return nil;
|
||||
}
|
||||
float saturation = [array[0] floatValue];
|
||||
filter = [CIFilter filterWithName:@"CIColorControls"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_HUEROTATE: {
|
||||
if (count != 1) {
|
||||
return nil;
|
||||
}
|
||||
double deg = [array[0] doubleValue];
|
||||
filter = [CIFilter filterWithName:@"CIHueAdjust"];
|
||||
[filter setDefaults];
|
||||
float radians = (float)deg2rad(deg);
|
||||
[filter setValue:[NSNumber numberWithFloat:radians] forKey:@"inputAngle"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA: {
|
||||
if (count != 0) {
|
||||
return nil;
|
||||
}
|
||||
filter = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
[filter setDefaults];
|
||||
CGFloat zero[4] = {0, 0, 0, 0};
|
||||
CGFloat alpha[4] = {0.2125, 0.7154, 0.0721, 0};
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:alpha count:4] forKey:@"inputAVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBiasVector"];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
||||
[filter setValue:inputImage forKey:@"inputImage"];
|
||||
|
||||
return [filter valueForKey:@"outputImage"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
Class<RCTComponentViewProtocol> RNSVGFeColorMatrixCls(void)
|
||||
{
|
||||
return RNSVGFeColorMatrix.class;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@interface RNSVGFilter : RNSVGNode
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *width;
|
||||
@property (nonatomic, strong) RNSVGLength *height;
|
||||
@property (nonatomic, assign) RNSVGUnits filterUnits;
|
||||
@property (nonatomic, assign) RNSVGUnits primitiveUnits;
|
||||
|
||||
- (CIImage *)applyFilter:(CIImage *)img
|
||||
backgroundImg:(CIImage *)backgroundImg
|
||||
renderableBounds:(CGRect)renderableBounds
|
||||
canvasBounds:(CGRect)canvasBounds
|
||||
ctm:(CGAffineTransform)ctm;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,232 @@
|
||||
#import "RNSVGFilter.h"
|
||||
#import "RNSVGFilterPrimitive.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 RNSVGFilter {
|
||||
NSMutableDictionary<NSString *, CIImage *> *resultsMap;
|
||||
}
|
||||
|
||||
#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 RNSVGFilterProps>();
|
||||
_props = defaultProps;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<RNSVGFilterComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = static_cast<const RNSVGFilterProps &>(*props);
|
||||
|
||||
self.name = RCTNSStringFromStringNilIfEmpty(newProps.name);
|
||||
id x = RNSVGConvertFollyDynamicToId(newProps.x);
|
||||
if (x != nil) {
|
||||
self.x = [RCTConvert RNSVGLength:x];
|
||||
}
|
||||
id y = RNSVGConvertFollyDynamicToId(newProps.y);
|
||||
if (y != nil) {
|
||||
self.y = [RCTConvert RNSVGLength:y];
|
||||
}
|
||||
id height = RNSVGConvertFollyDynamicToId(newProps.height);
|
||||
if (height != nil) {
|
||||
self.height = [RCTConvert RNSVGLength:height];
|
||||
}
|
||||
id width = RNSVGConvertFollyDynamicToId(newProps.width);
|
||||
if (width != nil) {
|
||||
self.width = [RCTConvert RNSVGLength:width];
|
||||
}
|
||||
self.filterUnits = [RNSVGConvert RNSVGUnitsFromFilterUnitsCppEquivalent:newProps.filterUnits];
|
||||
self.primitiveUnits = [RNSVGConvert RNSVGUnitsFromPrimitiveUnitsCppEquivalent:newProps.primitiveUnits];
|
||||
|
||||
_props = std::static_pointer_cast<RNSVGFilterProps const>(props);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_height = nil;
|
||||
_width = nil;
|
||||
_filterUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_primitiveUnits = kRNSVGUnitsUserSpaceOnUse;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
resultsMap = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CIImage *)applyFilter:(CIImage *)img
|
||||
backgroundImg:(CIImage *)backgroundImg
|
||||
renderableBounds:(CGRect)renderableBounds
|
||||
canvasBounds:(CGRect)canvasBounds
|
||||
ctm:(CGAffineTransform)ctm
|
||||
{
|
||||
[resultsMap removeAllObjects];
|
||||
[resultsMap setObject:img forKey:@"SourceGraphic"];
|
||||
[resultsMap setObject:applySourceAlphaFilter(img) forKey:@"SourceAlpha"];
|
||||
[resultsMap setObject:backgroundImg forKey:@"BackgroundImage"];
|
||||
[resultsMap setObject:applySourceAlphaFilter(backgroundImg) forKey:@"BackgroundAlpha"];
|
||||
|
||||
CIImage *result = img;
|
||||
RNSVGFilterPrimitive *currentFilter;
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGFilterPrimitive class]]) {
|
||||
currentFilter = (RNSVGFilterPrimitive *)node;
|
||||
result = [currentFilter applyFilter:resultsMap previousFilterResult:result];
|
||||
if (currentFilter.result) {
|
||||
[resultsMap setObject:result forKey:currentFilter.result];
|
||||
}
|
||||
} else {
|
||||
RCTLogError(@"Invalid `Filter` subview: Filter children can only be `Fe...` components");
|
||||
}
|
||||
}
|
||||
|
||||
// Crop results to filter bounds
|
||||
CIFilter *crop = [CIFilter filterWithName:@"CICrop"];
|
||||
[crop setDefaults];
|
||||
[crop setValue:result forKey:@"inputImage"];
|
||||
|
||||
CGFloat scaleX = ctm.a, scaleY = fabs(ctm.d);
|
||||
CGFloat x, y, width, height;
|
||||
if (self.filterUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
x = [self relativeOn:self.x relative:canvasBounds.size.width / scaleX];
|
||||
y = [self relativeOn:self.y relative:canvasBounds.size.height / scaleY];
|
||||
width = [self relativeOn:self.width relative:canvasBounds.size.width / scaleX];
|
||||
height = [self relativeOn:self.height relative:canvasBounds.size.height / scaleY];
|
||||
} else { // kRNSVGUnitsObjectBoundingBox
|
||||
x = renderableBounds.origin.x + [self relativeOnFraction:self.x relative:renderableBounds.size.width];
|
||||
y = renderableBounds.origin.y + [self relativeOnFraction:self.y relative:renderableBounds.size.height];
|
||||
width = [self relativeOnFraction:self.width relative:renderableBounds.size.width];
|
||||
height = [self relativeOnFraction:self.height relative:renderableBounds.size.height];
|
||||
}
|
||||
CGRect cropCGRect = CGRectMake(x, y, width, height);
|
||||
cropCGRect = CGRectApplyAffineTransform(cropCGRect, ctm);
|
||||
CIVector *cropRect = [CIVector vectorWithCGRect:cropCGRect];
|
||||
[crop setValue:cropRect forKey:@"inputRectangle"];
|
||||
|
||||
return [crop valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
static CIFilter *sourceAlphaFilter()
|
||||
{
|
||||
CIFilter *sourceAlpha = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
CGFloat zero[4] = {0, 0, 0, 0};
|
||||
[sourceAlpha setDefaults];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:1.0] forKey:@"inputAVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBiasVector"];
|
||||
return sourceAlpha;
|
||||
}
|
||||
|
||||
static CIImage *applySourceAlphaFilter(CIImage *inputImage)
|
||||
{
|
||||
CIFilter *sourceAlpha = sourceAlphaFilter();
|
||||
[sourceAlpha setValue:inputImage forKey:@"inputImage"];
|
||||
return [sourceAlpha valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
[self.svgView defineFilter:self filterName:self.name];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setFilterUnits:(RNSVGUnits)filterUnits
|
||||
{
|
||||
if (filterUnits == _filterUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filterUnits = filterUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPrimitiveUnits:(RNSVGUnits)primitiveUnits
|
||||
{
|
||||
if (primitiveUnits == _primitiveUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_primitiveUnits = primitiveUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
Class<RCTComponentViewProtocol> RNSVGFilterCls(void)
|
||||
{
|
||||
return RNSVGFilter.class;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
@@ -0,0 +1,14 @@
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@interface RNSVGFilterPrimitive : RNSVGNode
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *width;
|
||||
@property (nonatomic, strong) RNSVGLength *height;
|
||||
@property (nonatomic, strong) NSString *result;
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous;
|
||||
- (CIImage *)cropResult:(CIImage *)result;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,110 @@
|
||||
#import <RNSVGFilterPrimitive.h>
|
||||
#import <RNSVGNode.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 RNSVGFilterPrimitive
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_height = nil;
|
||||
_width = nil;
|
||||
_result = nil;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
self.dirty = false;
|
||||
[super invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setResult:(NSString *)result
|
||||
{
|
||||
if ([result isEqualToString:_result]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_result = result;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
|
||||
{
|
||||
return previous;
|
||||
}
|
||||
|
||||
- (CIImage *)cropResult:(CIImage *)result
|
||||
{
|
||||
CIFilter *filter = [CIFilter filterWithName:@"CICrop"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:result forKey:@"inputImage"];
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.width];
|
||||
CGFloat height = [self relativeOnHeight:self.height];
|
||||
|
||||
[filter setValue:[CIVector vectorWithX:x Y:y Z:width W:height] forKey:@"inputRectangle"];
|
||||
return [filter valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user