From 0434cddc8b8d382001285d84ff55bec3bd798670 Mon Sep 17 00:00:00 2001 From: Horcrux Date: Fri, 11 Nov 2016 13:25:16 +0800 Subject: [PATCH] Fix memory issue with ClipPath Fix memory issue with ClipPath. --- ios/Elements/RNSVGClipPath.m | 2 +- ios/Elements/RNSVGGroup.m | 9 +++-- ios/Elements/RNSVGImage.m | 4 +-- ios/Elements/RNSVGLinearGradient.m | 2 +- ios/Elements/RNSVGPath.m | 10 +++--- ios/Elements/RNSVGRadialGradient.m | 2 +- ios/Elements/RNSVGSvgView.h | 12 +++---- ios/Elements/RNSVGSvgView.m | 24 ++++++------- ios/RNSVGNode.h | 11 +++--- ios/RNSVGNode.m | 56 ++++++++++++++--------------- ios/RNSVGRenderable.m | 12 +++---- ios/Text/RNSVGText.m | 1 - ios/ViewManagers/RNSVGNodeManager.m | 3 +- lib/attributes.js | 5 +-- lib/extract/extractClipping.js | 4 +-- 15 files changed, 77 insertions(+), 80 deletions(-) diff --git a/ios/Elements/RNSVGClipPath.m b/ios/Elements/RNSVGClipPath.m index 6acaee06..2b56cd81 100644 --- a/ios/Elements/RNSVGClipPath.m +++ b/ios/Elements/RNSVGClipPath.m @@ -17,7 +17,7 @@ - (void)saveDefinition { - [[self getSvgView] defineClipPath:self clipPathRef:self.name]; + [[self getSvgView] defineClipPath:self clipPathName:self.name]; } diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 39538caf..18b434e5 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -46,7 +46,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - CGMutablePathRef path = CGPathCreateMutable(); + CGMutablePathRef __block path = CGPathCreateMutable(); [self traverseSubviews:^(RNSVGNode *node) { CGAffineTransform transform = node.matrix; CGPathAddPath(path, &transform, [node getPath:context]); @@ -60,6 +60,11 @@ { CGAffineTransform matrix = CGAffineTransformConcat(self.matrix, transform); + CGPathRef clip = [self getComputedClipPath]; + if (clip && !CGPathContainsPoint(clip, nil, point, NO)) { + return nil; + } + for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { if ([node isKindOfClass:[RNSVGNode class]]) { if (event) { @@ -87,7 +92,7 @@ { if (self.name) { RNSVGSvgView* svg = [self getSvgView]; - [svg defineTemplate:self templateRef:self.name]; + [svg defineTemplate:self templateName:self.name]; } [self traverseSubviews:^(RNSVGNode *node) { diff --git a/ios/Elements/RNSVGImage.m b/ios/Elements/RNSVGImage.m index 9a0a43c9..bfa6aa14 100644 --- a/ios/Elements/RNSVGImage.m +++ b/ios/Elements/RNSVGImage.m @@ -94,7 +94,7 @@ { CGRect rect = [self getRect:context]; // add hit area - self.hitArea = CFAutorelease(CGPathCreateWithRect(rect, nil)); + self.hitArea = CGPathCreateWithRect(rect, nil); [self clip:context]; CGContextSaveGState(context); @@ -152,7 +152,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - return CGPathCreateWithRect([self getRect:context], nil); + return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getRect:context], nil)); } @end diff --git a/ios/Elements/RNSVGLinearGradient.m b/ios/Elements/RNSVGLinearGradient.m index 96b5b12e..c098ddec 100644 --- a/ios/Elements/RNSVGLinearGradient.m +++ b/ios/Elements/RNSVGLinearGradient.m @@ -32,7 +32,7 @@ converter.colors = self.gradient; converter.points = @[self.x1, self.y1, self.x2, self.y2]; converter.type = kRNSVGLinearGradient; - [[self getSvgView] defineBrushConverter:converter brushConverterRef:self.name]; + [[self getSvgView] defineBrushConverter:converter brushConverterName:self.name]; } @end diff --git a/ios/Elements/RNSVGPath.m b/ios/Elements/RNSVGPath.m index c59f5c37..010b3e63 100644 --- a/ios/Elements/RNSVGPath.m +++ b/ios/Elements/RNSVGPath.m @@ -40,17 +40,17 @@ if ([self getSvgView].responsible) { // Add path to hitArea - CGMutablePathRef hitArea = CGPathCreateMutableCopy(path); + CGMutablePathRef hitAreaPath = CGPathCreateMutableCopy(path); if (self.stroke) { // Add stroke to hitArea - CGPathRef strokePath = CGPathCreateCopyByStrokingPath(hitArea, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit); - CGPathAddPath(hitArea, nil, strokePath); + CGPathRef strokePath = CGPathCreateCopyByStrokingPath(hitAreaPath, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit); + CGPathAddPath(hitAreaPath, nil, strokePath); CGPathRelease(strokePath); } CGAffineTransform transform = self.matrix; - self.hitArea = CFAutorelease(CGPathCreateCopyByTransformingPath(hitArea, &transform)); - CGPathRelease(hitArea); + self.hitArea = CGPathCreateCopyByTransformingPath(hitAreaPath, &transform); + CGPathRelease(hitAreaPath); } if (self.opacity == 0) { diff --git a/ios/Elements/RNSVGRadialGradient.m b/ios/Elements/RNSVGRadialGradient.m index 1f1a1c45..24bac54d 100644 --- a/ios/Elements/RNSVGRadialGradient.m +++ b/ios/Elements/RNSVGRadialGradient.m @@ -30,7 +30,7 @@ converter.colors = self.gradient; converter.points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy]; converter.type = kRNSVGRadialGradient; - [[self getSvgView] defineBrushConverter:converter brushConverterRef:self.name]; + [[self getSvgView] defineBrushConverter:converter brushConverterName:self.name]; } @end diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index 828c2a5a..79f89469 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -19,12 +19,12 @@ /** * define content as clipPath template. */ -- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathRef:(NSString *)clipPathRef; -- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathRef; -- (void)defineTemplate:(__kindof RNSVGNode *)template templateRef:(NSString *)templateRef; -- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef; -- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterRef:(NSString *)brushConverterRef; -- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterRef; +- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName; +- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName; +- (void)defineTemplate:(__kindof RNSVGNode *)template templateName:(NSString *)templateName; +- (RNSVGNode *)getDefinedTemplate:(NSString *)templateName; +- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterName:(NSString *)brushConverterName; +- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterName; - (NSString *)getDataURL; @end diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 4c2801f8..544db77c 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -83,44 +83,44 @@ self.backgroundColor = inheritedBackgroundColor; } -- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathRef:(NSString *)clipPathRef +- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName { if (!clipPaths) { clipPaths = [[NSMutableDictionary alloc] init]; } - [clipPaths setObject:clipPath forKey:clipPathRef]; + [clipPaths setObject:clipPath forKey:clipPathName]; } -- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathRef +- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName { - return clipPaths ? [clipPaths objectForKey:clipPathRef] : nil; + return clipPaths ? [clipPaths objectForKey:clipPathName] : nil; } -- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef +- (void)defineTemplate:(RNSVGNode *)template templateName:(NSString *)templateName { if (!templates) { templates = [[NSMutableDictionary alloc] init]; } - [templates setObject:template forKey:templateRef]; + [templates setObject:template forKey:templateName]; } -- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef +- (RNSVGNode *)getDefinedTemplate:(NSString *)templateName { - return templates ? [templates objectForKey:tempalteRef] : nil; + return templates ? [templates objectForKey:templateName] : nil; } -- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterRef:(NSString *)brushConverterRef +- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterName:(NSString *)brushConverterName { if (!brushConverters) { brushConverters = [[NSMutableDictionary alloc] init]; } - [brushConverters setObject:brushConverter forKey:brushConverterRef]; + [brushConverters setObject:brushConverter forKey:brushConverterName]; } -- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterRef +- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterName { - return brushConverters ? [brushConverters objectForKey:brushConverterRef] : nil; + return brushConverters ? [brushConverters objectForKey:brushConverterName] : nil; } @end diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index fe521687..6d75ea92 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -20,8 +20,7 @@ @property (nonatomic, strong) NSString *name; @property (nonatomic, assign) CGFloat opacity; @property (nonatomic, assign) RNSVGCGFCRule clipRule; -@property (nonatomic, assign) CGPathRef clipPath; // convert clipPath="M0,0 L0,10 L10,10z" into path -@property (nonatomic, strong) NSString *clipPathRef; // use clipPath="url(#clip)" as ClipPath +@property (nonatomic, assign) NSString *clipPath; @property (nonatomic, assign) BOOL responsible; @property (nonatomic, assign) CGAffineTransform matrix; @property (nonatomic, assign) BOOL active; @@ -37,10 +36,8 @@ */ - (void)renderLayerTo:(CGContextRef)context; -- (void)renderClip:(CGContextRef)context; - /** - * clip node by clipPath or clipPathRef. + * clip node by clipPath */ - (void)clip:(CGContextRef)context; @@ -49,6 +46,10 @@ */ - (CGPathRef)getPath:(CGContextRef) context; +/** + * getComputedClipPath will return the CGPathRef computed with clipPath + */ +- (CGPathRef)getComputedClipPath; /** * run hitTest diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 9eea3616..ee4be007 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -13,6 +13,7 @@ @implementation RNSVGNode { BOOL _transparent; + CGPathRef _computedClipPath; } - (instancetype)init @@ -78,24 +79,14 @@ _matrix = matrix; } -- (void)setClipPath:(CGPathRef)clipPath +- (void)setClipPath:(NSString *)clipPath { if (_clipPath == clipPath) { return; } [self invalidate]; - CGPathRelease(_clipPath); - _clipPath = CGPathRetain(clipPath); -} - -- (void)setClipPathRef:(NSString *)clipPathRef -{ - if (_clipPathRef == clipPathRef) { - return; - } - [self invalidate]; - self.clipPath = nil; - _clipPathRef = clipPathRef; + _clipPath = clipPath; + } - (void)beginTransparencyLayer:(CGContextRef)context @@ -117,27 +108,38 @@ // abstract } -- (void)renderClip:(CGContextRef)context -{ - if (self.clipPathRef) { - self.clipPath = [[[self getSvgView] getDefinedClipPath:self.clipPathRef] getPath:context]; - } -} - - (void)clip:(CGContextRef)context { - CGPathRef clipPath = self.clipPath; - - if (clipPath) { - CGContextAddPath(context, clipPath); + if (self.clipPath) { + CGPathRef clip = [[[self getSvgView] getDefinedClipPath:self.clipPath] getPath:context]; + + if (!clip) { + // TODO: WARNING ABOUT THIS + return; + } + + CGContextAddPath(context, clip); if (self.clipRule == kRNSVGCGFCRuleEvenodd) { CGContextEOClip(context); } else { CGContextClip(context); } + + CGAffineTransform matrix = self.matrix; + [self computeClipPath:CGPathCreateCopyByTransformingPath(clip, &matrix)]; } } +- (CGPathRef)getComputedClipPath{ + return _computedClipPath; +} + +- (void)computeClipPath:(CGPathRef)computedClipPath +{ + CGPathRelease(_computedClipPath); + _computedClipPath = computedClipPath; +} + - (CGPathRef)getPath: (CGContextRef) context { // abstract @@ -149,10 +151,8 @@ // abstract } -// hitTest delagate - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - // abstract return nil; } @@ -177,7 +177,7 @@ { if (self.name) { RNSVGSvgView* svg = [self getSvgView]; - [svg defineTemplate:self templateRef:self.name]; + [svg defineTemplate:self templateName:self.name]; } } @@ -198,7 +198,7 @@ - (void)dealloc { - CGPathRelease(_clipPath); + CGPathRelease(_computedClipPath); } @end diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 9228d107..268e6a20 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -137,9 +137,8 @@ return; } - [self invalidate]; CGPathRelease(_hitArea); - _hitArea = CGPathRetain(hitArea); + _hitArea = CGPathRetain(CFAutorelease(hitArea)); } - (void)setPropList:(NSArray *)propList @@ -168,7 +167,6 @@ CGContextSetAlpha(context, self.opacity); [self beginTransparencyLayer:context]; - [self renderClip:context]; [self renderLayerTo:context]; [self endTransparencyLayer:context]; @@ -191,14 +189,14 @@ } CGPathRef hitArea = CGPathCreateCopyByTransformingPath(self.hitArea, &transfrom); - CGPathRef clipPath = self.clipPath; BOOL contains = CGPathContainsPoint(hitArea, nil, point, NO); CGPathRelease(hitArea); if (contains) { - if (!clipPath) { - return self; - } else { + CGPathRef clipPath = [self getComputedClipPath]; + if (clipPath) { return CGPathContainsPoint(clipPath, nil, point, NO) ? self : nil; + } else { + return self; } } else { return nil; diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index a21ab386..da9c969e 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -35,7 +35,6 @@ CGMutablePathRef shape = [self getTextGroupPath:context]; CGAffineTransform translation = CGAffineTransformMakeTranslation([self getShift:context path:shape], 0); CGMutablePathRef path = CGPathCreateCopyByTransformingPath(shape, &translation); - CGPathRelease(shape); return (CGPathRef)CFAutorelease(path); } diff --git a/ios/ViewManagers/RNSVGNodeManager.m b/ios/ViewManagers/RNSVGNodeManager.m index 8af8568f..5cda8ca4 100644 --- a/ios/ViewManagers/RNSVGNodeManager.m +++ b/ios/ViewManagers/RNSVGNodeManager.m @@ -32,8 +32,7 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(name, NSString) RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform) -RCT_EXPORT_VIEW_PROPERTY(clipPathRef, NSString) -RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath) +RCT_EXPORT_VIEW_PROPERTY(clipPath, NSString) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL) diff --git a/lib/attributes.js b/lib/attributes.js index f26b2e9c..b829a853 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -46,10 +46,7 @@ const NodeAttributes = { }, opacity: true, clipRule: true, - clipPathRef: true, - clipPath: { - diff: arrayDiffer - }, + clipPath: true, propList: { diff: arrayDiffer }, diff --git a/lib/extract/extractClipping.js b/lib/extract/extractClipping.js index 6b03a60d..4024c3fe 100644 --- a/lib/extract/extractClipping.js +++ b/lib/extract/extractClipping.js @@ -16,9 +16,7 @@ export default function (props) { let matched = clipPath.match(clipReg); if (matched) { - clippingProps.clipPathRef = matched[1]; - } else { - clippingProps.clipPath = new SerializablePath(clipPath).toJSON(); + clippingProps.clipPath = matched[1]; } }