refactor clipPath native code

refactor clipPath native code
add strokeMiterlimit prop support
This commit is contained in:
Horcrux
2016-05-21 19:58:49 +08:00
parent bb9380b049
commit 6a0c47a898
30 changed files with 305 additions and 152 deletions

View File

@@ -22,9 +22,9 @@ class LinearGradientHorizontal extends Component{
width="300" width="300"
> >
<Defs> <Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0"> <LinearGradient id="grad" x1="65" y1="0" x2="235" y2="0">
<Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" /> <Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" />
<Stop offset="1" stopColor="red" stopOpacity="1" /> <Stop offset="1" stopColor="red" />
</LinearGradient> </LinearGradient>
</Defs> </Defs>
<Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" /> <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />

View File

@@ -568,12 +568,8 @@ npm install
1. add native method for elements 1. add native method for elements
2. more Text features support 2. more Text features support
3. Pattern element 3. Pattern element
4. Image element (Android) 4. implement touchable elements
5. calculate bounding box only if necessary. 5. implement Animated elements
6. miterLimit
7. move percentage convert and context bounding box to renderable
8. implement touchable elements ()
9. implement Animated elements
#### Thanks: #### Thanks:

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGClipPath view
*/
public class RNSVGClipPathShadowNode extends RNSVGGroupShadowNode {
private String mName = null;
@ReactProp(name = "name")
public void setName(String name) {
mName = name;
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
getSvgShadowNode().defineClipPath(getPath(canvas, paint), mName);
}
@Override
public int hitTest(Point point, View view) {
return -1;
}
}

View File

@@ -17,46 +17,30 @@ import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
/** /**
* Shadow node for virtual RNSVGGroup view * Shadow node for virtual RNSVGGroup view
*/ */
public class RNSVGGroupShadowNode extends RNSVGVirtualNode { public class RNSVGGroupShadowNode extends RNSVGVirtualNode {
private String mAsClipPath = null;
@ReactProp(name = "asClipPath")
public void setAsClipPath(String asClipPath) {
mAsClipPath = asClipPath;
markUpdated();
}
public void draw(Canvas canvas, Paint paint, float opacity) { public void draw(Canvas canvas, Paint paint, float opacity) {
opacity *= mOpacity; opacity *= mOpacity;
RNSVGSvgViewShadowNode svg = getSvgShadowNode(); RNSVGSvgViewShadowNode svg = getSvgShadowNode();
if (mAsClipPath == null) { if (opacity > MIN_OPACITY_FOR_DRAW) {
if (opacity > MIN_OPACITY_FOR_DRAW) { int count = saveAndSetupCanvas(canvas);
int count = saveAndSetupCanvas(canvas); clip(canvas, paint);
clip(canvas, paint);
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i); RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
child.setupDimensions(canvas); child.setupDimensions(canvas);
child.draw(canvas, paint, opacity); child.draw(canvas, paint, opacity);
if (child.isTouchable()) { if (child.isTouchable()) {
svg.enableTouchEvents(); svg.enableTouchEvents();
}
} }
restoreCanvas(canvas, count);
} }
} else { restoreCanvas(canvas, count);
svg.defineClipPath(getPath(canvas, paint), mAsClipPath);
} }
} }
@@ -74,11 +58,6 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode {
@Override @Override
public int hitTest(Point point, View view) { public int hitTest(Point point, View view) {
if (mAsClipPath != null) {
return -1;
}
int viewTag = -1; int viewTag = -1;
for (int i = getChildCount() - 1; i >= 0; i--) { for (int i = getChildCount() - 1; i >= 0; i--) {
viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point, ((ViewGroup) view).getChildAt(i)); viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point, ((ViewGroup) view).getChildAt(i));

View File

@@ -51,10 +51,12 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
private static final int FILL_RULE_EVENODD = 0; private static final int FILL_RULE_EVENODD = 0;
private static final int FILL_RULE_NONZERO = 1; private static final int FILL_RULE_NONZERO = 1;
private @Nullable ReadableArray mStrokeColor; private @Nullable ReadableArray mStrokeColor;
private @Nullable ReadableArray mFillColor; private @Nullable ReadableArray mFillColor;
private @Nullable float[] mStrokeDasharray; private @Nullable float[] mStrokeDasharray;
private float mStrokeWidth = 1; private float mStrokeWidth = 1;
private float mStrokeMiterlimit = 4;
private float mStrokeDashoffset = 0; private float mStrokeDashoffset = 0;
private Paint.Cap mStrokeLinecap = Paint.Cap.ROUND; private Paint.Cap mStrokeLinecap = Paint.Cap.ROUND;
private Paint.Join mStrokeLinejoin = Paint.Join.ROUND; private Paint.Join mStrokeLinejoin = Paint.Join.ROUND;
@@ -126,6 +128,12 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
markUpdated(); markUpdated();
} }
@ReactProp(name = "strokeMiterlimit", defaultFloat = 4f)
public void setStrokeMiterlimit(float strokeMiterlimit) {
mStrokeMiterlimit = strokeMiterlimit;
markUpdated();
}
@ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND) @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
public void setStrokeLinecap(int strokeLinecap) { public void setStrokeLinecap(int strokeLinecap) {
switch (strokeLinecap) { switch (strokeLinecap) {
@@ -185,8 +193,8 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
} }
restoreCanvas(canvas, count); restoreCanvas(canvas, count);
markUpdateSeen();
} }
markUpdateSeen();
} }
private void setupPath() { private void setupPath() {
@@ -243,6 +251,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
paint.setStyle(Paint.Style.STROKE); paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(mStrokeLinecap); paint.setStrokeCap(mStrokeLinecap);
paint.setStrokeJoin(mStrokeLinejoin); paint.setStrokeJoin(mStrokeLinejoin);
paint.setStrokeMiter(mStrokeMiterlimit * mScale);
paint.setStrokeWidth(mStrokeWidth * mScale); paint.setStrokeWidth(mStrokeWidth * mScale);
setupPaint(paint, opacity, mStrokeColor, box); setupPaint(paint, opacity, mStrokeColor, box);

View File

@@ -41,6 +41,7 @@ public class RNSVGRenderableViewManager extends ViewGroupManager<ViewGroup> {
/* package */ static final String CLASS_ELLIPSE = "RNSVGEllipse"; /* package */ static final String CLASS_ELLIPSE = "RNSVGEllipse";
/* package */ static final String CLASS_LINE = "RNSVGLine"; /* package */ static final String CLASS_LINE = "RNSVGLine";
/* package */ static final String CLASS_RECT = "RNSVGRect"; /* package */ static final String CLASS_RECT = "RNSVGRect";
/* package */ static final String CLASS_CLIP_PATH = "RNSVGClipPath";
private final String mClassName; private final String mClassName;
@@ -78,6 +79,10 @@ public class RNSVGRenderableViewManager extends ViewGroupManager<ViewGroup> {
return new RNSVGRenderableViewManager(CLASS_RECT); return new RNSVGRenderableViewManager(CLASS_RECT);
} }
public static RNSVGRenderableViewManager createRNSVGClipPathViewManager() {
return new RNSVGRenderableViewManager(CLASS_CLIP_PATH);
}
private RNSVGRenderableViewManager(String className) { private RNSVGRenderableViewManager(String className) {
mClassName = className; mClassName = className;
} }
@@ -114,6 +119,9 @@ public class RNSVGRenderableViewManager extends ViewGroupManager<ViewGroup> {
case CLASS_IMAGE: case CLASS_IMAGE:
mVirtualNode = new RNSVGImageShadowNode(); mVirtualNode = new RNSVGImageShadowNode();
break; break;
case CLASS_CLIP_PATH:
mVirtualNode = new RNSVGClipPathShadowNode();
break;
default: default:
throw new IllegalStateException("Unexpected type " + mClassName); throw new IllegalStateException("Unexpected type " + mClassName);
} }
@@ -141,6 +149,8 @@ public class RNSVGRenderableViewManager extends ViewGroupManager<ViewGroup> {
return RNSVGTextShadowNode.class; return RNSVGTextShadowNode.class;
case CLASS_IMAGE: case CLASS_IMAGE:
return RNSVGImageShadowNode.class; return RNSVGImageShadowNode.class;
case CLASS_CLIP_PATH:
return RNSVGClipPathShadowNode.class;
default: default:
throw new IllegalStateException("Unexpected type " + mClassName); throw new IllegalStateException("Unexpected type " + mClassName);
} }

