From 7e42a99cf3f01ca7f0ff69617d9903448ec3f8a5 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Thu, 13 Sep 2018 00:04:43 +0300 Subject: [PATCH] Clear cached data in tree when changing properties on root svg element. Fixes staleness issues when changing width and height. --- .../com/horcrux/svg/SvgViewShadowNode.java | 25 +++++++++ .../java/com/horcrux/svg/TextShadowNode.java | 10 ---- .../java/com/horcrux/svg/VirtualNode.java | 19 ++++++- ios/Elements/RNSVGSvgView.m | 41 +++++++++++--- ios/RNSVGNode.h | 6 +-- ios/RNSVGNode.m | 54 +++++++++---------- ios/Text/RNSVGText.h | 1 - ios/Text/RNSVGText.m | 18 +++---- 8 files changed, 110 insertions(+), 64 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java index e4f132bd..8851fa97 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java @@ -53,58 +53,82 @@ public class SvgViewShadowNode extends LayoutShadowNode { private int mMeetOrSlice; private Matrix mInvViewBoxMatrix = new Matrix(); private boolean mInvertible = true; + private boolean mRendered = false; public SvgViewShadowNode() { mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; } + private void releaseCachedPath() { + if (!mRendered) { + return; + } + mRendered = false; + traverseChildren(new VirtualNode.NodeRunnable() { + public void run(ReactShadowNode node) { + if (node instanceof VirtualNode) { + VirtualNode n = ((VirtualNode)node); + n.releaseCachedPath(); + } + } + }); + } + @ReactProp(name = "minX") public void setMinX(float minX) { mMinX = minX; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "minY") public void setMinY(float minY) { mMinY = minY; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "vbWidth") public void setVbWidth(float vbWidth) { mVbWidth = vbWidth; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "vbHeight") public void setVbHeight(float vbHeight) { mVbHeight = vbHeight; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "bbWidth") public void setVbWidth(String bbWidth) { mbbWidth = bbWidth; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "bbHeight") public void setVbHeight(String bbHeight) { mbbHeight = bbHeight; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "align") public void setAlign(String align) { mAlign = align; markUpdated(); + releaseCachedPath(); } @ReactProp(name = "meetOrSlice") public void setMeetOrSlice(int meetOrSlice) { mMeetOrSlice = meetOrSlice; markUpdated(); + releaseCachedPath(); } @Override @@ -130,6 +154,7 @@ public class SvgViewShadowNode extends LayoutShadowNode { } Bitmap drawOutput() { + mRendered = true; float width = getLayoutWidth(); float height = getLayoutHeight(); boolean early = Float.isNaN(width) || Float.isNaN(height) || width * height == 0 || (Math.log10(width) + Math.log10(height) > 42); diff --git a/android/src/main/java/com/horcrux/svg/TextShadowNode.java b/android/src/main/java/com/horcrux/svg/TextShadowNode.java index bb42e6fe..f3ab4df3 100644 --- a/android/src/main/java/com/horcrux/svg/TextShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TextShadowNode.java @@ -185,16 +185,6 @@ class TextShadowNode extends GroupShadowNode { return mBaselineShift; } - void releaseCachedPath() { - traverseChildren(new NodeRunnable() { - public void run(ReactShadowNode node) { - if (node instanceof TextShadowNode) { - ((TextShadowNode)node).releaseCachedPath(); - } - } - }); - } - Path getGroupPath(Canvas canvas, Paint paint) { pushGlyphContext(); Path groupPath = super.getPath(canvas, paint); diff --git a/android/src/main/java/com/horcrux/svg/VirtualNode.java b/android/src/main/java/com/horcrux/svg/VirtualNode.java index 23e43e33..b585efca 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/VirtualNode.java @@ -56,7 +56,7 @@ abstract class VirtualNode extends LayoutShadowNode { Matrix mMatrix = new Matrix(); Matrix mInvMatrix = new Matrix(); boolean mInvertible = true; - RectF mClientRect; + private RectF mClientRect; private int mClipRule; private @Nullable String mClipPath; @@ -99,6 +99,10 @@ abstract class VirtualNode extends LayoutShadowNode { @Override public void markUpdated() { super.markUpdated(); + clearPath(); + } + + private void clearPath() { canvasHeight = -1; canvasWidth = -1; mRegion = null; @@ -106,6 +110,17 @@ abstract class VirtualNode extends LayoutShadowNode { mBox = null; } + void releaseCachedPath() { + clearPath(); + traverseChildren(new NodeRunnable() { + public void run(ReactShadowNode node) { + if (node instanceof VirtualNode) { + ((VirtualNode)node).releaseCachedPath(); + } + } + }); + } + @Nullable GroupShadowNode getTextRoot() { VirtualNode node = this; @@ -156,7 +171,7 @@ abstract class VirtualNode extends LayoutShadowNode { public abstract void draw(Canvas canvas, Paint paint, float opacity); public void render(Canvas canvas, Paint paint, float opacity) { draw(canvas, paint, opacity); - }; + } /** * Sets up the transform matrix on the canvas before an element is drawn. diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 77814fa7..e4aabef7 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -19,16 +19,18 @@ NSMutableDictionary *_masks; CGAffineTransform _viewBoxTransform; CGAffineTransform _invviewBoxTransform; + bool rendered; } - (instancetype)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { - // This is necessary to ensure that [self setNeedsDisplay] actually triggers - // a redraw when our parent transitions between hidden and visible. - self.contentMode = UIViewContentModeRedraw; - } - return self; + if (self = [super initWithFrame:frame]) { + // This is necessary to ensure that [self setNeedsDisplay] actually triggers + // a redraw when our parent transitions between hidden and visible. + self.contentMode = UIViewContentModeRedraw; + } + rendered = false; + return self; } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex @@ -49,6 +51,20 @@ // Do nothing, as subviews are inserted by insertReactSubview: } +- (void)releaseCachedPath +{ + if (!rendered) { + return; + } + rendered = false; + for (UIView *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + RNSVGNode *n = (RNSVGNode *)node; + [n releaseCachedPath]; + } + }; +} + - (void)invalidate { [self setNeedsDisplay]; @@ -61,6 +77,7 @@ } [self invalidate]; + [self releaseCachedPath]; _minX = minX; } @@ -71,6 +88,7 @@ } [self invalidate]; + [self releaseCachedPath]; _minY = minY; } @@ -81,6 +99,7 @@ } [self invalidate]; + [self releaseCachedPath]; _vbWidth = vbWidth; } @@ -91,26 +110,29 @@ } [self invalidate]; + [self releaseCachedPath]; _vbHeight = vbHeight; } -- (void)setBBWidth:(NSString *)bbWidth +- (void)setBbWidth:(NSString *)bbWidth { if ([bbWidth isEqualToString:_bbWidth]) { return; } [self invalidate]; + [self releaseCachedPath]; _bbWidth = bbWidth; } -- (void)setBBHeight:(NSString *)bbHeight +- (void)setBbHeight:(NSString *)bbHeight { if ([bbHeight isEqualToString:_bbHeight]) { return; } [self invalidate]; + [self releaseCachedPath]; _bbHeight = bbHeight; } @@ -121,6 +143,7 @@ } [self invalidate]; + [self releaseCachedPath]; _align = align; } @@ -131,6 +154,7 @@ } [self invalidate]; + [self releaseCachedPath]; _meetOrSlice = meetOrSlice; } @@ -160,6 +184,7 @@ - (void)drawRect:(CGRect)rect { + rendered = true; _clipPaths = nil; _templates = nil; _painters = nil; diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index ec8304ea..65ceeae5 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -91,10 +91,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; - (CGFloat)getContextHeight; -- (CGFloat)getContextLeft; - -- (CGFloat)getContextTop; - /** * save element`s reference into svg element. */ @@ -106,4 +102,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; - (void)traverseSubviews:(BOOL (^)(__kindof UIView *node))block; +- (void)releaseCachedPath; + @end diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 136d3d47..aadf0247 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -58,6 +58,11 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; { id container = (id)self.superview; [container invalidate]; + [self clearPath]; +} + +- (void)clearPath +{ if (_path) { CGPathRelease(_path); _path = nil; @@ -123,7 +128,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; if ([name isEqualToString:_name]) { return; } - + [self invalidate]; _name = name; } @@ -338,8 +343,9 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (CGFloat)relativeOnOther:(NSString *)length { - CGFloat width = [self getContextWidth]; - CGFloat height = [self getContextHeight]; + CGRect bounds = [self getContextBounds]; + CGFloat width = CGRectGetWidth(bounds); + CGFloat height = CGRectGetHeight(bounds); CGFloat powX = width * width; CGFloat powY = height * height; CGFloat r = sqrt(powX + powY) * RNSVG_M_SQRT1_2l; @@ -350,36 +356,19 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; fontSize:[self getFontSizeFromContext]]; } +- (CGRect)getContextBounds +{ + return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext()); +} + - (CGFloat)getContextWidth { - RNSVGGroup * root = self.textRoot; - RNSVGGlyphContext * gc = [root getGlyphContext]; - if (root == nil || gc == nil) { - return CGRectGetWidth([self.svgView getContextBounds]); - } else { - return [gc getWidth]; - } + return CGRectGetWidth([self getContextBounds]); } - (CGFloat)getContextHeight { - RNSVGGroup * root = self.textRoot; - RNSVGGlyphContext * gc = [root getGlyphContext]; - if (root == nil || gc == nil) { - return CGRectGetHeight([self.svgView getContextBounds]); - } else { - return [gc getHeight]; - } -} - -- (CGFloat)getContextLeft -{ - return CGRectGetMinX([self.svgView getContextBounds]); -} - -- (CGFloat)getContextTop -{ - return CGRectGetMinY([self.svgView getContextBounds]); + return CGRectGetHeight([self getContextBounds]); } - (void)parseReference @@ -399,6 +388,17 @@ 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/Text/RNSVGText.h b/ios/Text/RNSVGText.h index 548544b9..43b98592 100644 --- a/ios/Text/RNSVGText.h +++ b/ios/Text/RNSVGText.h @@ -21,7 +21,6 @@ @property (nonatomic, strong) NSArray *positionY; @property (nonatomic, strong) NSArray *rotate; -- (void)releaseCachedPath; - (CGPathRef)getGroupPath:(CGContextRef)context; - (CTFontRef)getFontFromContext; diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index efc09656..b8c40c71 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -124,18 +124,12 @@ - (void)setupGlyphContext:(CGContextRef)context { - _glyphContext = [[RNSVGGlyphContext alloc] initWithScale:1 width:[self getContextWidth] - height:[self getContextHeight]]; -} - -// release the cached CGPathRef for RNSVGTSpan -- (void)releaseCachedPath -{ - [self traverseSubviews:^BOOL(__kindof RNSVGNode *node) { - RNSVGText *text = node; - [text releaseCachedPath]; - return YES; - }]; + CGRect bounds = CGContextGetClipBoundingBox(context); + CGSize size = bounds.size; + _glyphContext = [[RNSVGGlyphContext alloc] + initWithScale:1 + width:size.width + height:size.height]; } - (CGPathRef)getGroupPath:(CGContextRef)context