mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
merge master fix conflicts
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
@@ -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() {}
|
||||
@@ -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() {}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 + "");
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
57
android/src/main/java/com/horcrux/svg/PathShadowNode.java
Normal file
57
android/src/main/java/com/horcrux/svg/PathShadowNode.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
55
android/src/main/java/com/horcrux/svg/SvgPackage.java
Normal file
55
android/src/main/java/com/horcrux/svg/SvgPackage.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "RNSVGBaseBrush.h"
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGBaseBrush
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "RNSVGBrush.h"
|
||||
|
||||
#import "RCTDefines.h"
|
||||
#import <React/RCTDefines.h>
|
||||
|
||||
@implementation RNSVGBrush
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import "RNSVGPattern.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGPattern
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import "RNSVGSolidColorBrush.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGSolidColorBrush
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import "RNSVGSvgView.h"
|
||||
|
||||
#import "RNSVGNode.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGSvgView
|
||||
{
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#import "RNSVGPath.h"
|
||||
|
||||
@interface RNSVGCircle : RNSVGPath
|
||||
@interface RNSVGCircle : RNSVGRenderable
|
||||
|
||||
@property (nonatomic, strong) NSString* cx;
|
||||
@property (nonatomic, strong) NSString* cy;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGCircle.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGCircle
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGEllipse.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGEllipse
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGLine.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGLine
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#import "RNSVGPath.h"
|
||||
|
||||
@interface RNSVGRect : RNSVGPath
|
||||
@interface RNSVGRect : RNSVGRenderable
|
||||
|
||||
@property (nonatomic, strong) NSString* x;
|
||||
@property (nonatomic, strong) NSString* y;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGRect.h"
|
||||
#import "RCTLog.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGRect
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, (@{
|
||||
|
||||
17
ios/Utils/RNSVGPathParser.h
Normal file
17
ios/Utils/RNSVGPathParser.h
Normal 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
379
ios/Utils/RNSVGPathParser.m
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTViewManager.h"
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RNSVGDefsManager : RCTViewManager
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGNode.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RNSVGNodeManager : RCTViewManager
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTViewManager.h"
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RNSVGSvgViewManager : RCTViewManager
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -84,9 +84,7 @@ const UseAttributes = merge({
|
||||
}, RenderableAttributes);
|
||||
|
||||
const PathAttributes = merge({
|
||||
d: {
|
||||
diff: arrayDiffer
|
||||
}
|
||||
d: true
|
||||
}, RenderableAttributes);
|
||||
|
||||
const TextAttributes = merge({
|
||||
|
||||
@@ -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 + '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user