Fix spec conformance of clipping path with multiple child elements.

https://www.w3.org/TR/SVG11/masking.html#EstablishingANewClippingPath

The raw geometry of each child element exclusive of rendering properties such as ‘fill’, ‘stroke’, ‘stroke-width’ within a ‘clipPath’ conceptually defines a 1-bit mask (with the possible exception of anti-aliasing along the edge of the geometry) which represents the silhouette of the graphics associated with that element. Anything outside the outline of the object is masked out. If a child element is made invisible by ‘display’ or ‘visibility’ it does not contribute to the clipping path. When the ‘clipPath’ element contains multiple child elements, the silhouettes of the child elements are logically OR'd together to create a single silhouette which is then used to restrict the region onto which paint can be applied. Thus, a point is inside the clipping path if it is inside any of the children of the ‘clipPath’.

For a given graphics element, the actual clipping path used will be the intersection of the clipping path specified by its ‘clip-path’ property (if any) with any clipping paths on its ancestors, as specified by the ‘clip-path’ property on the ancestor elements, or by the ‘overflow’ property on ancestor elements which establish a new viewport. Also, see the discussion of the initial clipping path.)

Fixes issues highlighted by https://github.com/react-native-community/react-native-svg/issues/752
Fix https://github.com/react-native-community/react-native-svg/issues/280
Fix https://github.com/react-native-community/react-native-svg/issues/517

[android] Fix https://github.com/react-native-community/react-native-svg/issues/766
`Region.Op.REPLACE` is deprecated in API level 28
Replace with clipPath (Path path) to Intersect instead.
This commit is contained in:
Mikael Sand
2018-09-01 02:55:36 +03:00
parent f1f0e2f186
commit a1097b8594
8 changed files with 133 additions and 30 deletions
@@ -10,9 +10,11 @@
package com.horcrux.svg;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.ReactShadowNode;
@@ -119,7 +121,36 @@ class GroupShadowNode extends RenderableShadowNode {
traverseChildren(new NodeRunnable() {
public void run(ReactShadowNode node) {
if (node instanceof VirtualNode) {
path.addPath(((VirtualNode)node).getPath(canvas, paint));
VirtualNode n = (VirtualNode)node;
Matrix transform = n.mMatrix;
path.addPath(n.getPath(canvas, paint), transform);
}
}
});
return path;
}
protected Path getPath(final Canvas canvas, final Paint paint, final Path.Op op) {
final Path path = new Path();
traverseChildren(new NodeRunnable() {
public void run(ReactShadowNode node) {
if (node instanceof VirtualNode) {
VirtualNode n = (VirtualNode)node;
Matrix transform = n.mMatrix;
Path p2;
if (n instanceof GroupShadowNode) {
p2 = ((GroupShadowNode)n).getPath(canvas, paint, op);
} else {
p2 = n.getPath(canvas, paint);
}
p2.transform(transform);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path.op(p2, op);
} else {
path.addPath(p2);
}
}
}
});
@@ -140,6 +140,11 @@ class TextShadowNode extends GroupShadowNode {
return groupPath;
}
@Override
protected Path getPath(Canvas canvas, Paint paint, Path.Op op) {
return getPath(canvas, paint);
}
AlignmentBaseline getAlignmentBaseline() {
if (mAlignmentBaseline == null) {
ReactShadowNode parent = this.getParent();
@@ -27,6 +27,8 @@ import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.EventDispatcher;
import java.util.List;
import javax.annotation.Nullable;
import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE;
@@ -239,10 +241,15 @@ abstract class VirtualNode extends LayoutShadowNode {
@Nullable Path getClipPath(Canvas canvas, Paint paint) {
if (mClipPath != null) {
VirtualNode node = getSvgShadowNode().getDefinedClipPath(mClipPath);
ClipPathShadowNode mClipNode = (ClipPathShadowNode) getSvgShadowNode().getDefinedClipPath(mClipPath);
if (node != null) {
Path clipPath = node.getPath(canvas, paint);
if (mClipNode != null) {
Path clipPath;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
clipPath = mClipNode.getPath(canvas, paint, Path.Op.UNION);
} else {
clipPath = mClipNode.getPath(canvas, paint);
}
switch (mClipRule) {
case CLIP_RULE_EVENODD:
clipPath.setFillType(Path.FillType.EVEN_ODD);
@@ -265,7 +272,7 @@ abstract class VirtualNode extends LayoutShadowNode {
Path clip = getClipPath(canvas, paint);
if (clip != null) {
canvas.clipPath(clip, Region.Op.REPLACE);
canvas.clipPath(clip);
}
}