Skip to content

Commit

Permalink
First attempt at nested svg support.
Browse files Browse the repository at this point in the history
  • Loading branch information
msand committed Feb 21, 2018
1 parent 824a181 commit ee1d1f7
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 99 deletions.
16 changes: 12 additions & 4 deletions android/src/main/java/com/horcrux/svg/DefsShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import android.graphics.Canvas;
import android.graphics.Paint;

import com.facebook.react.uimanager.LayoutShadowNode;

/**
* Shadow node for virtual Defs view
*/
Expand All @@ -20,18 +22,24 @@ class DefsShadowNode extends DefinitionShadowNode {
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
NodeRunnable markUpdateSeenRecursive = new NodeRunnable() {
public void run(VirtualNode node) {
public void run(LayoutShadowNode node) {
node.markUpdateSeen();
node.traverseChildren(this);
if (node instanceof VirtualNode) {
((VirtualNode) node).traverseChildren(this);
} else if (node instanceof SvgViewShadowNode) {
((SvgViewShadowNode) node).traverseChildren(this);
}
}
};
traverseChildren(markUpdateSeenRecursive);
}

void saveDefinition() {
traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
node.saveDefinition();
public void run(LayoutShadowNode node) {
if (node instanceof VirtualNode) {
((VirtualNode)node).saveDefinition();
}
}
});
}
Expand Down
55 changes: 33 additions & 22 deletions android/src/main/java/com/horcrux/svg/GroupShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.graphics.RectF;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;

Expand Down Expand Up @@ -72,23 +73,29 @@ void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
final SvgViewShadowNode svg = getSvgShadowNode();
final GroupShadowNode self = this;
traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
if (node instanceof RenderableShadowNode) {
((RenderableShadowNode)node).mergeProperties(self);
}

int count = node.saveAndSetupCanvas(canvas);
node.draw(canvas, paint, opacity * mOpacity);
node.restoreCanvas(canvas, count);

if (node instanceof RenderableShadowNode) {
((RenderableShadowNode)node).resetProperties();
}

node.markUpdateSeen();

if (node.isResponsible()) {
svg.enableTouchEvents();
public void run(LayoutShadowNode lNode) {
if (lNode instanceof VirtualNode) {
VirtualNode node = ((VirtualNode)lNode);
if (node instanceof RenderableShadowNode) {
((RenderableShadowNode)node).mergeProperties(self);
}

int count = node.saveAndSetupCanvas(canvas);
node.draw(canvas, paint, opacity * mOpacity);
node.restoreCanvas(canvas, count);

if (node instanceof RenderableShadowNode) {
((RenderableShadowNode)node).resetProperties();
}

node.markUpdateSeen();

if (node.isResponsible()) {
svg.enableTouchEvents();
}
} else if (lNode instanceof SvgViewShadowNode) {
SvgViewShadowNode svgView = (SvgViewShadowNode)lNode;
svgView.drawChildren(canvas);
}
}
});
Expand All @@ -104,8 +111,10 @@ protected Path getPath(final Canvas canvas, final Paint paint) {
final Path path = new Path();

traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
path.addPath(node.getPath(canvas, paint));
public void run(LayoutShadowNode node) {
if (node instanceof VirtualNode) {
path.addPath(((VirtualNode)node).getPath(canvas, paint));
}
}
});

Expand Down Expand Up @@ -154,16 +163,18 @@ void saveDefinition() {
}

traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
node.saveDefinition();
public void run(LayoutShadowNode node) {
if (node instanceof VirtualNode) {
((VirtualNode)node).saveDefinition();
}
}
});
}

