diff --git a/android/src/main/java/com/horcrux/svg/GroupView.java b/android/src/main/java/com/horcrux/svg/GroupView.java index b7fc9b69..ed371d50 100644 --- a/android/src/main/java/com/horcrux/svg/GroupView.java +++ b/android/src/main/java/com/horcrux/svg/GroupView.java @@ -191,12 +191,13 @@ class GroupView extends RenderableView { @Override int hitTest(final float[] src) { - if (!mInvertible) { + if (!mInvertible || !mTransformInvertible) { return -1; } float[] dst = new float[2]; mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst, src); int x = Math.round(dst[0]); int y = Math.round(dst[1]); diff --git a/android/src/main/java/com/horcrux/svg/RenderableView.java b/android/src/main/java/com/horcrux/svg/RenderableView.java index ed96a4ed..ed895dc9 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableView.java +++ b/android/src/main/java/com/horcrux/svg/RenderableView.java @@ -428,12 +428,13 @@ abstract public class RenderableView extends VirtualView { @Override int hitTest(final float[] src) { - if (mPath == null || !mInvertible) { + if (mPath == null || !mInvertible || !mTransformInvertible) { return -1; } float[] dst = new float[2]; mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst, src); int x = Math.round(dst[0]); int y = Math.round(dst[1]); diff --git a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java index 32ceeed2..34a4ed62 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java @@ -9,6 +9,7 @@ package com.horcrux.svg; +import android.graphics.Matrix; import android.view.View; import com.facebook.infer.annotation.Assertions; @@ -990,8 +991,10 @@ class RenderableViewManager extends ViewGroupManager { resetTransformProperty(node); } else { setTransformProperty(node, matrix); - node.mTransform = node.getMatrix(); } + Matrix m = node.getMatrix(); + node.mTransform = m; + node.mTransformInvertible = m.invert(node.mInvTransform); } @ReactProp(name = "propList") diff --git a/android/src/main/java/com/horcrux/svg/TSpanView.java b/android/src/main/java/com/horcrux/svg/TSpanView.java index 23b5afc7..0967cb0f 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanView.java +++ b/android/src/main/java/com/horcrux/svg/TSpanView.java @@ -971,12 +971,13 @@ class TSpanView extends TextView { if (mContent == null) { return super.hitTest(src); } - if (mPath == null || !mInvertible) { + if (mPath == null || !mInvertible || !mTransformInvertible) { return -1; } float[] dst = new float[2]; mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst, src); int x = Math.round(dst[0]); int y = Math.round(dst[1]); diff --git a/android/src/main/java/com/horcrux/svg/UseView.java b/android/src/main/java/com/horcrux/svg/UseView.java index 4691eb7c..08983937 100644 --- a/android/src/main/java/com/horcrux/svg/UseView.java +++ b/android/src/main/java/com/horcrux/svg/UseView.java @@ -81,12 +81,13 @@ class UseView extends RenderableView { @Override int hitTest(float[] src) { - if (!mInvertible) { + if (!mInvertible || !mTransformInvertible) { return -1; } float[] dst = new float[2]; mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst, src); VirtualView template = getSvgView().getDefinedTemplate(mHref); int hitChild = template.hitTest(dst); diff --git a/android/src/main/java/com/horcrux/svg/VirtualView.java b/android/src/main/java/com/horcrux/svg/VirtualView.java index 7d11c10d..5e7eba08 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualView.java +++ b/android/src/main/java/com/horcrux/svg/VirtualView.java @@ -62,7 +62,9 @@ abstract public class VirtualView extends ViewGroup { Matrix mMatrix = new Matrix(); Matrix mTransform = new Matrix(); Matrix mInvMatrix = new Matrix(); + Matrix mInvTransform = new Matrix(); boolean mInvertible = true; + boolean mTransformInvertible = true; private RectF mClientRect; private int mClipRule; diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 3293b13f..57b25463 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -81,8 +81,7 @@ { CGRect clipBounds = CGContextGetClipBoundingBox(context); clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix); - CGAffineTransform transform = CATransform3DGetAffineTransform(self.layer.transform); - clipBounds = CGRectApplyAffineTransform(clipBounds, transform); + clipBounds = CGRectApplyAffineTransform(clipBounds, self.transform); CGFloat width = CGRectGetWidth(clipBounds); CGFloat height = CGRectGetHeight(clipBounds); @@ -128,6 +127,7 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); if (self.clipPath) { RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath]; diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 9071f83c..980c7034 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -211,30 +211,29 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + CGPoint transformed = point; if (self.align) { - CGPoint transformed = CGPointApplyAffineTransform(point, _invviewBoxTransform); - for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { - if (![node isKindOfClass:[RNSVGNode class]]) { - continue; - } - - if (event) { - node.active = NO; - } else if (node.active) { - return node; - } - - UIView *hitChild = [node hitTest:transformed withEvent:event]; - - if (hitChild) { - node.active = YES; - return (node.responsible || (node != hitChild)) ? hitChild : self; - } - } - return nil; - } else { - return [super hitTest:point withEvent:event]; + transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform); } + for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { + if (![node isKindOfClass:[RNSVGNode class]]) { + continue; + } + + if (event) { + node.active = NO; + } else if (node.active) { + return node; + } + + UIView *hitChild = [node hitTest:transformed withEvent:event]; + + if (hitChild) { + node.active = YES; + return (node.responsible || (node != hitChild)) ? hitChild : self; + } + } + return nil; } diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m index 3d8e81e8..286bce2c 100644 --- a/ios/Elements/RNSVGUse.m +++ b/ios/Elements/RNSVGUse.m @@ -73,7 +73,8 @@ } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - const CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); RNSVGNode const* template = [self.svgView getDefinedTemplate:self.href]; if (event) { self.active = NO; diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index ab01511c..d241078a 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -34,6 +34,7 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; @property (nonatomic, assign) BOOL responsible; @property (nonatomic, assign) CGAffineTransform matrix; @property (nonatomic, assign) CGAffineTransform invmatrix; +@property (nonatomic, assign) CGAffineTransform invTransform; @property (nonatomic, assign) BOOL active; @property (nonatomic, assign) CGPathRef path; @property (nonatomic, assign) CGRect clientRect; diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 4af2a96b..b75335b0 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -35,6 +35,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; { if (self = [super init]) { self.opacity = 1; + self.invTransform = CGAffineTransformIdentity; } return self; } diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 38121eae..bf8a3c35 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -169,8 +169,7 @@ UInt32 saturate(CGFloat value) { // This needs to be painted on a layer before being composited. CGContextSaveGState(context); CGContextConcatCTM(context, self.matrix); - CGAffineTransform transform = CATransform3DGetAffineTransform(self.layer.transform); - CGContextConcatCTM(context, transform); + CGContextConcatCTM(context, self.transform); CGContextSetAlpha(context, self.opacity); [self beginTransparencyLayer:context]; @@ -421,6 +420,7 @@ UInt32 saturate(CGFloat value) { } CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; if (!CGPathContainsPoint(_hitArea, nil, transformed, evenodd) && diff --git a/ios/ViewManagers/RNSVGNodeManager.m b/ios/ViewManagers/RNSVGNodeManager.m index 481bdef2..c36de86b 100644 --- a/ios/ViewManagers/RNSVGNodeManager.m +++ b/ios/ViewManagers/RNSVGNodeManager.m @@ -156,9 +156,10 @@ RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform) RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RNSVGNode) { - view.layer.transform = json ? [RNSVGNodeManager CATransform3D:json] : defaultView.layer.transform; - // TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing - view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform); + CATransform3D transform3d = json ? [RNSVGNodeManager CATransform3D:json] : defaultView.layer.transform; + CGAffineTransform transform = CATransform3DGetAffineTransform(transform3d); + view.invTransform = CGAffineTransformInvert(transform); + view.transform = transform; [view invalidate]; } RCT_EXPORT_VIEW_PROPERTY(mask, NSString)