View File

@@ -73,29 +73,27 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
public void draw(Canvas canvas, Paint paint, float opacity) { public void draw(Canvas canvas, Paint paint, float opacity) {
opacity *= mOpacity; opacity *= mOpacity;
if (opacity <= MIN_OPACITY_FOR_DRAW) { if (opacity > MIN_OPACITY_FOR_DRAW) {
return; String text = formatText();
} if (text == null) {
return;
}
String text = formatText(); // only set up the canvas if we have something to draw
if (text == null) { int count = saveAndSetupCanvas(canvas);
return; clip(canvas, paint);
} RectF box = getBox(paint, text);
// only set up the canvas if we have something to draw if (setupStrokePaint(paint, opacity, box)) {
int count = saveAndSetupCanvas(canvas); drawText(canvas, paint, text);
clip(canvas, paint); }
RectF box = getBox(paint, text); if (setupFillPaint(paint, opacity, box)) {
drawText(canvas, paint, text);
}
if (setupStrokePaint(paint, opacity, box)) { restoreCanvas(canvas, count);
drawText(canvas, paint, text); markUpdateSeen();
} }
if (setupFillPaint(paint, opacity, box)) {
drawText(canvas, paint, text);
}
restoreCanvas(canvas, count);
markUpdateSeen();
} }
private void drawText(Canvas canvas, Paint paint, String text) { private void drawText(Canvas canvas, Paint paint, String text) {

View File

@@ -33,6 +33,7 @@ public class RNSvgPackage implements ReactPackage {
RNSVGRenderableViewManager.createRNSVGRectViewManager(), RNSVGRenderableViewManager.createRNSVGRectViewManager(),
RNSVGRenderableViewManager.createRNSVGTextViewManager(), RNSVGRenderableViewManager.createRNSVGTextViewManager(),
RNSVGRenderableViewManager.createRNSVGImageViewManager(), RNSVGRenderableViewManager.createRNSVGImageViewManager(),
RNSVGRenderableViewManager.createRNSVGClipPathViewManager(),
new RNSVGSvgViewManager()); new RNSVGSvgViewManager());
} }

