merge master fix conflicts

This commit is contained in:
Horcrux
2017-01-09 11:11:12 +08:00
78 changed files with 1582 additions and 766 deletions

View File

@@ -21,11 +21,15 @@
```bash
npm install react-native-svg --save
```
# NOTICE:
- react-native-svg >= 3.2.0 only supports react-native >= 0.29.0
- react-native-svg >= 4.2.0 only supports react-native >= 0.32.0
- react-native-svg >= 4.3.0 only supports react-native >= 0.33.0
- react-native-svg >= 4.4.0 only supports react-native >= 0.38.0 and react >= 15.4.0
- react-native-svg >= 4.5.0 only supports react-native >= 0.40.0 and react >= 15.4.0
2. Link native code
```bash

View File

@@ -19,7 +19,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGCircleShadowNode extends RNSVGPathShadowNode {
public class CircleShadowNode extends RenderableShadowNode {
private String mCx;
private String mCy;
@@ -43,12 +43,6 @@ public class RNSVGCircleShadowNode extends RNSVGPathShadowNode {
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
mPath = getPath(canvas, paint);
super.draw(canvas, paint, opacity);
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();

View File

@@ -21,7 +21,7 @@ import com.facebook.react.common.ReactConstants;
/**
* Shadow node for virtual RNSVGClipPath view
*/
public class RNSVGClipPathShadowNode extends RNSVGGroupShadowNode {
public class ClipPathShadowNode extends GroupShadowNode {
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
@@ -44,10 +44,10 @@ public class RNSVGClipPathShadowNode extends RNSVGGroupShadowNode {
}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList, boolean inherited) {}
public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited) {}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList) {}
public void mergeProperties(VirtualNode target, ReadableArray mergeList) {}
@Override
public void resetProperties() {}

View File

@@ -20,7 +20,7 @@ import com.facebook.react.bridge.ReadableArray;
/**
* Shadow node for virtual Definition type views
*/
public class RNSVGDefinitionShadowNode extends RNSVGVirtualNode {
public class DefinitionShadowNode extends VirtualNode {
public void draw(Canvas canvas, Paint paint, float opacity) {}
@@ -40,10 +40,10 @@ public class RNSVGDefinitionShadowNode extends RNSVGVirtualNode {
}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList, boolean inherited) {}
public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited) {}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList) {}
public void mergeProperties(VirtualNode target, ReadableArray mergeList) {}
@Override
public void resetProperties() {}

View File

@@ -15,15 +15,23 @@ import android.graphics.Paint;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGDefsShadowNode extends RNSVGDefinitionShadowNode {
public class DefsShadowNode extends DefinitionShadowNode {
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
public boolean run(VirtualNode node) {
node.saveDefinition();
return true;
}
});
NodeRunnable markUpdateSeenRecursive = new NodeRunnable() {
public boolean run(VirtualNode node) {
node.markUpdateSeen();
node.traverseChildren(this);
return true;
}
};
traverseChildren(markUpdateSeenRecursive);
}
}

View File

@@ -19,7 +19,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGEllipseShadowNode extends RNSVGPathShadowNode {
public class EllipseShadowNode extends RenderableShadowNode {
private String mCx;
private String mCy;
@@ -50,12 +50,6 @@ public class RNSVGEllipseShadowNode extends RNSVGPathShadowNode {
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
mPath = getPath(canvas, paint);
super.draw(canvas, paint, opacity);
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();

View File

@@ -15,7 +15,6 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.ReactShadowNode;
@@ -24,21 +23,21 @@ import javax.annotation.Nullable;
/**
* Shadow node for virtual RNSVGGroup view
*/
public class RNSVGGroupShadowNode extends RNSVGPathShadowNode {
public class GroupShadowNode extends RenderableShadowNode {
public void draw(final Canvas canvas, final Paint paint, final float opacity) {
final RNSVGSvgViewShadowNode svg = getSvgShadowNode();
final RNSVGVirtualNode self = this;
final SvgViewShadowNode svg = getSvgShadowNode();
final VirtualNode self = this;
if (opacity > MIN_OPACITY_FOR_DRAW) {
int count = saveAndSetupCanvas(canvas);
clip(canvas, paint);
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
public boolean run(VirtualNode node) {
node.setupDimensions(canvas);
node.mergeProperties(self, mOwnedPropList, true);
node.mergeProperties(self, mAttributeList, true);
node.draw(canvas, paint, opacity * mOpacity);
node.markUpdateSeen();
@@ -58,7 +57,7 @@ public class RNSVGGroupShadowNode extends RNSVGPathShadowNode {
final Path path = new Path();
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
public boolean run(VirtualNode node) {
node.setupDimensions(canvas);
path.addPath(node.getPath(canvas, paint));
return true;
@@ -80,11 +79,11 @@ public class RNSVGGroupShadowNode extends RNSVGPathShadowNode {
for (int i = getChildCount() - 1; i >= 0; i--) {
ReactShadowNode child = getChildAt(i);
if (!(child instanceof RNSVGVirtualNode)) {
if (!(child instanceof VirtualNode)) {
continue;
}
RNSVGVirtualNode node = (RNSVGVirtualNode) child;
VirtualNode node = (VirtualNode) child;
int viewTag = node.hitTest(point, combinedMatrix);
if (viewTag != -1) {
@@ -101,27 +100,17 @@ public class RNSVGGroupShadowNode extends RNSVGPathShadowNode {
}
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
public boolean run(VirtualNode node) {
node.saveDefinition();
return true;
}
});
}
@Override
public void mergeProperties(final RNSVGVirtualNode target, final ReadableArray mergeList) {
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
node.mergeProperties(target, mergeList);
return true;
}
});
}
@Override
public void resetProperties() {
traverseChildren(new NodeRunnable() {
public boolean run(RNSVGVirtualNode node) {
public boolean run(VirtualNode node) {
node.resetProperties();
return true;
}

View File

@@ -41,7 +41,7 @@ import javax.annotation.Nullable;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
public class ImageShadowNode extends RenderableShadowNode {
private String mX;
private String mY;
@@ -107,8 +107,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
@Override
public void draw(final Canvas canvas, final Paint paint, final float opacity) {
mPath = new Path();
mPath.addRect(new RectF(getRect()), Path.Direction.CW);
mPath = getPath(canvas, paint);
if (!mLoading.get()) {
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(mUri).build();
@@ -116,12 +115,19 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
if (Fresco.getImagePipeline().isInBitmapMemoryCache(request)) {
tryRender(request, canvas, paint, opacity * mOpacity);
} else {
loadBitmap(request, canvas, paint);
loadBitmap(request);
}
}
}
private void loadBitmap(ImageRequest request, final Canvas canvas, final Paint paint) {
@Override
protected Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
path.addRect(new RectF(getRect()), Path.Direction.CW);
return path;
}
private void loadBitmap(ImageRequest request) {
final DataSource<CloseableReference<CloseableImage>> dataSource
= Fresco.getImagePipeline().fetchDecodedImage(request, getThemedContext());
@@ -129,7 +135,8 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
@Override
public void onNewResultImpl(Bitmap bitmap) {
mLoading.set(false);
getSvgShadowNode().drawOutput();
SvgViewShadowNode shadowNode = getSvgShadowNode();
shadowNode.markUpdated();
}
@Override
@@ -178,7 +185,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
renderRect = new RectF(0, 0, (int)rectWidth, (int)(rectWidth / mImageRatio));
}
RNSVGViewBoxShadowNode viewBox = new RNSVGViewBoxShadowNode();
ViewBoxShadowNode viewBox = new ViewBoxShadowNode();
viewBox.setMinX("0");
viewBox.setMinY("0");
viewBox.setVbWidth(renderRect.width() / mScale + "");

View File

@@ -17,7 +17,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGLineShadowNode extends RNSVGPathShadowNode {
public class LineShadowNode extends RenderableShadowNode {
private String mX1;
private String mY1;
@@ -48,12 +48,6 @@ public class RNSVGLineShadowNode extends RNSVGPathShadowNode {
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
mPath = getPath(canvas, paint);
super.draw(canvas, paint, opacity);
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();

View File

@@ -17,7 +17,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual LinearGradient definition view
*/
public class RNSVGLinearGradientShadowNode extends RNSVGDefinitionShadowNode {
public class LinearGradientShadowNode extends DefinitionShadowNode {
private String mX1;
private String mY1;

View File

@@ -0,0 +1,57 @@
/**
* 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 javax.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Color;
import android.util.Log;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Shadow node for virtual RNSVGPath view
*/
public class PathShadowNode extends RenderableShadowNode {
private Path mPath;
@ReactProp(name = "d")
public void setD(String d) {
PropHelper.PathParser parser = new PropHelper.PathParser(d, mScale);
mPath = parser.getPath();
markUpdated();
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
return mPath;
}
}

View File

@@ -10,15 +10,18 @@
package com.horcrux.svg;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.graphics.Matrix;
import android.util.Log;
import javax.annotation.Nullable;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import java.util.regex.Matcher;
@@ -35,7 +38,7 @@ import java.util.regex.Pattern;
* @return a {@code float[]} if converted successfully, or {@code null} if {@param value} was
* {@code null}.
*/
/*package*/
static
@Nullable
float[] toFloatArray(@Nullable ReadableArray value) {
@@ -57,7 +60,7 @@ import java.util.regex.Pattern;
* @param into output array
* @return number of items copied from input to the output array
*/
/*package*/
static int toFloatArray(ReadableArray value, float[] into) {
int length = value.size() > into.length ? into.length : value.size();
for (int i = 0; i < length; i++) {
@@ -76,7 +79,7 @@ import java.util.regex.Pattern;
* @param offset offset number
* @return actual float based on relative number
*/
/*package*/
static float fromPercentageToFloat(String percentage, float relative, float offset, float scale) {
Matcher matched = Pattern.compile("^(\\-?\\d+(?:\\.\\d+)?)%$").matcher(percentage);
if (matched.matches()) {
@@ -93,7 +96,7 @@ import java.util.regex.Pattern;
* @return string is percentage-like or not.
*/
/*package*/
static boolean isPercentage(String string) {
Pattern pattern = Pattern.compile("^(\\-?\\d+(?:\\.\\d+)?)%$");
return pattern.matcher(string).matches();
@@ -102,7 +105,7 @@ import java.util.regex.Pattern;
/**
*
*/
/*package*/ static class RNSVGBrush {
static class RNSVGBrush {
private GradientType mType = GradientType.LINEAR_GRADIENT;
private ReadableArray mPoints;
@@ -130,10 +133,10 @@ import java.util.regex.Pattern;
for (int i = 0; i < stopsCount; i++) {
stops[i] = (float) value.getDouble(startStops + i);
stopsColors[i] = Color.argb(
(int) (value.getDouble(i * 4 + 3) * 255 * opacity),
(int) (value.getDouble(i * 4) * 255),
(int) (value.getDouble(i * 4 + 1) * 255),
(int) (value.getDouble(i * 4 + 2) * 255));
(int) (value.getDouble(i * 4 + 3) * 255 * opacity),
(int) (value.getDouble(i * 4) * 255),
(int) (value.getDouble(i * 4 + 1) * 255),
(int) (value.getDouble(i * 4 + 2) * 255));
}
}
@@ -158,14 +161,14 @@ import java.util.regex.Pattern;
float x2 = PropHelper.fromPercentageToFloat(mPoints.getString(2), width, offsetX, scale);
float y2 = PropHelper.fromPercentageToFloat(mPoints.getString(3), height, offsetY, scale);
paint.setShader(
new LinearGradient(
x1,
y1,
x2,
y2,
stopsColors,
stops,
Shader.TileMode.CLAMP));
new LinearGradient(
x1,
y1,
x2,
y2,
stopsColors,
stops,
Shader.TileMode.CLAMP));
} else {
float rx = PropHelper.fromPercentageToFloat(mPoints.getString(2), width, 0f, scale);
float ry = PropHelper.fromPercentageToFloat(mPoints.getString(3), height, 0f, scale);
@@ -175,12 +178,12 @@ import java.util.regex.Pattern;
//float fx = PropHelper.fromPercentageToFloat(mPoints.getString(0), width, offsetX) * scale;
//float fy = PropHelper.fromPercentageToFloat(mPoints.getString(1), height, offsetY) * scale / (ry / rx);
Shader radialGradient = new RadialGradient(
cx,
cy,
rx,
stopsColors,
stops,
Shader.TileMode.CLAMP
cx,
cy,
rx,
stopsColors,
stops,
Shader.TileMode.CLAMP
);
Matrix radialMatrix = new Matrix();
@@ -190,4 +193,401 @@ import java.util.regex.Pattern;
}
}
}
static class PathParser {
static private Pattern PATH_REG_EXP = Pattern.compile("[a-df-z]|[\\-+]?(?:[\\d.]e[\\-+]?|[^\\s\\-+,a-z])+", Pattern.CASE_INSENSITIVE);
static private Pattern DECIMAL_REG_EXP = Pattern.compile("(\\.\\d+)(?=\\-?\\.)");
private Matcher mMatcher;
private Path mPath;
private String mString;
private float mPenX = 0f;
private float mPenY = 0f;
private float mPenDownX;
private float mPenDownY;
private float mPivotX = 0f;
private float mPivotY = 0f;
private float mScale = 1f;
private boolean mValid = true;
private boolean mPendDownSet = false;
private String mLastCommand;
private String mLastValue;
public PathParser(String d, float scale) {
mScale = scale;
mString = d;
mPath = new Path();
mMatcher = PATH_REG_EXP.matcher(DECIMAL_REG_EXP.matcher(mString).replaceAll("$1,"));
while (mMatcher.find() && mValid) {
executeCommand(mMatcher.group());
}
}
private void executeCommand(String command) {
switch (command) {
// moveTo command
case "m":
move(getNextFloat(), getNextFloat());
break;
case "M":
moveTo(getNextFloat(), getNextFloat());
break;
// lineTo command
case "l":
line(getNextFloat(), getNextFloat());
break;
case "L":
lineTo(getNextFloat(), getNextFloat());
break;
// horizontalTo command
case "h":
line(getNextFloat(), 0);
break;
case "H":
lineTo(getNextFloat(), mPenY);
break;
// verticalTo command
case "v":
line(0, getNextFloat());
break;
case "V":
lineTo(mPenX, getNextFloat());
break;
// curveTo command
case "c":
curve(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
case "C":
curveTo(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
// smoothCurveTo command
case "s":
smoothCurve(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
case "S":
smoothCurveTo(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
// quadraticBezierCurveTo command
case "q":
quadraticBezierCurve(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
case "Q":
quadraticBezierCurveTo(getNextFloat(), getNextFloat(), getNextFloat(), getNextFloat());
break;
// smoothQuadraticBezierCurveTo command
case "t":
smoothQuadraticBezierCurve(getNextFloat(), getNextFloat());
break;
case "T":
smoothQuadraticBezierCurveTo(getNextFloat(), getNextFloat());
break;
// arcTo command
case "a":
arc(getNextFloat(), getNextFloat(), getNextFloat(), getNextBoolean(), getNextBoolean(), getNextFloat(), getNextFloat());
break;
case "A":
arcTo(getNextFloat(), getNextFloat(), getNextFloat(), getNextBoolean(), getNextBoolean(), getNextFloat(), getNextFloat());
break;
// close command
case "Z":
case "z":
close();
break;
default:
mLastValue = command;
executeCommand(mLastCommand);
return;
}
mLastCommand = command;
if (command.equals("m")) {
mLastCommand = "l";
} else if (command.equals("M")) {
mLastCommand = "L";
}
}
public Path getPath() {
return mPath;
}
private boolean getNextBoolean() {
if (mMatcher.find()) {
return mMatcher.group().equals("1");
} else {
mValid = false;
mPath = new Path();
return false;
}
}
private float getNextFloat() {
if (mLastValue != null) {
String lastValue = mLastValue;
mLastValue = null;
return Float.parseFloat(lastValue);
} else if (mMatcher.find()) {
return Float.parseFloat(mMatcher.group());
} else {
mValid = false;
mPath = new Path();
return 0;
}
}
private void move(float x, float y) {
moveTo(x + mPenX, y + mPenY);
}
private void moveTo(float x, float y) {
mPivotX = mPenX = x;
mPivotY = mPenY = y;
mPath.moveTo(x * mScale, y * mScale);
}
private void line(float x, float y) {
lineTo(x + mPenX, y + mPenY);
}
private void lineTo(float x, float y) {
setPenDown();
mPivotX = mPenX = x;
mPivotY = mPenY = y;
mPath.lineTo(x * mScale, y * mScale);
}
private void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
curveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY, ex + mPenX, ey + mPenY);
}
private void curveTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
mPivotX = c2x;
mPivotY = c2y;
cubicTo(c1x, c1y, c2x, c2y, ex, ey);
}
private void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
setPenDown();
mPenX = ex;
mPenY = ey;
mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
}
private void smoothCurve(float c1x, float c1y, float ex, float ey) {
smoothCurveTo(c1x + mPenX, c1y + mPenY, ex + mPenX, ey + mPenY);
}
private void smoothCurveTo(float c1x, float c1y, float ex, float ey) {
float c2x = c1x;
float c2y = c1y;
c1x = (mPenX * 2) - mPivotX;
c1y = (mPenY * 2) - mPivotY;
mPivotX = c2x;
mPivotY = c2y;
cubicTo(c1x, c1y, c2x, c2y, ex, ey);
}
private void quadraticBezierCurve(float c1x, float c1y, float c2x, float c2y) {
quadraticBezierCurveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY);
}
private void quadraticBezierCurveTo(float c1x, float c1y, float c2x, float c2y) {
mPivotX = c1x;
mPivotY = c1y;
float ex = c2x;
float ey = c2y;
c2x = (ex + c1x * 2) / 3;
c2y = (ey + c1y * 2) / 3;
c1x = (mPenX + c1x * 2) / 3;
c1y = (mPenY + c1y * 2) / 3;
cubicTo(c1x, c1y, c2x, c2y, ex, ey);
}
private void smoothQuadraticBezierCurve(float c1x, float c1y) {
smoothQuadraticBezierCurveTo(c1x + mPenX, c1y + mPenY);
}
private void smoothQuadraticBezierCurveTo(float c1x, float c1y) {
float c2x = c1x;
float c2y = c1y;
c1x = (mPenX * 2) - mPivotX;
c1y = (mPenY * 2) - mPivotY;
quadraticBezierCurveTo(c1x, c1y, c2x, c2y);
}
private void arc(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
arcTo(rx, ry, rotation, outer, clockwise, x + mPenX, y + mPenY);
}
private void arcTo(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
float tX = mPenX;
float tY = mPenY;
ry = Math.abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry);
rx = Math.abs(rx == 0 ? (x - tX) : rx);
if (rx == 0 || ry == 0 || (x == tX && y == tY)) {
lineTo(x, y);
return;
}
float rad = (float) Math.toRadians(rotation);
float cos = (float) Math.cos(rad);
float sin = (float) Math.sin(rad);
x -= tX;
y -= tY;
// Ellipse Center
float cx = cos * x / 2 + sin * y / 2;
float cy = -sin * x / 2 + cos * y / 2;
float rxry = rx * rx * ry * ry;
float rycx = ry * ry * cx * cx;
float rxcy = rx * rx * cy * cy;
float a = rxry - rxcy - rycx;
if (a < 0){
a = (float)Math.sqrt(1 - a / rxry);
rx *= a;
ry *= a;
cx = x / 2;
cy = y / 2;
} else {
a = (float)Math.sqrt(a / (rxcy + rycx));
if (outer == clockwise) {
a = -a;
}
float cxd = -a * cy * rx / ry;
float cyd = a * cx * ry / rx;
cx = cos * cxd - sin * cyd + x / 2;
cy = sin * cxd + cos * cyd + y / 2;
}
// Rotation + Scale Transform
float xx = cos / rx;
float yx = sin / rx;
float xy = -sin / ry;
float yy = cos / ry;
// Start and End Angle
float sa = (float) Math.atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy);
float ea = (float) Math.atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy));
cx += tX;
cy += tY;
x += tX;
y += tY;
setPenDown();
mPenX = mPivotX = x;
mPenY = mPivotY = y;
if (rx != ry || rad != 0f) {
arcToBezier(cx, cy, rx, ry, sa, ea, clockwise, rad);
} else {
float start = (float) Math.toDegrees(sa);
float end = (float) Math.toDegrees(ea);
float sweep = Math.abs((start - end) % 360);
if (outer) {
if (sweep < 180) {
sweep = 360 - sweep;
}
} else {
if (sweep > 180) {
sweep = 360 - sweep;
}
}
if (!clockwise) {
sweep = -sweep;
}
RectF oval = new RectF(
(cx - rx) * mScale,
(cy - rx) * mScale,
(cx + rx) * mScale,
(cy + rx) * mScale);
mPath.arcTo(oval, start, sweep);
}
}
private void close() {
if (mPendDownSet) {
mPenX = mPenDownX;
mPenY = mPenDownY;
mPendDownSet = false;
mPath.close();
}
}
private void arcToBezier(float cx, float cy, float rx, float ry, float sa, float ea, boolean clockwise, float rad) {
// Inverse Rotation + Scale Transform
float cos = (float) Math.cos(rad);
float sin = (float) Math.sin(rad);
float xx = cos * rx;
float yx = -sin * ry;
float xy = sin * rx;
float yy = cos * ry;
// Bezier Curve Approximation
float arc = ea - sa;
if (arc < 0 && clockwise) {
arc += Math.PI * 2;
} else if (arc > 0 && !clockwise) {
arc -= Math.PI * 2;
}
int n = (int) Math.ceil(Math.abs(arc / (Math.PI / 2)));
float step = arc / n;
float k = (4 / 3) * (float) Math.tan(step / 4);
float x = (float) Math.cos(sa);
float y = (float) Math.sin(sa);
for (int i = 0; i < n; i++){
float cp1x = x - k * y;
float cp1y = y + k * x;
sa += step;
x = (float) Math.cos(sa);
y = (float) Math.sin(sa);
float cp2x = x + k * y;
float cp2y = y - k * x;
mPath.cubicTo(
(cx + xx * cp1x + yx * cp1y) * mScale,
(cy + xy * cp1x + yy * cp1y) * mScale,
(cx + xx * cp2x + yx * cp2y) * mScale,
(cy + xy * cp2x + yy * cp2y) * mScale,
(cx + xx * x + yx * y) * mScale,
(cy + xy * x + yy * y) * mScale
);
}
}
private void setPenDown() {
if (!mPendDownSet) {
mPenDownX = mPenX;
mPenDownY = mPenY;
mPendDownSet = true;
}
}
}
}

View File

@@ -1,55 +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.
*/
package com.horcrux.svg;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class RNSvgPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
RNSVGRenderableViewManager.createRNSVGGroupViewManager(),
RNSVGRenderableViewManager.createRNSVGPathViewManager(),
RNSVGRenderableViewManager.createRNSVGCircleViewManager(),
RNSVGRenderableViewManager.createRNSVGEllipseViewManager(),
RNSVGRenderableViewManager.createRNSVGLineViewManager(),
RNSVGRenderableViewManager.createRNSVGRectViewManager(),
RNSVGRenderableViewManager.createRNSVGTextViewManager(),
RNSVGRenderableViewManager.createRNSVGImageViewManager(),
RNSVGRenderableViewManager.createRNSVGClipPathViewManager(),
RNSVGRenderableViewManager.createRNSVGDefsViewManager(),
RNSVGRenderableViewManager.createRNSVGUseViewManager(),
RNSVGRenderableViewManager.createRNSVGViewBoxViewManager(),
RNSVGRenderableViewManager.createRNSVGLinearGradientManager(),
RNSVGRenderableViewManager.createRNSVGRadialGradientManager(),
RNSVGRenderableViewManager.createRNSVGSpanManager(),
new RNSVGSvgViewManager());
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -17,7 +17,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual LinearGradient definition view
*/
public class RNSVGRadialGradientShadowNode extends RNSVGDefinitionShadowNode {
public class RadialGradientShadowNode extends DefinitionShadowNode {
private String mFx;
private String mFy;
private String mRx;

View File

@@ -18,7 +18,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGRectShadowNode extends RNSVGPathShadowNode {
public class RectShadowNode extends RenderableShadowNode {
private String mX;
private String mY;
@@ -66,12 +66,6 @@ public class RNSVGRectShadowNode extends RNSVGPathShadowNode {
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
mPath = getPath(canvas, paint);
super.draw(canvas, paint, opacity);
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();

View File

@@ -21,8 +21,10 @@ import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Color;
import android.util.Log;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.ReadableArray;
@@ -38,47 +40,43 @@ import java.util.regex.Pattern;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGPathShadowNode extends RNSVGVirtualNode {
abstract public class RenderableShadowNode extends VirtualNode {
// strokeLinecap
private static final int CAP_BUTT = 0;
private static final int CAP_ROUND = 1;
private static final int CAP_SQUARE = 2;
// strokeLinejoin
private static final int JOIN_BEVEL = 2;
private static final int JOIN_MITER = 0;
private static final int JOIN_ROUND = 1;
// fillRule
private static final int FILL_RULE_EVENODD = 0;
private static final int FILL_RULE_NONZERO = 1;
public @Nullable ReadableArray mStroke;
public @Nullable float[] mStrokeDasharray;
public float mStrokeWidth = 1;
public float mStrokeOpacity = 1;
public float mStrokeMiterlimit = 4;
public float mStrokeDashoffset = 0;
public Paint.Cap mStrokeLinecap = Paint.Cap.ROUND;
public Paint.Join mStrokeLinejoin = Paint.Join.ROUND;
public @Nullable ReadableArray mFill;
public float mFillOpacity = 1;
public Path.FillType mFillRule = Path.FillType.WINDING;
private boolean mFillRuleSet;
protected Path mPath;
private float[] mD;
private ArrayList<String> mChangedList;
private ReadableArray mLastMergedList;
private ArrayList<Object> mOriginProperties;
protected ReadableArray mPropList = new JavaOnlyArray();
protected WritableArray mOwnedPropList = new JavaOnlyArray();
@ReactProp(name = "d")
public void setPath(@Nullable ReadableArray shapePath) {
mD = PropHelper.toFloatArray(shapePath);
setupPath();
markUpdated();
}
protected WritableArray mAttributeList = new JavaOnlyArray();
@ReactProp(name = "fill")
public void setFill(@Nullable ReadableArray fill) {
@@ -102,11 +100,10 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
break;
default:
throw new JSApplicationIllegalArgumentException(
"fillRule " + mFillRule + " unrecognized");
"fillRule " + mFillRule + " unrecognized");
}
mFillRuleSet = true;
setupPath();
mPath = null;
markUpdated();
}
@@ -140,7 +137,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
markUpdated();
}
@ReactProp(name = "strokeWidth", defaultFloat = 1f)
@ReactProp(name = "strokeWidth", defaultFloat = 0f)
public void setStrokeWidth(float strokeWidth) {
mStrokeWidth = strokeWidth;
markUpdated();
@@ -166,7 +163,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
break;
default:
throw new JSApplicationIllegalArgumentException(
"strokeLinecap " + mStrokeLinecap + " unrecognized");
"strokeLinecap " + mStrokeLinecap + " unrecognized");
}
markUpdated();
}
@@ -185,37 +182,40 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
break;
default:
throw new JSApplicationIllegalArgumentException(
"strokeLinejoin " + mStrokeLinejoin + " unrecognized");
"strokeLinejoin " + mStrokeLinejoin + " unrecognized");
}
markUpdated();
}
@ReactProp(name = "propList")
public void setPropList(@Nullable ReadableArray propList) {
WritableArray copy = new JavaOnlyArray();
if (propList != null) {
WritableArray copy = Arguments.createArray();
for (int i = 0; i < propList.size(); i++) {
String fieldName = propertyNameToFieldName(propList.getString(i));
copy.pushString(fieldName);
mOwnedPropList.pushString(fieldName);
mAttributeList.pushString(fieldName);
}
mPropList = copy;
}
mPropList = copy;
markUpdated();
}
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
if (mPath == null) {
mPath = getPath(canvas, paint);
mPath.setFillType(mFillRule);
}
opacity *= mOpacity;
if (opacity > MIN_OPACITY_FOR_DRAW) {
int count = saveAndSetupCanvas(canvas);
if (mPath == null) {
throw new JSApplicationIllegalArgumentException(
"Paths should have a valid path (d) prop");
"Paths should have a valid path (d) prop");
}
clip(canvas, paint);
@@ -231,32 +231,6 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
}
}
private void setupPath() {
// init path after both fillRule and path have been set
if (mFillRuleSet && mD != null) {
mPath = new Path();
mPath.setFillType(mFillRule);
super.createPath(mD, mPath);
}
}
/**
* sorting stops and stopsColors from array
*/
private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, int startColorsPosition) {
int startStops = value.size() - stopsCount;
for (int i = 0; i < stopsCount; i++) {
stops[i] = (float)value.getDouble(startStops + i);
stopsColors[i] = Color.argb(
(int) (value.getDouble(startColorsPosition + i * 4 + 3) * 255),
(int) (value.getDouble(startColorsPosition + i * 4) * 255),
(int) (value.getDouble(startColorsPosition + i * 4 + 1) * 255),
(int) (value.getDouble(startColorsPosition + i * 4 + 2) * 255));
}
}
/**
* Sets up paint according to the props set on a shadow view. Returns {@code true}
* if the fill should be drawn, {@code false} if not.
@@ -303,10 +277,10 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
if (colorType == 0) {
// solid color
paint.setARGB(
(int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
(int) (colors.getDouble(1) * 255),
(int) (colors.getDouble(2) * 255),
(int) (colors.getDouble(3) * 255));
(int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
(int) (colors.getDouble(1) * 255),
(int) (colors.getDouble(2) * 255),
(int) (colors.getDouble(3) * 255));
} else if (colorType == 1) {
if (box == null) {
box = new RectF();
@@ -323,17 +297,15 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
return mPath;
}
abstract protected Path getPath(Canvas canvas, Paint paint);
@Override
public int hitTest(Point point, @Nullable Matrix matrix) {
Bitmap bitmap = Bitmap.createBitmap(
mCanvasWidth,
mCanvasHeight,
Bitmap.Config.ARGB_8888);
mCanvasWidth,
mCanvasHeight,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
@@ -389,21 +361,15 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList, boolean inherited) {
public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited) {
mLastMergedList = mergeList;
if (mergeList.size() == 0) {
return;
}
if (!inherited) {
mOriginProperties = new ArrayList<>();
mChangedList = new ArrayList<>();
}
WritableArray propList = new JavaOnlyArray();
for (int i = 0; i < mPropList.size(); i++) {
propList.pushString(mPropList.getString(i));
}
mOwnedPropList = propList;
mOriginProperties = new ArrayList<>();
resetAttributeList();
for (int i = 0, size = mergeList.size(); i < size; i++) {
try {
@@ -413,12 +379,12 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
if (inherited) {
if (!hasOwnProperty(fieldName)) {
mAttributeList.pushString(fieldName);
mOriginProperties.add(field.get(this));
field.set(this, value);
propList.pushString(fieldName);
}
} else {
mOriginProperties.add(field.get(this));
mChangedList.add(fieldName);
field.set(this, value);
}
} catch (Exception e) {
@@ -428,25 +394,33 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList) {
public void mergeProperties(VirtualNode target, ReadableArray mergeList) {
mergeProperties(target, mergeList, false);
}
@Override
public void resetProperties() {
if (mChangedList != null) {
if (mLastMergedList != null) {
try {
for (int i = mChangedList.size() - 1; i >= 0; i--) {
Field field = getClass().getField(mChangedList.get(i));
for (int i = mLastMergedList.size() - 1; i >= 0; i--) {
Field field = getClass().getField(mLastMergedList.getString(i));
field.set(this, mOriginProperties.get(i));
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
mChangedList = null;
mLastMergedList = null;
mOriginProperties = null;
}
resetAttributeList();
}
private void resetAttributeList() {
mAttributeList = Arguments.createArray();
for (int i = 0; i < mPropList.size(); i++) {
mAttributeList.pushString(mPropList.getString(i));
}
}
// convert propertyName something like fillOpacity to fieldName like mFillOpacity
@@ -462,8 +436,8 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
}
private boolean hasOwnProperty(String propName) {
for (int i = mOwnedPropList.size() - 1; i >= 0; i--) {
if (mOwnedPropList.getString(i).equals(propName)) {
for (int i = mAttributeList.size() - 1; i >= 0; i--) {
if (mAttributeList.getString(i).equals(propName)) {
return true;
}
}

View File

@@ -17,10 +17,10 @@ import com.facebook.react.uimanager.ViewManager;
/**
* ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered
* into native views and don't need any logic (all the logic is in {@link RNSVGSvgView}), this
* into native views and don't need any logic (all the logic is in {@link SvgView}), this
* "stubbed" ViewManager is used for all of them.
*/
public class RNSVGRenderableViewManager extends ViewManager<View, LayoutShadowNode> {
public class RenderableViewManager extends ViewManager<View, LayoutShadowNode> {
/* package */ static final String CLASS_GROUP = "RNSVGGroup";
/* package */ static final String CLASS_PATH = "RNSVGPath";
@@ -41,70 +41,70 @@ public class RNSVGRenderableViewManager extends ViewManager<View, LayoutShadowNo
private final String mClassName;
public static RNSVGRenderableViewManager createRNSVGGroupViewManager() {
return new RNSVGRenderableViewManager(CLASS_GROUP);
public static RenderableViewManager createGroupViewManager() {
return new RenderableViewManager(CLASS_GROUP);
}
public static RNSVGRenderableViewManager createRNSVGPathViewManager() {
return new RNSVGRenderableViewManager(CLASS_PATH);
public static RenderableViewManager createPathViewManager() {
return new RenderableViewManager(CLASS_PATH);
}
public static RNSVGRenderableViewManager createRNSVGTextViewManager() {
return new RNSVGRenderableViewManager(CLASS_TEXT);
public static RenderableViewManager createTextViewManager() {
return new RenderableViewManager(CLASS_TEXT);
}
public static RNSVGRenderableViewManager createRNSVGImageViewManager() {
return new RNSVGRenderableViewManager(CLASS_IMAGE);
public static RenderableViewManager createImageViewManager() {
return new RenderableViewManager(CLASS_IMAGE);
}
public static RNSVGRenderableViewManager createRNSVGCircleViewManager() {
return new RNSVGRenderableViewManager(CLASS_CIRCLE);
public static RenderableViewManager createCircleViewManager() {
return new RenderableViewManager(CLASS_CIRCLE);
}
public static RNSVGRenderableViewManager createRNSVGEllipseViewManager() {
return new RNSVGRenderableViewManager(CLASS_ELLIPSE);
public static RenderableViewManager createEllipseViewManager() {
return new RenderableViewManager(CLASS_ELLIPSE);
}
public static RNSVGRenderableViewManager createRNSVGLineViewManager() {
return new RNSVGRenderableViewManager(CLASS_LINE);
public static RenderableViewManager createLineViewManager() {
return new RenderableViewManager(CLASS_LINE);
}
public static RNSVGRenderableViewManager createRNSVGRectViewManager() {
return new RNSVGRenderableViewManager(CLASS_RECT);
public static RenderableViewManager createRectViewManager() {
return new RenderableViewManager(CLASS_RECT);
}
public static RNSVGRenderableViewManager createRNSVGClipPathViewManager() {
return new RNSVGRenderableViewManager(CLASS_CLIP_PATH);
public static RenderableViewManager createClipPathViewManager() {
return new RenderableViewManager(CLASS_CLIP_PATH);
}
public static RNSVGRenderableViewManager createRNSVGDefsViewManager() {
return new RNSVGRenderableViewManager(CLASS_DEFS);
public static RenderableViewManager createDefsViewManager() {
return new RenderableViewManager(CLASS_DEFS);
}
public static RNSVGRenderableViewManager createRNSVGUseViewManager() {
return new RNSVGRenderableViewManager(CLASS_USE);
public static RenderableViewManager createUseViewManager() {
return new RenderableViewManager(CLASS_USE);
}
public static RNSVGRenderableViewManager createRNSVGViewBoxViewManager() {
return new RNSVGRenderableViewManager(CLASS_VIEW_BOX);
public static RenderableViewManager createViewBoxViewManager() {
return new RenderableViewManager(CLASS_VIEW_BOX);
}
public static RNSVGRenderableViewManager createRNSVGLinearGradientManager() {
return new RNSVGRenderableViewManager(CLASS_LINEAR_GRADIENT);
public static RenderableViewManager createLinearGradientManager() {
return new RenderableViewManager(CLASS_LINEAR_GRADIENT);
}
public static RNSVGRenderableViewManager createRNSVGRadialGradientManager() {
return new RNSVGRenderableViewManager(CLASS_RADIAL_GRADIENT);
public static RenderableViewManager createRadialGradientManager() {
return new RenderableViewManager(CLASS_RADIAL_GRADIENT);
}
private RenderableViewManager(String className) {
mClassName = className;
}
public static RNSVGRenderableViewManager createRNSVGSpanManager() {
return new RNSVGRenderableViewManager(CLASS_SPAN);
}
private RNSVGRenderableViewManager(String className) {
mClassName = className;
}
@Override
public String getName() {
return mClassName;
@@ -114,35 +114,35 @@ public class RNSVGRenderableViewManager extends ViewManager<View, LayoutShadowNo
public LayoutShadowNode createShadowNodeInstance() {
switch (mClassName) {
case CLASS_GROUP:
return new RNSVGGroupShadowNode();
return new GroupShadowNode();
case CLASS_PATH:
return new RNSVGPathShadowNode();
return new PathShadowNode();
case CLASS_CIRCLE:
return new RNSVGCircleShadowNode();
return new CircleShadowNode();
case CLASS_ELLIPSE:
return new RNSVGEllipseShadowNode();
return new EllipseShadowNode();
case CLASS_LINE:
return new RNSVGLineShadowNode();
return new LineShadowNode();
case CLASS_RECT:
return new RNSVGRectShadowNode();
return new RectShadowNode();
case CLASS_TEXT:
return new RNSVGTextShadowNode();
return new TextShadowNode();
case CLASS_IMAGE:
return new RNSVGImageShadowNode();
return new ImageShadowNode();
case CLASS_CLIP_PATH:
return new RNSVGClipPathShadowNode();
return new ClipPathShadowNode();
case CLASS_DEFS:
return new RNSVGDefsShadowNode();
return new DefsShadowNode();
case CLASS_USE:
return new RNSVGUseShadowNode();
return new UseShadowNode();
case CLASS_VIEW_BOX:
return new RNSVGViewBoxShadowNode();
return new ViewBoxShadowNode();
case CLASS_LINEAR_GRADIENT:
return new RNSVGLinearGradientShadowNode();
return new LinearGradientShadowNode();
case CLASS_RADIAL_GRADIENT:
return new RNSVGRadialGradientShadowNode();
return new RadialGradientShadowNode();
case CLASS_SPAN:
return new RNSVGSpanShadowNode();
return new SpanShadowNode();
default:
throw new IllegalStateException("Unexpected type " + mClassName);
}
@@ -152,35 +152,35 @@ public class RNSVGRenderableViewManager extends ViewManager<View, LayoutShadowNo
public Class<? extends LayoutShadowNode> getShadowNodeClass() {
switch (mClassName) {
case CLASS_GROUP:
return RNSVGGroupShadowNode.class;
return GroupShadowNode.class;
case CLASS_PATH:
return RNSVGPathShadowNode.class;
return PathShadowNode.class;
case CLASS_CIRCLE:
return RNSVGCircleShadowNode.class;
return CircleShadowNode.class;
case CLASS_ELLIPSE:
return RNSVGEllipseShadowNode.class;
return EllipseShadowNode.class;
case CLASS_LINE:
return RNSVGLineShadowNode.class;
return LineShadowNode.class;
case CLASS_RECT:
return RNSVGRectShadowNode.class;
return RectShadowNode.class;
case CLASS_TEXT:
return RNSVGTextShadowNode.class;
return TextShadowNode.class;
case CLASS_IMAGE:
return RNSVGImageShadowNode.class;
return ImageShadowNode.class;
case CLASS_CLIP_PATH:
return RNSVGClipPathShadowNode.class;
return ClipPathShadowNode.class;
case CLASS_DEFS:
return RNSVGDefsShadowNode.class;
return DefsShadowNode.class;
case CLASS_USE:
return RNSVGUseShadowNode.class;
return UseShadowNode.class;
case CLASS_VIEW_BOX:
return RNSVGViewBoxShadowNode.class;
return ViewBoxShadowNode.class;
case CLASS_LINEAR_GRADIENT:
return RNSVGLinearGradientShadowNode.class;
return LinearGradientShadowNode.class;
case CLASS_RADIAL_GRADIENT:
return RNSVGRadialGradientShadowNode.class;
return RadialGradientShadowNode.class;
case CLASS_SPAN:
return RNSVGSpanShadowNode.class;
return SpanShadowNode.class;
default:
throw new IllegalStateException("Unexpected type " + mClassName);
}
@@ -195,4 +195,8 @@ public class RNSVGRenderableViewManager extends ViewManager<View, LayoutShadowNo
public void updateExtraData(View root, Object extraData) {
throw new IllegalStateException("SVG elements does not map into a native view");
}
@Override
public void onDropViewInstance(View view) {
}
}

View File

@@ -26,7 +26,7 @@ import javax.annotation.Nullable;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGSpanShadowNode extends RNSVGPathShadowNode {
public class SpanShadowNode extends PathShadowNode {
private static final String PROP_FONT_FAMILY = "fontFamily";
private static final String PROP_FONT_SIZE = "fontSize";

View File

@@ -0,0 +1,29 @@
package com.horcrux.svg;
import android.util.SparseArray;
public class SvgInstancesManager {
private static final SparseArray<SvgViewShadowNode> mTagToShadowNode = new SparseArray<>();
private static final SparseArray<SvgView> mTagToSvgView = new SparseArray<>();
static void registerShadowNode(SvgViewShadowNode shadowNode) {
mTagToShadowNode.put(shadowNode.getReactTag(), shadowNode);
}
static void registerSvgView(SvgView svg) {
mTagToSvgView.put(svg.getId(), svg);
}
static void unregisterInstance(int tag) {
mTagToShadowNode.remove(tag);
mTagToSvgView.remove(tag);
}
static SvgView getSvgViewByTag(int tag) {
return mTagToSvgView.get(tag);
}
static SvgViewShadowNode getShadowNodeByTag(int tag) {
return mTagToShadowNode.get(tag);
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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 com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SvgPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
RenderableViewManager.createGroupViewManager(),
RenderableViewManager.createPathViewManager(),
RenderableViewManager.createCircleViewManager(),
RenderableViewManager.createEllipseViewManager(),
RenderableViewManager.createLineViewManager(),
RenderableViewManager.createRectViewManager(),
RenderableViewManager.createTextViewManager(),
RenderableViewManager.createImageViewManager(),
RenderableViewManager.createClipPathViewManager(),
RenderableViewManager.createDefsViewManager(),
RenderableViewManager.createUseViewManager(),
RenderableViewManager.createViewBoxViewManager(),
RenderableViewManager.createLinearGradientManager(),
RenderableViewManager.createRadialGradientManager(),
RenderableViewManager.createSpanManager(),
new SvgViewManager());
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -9,11 +9,14 @@
package com.horcrux.svg;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.TextureView;
import android.view.View;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.Arguments;
@@ -26,10 +29,12 @@ import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper;
import com.facebook.react.uimanager.events.TouchEventType;
import com.facebook.react.uimanager.events.EventDispatcher;
import javax.annotation.Nullable;
/**
* Custom {@link View} implementation that draws an RNSVGSvg React view and its \children.
* Custom {@link View} implementation that draws an RNSVGSvg React view and its \childrn.
*/
public class RNSVGSvgView extends TextureView {
public class SvgView extends View {
public enum Events {
EVENT_DATA_URL("onDataURL");
@@ -45,6 +50,7 @@ public class RNSVGSvgView extends TextureView {
}
}
private @Nullable Bitmap mBitmap;
private RCTEventEmitter mEventEmitter;
private EventDispatcher mEventDispatcher;
private int mTargetTag;
@@ -52,15 +58,36 @@ public class RNSVGSvgView extends TextureView {
private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper =
new TouchEventCoalescingKeyHelper();
public RNSVGSvgView(ReactContext reactContext) {
public SvgView(ReactContext reactContext) {
super(reactContext);
setOpaque(false);
mEventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
}
private RNSVGSvgViewShadowNode getShadowNode() {
return RNSVGSvgViewShadowNode.getShadowNodeByTag(getId());
@Override
public void setId(int id) {
super.setId(id);
SvgInstancesManager.registerSvgView(this);
}
public void setBitmap(Bitmap bitmap) {
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = bitmap;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
private SvgViewShadowNode getShadowNode() {
return SvgInstancesManager.getShadowNodeByTag(getId());
}
@Override
@@ -165,4 +192,6 @@ public class RNSVGSvgView extends TextureView {
event.putString("base64", getShadowNode().getBase64());
mEventEmitter.receiveEvent(getId(), Events.EVENT_DATA_URL.toString(), event);
}
}

View File

@@ -9,6 +9,11 @@
package com.horcrux.svg;
import android.graphics.Bitmap;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNodeAPI;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.BaseViewManager;
@@ -20,13 +25,24 @@ import java.util.Map;
import javax.annotation.Nullable;
/**
* ViewManager for RNSVGSvgView React views. Renders as a {@link RNSVGSvgView} and handles
* ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles
* invalidating the native view on shadow view updates happening in the underlying tree.
*/
public class RNSVGSvgViewManager extends BaseViewManager<RNSVGSvgView, RNSVGSvgViewShadowNode> {
public class SvgViewManager extends BaseViewManager<SvgView, SvgViewShadowNode> {
private static final String REACT_CLASS = "RNSVGSvgView";
private static final int COMMAND_TO_DATA_URL = 100;
private static final YogaMeasureFunction MEASURE_FUNCTION = new YogaMeasureFunction() {
@Override
public long measure(
YogaNodeAPI node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
throw new IllegalStateException("SurfaceView should have explicit width and height set");
}
};
@Override
public String getName() {
@@ -34,23 +50,30 @@ public class RNSVGSvgViewManager extends BaseViewManager<RNSVGSvgView, RNSVGSvgV
}
@Override
public Class<RNSVGSvgViewShadowNode> getShadowNodeClass() {
return RNSVGSvgViewShadowNode.class;
public Class<SvgViewShadowNode> getShadowNodeClass() {
return SvgViewShadowNode.class;
}
@Override
public RNSVGSvgViewShadowNode createShadowNodeInstance() {
return new RNSVGSvgViewShadowNode();
public SvgViewShadowNode createShadowNodeInstance() {
SvgViewShadowNode node = new SvgViewShadowNode();
node.setMeasureFunction(MEASURE_FUNCTION);
return node;
}
@Override
protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) {
return new RNSVGSvgView(reactContext);
public void onDropViewInstance(SvgView view) {
SvgInstancesManager.unregisterInstance(view.getId());
}
@Override
public void updateExtraData(RNSVGSvgView root, Object extraData) {
root.setSurfaceTextureListener((RNSVGSvgViewShadowNode) extraData);
protected SvgView createViewInstance(ThemedReactContext reactContext) {
return new SvgView(reactContext);
}
@Override
public void updateExtraData(SvgView root, Object extraData) {
root.setBitmap((Bitmap) extraData);
}
@Override
@@ -69,14 +92,14 @@ public class RNSVGSvgViewManager extends BaseViewManager<RNSVGSvgView, RNSVGSvgV
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
for (RNSVGSvgView.Events event : RNSVGSvgView.Events.values()) {
for (SvgView.Events event : SvgView.Events.values()) {
builder.put(event.toString(), MapBuilder.of("registrationName", event.toString()));
}
return builder.build();
}
@Override
public void receiveCommand(RNSVGSvgView root, int commandId, @Nullable ReadableArray args) {
public void receiveCommand(SvgView root, int commandId, @Nullable ReadableArray args) {
super.receiveCommand(root, commandId, args);
switch (commandId) {

View File

@@ -13,13 +13,15 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.Base64;
import android.util.SparseArray;
import android.view.TextureView;
import android.graphics.Color;
import android.view.Surface;
import android.graphics.PorterDuff;
import android.graphics.SurfaceTexture;
import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.view.Surface;
import android.view.TextureView;
import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;
@@ -27,7 +29,6 @@ import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
@@ -35,19 +36,12 @@ import java.util.Map;
/**
* Shadow node for RNSVG virtual tree root - RNSVGSvgView
*/
public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureView.SurfaceTextureListener {
private static final SparseArray<RNSVGSvgViewShadowNode> mTagToShadowNode = new SparseArray<>();
public static RNSVGSvgViewShadowNode getShadowNodeByTag(int tag) {
return mTagToShadowNode.get(tag);
}
private @Nullable Surface mSurface;
public class SvgViewShadowNode extends LayoutShadowNode {
private boolean mResponsible = false;
private static final Map<String, RNSVGVirtualNode> mDefinedClipPaths = new HashMap<>();
private static final Map<String, RNSVGVirtualNode> mDefinedTemplates = new HashMap<>();
private static final Map<String, PropHelper.RNSVGBrush> mDefinedBrushes = new HashMap<>();
private final Map<String, VirtualNode> mDefinedClipPaths = new HashMap<>();
private final Map<String, VirtualNode> mDefinedTemplates = new HashMap<>();
private final Map<String, PropHelper.RNSVGBrush> mDefinedBrushes = new HashMap<>();
@Override
public boolean isVirtual() {
@@ -62,27 +56,24 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) {
super.onCollectExtraUpdates(uiUpdater);
drawOutput();
uiUpdater.enqueueUpdateExtraData(getReactTag(), this);
uiUpdater.enqueueUpdateExtraData(getReactTag(), drawOutput());
}
public void drawOutput() {
if (mSurface == null || !mSurface.isValid()) {
markChildrenUpdatesSeen(this);
return;
}
@Override
public void setReactTag(int reactTag) {
super.setReactTag(reactTag);
SvgInstancesManager.registerShadowNode(this);
}
try {
Canvas canvas = mSurface.lockCanvas(null);
drawChildren(canvas);
public Object drawOutput() {
Bitmap bitmap = Bitmap.createBitmap(
(int) getLayoutWidth(),
(int) getLayoutHeight(),
Bitmap.Config.ARGB_8888);
if (mSurface != null) {
mSurface.unlockCanvasAndPost(canvas);
}
} catch (IllegalArgumentException | IllegalStateException e) {
FLog.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Svg.unlockCanvasAndPost");
}
Canvas canvas = new Canvas(bitmap);
drawChildren(canvas);
return bitmap;
}
private void drawChildren(Canvas canvas) {
@@ -90,11 +81,11 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
Paint paint = new Paint();
for (int i = 0; i < getChildCount(); i++) {
if (!(getChildAt(i) instanceof RNSVGVirtualNode)) {
if (!(getChildAt(i) instanceof VirtualNode)) {
continue;
}
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
VirtualNode child = (VirtualNode) getChildAt(i);
child.setupDimensions(canvas);
child.saveDefinition();
child.draw(canvas, paint, 1f);
@@ -106,19 +97,11 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
}
}
private void markChildrenUpdatesSeen(ReactShadowNode shadowNode) {
for (int i = 0; i < shadowNode.getChildCount(); i++) {
ReactShadowNode child = shadowNode.getChildAt(i);
child.markUpdateSeen();
markChildrenUpdatesSeen(child);
}
}
public String getBase64() {
Bitmap bitmap = Bitmap.createBitmap(
(int) getLayoutWidth(),
(int) getLayoutHeight(),
Bitmap.Config.ARGB_8888);
(int) getLayoutWidth(),
(int) getLayoutHeight(),
Bitmap.Config.ARGB_8888);
drawChildren(new Canvas(bitmap));
ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -128,27 +111,6 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
mTagToShadowNode.put(getReactTag(), this);
drawOutput();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mTagToShadowNode.remove(getReactTag());
surface.release();
mSurface = null;
return true;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
public void enableTouchEvents() {
if (!mResponsible) {
mResponsible = true;
@@ -163,11 +125,11 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
int count = getChildCount();
int viewTag = -1;
for (int i = count - 1; i >= 0; i--) {
if (!(getChildAt(i) instanceof RNSVGVirtualNode)) {
if (!(getChildAt(i) instanceof VirtualNode)) {
continue;
}
viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point);
viewTag = ((VirtualNode) getChildAt(i)).hitTest(point);
if (viewTag != -1) {
break;
}
@@ -176,19 +138,19 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureV
return viewTag;
}
public void defineClipPath(RNSVGVirtualNode clipPath, String clipPathRef) {
public void defineClipPath(VirtualNode clipPath, String clipPathRef) {
mDefinedClipPaths.put(clipPathRef, clipPath);
}
public RNSVGVirtualNode getDefinedClipPath(String clipPathRef) {
public VirtualNode getDefinedClipPath(String clipPathRef) {
return mDefinedClipPaths.get(clipPathRef);
}
public void defineTemplate(RNSVGVirtualNode template, String templateRef) {
public void defineTemplate(VirtualNode template, String templateRef) {
mDefinedTemplates.put(templateRef, template);
}
public RNSVGVirtualNode getDefinedTemplate(String templateRef) {
public VirtualNode getDefinedTemplate(String templateRef) {
return mDefinedTemplates.get(templateRef);
}

View File

@@ -24,6 +24,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGText view
*/
public class RNSVGTextShadowNode extends RNSVGGroupShadowNode {
private float mOffsetX = 0;

View File

@@ -11,6 +11,7 @@ package com.horcrux.svg;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;
@@ -19,7 +20,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGUseShadowNode extends RNSVGPathShadowNode {
public class UseShadowNode extends RenderableShadowNode {
private String mHref;
private String mWidth;
@@ -53,13 +54,13 @@ public class RNSVGUseShadowNode extends RNSVGPathShadowNode {
@Override
public void draw(Canvas canvas, Paint paint, float opacity) {
RNSVGVirtualNode template = getSvgShadowNode().getDefinedTemplate(mHref);
VirtualNode template = getSvgShadowNode().getDefinedTemplate(mHref);
if (template != null) {
int count = saveAndSetupCanvas(canvas);
clip(canvas, paint);
template.mergeProperties(this, mOwnedPropList);
template.mergeProperties(this, mAttributeList, true);
template.draw(canvas, paint, opacity * mOpacity);
template.resetProperties();
@@ -70,4 +71,10 @@ public class RNSVGUseShadowNode extends RNSVGPathShadowNode {
"template named: " + mHref + " is not defined.");
}
}
@Override
protected Path getPath(Canvas canvas, Paint paint) {
// todo:
return new Path();
}
}

View File

@@ -19,7 +19,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
/**
* Shadow node for virtual RNSVGPath view
*/
public class RNSVGViewBoxShadowNode extends RNSVGGroupShadowNode {
public class ViewBoxShadowNode extends GroupShadowNode {
private static final int MOS_MEET = 0;
private static final int MOS_SLICE = 1;
@@ -174,11 +174,11 @@ public class RNSVGViewBoxShadowNode extends RNSVGGroupShadowNode {
}
@Override
public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList) {
if (target instanceof RNSVGUseShadowNode) {
public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited) {
if (target instanceof UseShadowNode) {
mFromSymbol = true;
mBoxWidth = ((RNSVGUseShadowNode)target).getWidth();
mBoxHeight = ((RNSVGUseShadowNode)target).getHeight();
mBoxWidth = ((UseShadowNode)target).getWidth();
mBoxHeight = ((UseShadowNode)target).getHeight();
}
}

View File

@@ -17,8 +17,10 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
@@ -26,7 +28,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
public abstract class RNSVGVirtualNode extends LayoutShadowNode {
public abstract class VirtualNode extends LayoutShadowNode {
protected static final float MIN_OPACITY_FOR_DRAW = 0.01f;
@@ -35,8 +37,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
protected float mOpacity = 1f;
protected Matrix mMatrix = new Matrix();
protected @Nullable Path mClipPath;
protected @Nullable String mClipPathRef;
protected @Nullable String mClipPath;
private static final int PATH_TYPE_CLOSE = 1;
private static final int PATH_TYPE_CURVETO = 3;
@@ -47,10 +48,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
private static final int CLIP_RULE_NONZERO = 1;
protected final float mScale;
private float[] mClipData;
private int mClipRule;
private boolean mClipRuleSet;
private boolean mClipDataSet;
protected boolean mResponsible;
protected int mCanvasX;
protected int mCanvasY;
@@ -58,9 +56,9 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
protected int mCanvasHeight;
protected String mName;
private RNSVGSvgViewShadowNode mSvgShadowNode;
private SvgViewShadowNode mSvgShadowNode;
public RNSVGVirtualNode() {
public VirtualNode() {
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
}
@@ -96,14 +94,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
canvas.restoreToCount(count);
}
@ReactProp(name = "clipPath")
public void setClipPath(@Nullable ReadableArray clipPath) {
mClipData = PropHelper.toFloatArray(clipPath);
mClipDataSet = true;
setupClip();
markUpdated();
}
@ReactProp(name = "name")
public void setName(String name) {
mName = name;
@@ -111,17 +101,15 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
}
@ReactProp(name = "clipPathRef")
public void setClipPathRef(String clipPathRef) {
mClipPathRef = clipPathRef;
@ReactProp(name = "clipPath")
public void setClipPath(String clipPath) {
mClipPath = clipPath;
markUpdated();
}
@ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO)
public void setClipRule(int clipRule) {
public void clipRule(int clipRule) {
mClipRule = clipRule;
mClipRuleSet = true;
setupClip();
markUpdated();
}
@@ -138,7 +126,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
if (matrixSize == 6) {
setupMatrix();
} else if (matrixSize != -1) {
throw new JSApplicationIllegalArgumentException("Transform matrices must be of size 6");
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
@@ -153,24 +141,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
markUpdated();
}
private void setupClip() {
if (mClipDataSet && mClipRuleSet) {
mClipPath = new Path();
switch (mClipRule) {
case CLIP_RULE_EVENODD:
mClipPath.setFillType(Path.FillType.EVEN_ODD);
break;
case CLIP_RULE_NONZERO:
break;
default:
throw new JSApplicationIllegalArgumentException(
"clipRule " + mClipRule + " unrecognized");
}
createPath(mClipData, mClipPath);
}
}
protected void setupMatrix() {
sRawMatrix[0] = sMatrixData[0];
sRawMatrix[1] = sMatrixData[2];
@@ -226,13 +196,28 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
}
protected @Nullable Path getClipPath(Canvas canvas, Paint paint) {
Path clip = mClipPath;
if (clip == null && mClipPathRef != null) {
RNSVGVirtualNode node = getSvgShadowNode().getDefinedClipPath(mClipPathRef);
clip = node.getPath(canvas, paint);
if (mClipPath != null) {
VirtualNode node = getSvgShadowNode().getDefinedClipPath(mClipPath);
if (node != null) {
Path clipPath = node.getPath(canvas, paint);
switch (mClipRule) {
case CLIP_RULE_EVENODD:
clipPath.setFillType(Path.FillType.EVEN_ODD);
break;
case CLIP_RULE_NONZERO:
break;
default:
FLog.w(ReactConstants.TAG, "RNSVG: clipRule: " + mClipRule + " unrecognized");
}
return clipPath;
} else {
FLog.w(ReactConstants.TAG, "RNSVG: Undefined clipPath: " + mClipPath);
}
}
return clip;
return null;
}
protected void clip(Canvas canvas, Paint paint) {
@@ -255,21 +240,21 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
abstract protected Path getPath(Canvas canvas, Paint paint);
protected RNSVGSvgViewShadowNode getSvgShadowNode() {
protected SvgViewShadowNode getSvgShadowNode() {
if (mSvgShadowNode != null) {
return mSvgShadowNode;
}
ReactShadowNode parent = getParent();
while (!(parent instanceof RNSVGSvgViewShadowNode)) {
while (!(parent instanceof SvgViewShadowNode)) {
if (parent == null) {
return null;
} else {
parent = parent.getParent();
}
}
mSvgShadowNode = (RNSVGSvgViewShadowNode) parent;
mSvgShadowNode = (SvgViewShadowNode) parent;
return mSvgShadowNode;
}
@@ -290,24 +275,24 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
}
}
abstract public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList, boolean inherited);
abstract public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited);
abstract public void mergeProperties(RNSVGVirtualNode target, ReadableArray mergeList);
abstract public void mergeProperties(VirtualNode target, ReadableArray mergeList);
abstract public void resetProperties();
protected interface NodeRunnable {
boolean run(RNSVGVirtualNode node);
boolean run(VirtualNode node);
}
protected void traverseChildren(NodeRunnable runner) {
for (int i = 0; i < getChildCount(); i++) {
ReactShadowNode child = getChildAt(i);
if (!(child instanceof RNSVGVirtualNode)) {
if (!(child instanceof VirtualNode)) {
continue;
}
if (!runner.run((RNSVGVirtualNode) child)) {
if (!runner.run((VirtualNode) child)) {
break;
}
}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import Shape from './Shape';
import {CircleAttributes} from '../lib/attributes';
import {pathProps, numberProp} from '../lib/props';

View File

@@ -1,5 +1,5 @@
import React, {Component, PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {ClipPathAttributes} from '../lib/attributes';
class ClipPath extends Component{

View File

@@ -1,7 +1,7 @@
import React, {
Component,
} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
class Defs extends Component {
static displayName = 'Defs';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import Shape from './Shape';
import {pathProps, numberProp} from '../lib/props';
import {EllipseAttributes} from '../lib/attributes';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import Shape from './Shape';
import {transformProps} from '../lib/props';
import {GroupAttributes} from '../lib/attributes';

View File

@@ -1,5 +1,5 @@
import React, {PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {ImageAttributes} from '../lib/attributes';
import {numberProp, touchableProps, responderProps} from '../lib/props';
import Shape from './Shape';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {LineAttributes} from '../lib/attributes';
import Shape from './Shape';
import {pathProps, numberProp} from '../lib/props';

View File

@@ -1,7 +1,7 @@
import React, {PropTypes} from 'react';
import {numberProp} from '../lib/props';
import Gradient from './Gradient';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {LinearGradientAttributes} from '../lib/attributes';
class LinearGradient extends Gradient{

View File

@@ -1,6 +1,5 @@
import React, {PropTypes} from 'react';
import SerializablePath from '../lib/SerializablePath';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {PathAttributes} from '../lib/attributes';
import Shape from './Shape';
import {pathProps} from '../lib/props';
@@ -20,12 +19,11 @@ class Path extends Shape {
render() {
let props = this.props;
let d = new SerializablePath(props.d).toJSON();
return (
<RNSVGPath
ref={ele => {this.root = ele;}}
{...this.extractProps(props)}
d={d}
d={props.d}
/>
);
}

View File

@@ -1,7 +1,7 @@
import React, {PropTypes} from 'react';
import {numberProp} from '../lib/props';
import Gradient from './Gradient';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {RadialGradientAttributes} from '../lib/attributes';
class RadialGradient extends Gradient{

View File

@@ -1,6 +1,6 @@
import React from 'react';
import './Path'; // must import Path first, don`t know why. without this will throw an `Super expression must either be null or a function, not undefined`
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {pathProps, numberProp} from '../lib/props';
import {RectAttributes} from '../lib/attributes';
import Shape from './Shape';

