diff --git a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java index 2e347c31..6f92aed8 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java @@ -45,12 +45,11 @@ public class SvgViewShadowNode extends LayoutShadowNode implements TextureView.S return mTagToShadowNode.get(tag); } - private boolean mHasPendingUpdates; private boolean mResponsible = false; - private static final Map mDefinedClipPaths = new HashMap<>(); - private static final Map mDefinedTemplates = new HashMap<>(); - private static final Map mDefinedBrushes = new HashMap<>(); + private final Map mDefinedClipPaths = new HashMap<>(); + private final Map mDefinedTemplates = new HashMap<>(); + private final Map mDefinedBrushes = new HashMap<>(); @Override public boolean isVirtual() { diff --git a/android/src/main/java/com/horcrux/svg/VirtualNode.java b/android/src/main/java/com/horcrux/svg/VirtualNode.java index 8de108f7..ea64da33 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/VirtualNode.java @@ -17,8 +17,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.ReactShadowNode; @@ -35,8 +37,7 @@ public abstract class VirtualNode extends LayoutShadowNode { protected float mOpacity = 1f; protected Matrix mMatrix = new Matrix(); - protected @Nullable Path mClipPath; - protected @Nullable String mClipPathRef; + protected @Nullable String mClipPath; private static final int PATH_TYPE_CLOSE = 1; private static final int PATH_TYPE_CURVETO = 3; @@ -47,10 +48,7 @@ public abstract class VirtualNode extends LayoutShadowNode { private static final int CLIP_RULE_NONZERO = 1; protected final float mScale; - private float[] mClipData; private int mClipRule; - private boolean mClipRuleSet; - private boolean mClipDataSet; protected boolean mResponsible; protected int mCanvasX; protected int mCanvasY; @@ -96,14 +94,6 @@ public abstract class VirtualNode extends LayoutShadowNode { canvas.restoreToCount(count); } - @ReactProp(name = "clipPath") - public void setClipPath(@Nullable ReadableArray clipPath) { - mClipData = PropHelper.toFloatArray(clipPath); - mClipDataSet = true; - setupClip(); - markUpdated(); - } - @ReactProp(name = "name") public void setName(String name) { mName = name; @@ -111,17 +101,15 @@ public abstract class VirtualNode extends LayoutShadowNode { } - @ReactProp(name = "clipPathRef") - public void setClipPathRef(String clipPathRef) { - mClipPathRef = clipPathRef; + @ReactProp(name = "clipPath") + public void setClipPath(String clipPath) { + mClipPath = clipPath; markUpdated(); } @ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO) - public void setClipRule(int clipRule) { + public void clipRule(int clipRule) { mClipRule = clipRule; - mClipRuleSet = true; - setupClip(); markUpdated(); } @@ -138,7 +126,7 @@ public abstract class VirtualNode extends LayoutShadowNode { if (matrixSize == 6) { setupMatrix(); } else if (matrixSize != -1) { - throw new JSApplicationIllegalArgumentException("Transform matrices must be of size 6"); + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); } } else { mMatrix = null; @@ -153,24 +141,6 @@ public abstract class VirtualNode extends LayoutShadowNode { markUpdated(); } - private void setupClip() { - if (mClipDataSet && mClipRuleSet) { - mClipPath = new Path(); - - switch (mClipRule) { - case CLIP_RULE_EVENODD: - mClipPath.setFillType(Path.FillType.EVEN_ODD); - break; - case CLIP_RULE_NONZERO: - break; - default: - throw new JSApplicationIllegalArgumentException( - "clipRule " + mClipRule + " unrecognized"); - } - createPath(mClipData, mClipPath); - } - } - protected void setupMatrix() { sRawMatrix[0] = sMatrixData[0]; sRawMatrix[1] = sMatrixData[2]; @@ -226,13 +196,28 @@ public abstract class VirtualNode extends LayoutShadowNode { } protected @Nullable Path getClipPath(Canvas canvas, Paint paint) { - Path clip = mClipPath; - if (clip == null && mClipPathRef != null) { - VirtualNode node = getSvgShadowNode().getDefinedClipPath(mClipPathRef); - clip = node.getPath(canvas, paint); + if (mClipPath != null) { + VirtualNode node = getSvgShadowNode().getDefinedClipPath(mClipPath); + + if (node != null) { + Path clipPath = node.getPath(canvas, paint); + switch (mClipRule) { + case CLIP_RULE_EVENODD: + clipPath.setFillType(Path.FillType.EVEN_ODD); + break; + case CLIP_RULE_NONZERO: + break; + default: + FLog.w(ReactConstants.TAG, "RNSVG: clipRule: " + mClipRule + " unrecognized"); + } + + return clipPath; + } else { + FLog.w(ReactConstants.TAG, "RNSVG: Undefined clipPath: " + mClipPath); + } } - return clip; + return null; } protected void clip(Canvas canvas, Paint paint) { diff --git a/ios/Elements/RNSVGPath.m b/ios/Elements/RNSVGPath.m index c71a4081..6556886a 100644 --- a/ios/Elements/RNSVGPath.m +++ b/ios/Elements/RNSVGPath.m @@ -45,8 +45,7 @@ CGPathRelease(strokePath); } - CGAffineTransform transform = self.matrix; - self.hitArea = CFAutorelease(CGPathCreateCopyByTransformingPath(hitArea, &transform)); + self.hitArea = CGPathCreateCopy(hitArea); CGPathRelease(hitArea); } diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index e8adf1c8..710cd3aa 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, strong) NSString *clipPath; @property (nonatomic, assign) BOOL responsible; @property (nonatomic, assign) CGAffineTransform matrix; @property (nonatomic, assign) BOOL active; @@ -37,7 +36,9 @@ */ - (void)renderLayerTo:(CGContextRef)context; -- (void)renderClip:(CGContextRef)context; +- (CGPathRef)getClipPath; + +- (CGPathRef)getClipPath:(CGContextRef)context; /** * clip node by clipPath or clipPathRef. diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index a719123c..6d397e83 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -13,6 +13,7 @@ @implementation RNSVGNode { BOOL _transparent; + CGPathRef _cachedClipPath; } - (instancetype)init @@ -78,24 +79,15 @@ _matrix = matrix; } -- (void)setClipPath:(CGPathRef)clipPath +- (void)setClipPath:(NSString *)clipPath { if (_clipPath == clipPath) { return; } + CGPathRelease(_cachedClipPath); + _cachedClipPath = nil; + _clipPath = clipPath; [self invalidate]; - CGPathRelease(_clipPath); - _clipPath = CGPathRetain(clipPath); -} - -- (void)setClipPathRef:(NSString *)clipPathRef -{ - if (_clipPathRef == clipPathRef) { - return; - } - [self invalidate]; - self.clipPath = nil; - _clipPathRef = clipPathRef; } - (void)beginTransparencyLayer:(CGContextRef)context @@ -117,16 +109,23 @@ // abstract } -- (void)renderClip:(CGContextRef)context +- (CGPathRef)getClipPath { - if (self.clipPathRef) { - self.clipPath = [[[self getSvgView] getDefinedClipPath:self.clipPathRef] getPath:context]; + return _cachedClipPath; +} + +- (CGPathRef)getClipPath:(CGContextRef)context +{ + if (self.clipPath) { + _cachedClipPath = CGPathRetain([[[self getSvgView] getDefinedClipPath:self.clipPath] getPath:context]); } + + return [self getClipPath]; } - (void)clip:(CGContextRef)context { - CGPathRef clipPath = self.clipPath; + CGPathRef clipPath = [self getClipPath:context]; if (clipPath) { CGContextAddPath(context, clipPath); @@ -209,7 +208,7 @@ - (void)dealloc { - CGPathRelease(_clipPath); + CGPathRelease(_cachedClipPath); } @end diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 8e6ecbd7..27b10998 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -169,7 +169,6 @@ CGContextSetAlpha(context, self.opacity); [self beginTransparencyLayer:context]; - [self renderClip:context]; [self renderLayerTo:context]; [self endTransparencyLayer:context]; @@ -182,7 +181,7 @@ return [self hitTest:point withEvent:event withTransform:CGAffineTransformMakeRotation(0)]; } -- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event withTransform:(CGAffineTransform)transfrom +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event withTransform:(CGAffineTransform)transform { if (self.active) { if (!event) { @@ -190,16 +189,21 @@ } return self; } - - CGPathRef hitArea = CGPathCreateCopyByTransformingPath(self.hitArea, &transfrom); - CGPathRef clipPath = self.clipPath; + + CGAffineTransform matrix = CGAffineTransformConcat(self.matrix, transform); + CGPathRef hitArea = CGPathCreateCopyByTransformingPath(self.hitArea, &matrix); + CGPathRef clipPath = [self getClipPath]; BOOL contains = CGPathContainsPoint(hitArea, nil, point, NO); CGPathRelease(hitArea); + if (contains) { if (!clipPath) { return self; } else { - return CGPathContainsPoint(clipPath, nil, point, NO) ? self : nil; + clipPath = CGPathCreateCopyByTransformingPath(clipPath, &matrix); + BOOL result = CGPathContainsPoint(clipPath, nil, point, self.clipRule == kRNSVGCGFCRuleEvenodd); + CGPathRelease(clipPath); + return result ? self : nil; } } else { return nil; 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 90f5b039..6cda8031 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -58,10 +58,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 fea0bff1..605c42e5 100644 --- a/lib/extract/extractClipping.js +++ b/lib/extract/extractClipping.js @@ -15,9 +15,9 @@ export default function (props) { let matched = clipPath.match(clipReg); if (matched) { - clippingProps.clipPathRef = matched[1]; + clippingProps.clipPath = matched[1]; } else { - clippingProps.clipPath = clipPath; + console.warn('Invalid `clipPath` prop, expected a clipPath like `"#id"`, but got: "' + clipPath + '"'); } }