@Override
public void resetProperties() {
traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
public void run(LayoutShadowNode node) {
if (node instanceof RenderableShadowNode) {
((RenderableShadowNode)node).resetProperties();
}
Expand Down
60 changes: 44 additions & 16 deletions android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class SvgViewShadowNode extends LayoutShadowNode {
private float mMinY;
private float mVbWidth;
private float mVbHeight;
private String mbbWidth;
private String mbbHeight;
private String mAlign;
private int mMeetOrSlice;
private Matrix mViewBoxMatrix;
Expand Down Expand Up @@ -77,6 +79,18 @@ public void setVbHeight(float vbHeight) {
markUpdated();
}

@ReactProp(name = "bbWidth")
public void setVbWidth(String bbWidth) {
mbbWidth = bbWidth;
markUpdated();
}

@ReactProp(name = "bbHeight")
public void setVbHeight(String bbHeight) {
mbbHeight = bbHeight;
markUpdated();
}

@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
Expand Down Expand Up @@ -117,20 +131,29 @@ private Object drawOutput() {
(int) getLayoutHeight(),
Bitmap.Config.ARGB_8888);

mCanvas = new Canvas(bitmap);
drawChildren(mCanvas);
drawChildren(new Canvas(bitmap));
return bitmap;
}

Rect getCanvasBounds() {
return mCanvas.getClipBounds();
}

private void drawChildren(final Canvas canvas) {

void drawChildren(final Canvas canvas) {
mCanvas = canvas;
if (mAlign != null) {
RectF vbRect = getViewBox();
RectF eRect = new RectF(0, 0, getLayoutWidth(), getLayoutHeight());
float width = getLayoutWidth();
float height = getLayoutHeight();
boolean nested = Float.isNaN(width) || Float.isNaN(height);
if (nested) {
width = Float.parseFloat(mbbWidth) * mScale;
height = Float.parseFloat(mbbHeight) * mScale;
}
RectF eRect = new RectF(0,0, width, height);
if (nested) {
canvas.clipRect(eRect);
}
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
canvas.concat(mViewBoxMatrix);
}
Expand All @@ -143,20 +166,25 @@ private void drawChildren(final Canvas canvas) {


traverseChildren(new VirtualNode.NodeRunnable() {
public void run(VirtualNode node) {
node.saveDefinition();
public void run(LayoutShadowNode node) {
if (node instanceof VirtualNode) {
((VirtualNode)node).saveDefinition();
}
}
});

traverseChildren(new VirtualNode.NodeRunnable() {
public void run(VirtualNode node) {
int count = node.saveAndSetupCanvas(canvas);
node.draw(canvas, paint, 1f);
node.restoreCanvas(canvas, count);
node.markUpdateSeen();

if (node.isResponsible() && !mResponsible) {
mResponsible = true;
public void run(LayoutShadowNode lNode) {
if (lNode instanceof VirtualNode) {
VirtualNode node = (VirtualNode)lNode;
int count = node.saveAndSetupCanvas(canvas);
node.draw(canvas, paint, 1f);
node.restoreCanvas(canvas, count);
node.markUpdateSeen();

if (node.isResponsible() && !mResponsible) {
mResponsible = true;
}
}
}
});
Expand Down Expand Up @@ -238,7 +266,7 @@ void traverseChildren(VirtualNode.NodeRunnable runner) {
continue;
}

runner.run((VirtualNode) child);
runner.run((LayoutShadowNode) child);
}
}
}
8 changes: 5 additions & 3 deletions android/src/main/java/com/horcrux/svg/TextShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;

Expand Down Expand Up @@ -177,9 +178,10 @@ String getBaselineShift() {

void releaseCachedPath() {
traverseChildren(new NodeRunnable() {
public void run(VirtualNode node) {
TextShadowNode text = (TextShadowNode)node;
text.releaseCachedPath();
public void run(LayoutShadowNode node) {
if (node instanceof TextShadowNode) {
((TextShadowNode)node).releaseCachedPath();
}
}
});
}
Expand Down
6 changes: 3 additions & 3 deletions android/src/main/java/com/horcrux/svg/VirtualNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -308,17 +308,17 @@ void saveDefinition() {
}

interface NodeRunnable {
void run(VirtualNode node);
void run(LayoutShadowNode node);
}

void traverseChildren(NodeRunnable runner) {
for (int i = 0; i < getChildCount(); i++) {
ReactShadowNode child = getChildAt(i);
if (!(child instanceof VirtualNode)) {
if (!(child instanceof VirtualNode) && !(child instanceof SvgViewShadowNode)) {
continue;
}

runner.run((VirtualNode) child);
runner.run((LayoutShadowNode) child);
}
}
}
13 changes: 10 additions & 3 deletions elements/Svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,19 @@ class Svg extends Component{

if (width && height) {
dimensions = {
width: +width,
height: +height,
width: width[width.length - 1] === '%' ? width : +width,
height: height[height.length - 1] === '%' ? height : +height,
flex: 0
};
}

const w = `${width}`;
const h = `${height}`;

return <NativeSvgView
{...props}
bbWidth={w}
bbHeight={h}
{...extractViewBox({ viewBox, preserveAspectRatio })}
ref={ele => {this.root = ele;}}
style={[
Expand All @@ -98,7 +103,9 @@ class Svg extends Component{

const NativeSvgView = requireNativeComponent('RNSVGSvgView', null, {
nativeOnly: {
...ViewBoxAttributes
...ViewBoxAttributes,
width: true,
height: true,
}
});

Expand Down
4 changes: 3 additions & 1 deletion ios/Elements/RNSVGDefs.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ - (void)renderTo:(CGContextRef)context
- (void)parseReference
{
[self traverseSubviews:^(RNSVGNode *node) {
[node parseReference];
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
}
Expand Down
46 changes: 30 additions & 16 deletions ios/Elements/RNSVGGroup.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,29 @@ - (void)renderGroupTo:(CGContextRef)context
{
[self pushGlyphContext];
RNSVGSvgView* svg = [self getSvgView];
[self traverseSubviews:^(RNSVGNode *node) {
if (node.responsible && !svg.responsible) {
svg.responsible = YES;
}

if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node mergeProperties:self];
}

[node renderTo:context];

if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties];
[self traverseSubviews:^(UIView *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode* svgNode = (RNSVGNode*)node;
if (svgNode.responsible && !svg.responsible) {
svg.responsible = YES;
}

if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node mergeProperties:self];
}

[svgNode renderTo:context];

if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties];
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
CGRect rect = CGRectMake(0, 0, [svgView.bbWidth floatValue], [svgView.bbHeight floatValue]);
CGContextClipToRect(context, rect);
[svgView drawToContext:context withRect:(CGRect)rect];
} else {
RCTLogWarn(@"Not a RNSVGNode: %@", node.class);
}

return YES;
Expand Down Expand Up @@ -89,8 +99,10 @@ - (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef __block path = CGPathCreateMutable();
[self traverseSubviews:^(RNSVGNode *node) {
CGAffineTransform transform = node.matrix;
CGPathAddPath(path, &transform, [node getPath:context]);
if ([node isKindOfClass:[RNSVGNode class]]) {
CGAffineTransform transform = node.matrix;
CGPathAddPath(path, &transform, [node getPath:context]);
}
return YES;
}];

Expand Down Expand Up @@ -147,7 +159,9 @@ - (void)parseReference
}

[self traverseSubviews:^(__kindof RNSVGNode *node) {
[node parseReference];
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
}
Expand Down
Loading

0 comments on commit ee1d1f7

Please sign in to comment.