From 0d97399d06bac18146a3177dbf5c298bd01ec83f Mon Sep 17 00:00:00 2001 From: Jakub Grzywacz Date: Mon, 22 Jul 2024 16:18:12 +0200 Subject: [PATCH] fix: ignore invalid filter id (#2360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Summary Closes #2356 Currently, when `filter` prop is defined, but there is no equivalent `` component, on iOS nothing is rendered, with this fix this prop would be ignored, and the component should render as expected. ## Test Plan ```tsx ``` ## Affected platforms | OS | Implemented | | ------- | :---------: | | iOS | ✅ | --- apple/Elements/RNSVGSvgView.h | 15 +++++++++------ apple/Elements/RNSVGSvgView.mm | 18 +++++++++--------- apple/RNSVGRenderable.mm | 25 +++++++++++++------------ 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/apple/Elements/RNSVGSvgView.h b/apple/Elements/RNSVGSvgView.h index fa900d7b..c70a8d12 100644 --- a/apple/Elements/RNSVGSvgView.h +++ b/apple/Elements/RNSVGSvgView.h @@ -17,6 +17,9 @@ #endif // RCT_NEW_ARCH_ENABLED @class RNSVGNode; +@class RNSVGMarker; +@class RNSVGMask; +@class RNSVGFilter; @interface RNSVGSvgView : #ifdef RCT_NEW_ARCH_ENABLED @@ -55,17 +58,17 @@ - (RNSVGPainter *)getDefinedPainter:(NSString *)painterName; -- (void)defineMarker:(RNSVGNode *)marker markerName:(NSString *)markerName; +- (void)defineMarker:(RNSVGMarker *)marker markerName:(NSString *)markerName; -- (RNSVGNode *)getDefinedMarker:(NSString *)markerName; +- (RNSVGMarker *)getDefinedMarker:(NSString *)markerName; -- (void)defineMask:(RNSVGNode *)mask maskName:(NSString *)maskName; +- (void)defineMask:(RNSVGMask *)mask maskName:(NSString *)maskName; -- (RNSVGNode *)getDefinedMask:(NSString *)maskName; +- (RNSVGMask *)getDefinedMask:(NSString *)maskName; -- (void)defineFilter:(RNSVGNode *)filter filterName:(NSString *)filterName; +- (void)defineFilter:(RNSVGFilter *)filter filterName:(NSString *)filterName; -- (RNSVGNode *)getDefinedFilter:(NSString *)filterName; +- (RNSVGFilter *)getDefinedFilter:(NSString *)filterName; - (NSString *)getDataURLWithBounds:(CGRect)bounds; diff --git a/apple/Elements/RNSVGSvgView.mm b/apple/Elements/RNSVGSvgView.mm index 61b188ee..47ff584d 100644 --- a/apple/Elements/RNSVGSvgView.mm +++ b/apple/Elements/RNSVGSvgView.mm @@ -23,9 +23,9 @@ NSMutableDictionary *_clipPaths; NSMutableDictionary *_templates; NSMutableDictionary *_painters; - NSMutableDictionary *_markers; - NSMutableDictionary *_masks; - NSMutableDictionary *_filters; + NSMutableDictionary *_markers; + NSMutableDictionary *_masks; + NSMutableDictionary *_filters; CGAffineTransform _invviewBoxTransform; bool rendered; } @@ -412,7 +412,7 @@ using namespace facebook::react; return _painters ? [_painters objectForKey:painterName] : nil; } -- (void)defineMarker:(RNSVGNode *)marker markerName:(NSString *)markerName +- (void)defineMarker:(RNSVGMarker *)marker markerName:(NSString *)markerName { if (!_markers) { _markers = [[NSMutableDictionary alloc] init]; @@ -420,12 +420,12 @@ using namespace facebook::react; [_markers setObject:marker forKey:markerName]; } -- (RNSVGNode *)getDefinedMarker:(NSString *)markerName; +- (RNSVGMarker *)getDefinedMarker:(NSString *)markerName; { return _markers ? [_markers objectForKey:markerName] : nil; } -- (void)defineMask:(RNSVGNode *)mask maskName:(NSString *)maskName +- (void)defineMask:(RNSVGMask *)mask maskName:(NSString *)maskName { if (!_masks) { _masks = [[NSMutableDictionary alloc] init]; @@ -433,12 +433,12 @@ using namespace facebook::react; [_masks setObject:mask forKey:maskName]; } -- (RNSVGNode *)getDefinedMask:(NSString *)maskName; +- (RNSVGMask *)getDefinedMask:(NSString *)maskName; { return _masks ? [_masks objectForKey:maskName] : nil; } -- (void)defineFilter:(RNSVGNode *)filter filterName:(NSString *)filterName +- (void)defineFilter:(RNSVGFilter *)filter filterName:(NSString *)filterName { if (!_filters) { _filters = [[NSMutableDictionary alloc] init]; @@ -446,7 +446,7 @@ using namespace facebook::react; [_filters setObject:filter forKey:filterName]; } -- (RNSVGNode *)getDefinedFilter:(NSString *)filterName +- (RNSVGFilter *)getDefinedFilter:(NSString *)filterName { return _filters ? [_filters objectForKey:filterName] : nil; } diff --git a/apple/RNSVGRenderable.mm b/apple/RNSVGRenderable.mm index b1b68e03..cefc10ff 100644 --- a/apple/RNSVGRenderable.mm +++ b/apple/RNSVGRenderable.mm @@ -250,7 +250,10 @@ UInt32 saturate(CGFloat value) [self beginTransparencyLayer:context]; - if (self.mask || self.filter) { + RNSVGFilter *filterNode = self.filter ? [self.svgView getDefinedFilter:self.filter] : nil; + RNSVGMask *maskNode = self.mask ? [self.svgView getDefinedMask:self.mask] : nil; + + if (maskNode || filterNode) { CGRect bounds = CGContextGetClipBoundingBox(context); // Get current context transformations for offscreenContext CGAffineTransform currentCTM = CGContextGetCTM(context); @@ -269,9 +272,8 @@ UInt32 saturate(CGFloat value) CGImage *contentImage = [RNSVGRenderUtils renderToImage:self ctm:currentCTM rect:rect clip:nil]; - if (self.filter) { + if (filterNode) { // https://www.w3.org/TR/SVG11/filters.html#FilterElement - RNSVGFilter *filterNode = (RNSVGFilter *)[self.svgView getDefinedFilter:self.filter]; CIImage *content = [CIImage imageWithCGImage:contentImage]; @@ -288,7 +290,7 @@ UInt32 saturate(CGFloat value) CGImageRelease(contentImage); contentImage = [[RNSVGRenderUtils sharedCIContext] createCGImage:content fromRect:scaledRect]; - if (!self.mask) { + if (!maskNode) { CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM)); // On macOS the currentCTM is inverted, so we need to transform it again @@ -306,9 +308,8 @@ UInt32 saturate(CGFloat value) CGImageRelease(backgroundImage); } - if (self.mask) { + if (maskNode) { // https://www.w3.org/TR/SVG11/masking.html#MaskElement - RNSVGMask *_maskNode = (RNSVGMask *)[self.svgView getDefinedMask:self.mask]; // Allocate pixel buffer and bitmap context for mask NSUInteger bytesPerPixel = 4; @@ -334,18 +335,18 @@ UInt32 saturate(CGFloat value) CGContextConcatCTM(bcontext, currentCTM); // Clip to mask bounds and render the mask CGSize currentBoundsSize = self.pathBounds.size; - CGFloat x = [self relativeOnFraction:[_maskNode x] relative:currentBoundsSize.width]; - CGFloat y = [self relativeOnFraction:[_maskNode y] relative:currentBoundsSize.height]; - CGFloat w = [self relativeOnFraction:[_maskNode maskwidth] relative:currentBoundsSize.width]; - CGFloat h = [self relativeOnFraction:[_maskNode maskheight] relative:currentBoundsSize.height]; + CGFloat x = [self relativeOnFraction:[maskNode x] relative:currentBoundsSize.width]; + CGFloat y = [self relativeOnFraction:[maskNode y] relative:currentBoundsSize.height]; + CGFloat w = [self relativeOnFraction:[maskNode maskwidth] relative:currentBoundsSize.width]; + CGFloat h = [self relativeOnFraction:[maskNode maskheight] relative:currentBoundsSize.height]; CGRect maskBounds = CGRectApplyAffineTransform(CGRectMake(x, y, w, h), screenScaleCTM); CGContextClipToRect(bcontext, maskBounds); - [_maskNode renderLayerTo:bcontext rect:bounds]; + [maskNode renderLayerTo:bcontext rect:bounds]; // Apply luminanceToAlpha filter primitive // https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement UInt32 *currentPixel = pixels; - if (_maskNode.maskType == kRNSVGMaskTypeLuminance) { + if (maskNode.maskType == kRNSVGMaskTypeLuminance) { for (NSUInteger i = 0; i < npixels; i++) { UInt32 color = *currentPixel;