fix memory leak issue with CGMutablePathRef

This commit is contained in:
Horcrux
2016-07-21 18:22:23 +08:00
parent ff2395bcc2
commit 5ee5c2239a
35 changed files with 164 additions and 185 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ class StrokeCircle extends Component{
r="45" r="45"
stroke="purple" stroke="purple"
strokeWidth="2.5" strokeWidth="2.5"
fill={null} fill="none"
/> />
</Svg>; </Svg>;
} }
+4 -4
View File
@@ -25,10 +25,10 @@ class DefsExample extends Component{
> >
<Defs> <Defs>
<G id="path" x="5" y="2" opacity="0.9"> <G id="path" x="5" y="2" opacity="0.9">
<Path id="test" fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/> <Path id="test" d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z" />
</G> </G>
<ClipPath id="clip"> <ClipPath id="clip">
<Circle r="20%" cx="0" cy="0" /> <Circle r="25%" cx="0%" cy="0%" />
</ClipPath> </ClipPath>
<LinearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%"> <LinearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
<Stop offset="0%" stopColor="yellow" /> <Stop offset="0%" stopColor="yellow" />
@@ -41,9 +41,9 @@ class DefsExample extends Component{
<Stop offset="100%" stopColor="blue" /> <Stop offset="100%" stopColor="blue" />
</RadialGradient> </RadialGradient>
</Defs> </Defs>
<Use href="url(#path)" x="0" fill="blue" opacity="0.2" /> <Use href="url(#path)" x="0" fill="blue" opacity="0.6" />
<Use href="url(#path)" x="20" y="5" fill="url(#linear)" /> <Use href="url(#path)" x="20" y="5" fill="url(#linear)" />
<Use href="url(#path)" x="50" clipPath="url(#clip)" fillOpacity="0.6" /> <Use href="url(#path)" clipPath="url(#clip)" fillOpacity="0.6" stroke="#000" />
<Use href="url(#path)" x="-10" y="20" fill="red" fill="url(#radial)" /> <Use href="url(#path)" x="-10" y="20" fill="red" fill="url(#radial)" />
</Svg>; </Svg>;
} }
+1
View File
@@ -11,6 +11,7 @@ import Svg, {
class PathExample extends Component{ class PathExample extends Component{
static title = 'Path'; static title = 'Path';
render() { render() {
return <Svg return <Svg
height="100" height="100"
+1
View File
@@ -9,6 +9,7 @@ import Svg, {
class PolygonExample extends Component{ class PolygonExample extends Component{
static title = 'The following example creates a polygon with three sides'; static title = 'The following example creates a polygon with three sides';
render() { render() {
return <Svg return <Svg
height="100" height="100"
+20 -1
View File
@@ -28,6 +28,7 @@ class TextExample extends Component{
class TextRotate extends Component{ class TextRotate extends Component{
static title = 'Transform the text'; static title = 'Transform the text';
render() { render() {
return <Svg return <Svg
height="60" height="60"
@@ -115,13 +116,31 @@ class TextFill extends Component{
class TextPath extends Component{ class TextPath extends Component{
static title = 'Transform the text'; static title = 'Draw text along path';
constructor(...args) {
super(...args);
this.state = {
key: 0
};
}
componentDidMount() {
let self = this;
//setInterval(function () {
// self.setState({
// key: self.state.key + 1
// });
//}, 50);
}
render() { render() {
return <Svg return <Svg
height="60" height="60"
width="200" width="200"
> >
<Text <Text
key={this.state.key}
fill="red" fill="red"
path={` path={`
M 10 20 M 10 20
+1 -1
View File
@@ -111,7 +111,7 @@ const styles = StyleSheet.create({
} }
}); });
const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents']; const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents', 'Definations'];
//const names = ['Definations']; //const names = ['Definations'];
class SvgExample extends Component { class SvgExample extends Component {
+11 -2
View File
@@ -4,6 +4,7 @@ import createReactNativeComponentClass from 'react/lib/createReactNativeComponen
import {transformProps} from '../lib/props'; import {transformProps} from '../lib/props';
import {GroupAttributes} from '../lib/attributes'; import {GroupAttributes} from '../lib/attributes';
import extractProps from '../lib/extract/extractProps'; import extractProps from '../lib/extract/extractProps';
import reusableProps from '../lib/reusableProps';
class G extends Component{ class G extends Component{
static displayName = 'G'; static displayName = 'G';
@@ -15,10 +16,18 @@ class G extends Component{
}; };
render() { render() {
let {props} = this;
let extractedProps = extractProps(props, {
stroke: true,
fill: true,
transform: true
});
return <RNSVGGroup return <RNSVGGroup
{...extractProps(this.props, {transform: true})} {...extractedProps}
ref={ele => this.root = ele} ref={ele => this.root = ele}
asClipPath={this.props.asClipPath} mergeList={reusableProps(extractedProps, props)}
> >
{this.props.children} {this.props.children}
</RNSVGGroup>; </RNSVGGroup>;
+3 -20
View File
@@ -1,10 +1,11 @@
import {PropTypes} from 'react'; import {PropTypes} from 'react';
import {pathProps} from '../lib/props'; import {pathProps} from '../lib/props';
import {UseAttributes, RenderableOnlyAttributes} from '../lib/attributes'; import {UseAttributes} from '../lib/attributes';
import Shape from './Shape'; import Shape from './Shape';
import React from 'react'; import React from 'react';
import patternReg from '../lib/extract/patternReg'; import patternReg from '../lib/extract/patternReg';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import reusableProps from '../lib/reusableProps';
import _ from 'lodash'; import _ from 'lodash';
class Defs extends Shape { class Defs extends Shape {
@@ -33,8 +34,6 @@ class Defs extends Shape {
console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"'); console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"');
} }
let mergeList = [];
let extractedProps = this.extractProps(props, { let extractedProps = this.extractProps(props, {
stroke: true, stroke: true,
fill: true, fill: true,
@@ -42,26 +41,10 @@ class Defs extends Shape {
transform: true transform: true
}); });
Object.keys(RenderableOnlyAttributes).forEach(name => {
if (!_.isNil(props[name])) {
// clipPath prop may provide `clipPathRef` as native prop
if (name === 'clipPath') {
if (extractedProps[name]) {
mergeList.push(name);
} else if (extractedProps.clipPathRef) {
mergeList.push('clipPathRef');
}
} else {
mergeList.push(name);
}
}
});
return <RNSVGUse return <RNSVGUse
ref={ele => this.root = ele} ref={ele => this.root = ele}
{...extractedProps} {...extractedProps}
mergeList={mergeList} mergeList={reusableProps(extractedProps, props)}
href={href} href={href}
>{props.children}</RNSVGUse>; >{props.children}</RNSVGUse>;
} }
+2
View File
@@ -15,6 +15,8 @@
@interface RNSVGGroup : RNSVGNode <RNSVGContainer> @interface RNSVGGroup : RNSVGNode <RNSVGContainer>
@property (nonatomic, copy) NSArray<NSString *> *mergeList;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
@end @end
+2 -1
View File
@@ -17,6 +17,7 @@
[self clip:context]; [self clip:context];
for (RNSVGNode *node in self.subviews) { for (RNSVGNode *node in self.subviews) {
//[node mergeProperties:self mergeList:self.mergeList];
[node renderTo:context]; [node renderTo:context];
if (node.responsible && !svg.responsible) { if (node.responsible && !svg.responsible) {
@@ -32,7 +33,7 @@
CGAffineTransform transform = node.transform; CGAffineTransform transform = node.transform;
CGPathAddPath(path, &transform, [node getPath:context]); CGPathAddPath(path, &transform, [node getPath:context]);
} }
return path; return (CGPathRef)CFAutorelease(path);
} }
// hitTest delagate // hitTest delagate
+5 -1
View File
@@ -14,6 +14,7 @@
{ {
CGImageRef image; CGImageRef image;
} }
- (void)setSrc:(id)src - (void)setSrc:(id)src
{ {
if (src == _src) { if (src == _src) {
@@ -79,7 +80,10 @@
CGFloat h = [convert stringToFloat:self.height relative:height offset:0]; CGFloat h = [convert stringToFloat:self.height relative:height offset:0];
// add hit area // add hit area
CGPathAddPath(self.nodeArea, nil, CGPathCreateWithRect(CGRectMake(x, y, w, h), nil)); self.hitArea = CGPathCreateMutable();
CGPathRef rect = CGPathCreateWithRect(CGRectMake(x, y, w, h), nil);
CGPathAddPath(self.hitArea, nil, rect);
CGPathRelease(rect);
if (self.opacity == 0) { if (self.opacity == 0) {
return; return;
+5 -4
View File
@@ -31,13 +31,14 @@
return; return;
} }
// Add path to nodeArea // Add path to hitArea
CGPathAddPath(self.nodeArea, nil, _d); self.hitArea = CGPathCreateMutableCopy(_d);
if (self.stroke) { if (self.stroke) {
// Add stroke to nodeArea // Add stroke to hitArea
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(_d, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit); CGPathRef strokePath = CGPathCreateCopyByStrokingPath(_d, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit);
CGPathAddPath(self.nodeArea, nil, strokePath); CGPathAddPath(self.hitArea, nil, strokePath);
CGPathRelease(strokePath);
} }
if (self.opacity == 0) { if (self.opacity == 0) {
-1
View File
@@ -17,5 +17,4 @@
@property (nonatomic, strong) NSString *href; @property (nonatomic, strong) NSString *href;
@property (nonatomic, copy) NSArray<NSString *> *mergeList; @property (nonatomic, copy) NSArray<NSString *> *mergeList;
@end @end
+12
View File
@@ -23,9 +23,21 @@
{ {
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href]; RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
if (template) { if (template) {
CGFloat opacity = self.opacity;
BOOL transparent = opacity < 1;
if (transparent) {
CGContextBeginTransparencyLayer(context, NULL);
}
[self clip:context];
[template mergeProperties:self mergeList:self.mergeList]; [template mergeProperties:self mergeList:self.mergeList];
[template renderTo:context]; [template renderTo:context];
[template resetProperties]; [template resetProperties];
if (transparent) {
CGContextEndTransparencyLayer(context);
}
} else if (self.href) { } else if (self.href) {
// TODO: calling yellow box here // TODO: calling yellow box here
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href); RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
-6
View File
@@ -19,7 +19,6 @@
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; }; 1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; };
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; }; 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; };
1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */; }; 1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */; };
1039D2941CE71EC2001E90A8 /* RnSVGGlyphCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D28E1CE71EC2001E90A8 /* RnSVGGlyphCache.m */; };
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; }; 1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */; }; 1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */; };
1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */; }; 1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */; };
@@ -88,8 +87,6 @@
1039D2861CE71EB7001E90A8 /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPath.m; path = Elements/RNSVGPath.m; sourceTree = "<group>"; }; 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPath.m; path = Elements/RNSVGPath.m; sourceTree = "<group>"; };
1039D2871CE71EB7001E90A8 /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGSvgView.h; path = Elements/RNSVGSvgView.h; sourceTree = "<group>"; }; 1039D2871CE71EB7001E90A8 /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGSvgView.h; path = Elements/RNSVGSvgView.h; sourceTree = "<group>"; };
1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGSvgView.m; path = Elements/RNSVGSvgView.m; sourceTree = "<group>"; }; 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGSvgView.m; path = Elements/RNSVGSvgView.m; sourceTree = "<group>"; };
1039D28D1CE71EC2001E90A8 /* RNSVGGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphCache.h; path = Text/RNSVGGlyphCache.h; sourceTree = "<group>"; };
1039D28E1CE71EC2001E90A8 /* RnSVGGlyphCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RnSVGGlyphCache.m; path = Text/RnSVGGlyphCache.m; sourceTree = "<group>"; };
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGText.h; path = Text/RNSVGText.h; sourceTree = "<group>"; }; 1039D28F1CE71EC2001E90A8 /* RNSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGText.h; path = Text/RNSVGText.h; sourceTree = "<group>"; };
1039D2901CE71EC2001E90A8 /* RNSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGText.m; path = Text/RNSVGText.m; sourceTree = "<group>"; }; 1039D2901CE71EC2001E90A8 /* RNSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGText.m; path = Text/RNSVGText.m; sourceTree = "<group>"; };
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = "<group>"; }; 1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = "<group>"; };
@@ -265,8 +262,6 @@
1039D27F1CE71D9B001E90A8 /* Text */ = { 1039D27F1CE71D9B001E90A8 /* Text */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1039D28D1CE71EC2001E90A8 /* RNSVGGlyphCache.h */,
1039D28E1CE71EC2001E90A8 /* RnSVGGlyphCache.m */,
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */, 1039D28F1CE71EC2001E90A8 /* RNSVGText.h */,
1039D2901CE71EC2001E90A8 /* RNSVGText.m */, 1039D2901CE71EC2001E90A8 /* RNSVGText.m */,
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */, 1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */,
@@ -373,7 +368,6 @@
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */, 1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */,
10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */, 10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */,
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */, 10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */,
1039D2941CE71EC2001E90A8 /* RnSVGGlyphCache.m in Sources */,
10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */, 10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */,
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */, 1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */,
10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */,
+6 -7
View File
@@ -11,9 +11,6 @@
#import "RNSVGClipPath.h" #import "RNSVGClipPath.h"
@implementation RNSVGNode @implementation RNSVGNode
{
CGFloat originOpacity;
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{ {
@@ -76,11 +73,14 @@
CGContextSaveGState(context); CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform); CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, opacity); CGContextSetAlpha(context, opacity);
if (transparent) { if (transparent) {
CGContextBeginTransparencyLayer(context, NULL); CGContextBeginTransparencyLayer(context, NULL);
} }
[self renderClip:context]; [self renderClip:context];
[self renderLayerTo:context]; [self renderLayerTo:context];
if (transparent) { if (transparent) {
CGContextEndTransparencyLayer(context); CGContextEndTransparencyLayer(context);
} }
@@ -118,7 +118,7 @@
- (CGPathRef)getPath: (CGContextRef) context - (CGPathRef)getPath: (CGContextRef) context
{ {
// abstract // abstract
return CGPathCreateMutable(); return (CGPathRef)CFAutorelease(CGPathCreateMutable());
} }
- (void)clip:(CGContextRef)context - (void)clip:(CGContextRef)context
@@ -193,13 +193,12 @@
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList - (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{ {
originOpacity = self.opacity; // abstract
self.opacity = target.opacity * self.opacity;
} }
- (void)resetProperties - (void)resetProperties
{ {
self.opacity = originOpacity; // abstract
} }
- (void)dealloc - (void)dealloc
+1 -1
View File
@@ -26,7 +26,7 @@
@property (nonatomic, assign) CGFloat strokeMiterlimit; @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 hitArea;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
+16 -7
View File
@@ -16,13 +16,24 @@
- (id)init - (id)init
{ {
self = [super init]; if (self = [super init]) {
if (self) { _fillOpacity = 1;
_nodeArea = CGPathCreateMutable(); _strokeOpacity = 1;
_strokeWidth = 1;
} }
return self; return self;
} }
- (void)setHitArea:(CGMutablePathRef)hitArea
{
if (hitArea == _hitArea) {
return;
}
[self invalidate];
CGPathRelease(_hitArea);
_hitArea = hitArea;
}
- (void)setFill:(RNSVGBrush *)fill - (void)setFill:(RNSVGBrush *)fill
{ {
[self invalidate]; [self invalidate];
@@ -79,7 +90,7 @@
- (void)dealloc - (void)dealloc
{ {
CGPathRelease(_nodeArea); CGPathRelease(_hitArea);
if (_strokeDasharray.array) { if (_strokeDasharray.array) {
free(_strokeDasharray.array); free(_strokeDasharray.array);
} }
@@ -106,7 +117,7 @@
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ {
CGPathRef clipPath = self.clipPath; CGPathRef clipPath = self.clipPath;
if (self.nodeArea && CGPathContainsPoint(self.nodeArea, nil, point, NO)) { if (self.hitArea && CGPathContainsPoint(self.hitArea, nil, point, NO)) {
if (!clipPath) { if (!clipPath) {
return self; return self;
} else { } else {
@@ -131,8 +142,6 @@
[originProperties setValue:[self valueForKey:key] forKey:key]; [originProperties setValue:[self valueForKey:key] forKey:key];
[self setValue:[target valueForKey:key] forKey:key]; [self setValue:[target valueForKey:key] forKey:key];
} }
[super mergeProperties:target mergeList:mergeList];
} }
- (void)resetProperties - (void)resetProperties
+1 -1
View File
@@ -67,7 +67,7 @@
} }
CGPathAddArc(path, nil, cx, cy, r, 0, 2*M_PI, YES); CGPathAddArc(path, nil, cx, cy, r, 0, 2*M_PI, YES);
return path; return (CGPathRef)CFAutorelease(path);
} }
@end @end
+1 -1
View File
@@ -67,7 +67,7 @@
CGFloat rx = [convert stringToFloat:self.rx relative:width offset:0]; CGFloat rx = [convert stringToFloat:self.rx relative:width offset:0];
CGFloat ry = [convert stringToFloat:self.ry relative:height offset:0]; CGFloat ry = [convert stringToFloat:self.ry relative:height offset:0];
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2)); CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
return path; return (CGPathRef)CFAutorelease(path);
} }
@end @end
+1 -1
View File
@@ -69,7 +69,7 @@
CGPathMoveToPoint(path, nil, x1, y1); CGPathMoveToPoint(path, nil, x1, y1);
CGPathAddLineToPoint(path, nil, x2, y2); CGPathAddLineToPoint(path, nil, x2, y2);
return path; return (CGPathRef)CFAutorelease(path);
} }
@end @end
+1 -1
View File
@@ -109,7 +109,7 @@
CGPathAddRect(path, nil, CGRectMake(x, y, w, h)); CGPathAddRect(path, nil, CGRectMake(x, y, w, h));
} }
return path; return (CGPathRef)CFAutorelease(path);
} }
@end @end
-27
View File
@@ -1,27 +0,0 @@
/**
* 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.
*/
// This implements a very simple glyph cache.
// It maps from CTFontRef to CGGlyph to CGPathRef in order to reuse glyphs.
// It does NOT try to retain the keys that are used (CTFontRef or CGGlyph)
// but that is not an issue with respect to how it is used by this sample.
#import <Foundation/Foundation.h>
#import <CoreText/CoreText.h>
#import "RNSVGRenderable.h"
@interface RNSVGGlyphCache : NSObject
{
CFMutableDictionaryRef cache;
}
- (id)init;
- (CGPathRef)pathForGlyph:(CGGlyph)glyph fromFont:(CTFontRef)font;
@end
-1
View File
@@ -10,7 +10,6 @@
#import "UIBezierPath-Points.h" #import "UIBezierPath-Points.h"
#import "RNSVGPath.h" #import "RNSVGPath.h"
#import "RNSVGTextFrame.h" #import "RNSVGTextFrame.h"
#import "RNSVGGlyphCache.h"
@interface RNSVGText : RNSVGPath @interface RNSVGText : RNSVGPath
+22 -14
View File
@@ -32,7 +32,7 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
- (void)setTextFrame:(RNSVGTextFrame)frame - (void)setTextFrame:(RNSVGTextFrame)frame
{ {
if (frame.lines != _textFrame.lines) { if (frame.lines == _textFrame.lines) {
RNSVGFreeTextFrame(_textFrame); RNSVGFreeTextFrame(_textFrame);
} }
[self invalidate]; [self invalidate];
@@ -82,25 +82,25 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
// We should consider snapping this shift to device pixels to improve rendering quality // We should consider snapping this shift to device pixels to improve rendering quality
// when a line has subpixel width. // when a line has subpixel width.
CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path ? -frame.lineHeight : 0)); CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path ? -frame.lineHeight : 0));
CGPathAddPath(path, &offset, [self setLinePath:frame.lines[i]]);
CGMutablePathRef line = [self setLinePath:frame.lines[i]];
CGPathAddPath(path, &offset, line);
CGPathRelease(line);
} }
return path; return (CGPathRef)CFAutorelease(path);
} }
- (CGPathRef)setLinePath:(CTLineRef)line - (CGMutablePathRef)setLinePath:(CTLineRef)line
{ {
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0); CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable(); CGMutablePathRef path = CGPathCreateMutable();
RNSVGGlyphCache *cache = [[RNSVGGlyphCache alloc] init];
CTLineGetGlyphRuns(line); CTLineGetGlyphRuns(line);
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line); CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
CFIndex runCount = CFArrayGetCount(glyphRuns); CFIndex runCount = CFArrayGetCount(glyphRuns);
CFIndex glyphIndex = 0; CFIndex glyphIndex = 0;
for(CFIndex i = 0; i < runCount; ++i)
{ for(CFIndex i = 0; i < runCount; ++i) {
// For each run, we need to get the glyphs, their font (to get the path) and their locations. // For each run, we need to get the glyphs, their font (to get the path) and their locations.
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i); CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i);
CFIndex runGlyphCount = CTRunGetGlyphCount(run); CFIndex runGlyphCount = CTRunGetGlyphCount(run);
@@ -112,9 +112,11 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs);
CFDictionaryRef attributes = CTRunGetAttributes(run); CFDictionaryRef attributes = CTRunGetAttributes(run);
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName); CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName);
for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) { for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) {
CGPathRef letter = [cache pathForGlyph:glyphs[j] fromFont:runFont]; CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[j], nil);
CGPoint point = positions[j]; CGPoint point = positions[j];
if (letter) { if (letter) {
CGAffineTransform transform; CGAffineTransform transform;
@@ -122,23 +124,29 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
if (self.path) { if (self.path) {
CGPoint slope; CGPoint slope;
CGRect bounding = CGPathGetBoundingBox(letter); CGRect bounding = CGPathGetBoundingBox(letter);
UIBezierPath* path = [UIBezierPath bezierPathWithCGPath:self.path]; UIBezierPath* pathAlong = [UIBezierPath bezierPathWithCGPath:self.path];
CGFloat percentConsumed = (point.x + bounding.size.width) / path.length; CGFloat percentConsumed = (point.x + bounding.size.width) / pathAlong.length;
if (percentConsumed >= 1.0f) { if (percentConsumed >= 1.0f) {
CGPathRelease(letter);
continue; continue;
} }
CGPoint targetPoint = [path pointAtPercent:percentConsumed withSlope: &slope]; CGPoint targetPoint = [pathAlong pointAtPercent:percentConsumed withSlope: &slope];
float angle = atan(slope.y / slope.x); // + M_PI; float angle = atan(slope.y / slope.x); // + M_PI;
if (slope.x < 0) angle += M_PI; // going left, update the angle if (slope.x < 0) {
angle += M_PI; // going left, update the angle
}
transform = CGAffineTransformMakeTranslation(targetPoint.x - bounding.size.width, targetPoint.y); transform = CGAffineTransformMakeTranslation(targetPoint.x - bounding.size.width, targetPoint.y);
transform = CGAffineTransformRotate(transform, angle); transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0); transform = CGAffineTransformScale(transform, 1.0, -1.0);
} else { } else {
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y); transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
} }
CGPathAddPath(path, &transform, letter); CGPathAddPath(path, &transform, letter);
} }
CGPathRelease(letter);
} }
} }
-63
View File
@@ -1,63 +0,0 @@
/**
* 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 "RNSVGGlyphCache.h"
@implementation RNSVGGlyphCache
-(id)init
{
self = [super init];
if(self)
{
cache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
}
return self;
}
-(void)dealloc
{
CFRelease(cache);
}
-(CGPathRef)pathForGlyph:(CGGlyph)glyph fromFont:(CTFontRef)font
{
// First we lookup the font to get to its glyph dictionary
CFMutableDictionaryRef glyphDict = (CFMutableDictionaryRef)CFDictionaryGetValue(cache, font);
if(!glyphDict)
{
// And if this font hasn't been seen before, we'll create and set the dictionary for it
glyphDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(cache, font, glyphDict);
CFRelease(glyphDict);
}
// Next we try to get a path for the given glyph from the glyph dictionary
CGPathRef path = (CGPathRef)CFDictionaryGetValue(glyphDict, (const void *)(uintptr_t)glyph);
if(!path)
{
// If the path hasn't been seen before, then we'll create the path from the font & glyph and cache it.
path = CTFontCreatePathForGlyph(font, glyph, NULL);
if(!path)
{
// If a glyph does not have a path, then we need a placeholder to set in the dictionary
path = (CGPathRef)kCFNull;
}
CFDictionarySetValue(glyphDict, (const void *)(uintptr_t)glyph, path);
CFRelease(path);
}
if(path == (CGPathRef)kCFNull)
{
// If we got the placeholder, then set the path to NULL
// (this will happen either after discovering the glyph path is NULL,
// or after looking that up in the dictionary).
path = NULL;
}
return path;
}
@end
+1 -1
View File
@@ -76,7 +76,7 @@ void getPointsFromBezier(void *info, const CGPathElement *element)
{ {
// Use total length to calculate the percent of path consumed at each control point // Use total length to calculate the percent of path consumed at each control point
NSArray *points = self.points; NSArray *points = self.points;
int pointCount = points.count; NSUInteger pointCount = points.count;
float totalPointLength = self.length; float totalPointLength = self.length;
float distanceTravelled = 0.0f; float distanceTravelled = 0.0f;
+2 -2
View File
@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
#import "RNSVGNodeManager.h" #import "RNSVGRenderableManager.h"
@interface RNSVGGroupManager : RNSVGNodeManager @interface RNSVGGroupManager : RNSVGRenderableManager
@end @end
+1 -3
View File
@@ -19,8 +19,6 @@ RCT_EXPORT_MODULE()
return [RNSVGGroup new]; return [RNSVGGroup new];
} }
RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray<NSString *>)
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
@end @end
+2
View File
@@ -33,6 +33,8 @@ RCT_EXPORT_VIEW_PROPERTY(name, NSString)
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform)
RCT_EXPORT_VIEW_PROPERTY(clipPathRef, NSString) RCT_EXPORT_VIEW_PROPERTY(clipPathRef, NSString)
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL) RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL)
@end @end
@@ -28,8 +28,6 @@ RCT_EXPORT_VIEW_PROPERTY(strokeOpacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat) RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(strokeLinecap, CGLineCap) RCT_EXPORT_VIEW_PROPERTY(strokeLinecap, CGLineCap)
RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin) RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin)
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
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) RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
+6 -7
View File
@@ -47,7 +47,11 @@ const NodeAttributes = {
diff: arrayDiffer diff: arrayDiffer
}, },
opacity: true, opacity: true,
clipRule: true,
clipPathRef: true, clipPathRef: true,
clipPath: {
diff: arrayDiffer
},
responsible: true responsible: true
}; };
@@ -64,10 +68,6 @@ const RenderableOnlyAttributes = {
strokeWidth: true, strokeWidth: true,
strokeLinecap: true, strokeLinecap: true,
strokeLinejoin: true, strokeLinejoin: true,
clipPath: {
diff: arrayDiffer
},
clipRule: true,
strokeDasharray: { strokeDasharray: {
diff: arrayDiffer diff: arrayDiffer
}, },
@@ -78,10 +78,9 @@ const RenderableOnlyAttributes = {
const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes); const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes);
const GroupAttributes = merge({ const GroupAttributes = merge({
clipPath: { mergeList: {
diff: arrayDiffer diff: arrayDiffer
}, }
clipRule: true
}, NodeAttributes); }, NodeAttributes);
const UseAttributes = merge({ const UseAttributes = merge({
+2 -1
View File
@@ -3,11 +3,12 @@ import extractStroke from './extractStroke';
import extractTransform from './extractTransform'; import extractTransform from './extractTransform';
import extractClipping from './extractClipping'; import extractClipping from './extractClipping';
import extractResponder from './extractResponder'; import extractResponder from './extractResponder';
import extractOpacity from './extractOpacity';
import _ from 'lodash'; import _ from 'lodash';
export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) { export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) {
let extractedProps = { let extractedProps = {
opacity: +props.opacity || 1 opacity: extractOpacity(props.opacity)
}; };
if (props.id) { if (props.id) {
+11 -3
View File
@@ -1,5 +1,6 @@
import extractBrush from './extractBrush'; import extractBrush from './extractBrush';
import extractOpacity from './extractOpacity'; import extractOpacity from './extractOpacity';
import _ from 'lodash';
let separator = /\s*,\s*/; let separator = /\s*,\s*/;
const caps = { const caps = {
@@ -15,12 +16,19 @@ const joins = {
}; };
export default function(props) { export default function(props) {
let strokeWidth = +props.strokeWidth;
let {stroke} = props; let {stroke} = props;
if (!strokeWidth && !stroke) { if (!stroke) {
return null; return null;
} }
let strokeWidth = +props.strokeWidth;
if (_.isNil(props.strokeWidth)) {
strokeWidth = 1;
} else if (!strokeWidth) {
return;
}
let strokeDasharray = props.strokeDasharray; let strokeDasharray = props.strokeDasharray;
if (typeof strokeDasharray === 'string') { if (typeof strokeDasharray === 'string') {
@@ -42,7 +50,7 @@ export default function(props) {
strokeLinecap: caps[props.strokeLinecap] || 0, strokeLinecap: caps[props.strokeLinecap] || 0,
strokeLinejoin: joins[props.strokeLinejoin] || 0, strokeLinejoin: joins[props.strokeLinejoin] || 0,
strokeDasharray: strokeDasharray || null, strokeDasharray: strokeDasharray || null,
strokeWidth: strokeWidth || 1, strokeWidth: strokeWidth,
strokeDashoffset: strokeDasharray ? (+props.strokeDashoffset || 0) : null, strokeDashoffset: strokeDasharray ? (+props.strokeDashoffset || 0) : null,
strokeMiterlimit: props.strokeMiterlimit || 4 strokeMiterlimit: props.strokeMiterlimit || 4
}; };
+22
View File
@@ -0,0 +1,22 @@
import {RenderableOnlyAttributes} from '../lib/attributes';
import _ from 'lodash';
export default function (extractedProps, originProps) {
let reusableProps = [];
Object.keys(RenderableOnlyAttributes).forEach(name => {
if (!_.isNil(originProps[name])) {
// clipPath prop may provide `clipPathRef` as native prop
if (name === 'clipPath') {
if (extractedProps[name]) {
reusableProps.push(name);
} else if (extractedProps.clipPathRef) {
reusableProps.push('clipPathRef');
}
} else {
reusableProps.push(name);
}
}
});
return reusableProps;
}