View File

@@ -1,6 +1,7 @@
import React, {Component, PropTypes} from 'react'; import React, {Component, PropTypes} from 'react';
import {NativeGroup} from './G'; import {NativeGroup} from './G';
import {set, remove} from '../lib/extract/extractClipping'; import {set, remove} from '../lib/extract/extractClipping';
import createNativeComponent from '../lib/createNativeComponent';
class ClipPath extends Component{ class ClipPath extends Component{
static displayName = 'ClipPath'; static displayName = 'ClipPath';
@@ -26,12 +27,13 @@ class ClipPath extends Component{
render() { render() {
set(this.id, this.id); set(this.id, this.id);
return <RNSVGClipPath
return <NativeGroup name={this.id}
asClipPath={this.id} >{this.props.children}</RNSVGClipPath>;
>{this.props.children}</NativeGroup>;
} }
} }
const RNSVGClipPath = createNativeComponent('RNSVGClipPath');
export default ClipPath; export default ClipPath;

View File

@@ -12,6 +12,7 @@ class DefsItem extends Component{
static propType = { static propType = {
visible: PropTypes.bool visible: PropTypes.bool
}; };
static defaultProps = { static defaultProps = {
visible: false visible: false
}; };
@@ -75,16 +76,11 @@ class Defs extends Component{
static Item = DefsItem; static Item = DefsItem;
static Use = DefsUse; static Use = DefsUse;
shouldRender = false;
getChildren = () => { getChildren = () => {
return Children.map(this.props.children, child => { return Children.map(this.props.children, child => {
let {type} = child; let {type} = child;
if (type === LinearGradient || type === RadialGradient || type === ClipPath) { if (type === LinearGradient || type === RadialGradient || type === ClipPath) {
if (type === ClipPath) {
this.shouldRender = true;
}
return cloneElement(child, { return cloneElement(child, {
svgId: this.props.svgId svgId: this.props.svgId
}); });
@@ -99,10 +95,7 @@ class Defs extends Component{
}; };
render() { render() {
let children = this.getChildren(); return <NativeGroup>{this.getChildren()}</NativeGroup>;
return <NativeGroup opacity={this.shouldRender ? 1 : 0}>
{children}
</NativeGroup>;
} }
} }

View File

@@ -1,9 +1,15 @@
import {Component, PropTypes} from 'react'; import {Component, PropTypes} from 'react';
import {numberProp} from '../lib/props';
class Stop extends Component{ class Stop extends Component{
static displayName = 'Stop'; static displayName = 'Stop';
static propTypes = { static propTypes = {
stopColor: PropTypes.string, stopColor: PropTypes.string,
stopOpacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) stopOpacity: numberProp
};
static defaultProps = {
stopOpacity: 1
}; };
render() { render() {

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "RNSVGContainer.h"
#import "RNSVGGroup.h"
#import "RNSVGSvgView.h"
@interface RNSVGClipPath : RNSVGGroup
@property (nonatomic, strong) NSString *name;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
@end

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGClipPath.h"
@implementation RNSVGClipPath
- (void)renderLayerTo:(CGContextRef)context
{
[[self getSvgView] defineClipPath:[self getPath:context] clipPathId:self.name];
}
// hitTest delagate
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
}
@end

View File

@@ -7,16 +7,14 @@
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "RNSVGContainer.h" #import "RNSVGContainer.h"
#import "RNSVGNode.h"
#import "RNSVGCGFCRule.h" #import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h" #import "RNSVGSvgView.h"
#import "RNSVGNode.h"
@interface RNSVGGroup : RNSVGNode <RNSVGContainer> @interface RNSVGGroup : RNSVGNode <RNSVGContainer>
@property (nonatomic, strong) NSString *asClipPath; // Current group is a <ClipPath /> element and asClipPath is its id.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (RNSVGSvgView *)getSvgView;
@end @end

View File

@@ -11,20 +11,16 @@
@implementation RNSVGGroup @implementation RNSVGGroup
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
{ {
if (self.asClipPath == NULL) { RNSVGSvgView* svg = [self getSvgView];
RNSVGSvgView* svg = [self getSvgView]; [self clip:context];
[self clip:context];
for (RNSVGNode *node in self.subviews) {
[node renderTo:context];
for (RNSVGNode *node in self.subviews) { if (node.touchable && !svg.touchable) {
[node renderTo:context]; self.touchable = YES;
if (node.touchable && !svg.touchable) {
self.touchable = YES;
}
} }
} else {
[self defineClipPath:[self getPath:context] clipPathId:self.asClipPath];
} }
} }
@@ -51,14 +47,5 @@
return nil; return nil;
} }
- (RNSVGSvgView *)getSvgView
{
UIView *parent = self.superview;
while ([parent class] != [RNSVGSvgView class]) {
parent = parent.superview;
}
return (RNSVGSvgView *)parent;
}
@end @end

View File

@@ -81,6 +81,10 @@
// add hit area // add hit area
CGPathAddPath(self.nodeArea, nil, CGPathCreateWithRect(CGRectMake(x, y, w, h), nil)); CGPathAddPath(self.nodeArea, nil, CGPathCreateWithRect(CGRectMake(x, y, w, h), nil));
if (self.opacity == 0) {
return;
}
[self clip:context]; [self clip:context];
CGContextSaveGState(context); CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, h); CGContextTranslateCTM(context, 0, h);

View File

@@ -31,12 +31,22 @@
return; return;
} }
CGPathDrawingMode mode = kCGPathStroke;
BOOL fillColor = YES;
// Add path to nodeArea // Add path to nodeArea
CGPathAddPath(self.nodeArea, nil, _d); CGPathAddPath(self.nodeArea, nil, _d);
if (self.stroke) {
// Add stroke to nodeArea
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(_d, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit);
CGPathAddPath(self.nodeArea, nil, strokePath);
}
if (self.opacity == 0) {
return;
}
CGPathDrawingMode mode = kCGPathStroke;
BOOL fillColor = YES;
if (self.fill) { if (self.fill) {
mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill; mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill;
fillColor = [self.fill applyFillColor:context]; fillColor = [self.fill applyFillColor:context];
@@ -55,10 +65,6 @@
} }
} }
if (self.stroke) { if (self.stroke) {
// Add stroke to nodeArea
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(_d, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, 0);
CGPathAddPath(self.nodeArea, nil, strokePath);
CGContextSetLineWidth(context, self.strokeWidth); CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeLinecap); CGContextSetLineCap(context, self.strokeLinecap);
CGContextSetLineJoin(context, self.strokeLinejoin); CGContextSetLineJoin(context, self.strokeLinejoin);

View File

@@ -14,4 +14,13 @@
@property (nonatomic, assign) BOOL touchable; @property (nonatomic, assign) BOOL touchable;
/**
* define <ClipPath></ClipPath> content as clipPath template.
*/
- (void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId;
- (void)removeClipPath:(NSString *)clipPathId;
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathId;
@end @end

View File

@@ -12,6 +12,9 @@
#import "RCTLog.h" #import "RCTLog.h"
@implementation RNSVGSvgView @implementation RNSVGSvgView
{
NSMutableDictionary *clipPaths;
}
- (void)invalidate - (void)invalidate
{ {
@@ -41,4 +44,24 @@
return self.touchable ? [super hitTest:point withEvent:event] : nil; return self.touchable ? [super hitTest:point withEvent:event] : nil;
} }
- (void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId
{
if (clipPaths == NULL) {
clipPaths = [[NSMutableDictionary alloc] init];
}
[clipPaths setValue:[NSValue valueWithPointer:clipPath] forKey:clipPathId];
}
- (void)removeClipPath:(NSString *)clipPathId
{
if (clipPaths != NULL) {
[clipPaths removeObjectForKey:clipPathId];
}
}
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathId
{
return [[clipPaths valueForKey:clipPathId] pointerValue];
}
@end @end

View File

@@ -7,7 +7,6 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */; };
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */; }; 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */; };
0CF68B0B1AF0549300FF9E5C /* RNSVGBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* RNSVGBrush.m */; }; 0CF68B0B1AF0549300FF9E5C /* RNSVGBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* RNSVGBrush.m */; };
0CF68B0C1AF0549300FF9E5C /* RNSVGLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* RNSVGLinearGradient.m */; }; 0CF68B0C1AF0549300FF9E5C /* RNSVGLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* RNSVGLinearGradient.m */; };
@@ -38,6 +37,9 @@
10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D431CE74E3D00887C2B /* RNSVGEllipse.m */; }; 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D431CE74E3D00887C2B /* RNSVGEllipse.m */; };
10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D451CE74E3D00887C2B /* RNSVGLine.m */; }; 10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D451CE74E3D00887C2B /* RNSVGLine.m */; };
10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D471CE74E3D00887C2B /* RNSVGRect.m */; }; 10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D471CE74E3D00887C2B /* RNSVGRect.m */; };
10ED4A9B1CF065260078BC02 /* RNSVGClipPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */; };
10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */; };
10ED4AA21CF078830078BC02 /* RNSVGNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4AA11CF078830078BC02 /* RNSVGNode.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@@ -54,8 +56,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0CF68AC11AF0540F00FF9E5C /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNSVG.a; sourceTree = BUILT_PRODUCTS_DIR; }; 0CF68AC11AF0540F00FF9E5C /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNSVG.a; sourceTree = BUILT_PRODUCTS_DIR; };
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGNode.h; sourceTree = "<group>"; };
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGNode.m; sourceTree = "<group>"; };
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGRenderable.h; sourceTree = "<group>"; }; 0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGRenderable.h; sourceTree = "<group>"; };
0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRenderable.m; sourceTree = "<group>"; }; 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRenderable.m; sourceTree = "<group>"; };
0CF68AEB1AF0549300FF9E5C /* RNSVGBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrush.h; sourceTree = "<group>"; }; 0CF68AEB1AF0549300FF9E5C /* RNSVGBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrush.h; sourceTree = "<group>"; };
@@ -120,6 +120,12 @@
10BA0D451CE74E3D00887C2B /* RNSVGLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGLine.m; path = Shapes/RNSVGLine.m; sourceTree = "<group>"; }; 10BA0D451CE74E3D00887C2B /* RNSVGLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGLine.m; path = Shapes/RNSVGLine.m; sourceTree = "<group>"; };
10BA0D461CE74E3D00887C2B /* RNSVGRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGRect.h; path = Shapes/RNSVGRect.h; sourceTree = "<group>"; }; 10BA0D461CE74E3D00887C2B /* RNSVGRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGRect.h; path = Shapes/RNSVGRect.h; sourceTree = "<group>"; };
10BA0D471CE74E3D00887C2B /* RNSVGRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGRect.m; path = Shapes/RNSVGRect.m; sourceTree = "<group>"; }; 10BA0D471CE74E3D00887C2B /* RNSVGRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGRect.m; path = Shapes/RNSVGRect.m; sourceTree = "<group>"; };
10ED4A991CF065260078BC02 /* RNSVGClipPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGClipPath.h; path = Elements/RNSVGClipPath.h; sourceTree = "<group>"; };
10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGClipPath.m; path = Elements/RNSVGClipPath.m; sourceTree = "<group>"; };
10ED4A9C1CF0656A0078BC02 /* RNSVGClipPathManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGClipPathManager.h; sourceTree = "<group>"; };
10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGClipPathManager.m; sourceTree = "<group>"; };
10ED4AA01CF078830078BC02 /* RNSVGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGNode.h; sourceTree = "<group>"; };
10ED4AA11CF078830078BC02 /* RNSVGNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGNode.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -143,8 +149,8 @@
0CF68AEA1AF0549300FF9E5C /* Brushes */, 0CF68AEA1AF0549300FF9E5C /* Brushes */,
0CF68AF81AF0549300FF9E5C /* ViewManagers */, 0CF68AF81AF0549300FF9E5C /* ViewManagers */,
1039D2A11CE721A7001E90A8 /* RNSVGContainer.h */, 1039D2A11CE721A7001E90A8 /* RNSVGContainer.h */,
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */, 10ED4AA01CF078830078BC02 /* RNSVGNode.h */,
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */, 10ED4AA11CF078830078BC02 /* RNSVGNode.m */,
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */, 0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */, 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */,
0CF68AC21AF0540F00FF9E5C /* Products */, 0CF68AC21AF0540F00FF9E5C /* Products */,
@@ -179,6 +185,8 @@
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
10ED4A9C1CF0656A0078BC02 /* RNSVGClipPathManager.h */,
10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */,
10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */, 10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */,
10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */, 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */,
10BA0D1E1CE74E3100887C2B /* RNSVGEllipseManager.h */, 10BA0D1E1CE74E3100887C2B /* RNSVGEllipseManager.h */,
@@ -237,6 +245,8 @@
1039D2801CE71DCF001E90A8 /* Elements */ = { 1039D2801CE71DCF001E90A8 /* Elements */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
10ED4A991CF065260078BC02 /* RNSVGClipPath.h */,
10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */,
1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */, 1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */,
1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */, 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */,
1039D2831CE71EB7001E90A8 /* RNSVGImage.h */, 1039D2831CE71EB7001E90A8 /* RNSVGImage.h */,
@@ -331,7 +341,9 @@
10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */, 10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */,
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */, 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */, 1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */,
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */, 10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */,
10ED4AA21CF078830078BC02 /* RNSVGNode.m in Sources */,
10ED4A9B1CF065260078BC02 /* RNSVGClipPath.m in Sources */,
10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */, 10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */, 0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */,
10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */, 10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */,

View File

@@ -9,6 +9,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "RNSVGCGFCRule.h" #import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h"
/** /**
* RNSVG nodes are implemented as empty UIViews but this is just an implementation detail to fit * RNSVG nodes are implemented as empty UIViews but this is just an implementation detail to fit
@@ -42,11 +43,6 @@
- (CGPathRef)getClipPath; - (CGPathRef)getClipPath;
/**
* define <ClipPath></ClipPath> content as clipPath template.
*/
- (void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId;
/** /**
* getPath will return the path inside node as a ClipPath. * getPath will return the path inside node as a ClipPath.
*/ */
@@ -55,4 +51,6 @@
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (RNSVGSvgView *)getSvgView;
@end @end

View File

@@ -8,13 +8,9 @@
#import "RNSVGNode.h" #import "RNSVGNode.h"
#import "RNSVGContainer.h" #import "RNSVGContainer.h"
#import "RNSVGClipPath.h"
static NSMutableDictionary *ClipPaths;
@implementation RNSVGNode @implementation RNSVGNode
{
NSString *definedClipPathId;
}
- (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index - (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index
{ {
@@ -30,6 +26,16 @@ static NSMutableDictionary *ClipPaths;
- (void)setOpacity:(CGFloat)opacity - (void)setOpacity:(CGFloat)opacity
{ {
if (opacity == _opacity) {
return;
}
if (opacity < 0) {
opacity = 0;
} else if (opacity > 1) {
opacity = 1;
}
[self invalidate]; [self invalidate];
_opacity = opacity; _opacity = opacity;
} }
@@ -49,16 +55,8 @@ static NSMutableDictionary *ClipPaths;
- (void)renderTo:(CGContextRef)context - (void)renderTo:(CGContextRef)context
{ {
float opacity = self.opacity; float opacity = self.opacity;
if (opacity <= 0) {
// Nothing to paint
return;
}
BOOL transparent = opacity < 1; BOOL transparent = opacity < 1;
if (!transparent) {
opacity = 1;
}
// This needs to be painted on a layer before being composited. // This needs to be painted on a layer before being composited.
CGContextSaveGState(context); CGContextSaveGState(context);
@@ -84,22 +82,6 @@ static NSMutableDictionary *ClipPaths;
_clipPath = CGPathRetain(clipPath); _clipPath = CGPathRetain(clipPath);
} }
-(void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId
{
if (clipPath == _clipPath) {
return;
}
[self invalidate];
CGPathRelease(_clipPath);
_clipPath = CGPathRetain(clipPath);
if (ClipPaths == NULL) {
ClipPaths = [[NSMutableDictionary alloc] init];
}
definedClipPathId = clipPathId;
[ClipPaths setValue:[NSValue valueWithPointer:_clipPath] forKey:clipPathId];
}
- (CGPathRef)getPath: (CGContextRef) context - (CGPathRef)getPath: (CGContextRef) context
{ {
// abstract // abstract
@@ -113,7 +95,7 @@ static NSMutableDictionary *ClipPaths;
if (self.clipPath) { if (self.clipPath) {
clipPath = self.clipPath; clipPath = self.clipPath;
} else if (self.clipPathId) { } else if (self.clipPathId) {
clipPath = [[ClipPaths valueForKey:self.clipPathId] pointerValue]; clipPath = [[self getSvgView] getDefinedClipPath:self.clipPathId];
} }
return clipPath; return clipPath;
@@ -144,6 +126,16 @@ static NSMutableDictionary *ClipPaths;
// abstract // abstract
} }
- (RNSVGSvgView *)getSvgView
{
UIView *parent = self.superview;
while ([parent class] != [RNSVGSvgView class]) {
parent = parent.superview;
}
return (RNSVGSvgView *)parent;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
{ {
// abstract // abstract
@@ -153,9 +145,6 @@ static NSMutableDictionary *ClipPaths;
- (void)dealloc - (void)dealloc
{ {
CGPathRelease(_clipPath); CGPathRelease(_clipPath);
if (definedClipPathId) {
[ClipPaths removeObjectForKey:definedClipPathId];
}
} }
@end @end

View File

@@ -21,6 +21,7 @@
@property (nonatomic, assign) CGFloat strokeWidth; @property (nonatomic, assign) CGFloat strokeWidth;
@property (nonatomic, assign) CGLineCap strokeLinecap; @property (nonatomic, assign) CGLineCap strokeLinecap;
@property (nonatomic, assign) CGLineJoin strokeLinejoin; @property (nonatomic, assign) CGLineJoin strokeLinejoin;
@property (nonatomic, assign) CGFloat strokeMiterlimit;
@property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray; @property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray;
@property (nonatomic, assign) CGFloat strokeDashoffset; @property (nonatomic, assign) CGFloat strokeDashoffset;
@property (nonatomic, assign) CGMutablePathRef nodeArea; @property (nonatomic, assign) CGMutablePathRef nodeArea;

View File

@@ -67,6 +67,12 @@
_strokeDashoffset = strokeDashoffset; _strokeDashoffset = strokeDashoffset;
} }
- (void)setStrokeMiterlimit:(CGFloat)strokeMiterlimit
{
[self invalidate];
_strokeMiterlimit = strokeMiterlimit;
}
- (void)dealloc - (void)dealloc
{ {
CGPathRelease(_nodeArea); CGPathRelease(_nodeArea);

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGNodeManager.h"
@interface RNSVGClipPathManager : RNSVGNodeManager
@end

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGClipPathManager.h"
#import "RNSVGClipPath.h"
@implementation RNSVGClipPathManager
RCT_EXPORT_MODULE()
- (RNSVGNode *)node
{
return [RNSVGClipPath new];
}
RCT_EXPORT_VIEW_PROPERTY(name, NSString)
@end

View File

@@ -22,6 +22,5 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath) RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
RCT_EXPORT_VIEW_PROPERTY(asClipPath, NSString)
@end @end

View File

@@ -30,5 +30,6 @@ RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, RNSVGCGFloatArray) RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, RNSVGCGFloatArray)
RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat) RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
@end @end

View File

@@ -44,7 +44,8 @@ function strokeFilter(props) {
strokeLinejoin: joins[props.strokeLinejoin] || 0, strokeLinejoin: joins[props.strokeLinejoin] || 0,
strokeDasharray: strokeDasharray || null, strokeDasharray: strokeDasharray || null,
strokeWidth: strokeWidth || 1, strokeWidth: strokeWidth || 1,
strokeDashoffset: strokeDasharray ? (+props.strokeDashoffset || 0) : null strokeDashoffset: strokeDasharray ? (+props.strokeDashoffset || 0) : null,
strokeMiterlimit: props.strokeMiterlimit || 4
}; };
} }

View File

@@ -45,7 +45,8 @@ const strokeProps = {
strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]), strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]),
strokeDashoffset: numberProp, strokeDashoffset: numberProp,
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']), strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']) strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
strokeMiterlimit: numberProp
}; };
const textProps = { const textProps = {