mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-08 09:10:44 +00:00
[iOS] Cache node/group paths. Simpler memory handling, needs testing.
This commit is contained in:
@@ -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
|
||||
|
||||
+12
-13
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
|
||||
- (void)renderTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
self.dirty = false;
|
||||
// Do not render Symbol
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+34
-14
@@ -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<RNSVGContainer> container = (id<RNSVGContainer>)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);
|
||||
|
||||
+28
-10
@@ -19,6 +19,7 @@
|
||||
NSArray<RNSVGLength *> *_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<NSString *> *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
|
||||
|
||||
+5
-17
@@ -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];
|
||||
|
||||
|
||||
+14
-29
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user