From 6bd27dfeba3422482b43b15bef287b72d1bf7d54 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Mon, 11 Mar 2019 02:43:06 +0200 Subject: [PATCH] [bugfix] Fix calling toDataUrl early and with options Enable calling toDataUrl as soon as you get a ref to the svg root. Fix invalidation and rendering with options for width and height. --- .../main/java/com/horcrux/svg/SvgView.java | 23 ++++++++++++- .../java/com/horcrux/svg/SvgViewManager.java | 10 ++++++ .../java/com/horcrux/svg/SvgViewModule.java | 34 ++++++++++++++++--- ios/Elements/RNSVGSvgView.h | 2 +- ios/Elements/RNSVGSvgView.m | 12 +++++-- ios/RNSVG.xcodeproj/project.pbxproj | 2 ++ ios/ViewManagers/RNSVGSvgViewManager.m | 30 ++++++++++++---- 7 files changed, 98 insertions(+), 15 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/SvgView.java b/android/src/main/java/com/horcrux/svg/SvgView.java index 6709ed62..74c97d3c 100644 --- a/android/src/main/java/com/horcrux/svg/SvgView.java +++ b/android/src/main/java/com/horcrux/svg/SvgView.java @@ -101,8 +101,19 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC if (mBitmap == null) { mBitmap = drawOutput(); } - if (mBitmap != null) + if (mBitmap != null) { canvas.drawBitmap(mBitmap, 0, 0, null); + if (toDataUrlTask != null) { + toDataUrlTask.run(); + toDataUrlTask = null; + } + } + } + + private Runnable toDataUrlTask = null; + + void setToDataUrlTask(Runnable task) { + toDataUrlTask = task; } @Override @@ -138,6 +149,10 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC private boolean mRendered = false; int mTintColor = 0; + boolean isRendered() { + return mRendered; + } + private void clearChildCache() { if (!mRendered) { return; @@ -298,7 +313,10 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC getHeight(), Bitmap.Config.ARGB_8888); + clearChildCache(); drawChildren(new Canvas(bitmap)); + clearChildCache(); + this.invalidate(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); bitmap.recycle(); @@ -312,7 +330,10 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC height, Bitmap.Config.ARGB_8888); + clearChildCache(); drawChildren(new Canvas(bitmap)); + clearChildCache(); + this.invalidate(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); bitmap.recycle(); diff --git a/android/src/main/java/com/horcrux/svg/SvgViewManager.java b/android/src/main/java/com/horcrux/svg/SvgViewManager.java index 1ef27013..862e22dd 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewManager.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewManager.java @@ -28,9 +28,19 @@ class SvgViewManager extends ReactViewManager { private static final String REACT_CLASS = "RNSVGSvgView"; private static final SparseArray mTagToSvgView = new SparseArray<>(); + private static final SparseArray mTagToRunnable = new SparseArray<>(); static void setSvgView(int tag, SvgView svg) { mTagToSvgView.put(tag, svg); + Runnable task = mTagToRunnable.get(tag); + if (task != null) { + task.run(); + mTagToRunnable.delete(tag); + } + } + + static void runWhenViewIsAvailable(int tag, Runnable task) { + mTagToRunnable.put(tag, task); } static @Nullable SvgView getSvgViewByTag(int tag) { diff --git a/android/src/main/java/com/horcrux/svg/SvgViewModule.java b/android/src/main/java/com/horcrux/svg/SvgViewModule.java index a5cc4927..5790da74 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewModule.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewModule.java @@ -25,12 +25,33 @@ class SvgViewModule extends ReactContextBaseJavaModule { return "RNSVGSvgViewManager"; } - - @ReactMethod - public void toDataURL(int tag, ReadableMap options, Callback successCallback) { + static public void toDataURL(final int tag, final ReadableMap options, final Callback successCallback, final int attempt) { SvgView svg = SvgViewManager.getSvgViewByTag(tag); - if (svg != null) { + if (svg == null) { + SvgViewManager.runWhenViewIsAvailable(tag, new Runnable() { + @Override + public void run() { + SvgView svg = SvgViewManager.getSvgViewByTag(tag); + if (svg == null) { // Should never happen + return; + } + svg.setToDataUrlTask(new Runnable() { + @Override + public void run() { + toDataURL(tag, options, successCallback, attempt + 1); + } + }); + } + }); + } else if (!svg.isRendered()) { + svg.setToDataUrlTask(new Runnable() { + @Override + public void run() { + toDataURL(tag, options, successCallback, attempt + 1); + } + }); + } else { if (options != null) { successCallback.invoke( svg.toDataURL( @@ -43,4 +64,9 @@ class SvgViewModule extends ReactContextBaseJavaModule { } } } + + @ReactMethod + public void toDataURL(int tag, ReadableMap options, Callback successCallback) { + toDataURL(tag, options, successCallback, 0); + } } diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index 9ba992cf..808c41ab 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -52,7 +52,7 @@ - (NSString *)getDataURL; -- (NSString *)getDataURLwithBounds:(CGSize)bounds; +- (NSString *)getDataURLwithBounds:(CGRect)bounds; - (CGRect)getContextBounds; diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index f7c10826..856a83d4 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -250,17 +250,23 @@ - (NSString *)getDataURL { UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0); + [self clearChildCache]; [self drawRect:_boundingBox]; + [self clearChildCache]; + [self invalidate]; NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; UIGraphicsEndImageContext(); return base64; } -- (NSString *)getDataURLwithBounds:(CGSize)bounds +- (NSString *)getDataURLwithBounds:(CGRect)bounds { - UIGraphicsBeginImageContextWithOptions(bounds, NO, 0); - [self drawRect:_boundingBox]; + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); + [self clearChildCache]; + [self drawRect:bounds]; + [self clearChildCache]; + [self invalidate]; NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; UIGraphicsEndImageContext(); diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index 8e861247..819a6631 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -251,6 +251,7 @@ 94241669213B0DB800088E93 /* RNSVGPattern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSVGPattern.h; sourceTree = ""; }; 9424166A213B2FF100088E93 /* RNSVGPatternManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSVGPatternManager.h; sourceTree = ""; }; 9424166C213B302600088E93 /* RNSVGPatternManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSVGPatternManager.m; sourceTree = ""; }; + 94696EE92235A7F200C1D558 /* RNSVGVectorEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGVectorEffect.h; path = Utils/RNSVGVectorEffect.h; sourceTree = ""; }; 947F3809214810B800677F2A /* RNSVGMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGMask.h; path = Elements/RNSVGMask.h; sourceTree = ""; }; 947F380A214810DC00677F2A /* RNSVGMask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGMask.m; path = Elements/RNSVGMask.m; sourceTree = ""; }; 947F380D2148118300677F2A /* RNSVGMaskManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGMaskManager.h; sourceTree = ""; }; @@ -463,6 +464,7 @@ 1039D29A1CE7212C001E90A8 /* Utils */ = { isa = PBXGroup; children = ( + 94696EE92235A7F200C1D558 /* RNSVGVectorEffect.h */, B56895A920352B36004DBF1E /* RNSVGBezierElement.h */, B56895A820352B35004DBF1E /* RNSVGBezierElement.m */, 7F69160D1E3703D800DA6EDC /* RNSVGUnits.h */, diff --git a/ios/ViewManagers/RNSVGSvgViewManager.m b/ios/ViewManagers/RNSVGSvgViewManager.m index bbd015be..c07a9f08 100644 --- a/ios/ViewManagers/RNSVGSvgViewManager.m +++ b/ios/ViewManagers/RNSVGSvgViewManager.m @@ -8,6 +8,7 @@ #import #import +#import #import "RNSVGSvgViewManager.h" #import "RNSVGSvgView.h" @@ -30,15 +31,15 @@ RCT_EXPORT_VIEW_PROPERTY(align, NSString) RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) -RCT_EXPORT_METHOD(toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) -{ + +- (void)toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback attempt:(int)attempt { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { __kindof UIView *view = viewRegistry[reactTag]; + NSString * b64; if ([view isKindOfClass:[RNSVGSvgView class]]) { RNSVGSvgView *svg = view; if (options == nil) { - RNSVGSvgView *svg = view; - callback(@[[svg getDataURL]]); + b64 = [svg getDataURL]; } else { id width = [options objectForKey:@"width"]; id height = [options objectForKey:@"height"]; @@ -52,13 +53,30 @@ RCT_EXPORT_METHOD(toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary * NSNumber* h = height; NSInteger hi = (NSInteger)[h intValue]; - CGSize bounds = CGSizeMake(wi, hi); - callback(@[[svg getDataURLwithBounds:bounds]]); + CGRect bounds = CGRectMake(0, 0, wi, hi); + b64 = [svg getDataURLwithBounds:bounds]; } } else { RCTLogError(@"Invalid svg returned frin registry, expecting RNSVGSvgView, got: %@", view); + return; + } + if (b64) { + callback(@[b64]); + } else if (attempt < 1) { + void (^retryBlock)(void) = ^{ + [self toDataURL:reactTag options:options callback:callback attempt:(attempt + 1)]; + }; + + RCTExecuteOnUIManagerQueue(retryBlock); + } else { + callback(@[]); } }]; } +RCT_EXPORT_METHOD(toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) +{ + [self toDataURL:reactTag options:options callback:callback attempt:0]; +} + @end