View File

@@ -1,5 +1,5 @@
import React, {PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import extractText from '../lib/extract/extractText';
import {numberProp, pathProps, fontProps} from '../lib/props';
import {TextAttributes} from '../lib/attributes';

View File

@@ -3,7 +3,7 @@ import {pathProps, numberProp} from '../lib/props';
import {UseAttributes} from '../lib/attributes';
import Shape from './Shape';
import React from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
const idExpReg = /^#(.+)$/;
class Use extends Shape {

View File

@@ -1,8 +1,7 @@
import React, {Component, PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass';
import {ViewBoxAttributes} from '../lib/attributes';
import G from './G';
import _ from 'lodash';
const meetOrSliceTypes = {
meet: 0,
@@ -10,12 +9,12 @@ const meetOrSliceTypes = {
none: 2
};
const alignEnum = _.reduce([
const alignEnum = [
'xMinYMin', 'xMidYMin', 'xMaxYMin',
'xMinYMid', 'xMidYMid', 'xMaxYMid',
'xMinYMax', 'xMidYMax', 'xMaxYMax',
'none'
], (prev, name) => {
].reduce((prev, name) => {
prev[name] = name;
return prev;
}, {});
@@ -36,11 +35,11 @@ class ViewBox extends Component{
};
render() {
let {viewBox, preserveAspectRatio, name} = this.props;
const {viewBox, preserveAspectRatio, name} = this.props;
let params = viewBox.trim().split(spacesRegExp);
if (params.length !== 4 || !_.some(params, param => param && numberRegExp.test(param))) {
if (params.length !== 4 || !params.some(param => param && numberRegExp.test(param))) {
console.warn('`viewBox` expected a string like `minX minY width height`, but got:' + viewBox);
return <G>
{this.props.children}

View File

@@ -8,7 +8,7 @@
#import "RNSVGBaseBrush.h"
#import "RCTConvert+RNSVG.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGBaseBrush

View File

@@ -8,7 +8,7 @@
#import "RNSVGBrush.h"
#import "RCTDefines.h"
#import <React/RCTDefines.h>
@implementation RNSVGBrush

View File

@@ -9,7 +9,7 @@
#import "RNSVGPattern.h"
#import "RCTConvert+RNSVG.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGPattern
{

View File

@@ -9,7 +9,7 @@
#import "RNSVGSolidColorBrush.h"
#import "RCTConvert+RNSVG.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGSolidColorBrush
{

View File

@@ -13,11 +13,10 @@
- (void)renderTo:(CGContextRef)context
{
for (RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node saveDefinition];
}
}
[self traverseSubviews:^(RNSVGNode *node) {
[node saveDefinition];
return YES;
}];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

View File

@@ -19,7 +19,7 @@
{
RNSVGSvgView* svg = [self getSvgView];
[self clip:context];
CGContextConcatCTM(context, transform);
[self traverseSubviews:^(RNSVGNode *node) {
if (node.responsible && !svg.responsible) {
@@ -28,11 +28,11 @@
}
return YES;
}];
[self traverseSubviews:^(RNSVGNode *node) {
[node mergeProperties:self mergeList:self.ownedPropList inherited:YES];
[node mergeProperties:self mergeList:self.attributeList inherited:YES];
[node renderTo:context];
if ([node isKindOfClass: [RNSVGRenderable class]]) {
RNSVGRenderable *renderable = node;
[self concatLayoutBoundingBox:[renderable getLayoutBoundingBox]];
@@ -59,19 +59,19 @@
CGPathAddPath(path, &transform, [node getPath:context]);
return YES;
}];
return (CGPathRef)CFAutorelease(path);
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event withTransform:(CGAffineTransform)transform
{
CGAffineTransform matrix = CGAffineTransformConcat(self.matrix, transform);
CGPathRef clip = [self getComputedClipPath];
if (clip && !CGPathContainsPoint(clip, nil, point, NO)) {
return nil;
}
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
if ([node isKindOfClass:[RNSVGNode class]]) {
if (event) {
@@ -81,7 +81,7 @@
}
UIView *view = [node hitTest: point withEvent:event withTransform:matrix];
if (view) {
node.active = YES;
if (node.responsible || (node != view)) {
@@ -101,20 +101,12 @@
RNSVGSvgView* svg = [self getSvgView];
[svg defineTemplate:self templateName:self.name];
}
[self traverseSubviews:^(RNSVGNode *node) {
[node saveDefinition];
return YES;
}];
}
- (void)mergeProperties:(RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{
[self traverseSubviews:^(RNSVGNode *node) {
[node mergeProperties:target mergeList:mergeList];
return YES;
}];
}
- (void)resetProperties
@@ -125,16 +117,4 @@
}];
}
- (void)traverseSubviews:(BOOL (^)(__kindof RNSVGNode *node))block
{
for (RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
if (!block(node)) {
break;
}
}
}
}
@end

View File

@@ -7,9 +7,9 @@
*/
#import "RNSVGImage.h"
#import "RCTImageSource.h"
#import "RCTConvert+RNSVG.h"
#import "RCTLog.h"
#import <React/RCTImageSource.h>
#import <React/RCTLog.h>
#import "RNSVGViewBox.h"
@implementation RNSVGImage

View File

@@ -15,116 +15,20 @@
if (d == _d) {
return;
}
[self invalidate];
CGPathRelease(_d);
_d = CGPathRetain(d);
}
- (void)dealloc
{
CGPathRelease(_d);
}
- (void)renderLayerTo:(CGContextRef)context
{
// todo: add detection if path has changed since last update.
self.d = [self getPath:context];
CGPathRef path = self.d;
[self setLayoutBoundingBox:CGPathGetBoundingBox(path)];
if ((!self.fill && !self.stroke) || !path) {
return;
}
if ([self getSvgView].responsible) {
// Add path to hitArea
CGMutablePathRef hitAreaPath = CGPathCreateMutableCopy(path);
if (self.stroke) {
// Add stroke to hitArea
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(hitAreaPath, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit);
CGPathAddPath(hitAreaPath, nil, strokePath);
CGPathRelease(strokePath);
}
CGAffineTransform transform = self.matrix;
self.hitArea = CGPathCreateCopyByTransformingPath(hitAreaPath, &transform);
CGPathRelease(hitAreaPath);
}
if (self.opacity == 0) {
return;
}
CGPathDrawingMode mode = kCGPathStroke;
BOOL fillColor = YES;
if (self.fill) {
mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill;
fillColor = [self.fill applyFillColor:context opacity:self.fillOpacity];
if (!fillColor) {
[self clip:context];
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextClip(context);
RNSVGBrushConverter *brushConverter = [[self getSvgView] getDefinedBrushConverter:[self.fill brushRef]];
[self.fill paint:context opacity:self.fillOpacity brushConverter:brushConverter];
CGContextRestoreGState(context);
if (!self.stroke) {
return;
}
}
}
if (self.stroke) {
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeLinecap);
CGContextSetLineJoin(context, self.strokeLinejoin);
RNSVGCGFloatArray dash = self.strokeDasharray;
if (dash.count) {
CGContextSetLineDash(context, self.strokeDashoffset, dash.array, dash.count);
}
if (!fillColor) {
CGContextAddPath(context, path);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
}
if ([self.stroke applyStrokeColor:context opacity:self.strokeOpacity]) {
if (mode == kCGPathFill) {
mode = kCGPathFillStroke;
} else if (mode == kCGPathEOFill) {
mode = kCGPathEOFillStroke;
}
} else {
// draw fill
[self clip:context];
CGContextAddPath(context, path);
CGContextDrawPath(context, mode);
// draw stroke
CGContextAddPath(context, path);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
RNSVGBrushConverter *brushConverter = [[self getSvgView] getDefinedBrushConverter:[self.stroke brushRef]];
[self.stroke paint:context opacity:self.strokeOpacity brushConverter:brushConverter];
return;
}
}
[self clip:context];
CGContextAddPath(context, path);
CGContextDrawPath(context, mode);
}
- (CGPathRef)getPath:(CGContextRef)context
{
return self.d;
}
- (void)dealloc
{
CGPathRelease(_d);
}
@end

View File

@@ -9,7 +9,7 @@
#import "RNSVGSvgView.h"
#import "RNSVGNode.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGSvgView
{

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGUse.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGUse
@@ -27,7 +27,7 @@
if (template) {
[self beginTransparencyLayer:context];
[self clip:context];
[template mergeProperties:self mergeList:self.ownedPropList];
[template mergeProperties:self mergeList:self.attributeList inherited:YES];
[template renderTo:context];
[template resetProperties];
[self endTransparencyLayer:context];

View File

@@ -51,6 +51,7 @@
10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */; };
10ED4AA21CF078830078BC02 /* RNSVGNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4AA11CF078830078BC02 /* RNSVGNode.m */; };
10FDEEB21D3FB60500A5C46C /* RNSVGBaseBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */; };
7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -160,8 +161,12 @@
10FDEEB01D3FB60500A5C46C /* RNSVGBaseBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBaseBrush.h; sourceTree = "<group>"; };
10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGBaseBrush.m; sourceTree = "<group>"; };
10FDEEB31D3FBED400A5C46C /* RNSVGBrushType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrushType.h; sourceTree = "<group>"; };
7F888B9C1DD378000038D083 /* RNSVGGlyphPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphPoint.h; path = Utils/RNSVGGlyphPoint.h; sourceTree = "<group>"; };
7FF070191DC249BE000E28A0 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = "<group>"; };
7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = "<group>"; };
7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -320,15 +325,21 @@
1039D29A1CE7212C001E90A8 /* Utils */ = {
isa = PBXGroup;
children = (
1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */,
10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */,
7FF070191DC249BE000E28A0 /* RNSVGTextAnchor.h */,
10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */,
1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */,
1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */,
7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */,
7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */,
1039D29B1CE72177001E90A8 /* RCTConvert+RNSVG.h */,
1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */,
<<<<<<< HEAD
1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */,
7F888B9C1DD378000038D083 /* RNSVGGlyphPoint.h */,
=======
>>>>>>> master
);
name = Utils;
sourceTree = "<group>";
@@ -426,6 +437,7 @@
10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */,
1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */,
0CF68B0B1AF0549300FF9E5C /* RNSVGBrush.m in Sources */,
7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */,
10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */,
10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */,
10FDEEB21D3FB60500A5C46C /* RNSVGBaseBrush.m in Sources */,
@@ -528,10 +540,11 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../react-native/React/**",
"$(BUILT_PRODUCTS_DIR)/usr/local/include",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNSVG;
PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/;
SKIP_INSTALL = YES;
};
name = Debug;
@@ -542,10 +555,11 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../react-native/React/**",
"$(BUILT_PRODUCTS_DIR)/usr/local/include",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNSVG;
PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/;
SKIP_INSTALL = YES;
};
name = Release;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import "UIView+React.h"
#import <React/UIView+React.h>
#import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h"
@@ -20,7 +20,7 @@
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) CGFloat opacity;
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
@property (nonatomic, assign) NSString *clipPath;
@property (nonatomic, strong) NSString *clipPath;
@property (nonatomic, assign) BOOL responsible;
@property (nonatomic, assign) CGAffineTransform matrix;
@property (nonatomic, assign) BOOL active;
@@ -36,6 +36,10 @@
*/
- (void)renderLayerTo:(CGContextRef)context;
- (CGPathRef)getClipPath;
- (CGPathRef)getClipPath:(CGContextRef)context;
/**
* clip node by clipPath
*/
@@ -79,4 +83,6 @@
- (void)endTransparencyLayer:(CGContextRef)context;
- (void)traverseSubviews:(BOOL (^)(RNSVGNode *node))block;
@end

View File

@@ -13,7 +13,7 @@
@implementation RNSVGNode
{
BOOL _transparent;
CGPathRef _computedClipPath;
CGPathRef _cachedClipPath;
}
- (instancetype)init
@@ -84,9 +84,10 @@
if (_clipPath == clipPath) {
return;
}
[self invalidate];
CGPathRelease(_cachedClipPath);
_cachedClipPath = nil;
_clipPath = clipPath;
[self invalidate];
}
- (void)beginTransparencyLayer:(CGContextRef)context
@@ -108,38 +109,35 @@
// abstract
}
- (CGPathRef)getClipPath
{
return _cachedClipPath;
}
- (CGPathRef)getClipPath:(CGContextRef)context
{
if (self.clipPath && !_cachedClipPath) {
CGPathRelease(_cachedClipPath);
_cachedClipPath = CGPathRetain([[[self getSvgView] getDefinedClipPath:self.clipPath] getPath:context]);
}
return [self getClipPath];
}
- (void)clip:(CGContextRef)context
{
if (self.clipPath) {
CGPathRef clip = [[[self getSvgView] getDefinedClipPath:self.clipPath] getPath:context];
if (!clip) {
// TODO: WARNING ABOUT THIS
return;
}
CGContextAddPath(context, clip);
CGPathRef clipPath = [self getClipPath:context];
if (clipPath) {
CGContextAddPath(context, clipPath);
if (self.clipRule == kRNSVGCGFCRuleEvenodd) {
CGContextEOClip(context);
} else {
CGContextClip(context);
}
CGAffineTransform matrix = self.matrix;
[self computeClipPath:CGPathCreateCopyByTransformingPath(clip, &matrix)];
}
}
- (CGPathRef)getComputedClipPath{
return _computedClipPath;
}
- (void)computeClipPath:(CGPathRef)computedClipPath
{
CGPathRelease(_computedClipPath);
_computedClipPath = computedClipPath;
}
- (CGPathRef)getPath: (CGContextRef) context
{
// abstract
@@ -151,8 +149,10 @@
// abstract
}
// hitTest delagate
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// abstract
return nil;
}
@@ -177,7 +177,7 @@
{
if (self.name) {
RNSVGSvgView* svg = [self getSvgView];
[svg defineTemplate:self templateName:self.name];
[svg defineTemplate:self templateRef:self.name];
}
}
@@ -191,6 +191,17 @@
// abstract
}
- (void)traverseSubviews:(BOOL (^)(RNSVGNode *node))block
{
for (RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
if (!block(node)) {
break;
}
}
}
}
- (void)resetProperties
{
// abstract
@@ -198,7 +209,7 @@
- (void)dealloc
{
CGPathRelease(_computedClipPath);
CGPathRelease(_cachedClipPath);
}
@end

View File

@@ -28,7 +28,7 @@
@property (nonatomic, assign) CGFloat strokeDashoffset;
@property (nonatomic, assign) CGPathRef hitArea;
@property (nonatomic, copy) NSArray<NSString *> *propList;
@property (nonatomic, strong) NSMutableArray<NSString *> *ownedPropList;
@property (nonatomic, strong) NSArray<NSString *> *attributeList;
- (void)setContextBoundingBox:(CGRect)contextBoundingBox;
- (CGRect)getContextBoundingBox;

View File

@@ -12,7 +12,7 @@
@implementation RNSVGRenderable
{
NSMutableDictionary *_originProperties;
NSArray *_changedList;
NSArray *_lastMergedList;
RNSVGPercentageConverter *_widthConverter;
RNSVGPercentageConverter *_heightConverter;
CGRect _contextBoundingBox;
@@ -24,7 +24,7 @@
if (self = [super init]) {
_fillOpacity = 1;
_strokeOpacity = 1;
_strokeWidth = 1;
_strokeWidth = 0;
}
return self;
}
@@ -136,7 +136,7 @@
if (hitArea == _hitArea) {
return;
}
CGPathRelease(_hitArea);
_hitArea = CGPathRetain(CFAutorelease(hitArea));
}
@@ -146,8 +146,8 @@
if (propList == _propList) {
return;
}
_attributeList = [propList copy];
_propList = propList;
self.ownedPropList = [propList mutableCopy];
[self invalidate];
}
@@ -165,7 +165,7 @@
CGContextSaveGState(context);
CGContextConcatCTM(context, self.matrix);
CGContextSetAlpha(context, self.opacity);
[self beginTransparencyLayer:context];
[self renderLayerTo:context];
[self endTransparencyLayer:context];
@@ -173,13 +173,105 @@
CGContextRestoreGState(context);
}
- (void)renderLayerTo:(CGContextRef)context
{
// todo: add detection if path has changed since last update.
CGPathRef path = [self getPath:context];
if ((!self.fill && !self.stroke) || !path) {
return;
}
if ([self getSvgView].responsible) {
// Add path to hitArea
CGMutablePathRef hitArea = CGPathCreateMutableCopy(path);
if (self.stroke && self.strokeWidth) {
// Add stroke to hitArea
CGPathRef strokePath = CGPathCreateCopyByStrokingPath(hitArea, nil, self.strokeWidth, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit);
CGPathAddPath(hitArea, nil, strokePath);
CGPathRelease(strokePath);
}
self.hitArea = CFAutorelease(CGPathCreateCopy(hitArea));
CGPathRelease(hitArea);
}
if (self.opacity == 0) {
return;
}
CGPathDrawingMode mode = kCGPathStroke;
BOOL fillColor = YES;
if (self.fill) {
mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill;
fillColor = [self.fill applyFillColor:context opacity:self.fillOpacity];
if (!fillColor) {
[self clip:context];
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextClip(context);
RNSVGBrushConverter *brushConverter = [[self getSvgView] getDefinedBrushConverter:[self.fill brushRef]];
[self.fill paint:context opacity:self.fillOpacity brushConverter:brushConverter];
CGContextRestoreGState(context);
if (!self.stroke) {
return;
}
}
}
if (self.stroke && self.strokeWidth) {
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeLinecap);
CGContextSetLineJoin(context, self.strokeLinejoin);
RNSVGCGFloatArray dash = self.strokeDasharray;
if (dash.count) {
CGContextSetLineDash(context, self.strokeDashoffset, dash.array, dash.count);
}
if (!fillColor) {
CGContextAddPath(context, path);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
}
if ([self.stroke applyStrokeColor:context opacity:self.strokeOpacity]) {
if (mode == kCGPathFill) {
mode = kCGPathFillStroke;
} else if (mode == kCGPathEOFill) {
mode = kCGPathEOFillStroke;
}
} else {
// draw fill
[self clip:context];
CGContextAddPath(context, path);
CGContextDrawPath(context, mode);
// draw stroke
CGContextAddPath(context, path);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
RNSVGBrushConverter *brushConverter = [[self getSvgView] getDefinedBrushConverter:[self.stroke brushRef]];
[self.stroke paint:context opacity:self.strokeOpacity brushConverter:brushConverter];
return;
}
}
[self clip:context];
CGContextAddPath(context, path);
CGContextDrawPath(context, mode);
}
// hitTest delagate
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return [self hitTest:point withEvent:event withTransform:CGAffineTransformMakeRotation(0)];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event withTransform:(CGAffineTransform)transfrom
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event withTransform:(CGAffineTransform)transform
{
if (self.active) {
if (!event) {
@@ -188,15 +280,21 @@
return self;
}
CGPathRef hitArea = CGPathCreateCopyByTransformingPath(self.hitArea, &transfrom);
CGAffineTransform matrix = CGAffineTransformConcat(self.matrix, transform);
CGPathRef hitArea = CGPathCreateCopyByTransformingPath(self.hitArea, &matrix);
BOOL contains = CGPathContainsPoint(hitArea, nil, point, NO);
CGPathRelease(hitArea);
if (contains) {
CGPathRef clipPath = [self getComputedClipPath];
if (clipPath) {
return CGPathContainsPoint(clipPath, nil, point, NO) ? self : nil;
} else {
CGPathRef clipPath = [self getClipPath];
if (!clipPath) {
return self;
} else {
CGPathRef transformedClipPath = CGPathCreateCopyByTransformingPath(clipPath, &matrix);
BOOL result = CGPathContainsPoint(transformedClipPath, nil, point, self.clipRule == kRNSVGCGFCRuleEvenodd);
CGPathRelease(transformedClipPath);
return result ? self : nil;
}
} else {
return nil;
@@ -245,49 +343,38 @@
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList inherited:(BOOL)inherited
{
_lastMergedList = mergeList;
if (mergeList.count == 0) {
return;
}
self.ownedPropList = [self.propList mutableCopy];
if (!inherited) {
_originProperties = [[NSMutableDictionary alloc] init];
_changedList = mergeList;
}
NSMutableArray* attributeList = [self.propList mutableCopy];
_originProperties = [[NSMutableDictionary alloc] init];
for (NSString *key in mergeList) {
if (inherited) {
[self inheritProperty:target propName:key];
if (![attributeList containsObject:key]) {
[attributeList addObject:key];
[_originProperties setValue:[self valueForKey:key] forKey:key];
[self setValue:[target valueForKey:key] forKey:key];
}
} else {
[_originProperties setValue:[self valueForKey:key] forKey:key];
[self setValue:[target valueForKey:key] forKey:key];
}
}
_attributeList = [attributeList copy];
}
- (void)resetProperties
{
if (_changedList) {
for (NSString *key in _changedList) {
[self setValue:[_originProperties valueForKey:key] forKey:key];
}
for (NSString *key in _lastMergedList) {
[self setValue:[_originProperties valueForKey:key] forKey:key];
}
_changedList = nil;
}
- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName
{
if (![self.ownedPropList containsObject:propName]) {
// add prop to props
[self.ownedPropList addObject:propName];
[self setValue:[parent valueForKey:propName] forKey:propName];
}
}
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
_attributeList = [_propList copy];
}
@end

View File

@@ -160,7 +160,7 @@
return CGAffineTransformTranslate(transform, -translateX * (_fromSymbol ? scaleX : 1), -translateY * (_fromSymbol ? scaleY : 1));
}
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList inherited:(BOOL)inherited
{
if ([target isKindOfClass:[RNSVGUse class]]) {
RNSVGUse *use = target;
@@ -172,8 +172,10 @@
- (void)resetProperties
{
self.width = self.height = nil;
_fromSymbol = NO;
if (_fromSymbol) {
self.width = self.height = nil;
_fromSymbol = NO;
}
}
@end

View File

@@ -10,7 +10,7 @@
#import "RNSVGPath.h"
@interface RNSVGCircle : RNSVGPath
@interface RNSVGCircle : RNSVGRenderable
@property (nonatomic, strong) NSString* cx;
@property (nonatomic, strong) NSString* cy;

View File

@@ -7,7 +7,7 @@
*/
#import "RNSVGCircle.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGCircle

View File

@@ -10,7 +10,7 @@
#import "RNSVGPath.h"
@interface RNSVGEllipse : RNSVGPath
@interface RNSVGEllipse : RNSVGRenderable
@property (nonatomic, strong) NSString* cx;
@property (nonatomic, strong) NSString* cy;
@property (nonatomic, strong) NSString* rx;

View File

@@ -7,7 +7,7 @@
*/
#import "RNSVGEllipse.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGEllipse

View File

@@ -10,7 +10,7 @@
#import "RNSVGPath.h"
@interface RNSVGLine : RNSVGPath
@interface RNSVGLine : RNSVGRenderable
@property (nonatomic, strong) NSString* x1;
@property (nonatomic, strong) NSString* y1;
@property (nonatomic, strong) NSString* x2;

View File

@@ -7,7 +7,7 @@
*/
#import "RNSVGLine.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGLine

View File

@@ -10,7 +10,7 @@
#import "RNSVGPath.h"
@interface RNSVGRect : RNSVGPath
@interface RNSVGRect : RNSVGRenderable
@property (nonatomic, strong) NSString* x;
@property (nonatomic, strong) NSString* y;

View File

@@ -7,7 +7,7 @@
*/
#import "RNSVGRect.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
@implementation RNSVGRect

View File

@@ -10,7 +10,8 @@
#import <CoreText/CoreText.h>
#import "RCTConvert+RNSVG.h"
#import "RNSVGCGFloatArray.h"
#import "RCTConvert.h"
#import "RNSVGTextFrame.h"
#import <React/RCTConvert.h>
#import "RNSVGCGFCRule.h"
#import "RNSVGVBMOS.h"
#import "RNSVGTextAnchor.h"
@@ -19,13 +20,19 @@
@interface RCTConvert (RNSVG)
<<<<<<< HEAD
+ (CGPathRef)CGPath:(id)json;
+ (RNSVGTextAnchor)RNSVGTextAnchor:(id)json;
=======
+ (CGPathRef)CGPath:(NSString *)d;
+ (CTTextAlignment)CTTextAlignment:(id)json;
>>>>>>> master
+ (RNSVGCGFCRule)RNSVGCGFCRule:(id)json;
+ (RNSVGVBMOS)RNSVGVBMOS:(id)json;
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json;
+ (RNSVGBrush *)RNSVGBrush:(id)json;
+ (NSArray *)RNSVGBezier:(id)json;
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset;

View File

@@ -11,55 +11,17 @@
#import "RNSVGBaseBrush.h"
#import "RNSVGPattern.h"
#import "RNSVGSolidColorBrush.h"
#import "RCTLog.h"
#import <React/RCTLog.h>
#import "RNSVGCGFCRule.h"
#import "RNSVGVBMOS.h"
#import <React/RCTFont.h>
#import "RNSVGPathParser.h"
@implementation RCTConvert (RNSVG)
+ (CGPathRef)CGPath:(id)json
+ (CGPathRef)CGPath:(NSString *)d
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = [arr count];
#define NEXT_VALUE [self double:arr[i++]]
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 0, 0);
@try {
NSUInteger i = 0;
while (i < count) {
NSUInteger type = [arr[i++] unsignedIntegerValue];
switch (type) {
case 0:
CGPathMoveToPoint(path, nil, NEXT_VALUE, NEXT_VALUE);
break;
case 1:
CGPathCloseSubpath(path);
break;
case 2:
CGPathAddLineToPoint(path, nil, NEXT_VALUE, NEXT_VALUE);
break;
case 3:
CGPathAddCurveToPoint(path, nil, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
break;
case 4:
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
break;
default:
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
CGPathRelease(path);
return nil;
}
}
}
@catch (NSException *exception) {
RCTLogError(@"Invalid CGPath format: %@", arr);
CGPathRelease(path);
return nil;
}
return (CGPathRef)CFAutorelease(path);
return [[[RNSVGPathParser alloc] initWithPathString: d] getPath];
}
RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{

View File

@@ -0,0 +1,17 @@
/**
* 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 <QuartzCore/QuartzCore.h>
#import <Foundation/Foundation.h>
@interface RNSVGPathParser : NSObject
- (instancetype) initWithPathString:(NSString *)d;
- (CGPathRef)getPath;
@end

379
ios/Utils/RNSVGPathParser.m Normal file
View File

@@ -0,0 +1,379 @@
/**
* 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 "RNSVGPathParser.h"
#import <React/RCTLog.h>
@implementation RNSVGPathParser : NSObject
{
NSString* _d;
NSString* _originD;
NSRegularExpression* _pathRegularExpression;
double _penX;
double _penY;
double _penDownX;
double _penDownY;
double _pivotX;
double _pivotY;
BOOL _valid;
BOOL _penDownSet;
}
- (instancetype) initWithPathString:(NSString *)d
{
if (self = [super init]) {
NSRegularExpression* decimalRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"(\\.\\d+)(?=\\-?\\.)" options:0 error:nil];
_originD = d;
_d = [decimalRegularExpression stringByReplacingMatchesInString:d options:0 range:NSMakeRange(0, [d length]) withTemplate:@"$1\,"];
_pathRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"[a-df-z]|[\\-+]?(?:[\\d.]e[\\-+]?|[^\\s\\-+,a-z])+" options:NSRegularExpressionCaseInsensitive error:nil];
}
return self;
}
- (CGPathRef)getPath
{
CGMutablePathRef path = CGPathCreateMutable();
NSArray<NSTextCheckingResult *>* results = [_pathRegularExpression matchesInString:_d options:0 range:NSMakeRange(0, [_d length])];
int count = [results count];
if (count) {
NSUInteger i = 0;
#define NEXT_VALUE [self getNextValue:results[i++]]
#define NEXT_DOUBLE [self double:NEXT_VALUE]
#define NEXT_BOOL [self bool:NEXT_VALUE]
NSString* lastCommand;
NSString* command = NEXT_VALUE;
@try {
while (command) {
if ([command isEqualToString:@"m"]) { // moveTo command
[self move:path x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"M"]) {
[self moveTo:path x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"l"]) { // lineTo command
[self line:path x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"L"]) {
[self lineTo:path x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"h"]) { // horizontalTo command
[self line:path x:NEXT_DOUBLE y:0];
} else if ([command isEqualToString:@"H"]) {
[self lineTo:path x:NEXT_DOUBLE y:_penY];
} else if ([command isEqualToString:@"v"]) { // verticalTo command
[self line:path x:0 y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"V"]) {
[self lineTo:path x:_penX y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"c"]) { // curveTo command
[self curve:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE c2x:NEXT_DOUBLE c2y:NEXT_DOUBLE ex:NEXT_DOUBLE ey:NEXT_DOUBLE];
} else if ([command isEqualToString:@"C"]) {
[self curveTo:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE c2x:NEXT_DOUBLE c2y:NEXT_DOUBLE ex:NEXT_DOUBLE ey:NEXT_DOUBLE];
} else if ([command isEqualToString:@"s"]) { // smoothCurveTo command
[self smoothCurve:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE ex:NEXT_DOUBLE ey:NEXT_DOUBLE];
} else if ([command isEqualToString:@"S"]) {
[self smoothCurveTo:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE ex:NEXT_DOUBLE ey:NEXT_DOUBLE];
} else if ([command isEqualToString:@"q"]) { // quadraticBezierCurveTo command
[self quadraticBezierCurve:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE c2x:NEXT_DOUBLE c2y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"Q"]) {
[self quadraticBezierCurveTo:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE c2x:NEXT_DOUBLE c2y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"t"]) {// smoothQuadraticBezierCurveTo command
[self smoothQuadraticBezierCurve:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"T"]) {
[self smoothQuadraticBezierCurveTo:path c1x:NEXT_DOUBLE c1y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"a"]) { // arcTo command
[self arc:path rx:NEXT_DOUBLE ry:NEXT_DOUBLE rotation:NEXT_DOUBLE outer:NEXT_BOOL clockwise:NEXT_BOOL x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"A"]) {
[self arcTo:path rx:NEXT_DOUBLE ry:NEXT_DOUBLE rotation:NEXT_DOUBLE outer:NEXT_BOOL clockwise:NEXT_BOOL x:NEXT_DOUBLE y:NEXT_DOUBLE];
} else if ([command isEqualToString:@"z"]) { // close command
[self close:path];
} else if ([command isEqualToString:@"Z"]) {
[self close:path];
} else {
command = lastCommand;
i--;
continue;
}
lastCommand = command;
if ([lastCommand isEqualToString:@"m"]) {
lastCommand = @"l";
} else if ([lastCommand isEqualToString:@"M"]) {
lastCommand = @"L";
}
command = i < count ? NEXT_VALUE : nil;
}
} @catch (NSException *exception) {
RCTLogWarn(@"Invalid CGPath format: %@", _originD);
CGPathRelease(path);
return nil;
}
}
return (CGPathRef)CFAutorelease(path);
}
- (NSString *)getNextValue:(NSTextCheckingResult *)result
{
if (!result) {
return nil;
}
return [_d substringWithRange:NSMakeRange(result.range.location, result.range.length)];
}
- (double)double:(NSString *)value
{
return [value doubleValue];
}
- (BOOL)bool:(NSString *)value
{
return ![value isEqualToString:@"0"];
}
- (void)move:(CGPathRef)path x:(double)x y:(double)y
{
[self moveTo:path x:x + _penX y:y + _penY];
}
- (void)moveTo:(CGPathRef)path x:(double)x y:(double)y
{
_pivotX = _penX = x;
_pivotY = _penY = y;
CGPathMoveToPoint(path, nil, x, y);
}
- (void)line:(CGPathRef)path x:(double)x y:(double)y
{
[self lineTo:path x:x + _penX y:y + _penY];
}
- (void)lineTo:(CGPathRef)path x:(double)x y:(double)y{
[self setPenDown];
_pivotX = _penX = x;
_pivotY = _penY = y;
CGPathAddLineToPoint(path, nil, x, y);
}
- (void)curve:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey
{
[self curveTo:path c1x:c1x + _penX
c1y:c1y + _penY
c2x:c2x + _penX
c2y:c2y + _penY
ex:ex + _penX
ey:ey + _penY];
}
- (void)curveTo:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey
{
_pivotX = ex;
_pivotY = ey;
[self curveToPoint:path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey];
}
- (void)curveToPoint:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey
{
[self setPenDown];
_penX = ex;
_penY = ey;
CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey);
}
- (void)smoothCurve:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y ex:(double)ex ey:(double)ey
{
[self smoothCurveTo:path c1x:c1x + _penX c1y:c1y + _penY ex:ex + _penX ey:ey + _penY];
}
- (void)smoothCurveTo:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y ex:(double)ex ey:(double)ey
{
double c2x = c1x;
double c2y = c1y;
c1x = (_penX * 2) - _pivotX;
c1y = (_penY * 2) - _pivotY;
_pivotX = c2x;
_pivotY = c2y;
[self curveToPoint:path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey];
}
- (void)quadraticBezierCurve:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y
{
[self quadraticBezierCurveTo:path c1x:(double)c1x + _penX c1y:(double)c1y + _penY c2x:(double)c2x + _penX c2y:(double)c2y + _penY];
}
- (void)quadraticBezierCurveTo:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y
{
_pivotX = c1x;
_pivotY = c1y;
double ex = c2x;
double ey = c2y;
c2x = (ex + c1x * 2) / 3;
c2y = (ey + c1y * 2) / 3;
c1x = (_penX + c1x * 2) / 3;
c1y = (_penY + c1y * 2) / 3;
[self curveToPoint:path c1x:(double)c1x c1y:(double)c1y c2x:(double)c2x c2y:(double)c2y ex:(double)ex ey:(double)ey];
}
- (void)smoothQuadraticBezierCurve:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y
{
[self smoothQuadraticBezierCurveTo:path c1x:c1x + _penX c1y:c1y + _penY];
}
- (void)smoothQuadraticBezierCurveTo:(CGPathRef)path c1x:(double)c1x c1y:(double)c1y
{
double c2x = c1x;
double c2y = c1y;
c1x = (_penX * 2) - _pivotX;
c1y = (_penY * 2) - _pivotY;
[self quadraticBezierCurveTo:path c1x:c1x c1y:c1y c2x:c2x c2y:c2y];
}
- (void)arc:(CGPathRef)path rx:(double)rx ry:(double)ry rotation:(double)rotation outer:(BOOL)outer clockwise:(BOOL)clockwise x:(double)x y:(double)y
{
[self arcTo:path rx:rx ry:ry rotation:rotation outer:outer clockwise:clockwise x:x + _penX y:y + _penY];
}
- (void)arcTo:(CGPathRef)path rx:(double)rx ry:(double)ry rotation:(double)rotation outer:(BOOL)outer clockwise:(BOOL)clockwise x:(double)x y:(double)y
{
double tX = _penX;
double tY = _penY;
ry = abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry);
rx = abs(rx == 0 ? (x - tX) : rx);
if (rx == 0 || ry == 0 || (x == tX && y == tY)) {
[self lineTo:path x:x y:y];
return;
}
double rad = rotation * M_PI / 180;
double cosed = cos(rad);
double sined = sin(rad);
x -= tX;
y -= tY;
// Ellipse Center
float cx = cosed * x / 2 + sined * y / 2;
float cy = -sined * x / 2 + cosed * y / 2;
float rxry = rx * rx * ry * ry;
float rycx = ry * ry * cx * cx;
float rxcy = rx * rx * cy * cy;
float a = rxry - rxcy - rycx;
if (a < 0){
a = sqrt(1 - a / rxry);
rx *= a;
ry *= a;
cx = x / 2;
cy = y / 2;
} else {
a = sqrt(a / (rxcy + rycx));
if (outer == clockwise) {
a = -a;
}
float cxd = -a * cy * rx / ry;
float cyd = a * cx * ry / rx;
cx = cosed * cxd - sined * cyd + x / 2;
cy = sined * cxd + cosed * cyd + y / 2;
}
// Rotation + Scale Transform
float xx = cosed / rx;
float yx = sined / rx;
float xy = -sined / ry;
float yy = cosed / ry;
// Start and End Angle
float sa = atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy);
float ea = atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy));
cx += tX;
cy += tY;
x += tX;
y += tY;
[self setPenDown];
_penX = _pivotX = x;
_penY = _pivotY = y;
if (rx != ry || rad != 0) {
[self arcToBezier:path cx:cx cy:cy rx:rx ry:ry sa:sa ea:ea clockwise:clockwise rad:rad];
} else {
CGPathAddArc(path, nil, cx, cy, rx, sa, ea, !clockwise);
}
}
- (void)arcToBezier:(CGPathRef)path cx:(double)cx cy:(double)cy rx:(double)rx ry:(double)ry sa:(double)sa ea:(double)ea clockwise:(BOOL)clockwise rad:(double)rad
{
// Inverse Rotation + Scale Transform
double cosed = cos(rad);
double sined = sin(rad);
double xx = cosed * rx;
double yx = -sined * ry;
double xy = sined * rx;
double yy = cosed * ry;
// Bezier Curve Approximation
double arc = ea - sa;
if (arc < 0 && clockwise) {
arc += M_PI * 2;
} else if (arc > 0 && !clockwise) {
arc -= M_PI * 2;
}
int n = ceil(abs(arc / (M_PI / 2)));
double step = arc / n;
double k = (4 / 3) * tan(step / 4);
double x = cos(sa);
double y = sin(sa);
for (int i = 0; i < n; i++){
double cp1x = x - k * y;
double cp1y = y + k * x;
sa += step;
x = cos(sa);
y = sin(sa);
double cp2x = x + k * y;
double cp2y = y - k * x;
CGPathAddCurveToPoint(path,
nil,
cx + xx * cp1x + yx * cp1y,
cy + xy * cp1x + yy * cp1y,
cx + xx * cp2x + yx * cp2y,
cy + xy * cp2x + yy * cp2y,
cx + xx * x + yx * y,
cy + xy * x + yy * y);
}
}
- (void)close:(CGPathRef)path
{
if (_penDownSet) {
_penX = _penDownX;
_penY = _penDownY;
_penDownSet = NO;
CGPathCloseSubpath(path);
}
}
- (void)setPenDown
{
if (!_penDownSet) {
_penDownX = _penX;
_penDownY = _penY;
_penDownSet = YES;
}
}
@end

View File

@@ -12,7 +12,7 @@
{
CGFloat _relative;
CGFloat _offset;
NSRegularExpression *percentageRegularExpression;
NSRegularExpression *_percentageRegularExpression;
}
- (instancetype) initWithRelativeAndOffset:(CGFloat)relative offset:(CGFloat)offset
@@ -20,7 +20,7 @@
if (self = [super init]) {
_relative = relative;
_offset = offset;
percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
_percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
}
return self;
}
@@ -28,14 +28,14 @@
- (id)init
{
if (self = [super init]) {
percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
_percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
}
return self;
}
- (NSRegularExpression *) getPercentageRegularExpression
{
return percentageRegularExpression;
return _percentageRegularExpression;
}
- (CGFloat) stringToFloat:(NSString *)string
@@ -61,7 +61,7 @@
{
__block CGFloat matched;
[percentageRegularExpression enumerateMatchesInString:percentage
[_percentageRegularExpression enumerateMatchesInString:percentage
options:0
range:NSMakeRange(0, percentage.length)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
@@ -76,7 +76,7 @@
- (BOOL) isPercentage:(NSString *) string
{
return [percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != nil;
return [_percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != nil;
}
@end

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RCTViewManager.h"
#import <React/RCTViewManager.h>
@interface RNSVGDefsManager : RCTViewManager

View File

@@ -7,7 +7,7 @@
*/
#import "RNSVGNode.h"
#import "RCTViewManager.h"
#import <React/RCTViewManager.h>
@interface RNSVGNodeManager : RCTViewManager

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RCTViewManager.h"
#import <React/RCTViewManager.h>
@interface RNSVGSvgViewManager : RCTViewManager

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RCTBridge.h"
#import "RCTUIManager.h"
#import <React/RCTBridge.h>
#import <React/RCTUIManager.h>
#import "RNSVGSvgViewManager.h"
#import "RNSVGSvgView.h"

View File

@@ -84,9 +84,7 @@ const UseAttributes = merge({
}, RenderableAttributes);
const PathAttributes = merge({
d: {
diff: arrayDiffer
}
d: true
}, RenderableAttributes);
const TextAttributes = merge({

View File

@@ -1,4 +1,3 @@
import SerializablePath from '../SerializablePath';
import clipReg from './patternReg';
const clipRules = {
@@ -17,6 +16,8 @@ export default function (props) {
if (matched) {
clippingProps.clipPath = matched[1];
} else {
console.warn('Invalid `clipPath` prop, expected a clipPath like `"#id"`, but got: "' + clipPath + '"');
}
}

View File

@@ -20,10 +20,6 @@ export default function(props) {
let strokeWidth = +props.strokeWidth;
if (_.isNil(props.strokeWidth)) {
strokeWidth = null;
}
let strokeDasharray = props.strokeDasharray;
if (typeof strokeDasharray === 'string') {
@@ -41,7 +37,7 @@ export default function(props) {
strokeLinecap: caps[props.strokeLinecap] || 0,
strokeLinejoin: joins[props.strokeLinejoin] || 0,
strokeDasharray: strokeDasharray || null,
strokeWidth: strokeWidth,
strokeWidth: strokeWidth || null,
strokeDashoffset: strokeDasharray ? (+props.strokeDashoffset || 0) : null,
strokeMiterlimit: props.strokeMiterlimit || 4
};

View File

@@ -1,5 +1,5 @@
{
"version": "4.3.2",
"version": "4.6.1",
"name": "react-native-svg",
"description": "SVG library for react-native",
"repository": {
@@ -22,7 +22,8 @@
"lint": "eslint ./"
},
"peerDependencies": {
"react-native": ">=0.33.0"
"react-native": ">=0.40.0",
"react": ">=15.4.0"
},
"dependencies": {
"color": "^0.11.1",