diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 93f02331..3cff5823 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -122,6 +122,10 @@ - (CGPathRef)getPath:(CGContextRef)context { + CGPathRef cached = self.path; + if (cached) { + return cached; + } CGMutablePathRef __block path = CGPathCreateMutable(); [self traverseSubviews:^(RNSVGNode *node) { if ([node isKindOfClass:[RNSVGNode class]]) { @@ -131,7 +135,9 @@ return YES; }]; - return (CGPathRef)CFAutorelease(path); + cached = CGPathRetain(path); + self.path = cached; + return cached; } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index a3ed3ae1..8e566d2d 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -51,18 +51,17 @@ // Do nothing, as subviews are inserted by insertReactSubview: } -- (void)releaseCachedPath +- (void)clearChildCache { if (!rendered) { return; } rendered = false; - for (UIView *node in self.subviews) { + for (__kindof RNSVGNode *node in self.subviews) { if ([node isKindOfClass:[RNSVGNode class]]) { - RNSVGNode *n = (RNSVGNode *)node; - [n releaseCachedPath]; + [node clearChildCache]; } - }; + } } - (void)invalidate @@ -77,7 +76,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _minX = minX; } @@ -88,7 +87,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _minY = minY; } @@ -99,7 +98,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _vbWidth = vbWidth; } @@ -110,7 +109,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _vbHeight = vbHeight; } @@ -121,7 +120,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _bbWidth = bbWidth; } @@ -132,7 +131,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _bbHeight = bbHeight; } @@ -143,7 +142,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _align = align; } @@ -154,7 +153,7 @@ } [self invalidate]; - [self releaseCachedPath]; + [self clearChildCache]; _meetOrSlice = meetOrSlice; } diff --git a/ios/Elements/RNSVGSymbol.m b/ios/Elements/RNSVGSymbol.m index 8f3b41f0..615718b6 100644 --- a/ios/Elements/RNSVGSymbol.m +++ b/ios/Elements/RNSVGSymbol.m @@ -74,6 +74,7 @@ - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { + self.dirty = false; // Do not render Symbol } diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index bb361b21..5ff803bb 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -37,6 +37,8 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; @property (nonatomic, assign) CGAffineTransform invmatrix; @property (nonatomic, assign) CGAffineTransform invTransform; @property (nonatomic, assign) BOOL active; +@property (nonatomic, assign) BOOL dirty; +@property (nonatomic, assign) BOOL merging; @property (nonatomic, assign) CGPathRef path; @property (nonatomic, assign) CGRect clientRect; @property (nonatomic, copy) RCTDirectEventBlock onLayout; @@ -112,6 +114,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; - (void)traverseSubviews:(BOOL (^)(__kindof UIView *node))block; -- (void)releaseCachedPath; +- (void)clearChildCache; @end diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 1d1708ef..9d89e3fd 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -38,6 +38,8 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; self.opacity = 1; self.transforms = CGAffineTransformIdentity; self.invTransform = CGAffineTransformIdentity; + _merging = false; + _dirty = false; } return self; } @@ -62,6 +64,10 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (void)invalidate { + if (_dirty || _merging) { + return; + } + _dirty = true; id container = (id)self.superview; [container invalidate]; [self clearPath]; @@ -72,9 +78,33 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (void)clearPath { - if (_path) { - CGPathRelease(_path); - _path = nil; + self.path = nil; +} + +- (void)clearChildCache +{ + [self clearPath]; + for (__kindof RNSVGNode *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node clearChildCache]; + } + } +} + +- (void)clearParentCache +{ + RNSVGNode* node = self; + while (node != nil) { + UIView* parent = [node superview]; + + if (![parent isKindOfClass:[RNSVGNode class]]) { + return; + } + node = (RNSVGNode*)parent; + if (!node.path) { + return; + } + [node clearPath]; } } @@ -239,6 +269,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { + self.dirty = false; // abstract } @@ -511,17 +542,6 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; } } -- (void)releaseCachedPath -{ - [self clearPath]; - [self traverseSubviews:^BOOL(__kindof RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node releaseCachedPath]; - } - return YES; - }]; -} - - (void)dealloc { CGPathRelease(_cachedClipPath); diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index a33d7e18..8f66f37b 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -19,6 +19,7 @@ NSArray *_sourceStrokeDashArray; CGFloat *_strokeDashArrayData; CGPathRef _strokePath; + CGPathRef _srcHitPath; CGPathRef _hitArea; } @@ -36,7 +37,11 @@ - (void)invalidate { _sourceStrokeDashArray = nil; + if (self.dirty || self.merging) { + return; + } [super invalidate]; + self.dirty = true; } - (void)setFill:(RNSVGBrush *)fill @@ -166,6 +171,7 @@ UInt32 saturate(CGFloat value) { - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { + self.dirty = false; // This needs to be painted on a layer before being composited. CGContextSaveGState(context); CGContextConcatCTM(context, self.matrix); @@ -290,12 +296,16 @@ UInt32 saturate(CGFloat value) { return; } - if (!self.path) { - self.path = CGPathRetain(CFAutorelease(CGPathCreateCopy([self getPath:context]))); - [self setHitArea:self.path]; + CGPathRef path = self.path; + if (!path) { + path = [self getPath:context]; + if (!self.path) { + self.path = CGPathRetain(path); + } + [self setHitArea:path]; } - const CGRect fillBounds = CGPathGetBoundingBox(self.path); + const CGRect fillBounds = CGPathGetBoundingBox(path); const CGRect strokeBounds = CGPathGetBoundingBox(_strokePath); const CGRect pathBounding = CGRectUnion(fillBounds, strokeBounds); @@ -334,7 +344,7 @@ UInt32 saturate(CGFloat value) { mode = evenodd ? kCGPathEOFill : kCGPathFill; } else { CGContextSaveGState(context); - CGContextAddPath(context, self.path); + CGContextAddPath(context, path); CGContextClip(context); [self.fill paint:context opacity:self.fillOpacity @@ -365,7 +375,7 @@ UInt32 saturate(CGFloat value) { } if (!fillColor) { - CGContextAddPath(context, self.path); + CGContextAddPath(context, path); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); } @@ -384,12 +394,12 @@ UInt32 saturate(CGFloat value) { } else if (!strokeColor) { // draw fill if (fillColor) { - CGContextAddPath(context, self.path); + CGContextAddPath(context, path); CGContextDrawPath(context, mode); } // draw stroke - CGContextAddPath(context, self.path); + CGContextAddPath(context, path); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); @@ -402,15 +412,19 @@ UInt32 saturate(CGFloat value) { } } - CGContextAddPath(context, self.path); + CGContextAddPath(context, path); CGContextDrawPath(context, mode); } - (void)setHitArea:(CGPathRef)path { + if (_srcHitPath == path) { + return; + } + _srcHitPath = path; CGPathRelease(_hitArea); CGPathRelease(_strokePath); - _hitArea = CGPathRetain(CFAutorelease(CGPathCreateCopy(path))); + _hitArea = CGPathCreateCopy(path); _strokePath = nil; if (self.stroke && self.strokeWidth) { // Add stroke to hitArea @@ -474,6 +488,7 @@ UInt32 saturate(CGFloat value) { - (void)mergeProperties:(__kindof RNSVGRenderable *)target { + self.merging = true; NSArray *targetAttributeList = [target getAttributeList]; if (targetAttributeList.count == 0) { @@ -493,16 +508,19 @@ UInt32 saturate(CGFloat value) { _lastMergedList = targetAttributeList; _attributeList = [attributeList copy]; + self.merging = false; } - (void)resetProperties { + self.merging = true; for (NSString *key in _lastMergedList) { [self setValue:[_originProperties valueForKey:key] forKey:key]; } _lastMergedList = nil; _attributeList = _propList; + self.merging = false; } @end diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index bb3510d6..9a25fefc 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -17,7 +17,6 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; @implementation RNSVGTSpan { CGFloat startOffset; - CGPathRef _cache; CGFloat _pathLength; RNSVGTextPath *textPath; NSArray *lengths; @@ -56,22 +55,11 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; } } -- (void)releaseCachedPath -{ - CGPathRelease(_cache); - _cache = nil; - self.path = nil; -} - -- (void)dealloc -{ - CGPathRelease(_cache); -} - - (CGPathRef)getPath:(CGContextRef)context { - if (_cache) { - return _cache; + CGPathRef path = self.path; + if (path) { + return path; } NSString *text = self.content; @@ -83,9 +71,9 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; [self pushGlyphContext]; - CGPathRef path = [self getLinePath:text context:context]; + path = [self getLinePath:text context:context]; - _cache = CGPathRetain(CFAutorelease(CGPathCreateCopy(path))); + self.path = CGPathRetain(path); [self popGlyphContext]; diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index 5a7f941a..7efc8cf0 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -18,30 +18,15 @@ RNSVGGlyphContext *_glyphContext; NSString *_alignmentBaseline; NSString *_baselineShift; - BOOL _merging; } - (void)invalidate { - if (_merging) { + if (self.dirty || self.merging) { return; } [super invalidate]; - [self releaseCachedPath]; -} - -- (void)mergeProperties:(__kindof RNSVGRenderable *)target -{ - _merging = true; - [super mergeProperties:target]; - _merging = false; -} - -- (void)resetProperties -{ - _merging = true; - [super resetProperties]; - _merging = false; + [self clearChildCache]; } - (void)setTextLength:(RNSVGLength *)textLength @@ -130,14 +115,8 @@ [self clip:context]; CGContextSaveGState(context); [self setupGlyphContext:context]; - - CGPathRef path = [self getGroupPath:context]; [self renderGroupTo:context rect:rect]; CGContextRestoreGState(context); - - CGPathRef transformedPath = CGPathCreateCopy(path); - [self setHitArea:transformedPath]; - CGPathRelease(transformedPath); } - (void)setupGlyphContext:(CGContextRef)context @@ -150,19 +129,25 @@ - (CGPathRef)getGroupPath:(CGContextRef)context { + CGPathRef path = self.path; + if (path) { + return path; + } [self pushGlyphContext]; - CGPathRef groupPath = [super getPath:context]; + path = [super getPath:context]; [self popGlyphContext]; - - return groupPath; + self.path = path; + return path; } - (CGPathRef)getPath:(CGContextRef)context { + CGPathRef path = self.path; + if (path) { + return path; + } [self setupGlyphContext:context]; - CGPathRef groupPath = [self getGroupPath:context]; - - return (CGPathRef)CFAutorelease(CGPathCreateCopy(groupPath)); + return [self getGroupPath:context]; } - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect