diff --git a/android/src/main/java/com/horcrux/svg/Brush.java b/android/src/main/java/com/horcrux/svg/Brush.java new file mode 100644 index 00000000..d0f7237c --- /dev/null +++ b/android/src/main/java/com/horcrux/svg/Brush.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2015-present, Horcrux. + * All rights reserved. + * + * This source code is licensed under the MIT-style license found in the + * LICENSE file in the root directory of this source tree. + */ + + +package com.horcrux.svg; + +import android.graphics.BitmapShader; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; + +import com.facebook.react.bridge.ReadableArray; + +public class Brush { + private BrushType mType = BrushType.LINEAR_GRADIENT; + private ReadableArray mPoints; + private ReadableArray mColors; + private boolean mUseObjectBoundingBox; + private Rect mUserSpaceBoundingBox; + + public Brush(BrushType type, ReadableArray points, BrushUnits units) { + mType = type; + mPoints = points; + mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX; + } + + public enum BrushType { + LINEAR_GRADIENT(0), + RADIAL_GRADIENT(1), + PATTERN(2); + BrushType(int ni) { + nativeInt = ni; + } + + final int nativeInt; + } + + public enum BrushUnits { + OBJECT_BOUNDING_BOX(0), + USER_SPACE_ON_USE(1); + BrushUnits(int ni) { + nativeInt = ni; + } + final int nativeInt; + } + + private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) { + 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(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)); + + } + } + + public void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) { + mUserSpaceBoundingBox = userSpaceBoundingBox; + } + + public void setGradientColors(ReadableArray colors) { + mColors = colors; + } + + private RectF getPaintRect(RectF pathBoundingBox) { + RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox); + float width = rect.width(); + float height = rect.height(); + float x = 0f; + float y = 0f; + + if (mUseObjectBoundingBox) { + x = rect.left; + y = rect.top; + } + + return new RectF(x, y, x + width, y + height); + } + + public void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) { + RectF rect = getPaintRect(pathBoundingBox); + float width = rect.width(); + float height = rect.height(); + float offsetX = rect.left; + float offsetY = rect.top; + + int stopsCount = mColors.size() / 5; + int[] stopsColors = new int[stopsCount]; + float[] stops = new float[stopsCount]; + parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity); + + if (mType == BrushType.LINEAR_GRADIENT) { + float x1 = PropHelper.fromPercentageToFloat(mPoints.getString(0), width, offsetX, scale); + float y1 = PropHelper.fromPercentageToFloat(mPoints.getString(1), height, offsetY, scale); + 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)); + } else if (mType == BrushType.RADIAL_GRADIENT) { + float rx = PropHelper.fromPercentageToFloat(mPoints.getString(2), width, 0f, scale); + float ry = PropHelper.fromPercentageToFloat(mPoints.getString(3), height, 0f, scale); + float cx = PropHelper.fromPercentageToFloat(mPoints.getString(4), width, offsetX, scale); + float cy = PropHelper.fromPercentageToFloat(mPoints.getString(5), height, offsetY, scale) / (ry / rx); + // TODO: support focus point. + //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 + ); + + Matrix radialMatrix = new Matrix(); + radialMatrix.preScale(1f, ry / rx); + radialGradient.setLocalMatrix(radialMatrix); + paint.setShader(radialGradient); + } else { + // todo: pattern support + + //Shader mShader1 = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + //paint.setShader(mShader1); + //bitmap.recycle(); + } + } +} diff --git a/android/src/main/java/com/horcrux/svg/LinearGradientShadowNode.java b/android/src/main/java/com/horcrux/svg/LinearGradientShadowNode.java index 241c6d73..b5c35bcb 100644 --- a/android/src/main/java/com/horcrux/svg/LinearGradientShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/LinearGradientShadowNode.java @@ -9,7 +9,7 @@ package com.horcrux.svg; -import com.facebook.react.bridge.JavaOnlyArray; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; import com.facebook.react.uimanager.annotations.ReactProp; @@ -24,6 +24,7 @@ public class LinearGradientShadowNode extends DefinitionShadowNode { private String mX2; private String mY2; private ReadableArray mGradient; + private Brush.BrushUnits mGradientUnits; @ReactProp(name = "x1") public void setX1(String x1) { @@ -32,7 +33,7 @@ public class LinearGradientShadowNode extends DefinitionShadowNode { } @ReactProp(name = "y1") - public void setCx(String y1) { + public void setY1(String y1) { mY1 = y1; markUpdated(); } @@ -55,17 +56,37 @@ public class LinearGradientShadowNode extends DefinitionShadowNode { markUpdated(); } + @ReactProp(name = "gradientUnits") + public void setGradientUnits(int gradientUnits) { + switch (gradientUnits) { + case 0: + mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + markUpdated(); + } + @Override protected void saveDefinition() { if (mName != null) { - WritableArray points = new JavaOnlyArray(); + WritableArray points = Arguments.createArray(); points.pushString(mX1); points.pushString(mY1); points.pushString(mX2); points.pushString(mY2); - PropHelper.RNSVGBrush brush = new PropHelper.RNSVGBrush(PropHelper.RNSVGBrush.GradientType.LINEAR_GRADIENT, points, mGradient); - getSvgShadowNode().defineBrush(brush, mName); + Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits); + brush.setGradientColors(mGradient); + + SvgViewShadowNode svg = getSvgShadowNode(); + if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { + brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); + } + + svg.defineBrush(brush, mName); } } } diff --git a/android/src/main/java/com/horcrux/svg/PropHelper.java b/android/src/main/java/com/horcrux/svg/PropHelper.java index eeb0dd0d..1e1baa2f 100644 --- a/android/src/main/java/com/horcrux/svg/PropHelper.java +++ b/android/src/main/java/com/horcrux/svg/PropHelper.java @@ -87,114 +87,21 @@ class PropHelper { if (matched.matches()) { return Float.valueOf(matched.group(1)) / 100 * relative + offset; } else { - return Float.valueOf(percentage) * scale; + return Float.valueOf(percentage) * scale + offset; } } /** - * Judge given string is a percentage-like string or not. + * Matches if the `string` is percentage-like. * * @param string percentage string - * @return string is percentage-like or not. + * @return if `string` is percentage-like. */ - static boolean isPercentage(String string) { return percentageRegExp.matcher(string).matches(); } - /** - * - */ - static class RNSVGBrush { - - private GradientType mType = GradientType.LINEAR_GRADIENT; - private ReadableArray mPoints; - private ReadableArray mColors; - - public RNSVGBrush(GradientType type, ReadableArray points, ReadableArray colors) { - mType = type; - mPoints = points; - mColors = colors; - } - - public enum GradientType { - LINEAR_GRADIENT(0), - RADIAL_GRADIENT(1); - - GradientType(int ni) { - nativeInt = ni; - } - - final int nativeInt; - } - - private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) { - 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(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)); - - } - } - - public void setupPaint(Paint paint, RectF box, float scale, float opacity) { - float height = box.height(); - float width = box.width(); - float midX = box.centerX(); - float midY = box.centerY(); - float offsetX = (midX - width / 2); - float offsetY = (midY - height / 2); - - - int stopsCount = mColors.size() / 5; - int[] stopsColors = new int[stopsCount]; - float[] stops = new float[stopsCount]; - parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity); - - if (mType == GradientType.LINEAR_GRADIENT) { - float x1 = PropHelper.fromPercentageToFloat(mPoints.getString(0), width, offsetX, scale); - float y1 = PropHelper.fromPercentageToFloat(mPoints.getString(1), height, offsetY, scale); - 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)); - } else { - float rx = PropHelper.fromPercentageToFloat(mPoints.getString(2), width, 0f, scale); - float ry = PropHelper.fromPercentageToFloat(mPoints.getString(3), height, 0f, scale); - float cx = PropHelper.fromPercentageToFloat(mPoints.getString(4), width, offsetX, scale); - float cy = PropHelper.fromPercentageToFloat(mPoints.getString(5), height, offsetY, scale) / (ry / rx); - // TODO: support focus point. - //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 - ); - - Matrix radialMatrix = new Matrix(); - radialMatrix.preScale(1f, ry / rx); - radialGradient.setLocalMatrix(radialMatrix); - paint.setShader(radialGradient); - } - } - } - 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+)(?=\\-?\\.)"); diff --git a/android/src/main/java/com/horcrux/svg/RadialGradientShadowNode.java b/android/src/main/java/com/horcrux/svg/RadialGradientShadowNode.java index e29fdb82..01dcae31 100644 --- a/android/src/main/java/com/horcrux/svg/RadialGradientShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RadialGradientShadowNode.java @@ -9,7 +9,7 @@ package com.horcrux.svg; -import com.facebook.react.bridge.JavaOnlyArray; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; import com.facebook.react.uimanager.annotations.ReactProp; @@ -25,9 +25,10 @@ public class RadialGradientShadowNode extends DefinitionShadowNode { private String mCx; private String mCy; private ReadableArray mGradient; + private Brush.BrushUnits mGradientUnits; @ReactProp(name = "fx") - public void setFX(String fx) { + public void setFx(String fx) { mFx = fx; markUpdated(); } @@ -68,10 +69,23 @@ public class RadialGradientShadowNode extends DefinitionShadowNode { markUpdated(); } + @ReactProp(name = "gradientUnits") + public void setGradientUnits(int gradientUnits) { + switch (gradientUnits) { + case 0: + mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + markUpdated(); + } + @Override protected void saveDefinition() { if (mName != null) { - WritableArray points = new JavaOnlyArray(); + WritableArray points = Arguments.createArray(); points.pushString(mFx); points.pushString(mFy); points.pushString(mRx); @@ -79,8 +93,15 @@ public class RadialGradientShadowNode extends DefinitionShadowNode { points.pushString(mCx); points.pushString(mCy); - PropHelper.RNSVGBrush brush = new PropHelper.RNSVGBrush(PropHelper.RNSVGBrush.GradientType.RADIAL_GRADIENT, points, mGradient); - getSvgShadowNode().defineBrush(brush, mName); + Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits); + brush.setGradientColors(mGradient); + + SvgViewShadowNode svg = getSvgShadowNode(); + if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { + brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); + } + + svg.defineBrush(brush, mName); } } } diff --git a/android/src/main/java/com/horcrux/svg/RenderableShadowNode.java b/android/src/main/java/com/horcrux/svg/RenderableShadowNode.java index a4182bb0..2e015be0 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RenderableShadowNode.java @@ -21,13 +21,10 @@ import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; -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; import com.facebook.react.bridge.WritableArray; -import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; import java.lang.reflect.Field; @@ -208,10 +205,10 @@ abstract public class RenderableShadowNode extends VirtualNode { mPath.setFillType(mFillRule); clip(canvas, paint); - if (setupFillPaint(paint, opacity * mFillOpacity, null)) { + if (setupFillPaint(paint, opacity * mFillOpacity)) { canvas.drawPath(mPath, paint); } - if (setupStrokePaint(paint, opacity * mStrokeOpacity, null)) { + if (setupStrokePaint(paint, opacity * mStrokeOpacity)) { canvas.drawPath(mPath, paint); } } @@ -221,12 +218,12 @@ abstract public class RenderableShadowNode extends VirtualNode { * 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. */ - protected boolean setupFillPaint(Paint paint, float opacity, @Nullable RectF box) { + protected boolean setupFillPaint(Paint paint, float opacity) { if (mFill != null && mFill.size() > 0) { paint.reset(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL); - setupPaint(paint, opacity, mFill, box); + setupPaint(paint, opacity, mFill); return true; } return false; @@ -236,7 +233,7 @@ abstract public class RenderableShadowNode extends VirtualNode { * Sets up paint according to the props set on a shadow view. Returns {@code true} * if the stroke should be drawn, {@code false} if not. */ - protected boolean setupStrokePaint(Paint paint, float opacity, @Nullable RectF box) { + protected boolean setupStrokePaint(Paint paint, float opacity) { paint.reset(); if (mStrokeWidth == 0 || mStroke == null || mStroke.size() == 0) { return false; @@ -248,7 +245,7 @@ abstract public class RenderableShadowNode extends VirtualNode { paint.setStrokeJoin(mStrokeLinejoin); paint.setStrokeMiter(mStrokeMiterlimit * mScale); paint.setStrokeWidth(mStrokeWidth * mScale); - setupPaint(paint, opacity, mStroke, box); + setupPaint(paint, opacity, mStroke); if (mStrokeDasharray != null && mStrokeDasharray.length > 0) { paint.setPathEffect(new DashPathEffect(mStrokeDasharray, mStrokeDashoffset)); @@ -258,7 +255,7 @@ abstract public class RenderableShadowNode extends VirtualNode { } - private void setupPaint(Paint paint, float opacity, ReadableArray colors, @Nullable RectF box) { + private void setupPaint(Paint paint, float opacity, ReadableArray colors) { int colorType = colors.getInt(0); if (colorType == 0) { // solid color @@ -268,17 +265,13 @@ abstract public class RenderableShadowNode extends VirtualNode { (int) (colors.getDouble(2) * 255), (int) (colors.getDouble(3) * 255)); } else if (colorType == 1) { - if (box == null) { - box = new RectF(); - mPath.computeBounds(box, true); - } - PropHelper.RNSVGBrush brush = getSvgShadowNode().getDefinedBrush(colors.getString(1)); + RectF box = new RectF(); + mPath.computeBounds(box, true); + + Brush brush = getSvgShadowNode().getDefinedBrush(colors.getString(1)); if (brush != null) { brush.setupPaint(paint, box, mScale, opacity); } - } else { - // TODO: Support pattern. - FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!"); } } @@ -334,7 +327,7 @@ abstract public class RenderableShadowNode extends VirtualNode { targetAttributeList.size() == 0) { return; } - + mOriginProperties = new ArrayList<>(); mAttributeList = clonePropList(); diff --git a/android/src/main/java/com/horcrux/svg/SvgInstancesManager.java b/android/src/main/java/com/horcrux/svg/SvgInstancesManager.java deleted file mode 100644 index 81fa14e0..00000000 --- a/android/src/main/java/com/horcrux/svg/SvgInstancesManager.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.horcrux.svg; - -import android.util.SparseArray; - -import javax.annotation.Nullable; - -public class SvgInstancesManager { - private static final SparseArray mTagToShadowNode = new SparseArray<>(); - private static final SparseArray 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 @Nullable SvgView getSvgViewByTag(int tag) { - return mTagToSvgView.get(tag); - } - - static @Nullable SvgViewShadowNode getShadowNodeByTag(int tag) { - return mTagToShadowNode.get(tag); - } -} diff --git a/android/src/main/java/com/horcrux/svg/SvgView.java b/android/src/main/java/com/horcrux/svg/SvgView.java index c38e2162..8170d1eb 100644 --- a/android/src/main/java/com/horcrux/svg/SvgView.java +++ b/android/src/main/java/com/horcrux/svg/SvgView.java @@ -19,7 +19,6 @@ import android.view.View; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; import com.facebook.react.uimanager.events.TouchEventType; @@ -61,7 +60,7 @@ public class SvgView extends View { @Override public void setId(int id) { super.setId(id); - SvgInstancesManager.registerSvgView(this); + SvgViewManager.setSvgView(this); } public void setBitmap(Bitmap bitmap) { @@ -81,7 +80,7 @@ public class SvgView extends View { } private SvgViewShadowNode getShadowNode() { - return SvgInstancesManager.getShadowNodeByTag(getId()); + return SvgViewManager.getShadowNodeByTag(getId()); } @Override diff --git a/android/src/main/java/com/horcrux/svg/SvgViewManager.java b/android/src/main/java/com/horcrux/svg/SvgViewManager.java index c533ea4f..a4f84eff 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewManager.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewManager.java @@ -10,6 +10,7 @@ package com.horcrux.svg; import android.graphics.Bitmap; +import android.util.SparseArray; import com.facebook.yoga.YogaMeasureMode; import com.facebook.yoga.YogaMeasureFunction; @@ -17,6 +18,8 @@ import com.facebook.yoga.YogaNodeAPI; import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.ThemedReactContext; +import javax.annotation.Nullable; + /** * 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. @@ -24,6 +27,7 @@ import com.facebook.react.uimanager.ThemedReactContext; public class SvgViewManager extends BaseViewManager { private static final String REACT_CLASS = "RNSVGSvgView"; + private static final YogaMeasureFunction MEASURE_FUNCTION = new YogaMeasureFunction() { @Override public long measure( @@ -36,6 +40,25 @@ public class SvgViewManager extends BaseViewManager } }; + private static final SparseArray mTagToShadowNode = new SparseArray<>(); + private static final SparseArray mTagToSvgView = new SparseArray<>(); + + static void setShadowNode(SvgViewShadowNode shadowNode) { + mTagToShadowNode.put(shadowNode.getReactTag(), shadowNode); + } + + static void setSvgView(SvgView svg) { + mTagToSvgView.put(svg.getId(), svg); + } + + static @Nullable SvgView getSvgViewByTag(int tag) { + return mTagToSvgView.get(tag); + } + + static @Nullable SvgViewShadowNode getShadowNodeByTag(int tag) { + return mTagToShadowNode.get(tag); + } + @Override public String getName() { return REACT_CLASS; @@ -55,7 +78,9 @@ public class SvgViewManager extends BaseViewManager @Override public void onDropViewInstance(SvgView view) { - SvgInstancesManager.unregisterInstance(view.getId()); + int tag = view.getId(); + mTagToShadowNode.remove(tag); + mTagToSvgView.remove(tag); } @Override diff --git a/android/src/main/java/com/horcrux/svg/SvgViewModule.java b/android/src/main/java/com/horcrux/svg/SvgViewModule.java index b391dba7..9c6fff62 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewModule.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewModule.java @@ -29,7 +29,7 @@ public class SvgViewModule extends ReactContextBaseJavaModule { @ReactMethod public void toDataURL(int tag, Callback successCallback) { - SvgViewShadowNode svg = SvgInstancesManager.getShadowNodeByTag(tag); + SvgViewShadowNode svg = SvgViewManager.getShadowNodeByTag(tag); if (svg != null) { successCallback.invoke(svg.toDataURL()); diff --git a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java index 58c16fe8..1d60f88c 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java @@ -35,7 +35,7 @@ public class SvgViewShadowNode extends LayoutShadowNode { private final Map mDefinedClipPaths = new HashMap<>(); private final Map mDefinedTemplates = new HashMap<>(); - private final Map mDefinedBrushes = new HashMap<>(); + private final Map mDefinedBrushes = new HashMap<>(); private Canvas mCanvas; protected final float mScale; @@ -106,7 +106,7 @@ public class SvgViewShadowNode extends LayoutShadowNode { @Override public void setReactTag(int reactTag) { super.setReactTag(reactTag); - SvgInstancesManager.registerShadowNode(this); + SvgViewManager.setShadowNode(this); } public Object drawOutput() { @@ -211,11 +211,11 @@ public class SvgViewShadowNode extends LayoutShadowNode { return mDefinedTemplates.get(templateRef); } - public void defineBrush(PropHelper.RNSVGBrush brush, String brushRef) { + public void defineBrush(Brush brush, String brushRef) { mDefinedBrushes.put(brushRef, brush); } - public PropHelper.RNSVGBrush getDefinedBrush(String brushRef) { + public Brush getDefinedBrush(String brushRef) { return mDefinedBrushes.get(brushRef); } } diff --git a/elements/Gradient.js b/elements/Gradient.js deleted file mode 100644 index 84a402e3..00000000 --- a/elements/Gradient.js +++ /dev/null @@ -1,46 +0,0 @@ -import {Children, Component} from 'react'; -import percentToFloat from '../lib/percentToFloat'; -import Stop from './Stop'; -import Color from 'color'; -import extractOpacity from '../lib/extract/extractOpacity'; -import _ from 'lodash'; - -class Gradient extends Component{ - static displayName = 'Gradient'; - - getGradient = () => { - let stops = {}; - Children.forEach(this.props.children, child => { - if (child.type === Stop) { - if (child.props.stopColor && child.props.offset) { - // convert percent to float. - let offset = percentToFloat(child.props.offset); - - // add stop - stops[offset] = Color(child.props.stopColor).alpha(extractOpacity(child.props.stopOpacity)); - } - } else { - console.warn('\'Gradient\' can only receive \'Stop\' elements as children'); - } - }); - - let sorted = _.sortBy(_.map(stops, (stop, offset) => { - return {stop, offset}; - }), 'offset'); - let gradient = []; - - sorted.forEach(({stop}) => { - let channels = stop.rgbaArray(); - gradient.push(channels[0] / 255); - gradient.push(channels[1] / 255); - gradient.push(channels[2] / 255); - gradient.push(channels[3]); - }); - - gradient.push(...sorted.map(({offset}) => +offset)); - return gradient; - }; -} - -export default Gradient; - diff --git a/elements/LinearGradient.js b/elements/LinearGradient.js index 4f0626ad..b3fdfc36 100644 --- a/elements/LinearGradient.js +++ b/elements/LinearGradient.js @@ -1,10 +1,10 @@ -import React, {PropTypes} from 'react'; +import React, {PropTypes, Component} from 'react'; import {numberProp} from '../lib/props'; -import Gradient from './Gradient'; +import extractGradient from '../lib/extract/extractGradient'; import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; import {LinearGradientAttributes} from '../lib/attributes'; -class LinearGradient extends Gradient{ +class LinearGradient extends Component{ static displayName = 'LinearGradient'; static propTypes = { x1: numberProp.isRequired, @@ -28,8 +28,7 @@ class LinearGradient extends Gradient{ y1={props.y1.toString()} x2={props.x2.toString()} y2={props.y2.toString()} - gradient={this.getGradient()} - name={props.id} + {...extractGradient(this.props)} />; } diff --git a/elements/RadialGradient.js b/elements/RadialGradient.js index 7fad89ee..821b07a1 100644 --- a/elements/RadialGradient.js +++ b/elements/RadialGradient.js @@ -1,10 +1,10 @@ -import React, {PropTypes} from 'react'; +import React, {PropTypes, Component} from 'react'; import {numberProp} from '../lib/props'; -import Gradient from './Gradient'; +import extractGradient from '../lib/extract/extractGradient'; import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; import {RadialGradientAttributes} from '../lib/attributes'; -class RadialGradient extends Gradient{ +class RadialGradient extends Component{ static displayName = 'RadialGradient'; static propTypes = { fx: numberProp.isRequired, @@ -34,8 +34,7 @@ class RadialGradient extends Gradient{ ry={(props.ry || props.r).toString()} cx={props.cx.toString()} cy={props.cy.toString()} - gradient={this.getGradient()} - name={props.id} + {...extractGradient(this.props)} />; } diff --git a/ios/Brushes/RNSVGBrush.h b/ios/Brushes/RNSVGBrush.h index 972d19f7..7ffe6c89 100644 --- a/ios/Brushes/RNSVGBrush.h +++ b/ios/Brushes/RNSVGBrush.h @@ -9,12 +9,9 @@ #import #import #import "RNSVGPercentageConverter.h" -#import "RNSVGBrushConverter.h" +#import "RNSVGPainter.h" @interface RNSVGBrush : NSObject -{ - NSArray *_points; -} @property (nonatomic, strong) NSString* brushRef; @@ -38,6 +35,6 @@ * be clipped. * @abstract */ -- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity brushConverter:(RNSVGBrushConverter *)brushConverter; +- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter; @end diff --git a/ios/Brushes/RNSVGBrush.m b/ios/Brushes/RNSVGBrush.m index d99b4bfb..b5b64dcf 100644 --- a/ios/Brushes/RNSVGBrush.m +++ b/ios/Brushes/RNSVGBrush.m @@ -7,7 +7,6 @@ */ #import "RNSVGBrush.h" - #import @implementation RNSVGBrush @@ -29,7 +28,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return NO; } -- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity brushConverter:(RNSVGBrushConverter *)brushConverter +- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter { // abstract } diff --git a/ios/Brushes/RNSVGBrushConverter.h b/ios/Brushes/RNSVGBrushConverter.h deleted file mode 100644 index 9580a3d2..00000000 --- a/ios/Brushes/RNSVGBrushConverter.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2015-present, Horcrux. - * All rights reserved. - * - * This source code is licensed under the MIT-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTConvert+RNSVG.h" -#import "RNSVGBrushType.h" - -@interface RNSVGBrushConverter : NSObject - -@property (nonatomic, copy) NSArray *points; -@property (nonatomic, copy) NSArray *colors; -@property (nonatomic, assign) RNSVGBrushType type; - -- (void) drawLinearGradient:(CGContextRef)context; - -- (void) drawRidialGradient:(CGContextRef)context; - -@end \ No newline at end of file diff --git a/ios/Brushes/RNSVGBrushType.h b/ios/Brushes/RNSVGBrushType.h index 4053111b..3cc4a303 100644 --- a/ios/Brushes/RNSVGBrushType.h +++ b/ios/Brushes/RNSVGBrushType.h @@ -7,6 +7,7 @@ */ typedef enum { + kRNSVGUndefinedType, kRNSVGLinearGradient, kRNSVGRadialGradient, kRNSVGPattern diff --git a/ios/Brushes/RNSVGPainter.h b/ios/Brushes/RNSVGPainter.h new file mode 100644 index 00000000..51347e9e --- /dev/null +++ b/ios/Brushes/RNSVGPainter.h @@ -0,0 +1,29 @@ +/** + * 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 "RCTConvert+RNSVG.h" +#import "RNSVGBrushType.h" +#import "RNSVGUnits.h" + +@interface RNSVGPainter : NSObject + +- (instancetype)initWithPointsArray:(NSArray *)pointsArray NS_DESIGNATED_INITIALIZER; + +- (void)paint:(CGContextRef)context; + +- (void)setUnits:(RNSVGUnits)unit; + +- (void)setUserSpaceBoundingBox:(CGRect)userSpaceBoundingBox; + +- (void)setTransform:(CGAffineTransform)transform; + +- (void)setLinearGradientColors:(NSArray *)colors; + +- (void)setRadialGradientColors:(NSArray *)colors; + +@end diff --git a/ios/Brushes/RNSVGBrushConverter.m b/ios/Brushes/RNSVGPainter.m similarity index 51% rename from ios/Brushes/RNSVGBrushConverter.m rename to ios/Brushes/RNSVGPainter.m index 146b6800..c49fd82e 100644 --- a/ios/Brushes/RNSVGBrushConverter.m +++ b/ios/Brushes/RNSVGPainter.m @@ -6,71 +6,150 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGBrushConverter.h" +#import "RNSVGPainter.h" #import "RNSVGPercentageConverter.h" -@implementation RNSVGBrushConverter +@implementation RNSVGPainter +{ + NSArray *_points; + NSArray *_colors; + RNSVGBrushType _type; + BOOL _useObjectBoundingBox; + CGAffineTransform _transform; + CGRect _userSpaceBoundingBox; +} -- (void)drawLinearGradient:(CGContextRef)context +- (instancetype)initWithPointsArray:(NSArray *)pointsArray +{ + if ((self = [super init])) { + _points = pointsArray; + } + return self; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)init) + +- (void)setUnits:(RNSVGUnits)unit +{ + _useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox; +} + +- (void)setUserSpaceBoundingBox:(CGRect)userSpaceBoundingBox +{ + _userSpaceBoundingBox = userSpaceBoundingBox; +} + +- (void)setTransform:(CGAffineTransform)transform +{ + _transform = transform; +} + +- (void)setLinearGradientColors:(NSArray *)colors +{ + if (_type != kRNSVGUndefinedType) { + // todo: throw error + return; + } + + _type = kRNSVGLinearGradient; + _colors = colors; +} + +- (void)setRadialGradientColors:(NSArray *)colors +{ + if (_type != kRNSVGUndefinedType) { + // todo: throw error + return; + } + + _type = kRNSVGRadialGradient; + _colors = colors; +} + +- (void)paint:(CGContextRef)context +{ + if (_type == kRNSVGLinearGradient) { + [self paintLinearGradient:context]; + } else if (_type == kRNSVGRadialGradient) { + [self paintRidialGradient:context]; + } else if (_type == kRNSVGPattern) { + // todo: + } +} + +- (CGRect)getPaintRect:(CGContextRef)context +{ + CGRect rect = _useObjectBoundingBox ? CGContextGetClipBoundingBox(context) : _userSpaceBoundingBox; + float height = CGRectGetHeight(rect); + float width = CGRectGetWidth(rect); + float x = 0.0; + float y = 0.0; + + if (_useObjectBoundingBox) { + x = CGRectGetMinX(rect); + y = CGRectGetMinY(rect); + } + + return CGRectMake(x, y, width, height); +} + +- (void)paintLinearGradient:(CGContextRef)context { - CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:self.colors offset:0]); + CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors offset:0]); CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGRect box = CGContextGetClipBoundingBox(context); - float height = CGRectGetHeight(box); - float width = CGRectGetWidth(box); - float midX = CGRectGetMidX(box); - float midY = CGRectGetMidY(box); - float offsetX = (midX - width / 2); - float offsetY = (midY - height / 2); + CGRect rect = [self getPaintRect:context]; + float height = CGRectGetHeight(rect); + float width = CGRectGetWidth(rect); + float offsetX = CGRectGetMinX(rect); + float offsetY = CGRectGetMinY(rect); - CGFloat x1 = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:0] + CGFloat x1 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:0] relative:width offset:offsetX]; - CGFloat y1 = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:1] + CGFloat y1 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:1] relative:height offset:offsetY]; - CGFloat x2 = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:2] + CGFloat x2 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:2] relative:width offset:offsetX]; - CGFloat y2 = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:3] + CGFloat y2 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:3] relative:height offset:offsetY]; + CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions); CGGradientRelease(gradient); } -- (void)drawRidialGradient:(CGContextRef)context +- (void)paintRidialGradient:(CGContextRef)context { - CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:self.colors offset:0]); + CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors offset:0]); CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGRect box = CGContextGetClipBoundingBox(context); - float height = CGRectGetHeight(box); - float width = CGRectGetWidth(box); - float midX = CGRectGetMidX(box); - float midY = CGRectGetMidY(box); - float offsetX = (midX - width / 2); - float offsetY = (midY - height / 2); + CGRect rect = [self getPaintRect:context]; + float height = CGRectGetHeight(rect); + float width = CGRectGetWidth(rect); + float offsetX = CGRectGetMinX(rect); + float offsetY = CGRectGetMinY(rect); - CGFloat rx = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:2] + CGFloat rx = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:2] relative:width offset:0]; - CGFloat ry = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:3] + CGFloat ry = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:3] relative:height offset:0]; - CGFloat fx = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:0] + CGFloat fx = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:0] relative:width offset:offsetX]; - CGFloat fy = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:1] + CGFloat fy = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:1] relative:height offset:offsetY] / (ry / rx); - CGFloat cx = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:4] + CGFloat cx = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:4] relative:width offset:offsetX]; - CGFloat cy = [RNSVGPercentageConverter stringToFloat:(NSString *)[self.points objectAtIndex:5] + CGFloat cy = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:5] relative:height offset:offsetY] / (ry / rx); diff --git a/ios/Brushes/RNSVGBaseBrush.h b/ios/Brushes/RNSVGPainterBrush.h similarity index 53% rename from ios/Brushes/RNSVGBaseBrush.h rename to ios/Brushes/RNSVGPainterBrush.h index c84dde5e..e075d58e 100644 --- a/ios/Brushes/RNSVGBaseBrush.h +++ b/ios/Brushes/RNSVGPainterBrush.h @@ -8,10 +8,6 @@ #import "RNSVGBrush.h" -@interface RNSVGBaseBrush : RNSVGBrush - -- (instancetype)initWithArray:(NSArray *)array; - -- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity brushConverter:(RNSVGBrushConverter *)brushConverter; +@interface RNSVGPainterBrush : RNSVGBrush @end diff --git a/ios/Brushes/RNSVGBaseBrush.m b/ios/Brushes/RNSVGPainterBrush.m similarity index 65% rename from ios/Brushes/RNSVGBaseBrush.m rename to ios/Brushes/RNSVGPainterBrush.m index 5e587eb2..8b1b5d50 100644 --- a/ios/Brushes/RNSVGBaseBrush.m +++ b/ios/Brushes/RNSVGPainterBrush.m @@ -6,11 +6,12 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGBaseBrush.h" +#import "RNSVGPainterBrush.h" +#import "RNSVGPainter.h" #import "RCTConvert+RNSVG.h" #import -@implementation RNSVGBaseBrush +@implementation RNSVGPainterBrush - (instancetype)initWithArray:(NSArray *)array { @@ -26,7 +27,7 @@ return self; } -- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity brushConverter:(RNSVGBrushConverter *)brushConverter +- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter { BOOL transparency = opacity < 1; if (transparency) { @@ -34,13 +35,7 @@ CGContextBeginTransparencyLayer(context, NULL); } - if (brushConverter.type == kRNSVGLinearGradient) { - [brushConverter drawLinearGradient:context]; - } else if (brushConverter.type == kRNSVGRadialGradient) { - [brushConverter drawRidialGradient:context]; - } else if (brushConverter.type == kRNSVGPattern) { - // todo: - } + [painter paint:context]; if (transparency) { CGContextEndTransparencyLayer(context); diff --git a/ios/Elements/RNSVGLinearGradient.h b/ios/Elements/RNSVGLinearGradient.h index 3515ef38..483bd436 100644 --- a/ios/Elements/RNSVGLinearGradient.h +++ b/ios/Elements/RNSVGLinearGradient.h @@ -15,5 +15,7 @@ @property (nonatomic, strong) NSString *x2; @property (nonatomic, strong) NSString *y2; @property (nonatomic, copy) NSArray *gradient; +@property (nonatomic, assign)RNSVGUnits gradientUnits; +@property (nonatomic, assign)CGAffineTransform gradientTransform; @end diff --git a/ios/Elements/RNSVGLinearGradient.m b/ios/Elements/RNSVGLinearGradient.m index c098ddec..3f62c3f6 100644 --- a/ios/Elements/RNSVGLinearGradient.m +++ b/ios/Elements/RNSVGLinearGradient.m @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGLinearGradient.h" -#import "RNSVGBrushConverter.h" +#import "RNSVGPainter.h" #import "RNSVGBrushType.h" @implementation RNSVGLinearGradient @@ -28,11 +28,18 @@ - (void)saveDefinition { - RNSVGBrushConverter *converter = [[RNSVGBrushConverter alloc] init]; - converter.colors = self.gradient; - converter.points = @[self.x1, self.y1, self.x2, self.y2]; - converter.type = kRNSVGLinearGradient; - [[self getSvgView] defineBrushConverter:converter brushConverterName:self.name]; + NSArray *points = @[self.x1, self.y1, self.x2, self.y2]; + RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; + [painter setUnits:self.gradientUnits]; + [painter setTransform:self.gradientTransform]; + [painter setLinearGradientColors:self.gradient]; + + RNSVGSvgView *svg = [self getSvgView]; + if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { + [painter setUserSpaceBoundingBox:[svg getContextBounds]]; + } + + [svg definePainter:painter painterName:self.name]; } @end diff --git a/ios/Elements/RNSVGRadialGradient.h b/ios/Elements/RNSVGRadialGradient.h index a7fc282e..cb290f7d 100644 --- a/ios/Elements/RNSVGRadialGradient.h +++ b/ios/Elements/RNSVGRadialGradient.h @@ -17,5 +17,7 @@ @property (nonatomic, strong) NSString *cx; @property (nonatomic, strong) NSString *cy; @property (nonatomic, copy) NSArray *gradient; +@property (nonatomic, assign)RNSVGUnits gradientUnits; +@property (nonatomic, assign)CGAffineTransform gradientTransform; @end diff --git a/ios/Elements/RNSVGRadialGradient.m b/ios/Elements/RNSVGRadialGradient.m index 24bac54d..61e10c42 100644 --- a/ios/Elements/RNSVGRadialGradient.m +++ b/ios/Elements/RNSVGRadialGradient.m @@ -26,11 +26,18 @@ - (void)saveDefinition { - RNSVGBrushConverter *converter = [[RNSVGBrushConverter alloc] init]; - converter.colors = self.gradient; - converter.points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy]; - converter.type = kRNSVGRadialGradient; - [[self getSvgView] defineBrushConverter:converter brushConverterName:self.name]; + NSArray *points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy]; + RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; + [painter setUnits:self.gradientUnits]; + [painter setTransform:self.gradientTransform]; + [painter setRadialGradientColors:self.gradient]; + + RNSVGSvgView *svg = [self getSvgView]; + if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { + [painter setUserSpaceBoundingBox:[svg getContextBounds]]; + } + + [svg definePainter:painter painterName:self.name]; } @end diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index d8df638a..e21087e0 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -7,7 +7,7 @@ */ #import -#import "RNSVGBrushCOnverter.h" +#import "RNSVGPainter.h" #import "RNSVGContainer.h" #import "RNSVGVBMOS.h" @@ -34,9 +34,9 @@ - (RNSVGNode *)getDefinedTemplate:(NSString *)templateName; -- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterName:(NSString *)brushConverterName; +- (void)definePainter:(RNSVGPainter *)painter painterName:(NSString *)painterName; -- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterName; +- (RNSVGPainter *)getDefinedPainter:(NSString *)painterName; - (NSString *)getDataURL; diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 81170dcf..2904a4f7 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -13,9 +13,9 @@ @implementation RNSVGSvgView { - NSMutableDictionary *clipPaths; - NSMutableDictionary *templates; - NSMutableDictionary *brushConverters; + NSMutableDictionary *_clipPaths; + NSMutableDictionary *_templates; + NSMutableDictionary *_painters; CGRect _boundingBox; CGAffineTransform _viewBoxTransform; } @@ -105,9 +105,9 @@ - (void)drawRect:(CGRect)rect { - clipPaths = nil; - templates = nil; - brushConverters = nil; + _clipPaths = nil; + _templates = nil; + _painters = nil; _boundingBox = rect; CGContextRef context = UIGraphicsGetCurrentContext(); @@ -177,42 +177,42 @@ - (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName { - if (!clipPaths) { - clipPaths = [[NSMutableDictionary alloc] init]; + if (!_clipPaths) { + _clipPaths = [[NSMutableDictionary alloc] init]; } - [clipPaths setObject:clipPath forKey:clipPathName]; + [_clipPaths setObject:clipPath forKey:clipPathName]; } - (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName { - return clipPaths ? [clipPaths objectForKey:clipPathName] : nil; + return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil; } - (void)defineTemplate:(RNSVGNode *)template templateName:(NSString *)templateName { - if (!templates) { - templates = [[NSMutableDictionary alloc] init]; + if (!_templates) { + _templates = [[NSMutableDictionary alloc] init]; } - [templates setObject:template forKey:templateName]; + [_templates setObject:template forKey:templateName]; } - (RNSVGNode *)getDefinedTemplate:(NSString *)templateName { - return templates ? [templates objectForKey:templateName] : nil; + return _templates ? [_templates objectForKey:templateName] : nil; } -- (void)defineBrushConverter:(RNSVGBrushConverter *)brushConverter brushConverterName:(NSString *)brushConverterName +- (void)definePainter:(RNSVGPainter *)painter painterName:(NSString *)painterName { - if (!brushConverters) { - brushConverters = [[NSMutableDictionary alloc] init]; + if (!_painters) { + _painters = [[NSMutableDictionary alloc] init]; } - [brushConverters setObject:brushConverter forKey:brushConverterName]; + [_painters setObject:painter forKey:painterName]; } -- (RNSVGBrushConverter *)getDefinedBrushConverter:(NSString *)brushConverterName +- (RNSVGPainter *)getDefinedPainter:(NSString *)painterName; { - return brushConverters ? [brushConverters objectForKey:brushConverterName] : nil; + return _painters ? [_painters objectForKey:painterName] : nil; } - (CGRect)getContextBounds diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index 4f646da0..50040e88 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */; }; 0CF68B0B1AF0549300FF9E5C /* RNSVGBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* RNSVGBrush.m */; }; - 0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* RNSVGPattern.m */; }; 0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColorBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColorBrush.m */; }; 1023B48D1D3DDCCE0051496D /* RNSVGDefsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48C1D3DDCCE0051496D /* RNSVGDefsManager.m */; }; 1023B4901D3DF4C40051496D /* RNSVGDefs.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefs.m */; }; @@ -42,11 +41,11 @@ 10BEC1BD1D3F66F500FDCB19 /* RNSVGRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BEC1BB1D3F66F500FDCB19 /* RNSVGRadialGradient.m */; }; 10BEC1C21D3F680F00FDCB19 /* RNSVGLinearGradientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BEC1BF1D3F680F00FDCB19 /* RNSVGLinearGradientManager.m */; }; 10BEC1C31D3F680F00FDCB19 /* RNSVGRadialGradientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BEC1C11D3F680F00FDCB19 /* RNSVGRadialGradientManager.m */; }; - 10BEC1C61D3F7BD300FDCB19 /* RNSVGBrushConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BEC1C51D3F7BD300FDCB19 /* RNSVGBrushConverter.m */; }; + 10BEC1C61D3F7BD300FDCB19 /* RNSVGPainter.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BEC1C51D3F7BD300FDCB19 /* RNSVGPainter.m */; }; 10ED4A9B1CF065260078BC02 /* RNSVGClipPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */; }; 10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */; }; 10ED4AA21CF078830078BC02 /* RNSVGNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED4AA11CF078830078BC02 /* RNSVGNode.m */; }; - 10FDEEB21D3FB60500A5C46C /* RNSVGBaseBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */; }; + 10FDEEB21D3FB60500A5C46C /* RNSVGPainterBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FDEEB11D3FB60500A5C46C /* RNSVGPainterBrush.m */; }; 7F08CE9A1E23476900650F83 /* RNSVGTextPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE971E23476900650F83 /* RNSVGTextPathManager.m */; }; 7F08CE9B1E23476900650F83 /* RNSVGTSpanManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE991E23476900650F83 /* RNSVGTSpanManager.m */; }; 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */; }; @@ -76,8 +75,6 @@ 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRenderable.m; sourceTree = ""; }; 0CF68AEB1AF0549300FF9E5C /* RNSVGBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrush.h; sourceTree = ""; }; 0CF68AEC1AF0549300FF9E5C /* RNSVGBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGBrush.m; sourceTree = ""; }; - 0CF68AEF1AF0549300FF9E5C /* RNSVGPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGPattern.h; sourceTree = ""; }; - 0CF68AF01AF0549300FF9E5C /* RNSVGPattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPattern.m; sourceTree = ""; }; 0CF68AF31AF0549300FF9E5C /* RNSVGSolidColorBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSolidColorBrush.h; sourceTree = ""; }; 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColorBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSolidColorBrush.m; sourceTree = ""; }; 1023B48B1D3DDCCE0051496D /* RNSVGDefsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGDefsManager.h; sourceTree = ""; }; @@ -146,16 +143,16 @@ 10BEC1BF1D3F680F00FDCB19 /* RNSVGLinearGradientManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGLinearGradientManager.m; sourceTree = ""; }; 10BEC1C01D3F680F00FDCB19 /* RNSVGRadialGradientManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGRadialGradientManager.h; sourceTree = ""; }; 10BEC1C11D3F680F00FDCB19 /* RNSVGRadialGradientManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRadialGradientManager.m; sourceTree = ""; }; - 10BEC1C41D3F793100FDCB19 /* RNSVGBrushConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSVGBrushConverter.h; sourceTree = ""; }; - 10BEC1C51D3F7BD300FDCB19 /* RNSVGBrushConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGBrushConverter.m; sourceTree = ""; }; + 10BEC1C41D3F793100FDCB19 /* RNSVGPainter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSVGPainter.h; sourceTree = ""; }; + 10BEC1C51D3F7BD300FDCB19 /* RNSVGPainter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPainter.m; sourceTree = ""; }; 10ED4A991CF065260078BC02 /* RNSVGClipPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGClipPath.h; path = Elements/RNSVGClipPath.h; sourceTree = ""; }; 10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGClipPath.m; path = Elements/RNSVGClipPath.m; sourceTree = ""; }; 10ED4A9C1CF0656A0078BC02 /* RNSVGClipPathManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGClipPathManager.h; sourceTree = ""; }; 10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGClipPathManager.m; sourceTree = ""; }; 10ED4AA01CF078830078BC02 /* RNSVGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGNode.h; sourceTree = ""; }; 10ED4AA11CF078830078BC02 /* RNSVGNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGNode.m; sourceTree = ""; }; - 10FDEEB01D3FB60500A5C46C /* RNSVGBaseBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBaseBrush.h; sourceTree = ""; }; - 10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGBaseBrush.m; sourceTree = ""; }; + 10FDEEB01D3FB60500A5C46C /* RNSVGPainterBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGPainterBrush.h; sourceTree = ""; }; + 10FDEEB11D3FB60500A5C46C /* RNSVGPainterBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPainterBrush.m; sourceTree = ""; }; 10FDEEB31D3FBED400A5C46C /* RNSVGBrushType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrushType.h; sourceTree = ""; }; 7F08CE961E23476900650F83 /* RNSVGTextPathManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGTextPathManager.h; sourceTree = ""; }; 7F08CE971E23476900650F83 /* RNSVGTextPathManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGTextPathManager.m; sourceTree = ""; }; @@ -166,6 +163,7 @@ 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = ""; }; 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = ""; }; 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; }; + 7F69160D1E3703D800DA6EDC /* RNSVGUnits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGUnits.h; path = Utils/RNSVGUnits.h; sourceTree = ""; }; 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = ""; }; 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = ""; }; 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGViewBox.h; path = Utils/RNSVGViewBox.h; sourceTree = ""; }; @@ -219,16 +217,14 @@ isa = PBXGroup; children = ( 10FDEEB31D3FBED400A5C46C /* RNSVGBrushType.h */, - 10FDEEB01D3FB60500A5C46C /* RNSVGBaseBrush.h */, - 10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */, + 10FDEEB01D3FB60500A5C46C /* RNSVGPainterBrush.h */, + 10FDEEB11D3FB60500A5C46C /* RNSVGPainterBrush.m */, 0CF68AEB1AF0549300FF9E5C /* RNSVGBrush.h */, 0CF68AEC1AF0549300FF9E5C /* RNSVGBrush.m */, - 0CF68AEF1AF0549300FF9E5C /* RNSVGPattern.h */, - 0CF68AF01AF0549300FF9E5C /* RNSVGPattern.m */, 0CF68AF31AF0549300FF9E5C /* RNSVGSolidColorBrush.h */, 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColorBrush.m */, - 10BEC1C41D3F793100FDCB19 /* RNSVGBrushConverter.h */, - 10BEC1C51D3F7BD300FDCB19 /* RNSVGBrushConverter.m */, + 10BEC1C41D3F793100FDCB19 /* RNSVGPainter.h */, + 10BEC1C51D3F7BD300FDCB19 /* RNSVGPainter.m */, ); path = Brushes; sourceTree = ""; @@ -340,12 +336,13 @@ 1039D29A1CE7212C001E90A8 /* Utils */ = { isa = PBXGroup; children = ( - 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */, - 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */, - 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, - 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, + 7F69160D1E3703D800DA6EDC /* RNSVGUnits.h */, 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */, 10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */, + 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, + 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */, + 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */, + 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, 1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */, 1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */, 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */, @@ -421,7 +418,6 @@ 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */, 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */, - 0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */, 1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */, 10BEC1C21D3F680F00FDCB19 /* RNSVGLinearGradientManager.m in Sources */, 1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */, @@ -429,7 +425,7 @@ 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */, 1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */, 10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */, - 10BEC1C61D3F7BD300FDCB19 /* RNSVGBrushConverter.m in Sources */, + 10BEC1C61D3F7BD300FDCB19 /* RNSVGPainter.m in Sources */, 10ED4AA21CF078830078BC02 /* RNSVGNode.m in Sources */, 10ED4A9B1CF065260078BC02 /* RNSVGClipPath.m in Sources */, 10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */, @@ -456,7 +452,7 @@ 7FC260CE1E3499BC00A39833 /* RNSVGViewBox.m in Sources */, 7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */, 10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */, - 10FDEEB21D3FB60500A5C46C /* RNSVGBaseBrush.m in Sources */, + 10FDEEB21D3FB60500A5C46C /* RNSVGPainterBrush.m in Sources */, 1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */, 1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */, 1023B48D1D3DDCCE0051496D /* RNSVGDefsManager.m in Sources */, diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index c07bce3a..ccf972ea 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -175,7 +175,6 @@ return; } - CGPathDrawingMode mode = kCGPathStroke; BOOL fillColor = NO; [self clip:context]; @@ -193,7 +192,7 @@ CGContextClip(context); [self.fill paint:context opacity:self.fillOpacity - brushConverter:[[self getSvgView] getDefinedBrushConverter:self.fill.brushRef] + painter:[[self getSvgView] getDefinedPainter:self.fill.brushRef] ]; CGContextRestoreGState(context); @@ -237,7 +236,7 @@ [self.stroke paint:context opacity:self.strokeOpacity - brushConverter:[[self getSvgView] getDefinedBrushConverter:self.stroke.brushRef] + painter:[[self getSvgView] getDefinedPainter:self.stroke.brushRef] ]; return; } diff --git a/ios/Utils/RCTConvert+RNSVG.h b/ios/Utils/RCTConvert+RNSVG.h index 41a4fd53..0b266d45 100644 --- a/ios/Utils/RCTConvert+RNSVG.h +++ b/ios/Utils/RCTConvert+RNSVG.h @@ -14,6 +14,7 @@ #import "RNSVGCGFCRule.h" #import "RNSVGVBMOS.h" #import "RNSVGTextAnchor.h" +#import "RNSVGUnits.h" #import "RNSVGPathParser.h" @class RNSVGBrush; @@ -23,6 +24,7 @@ + (RNSVGTextAnchor)RNSVGTextAnchor:(id)json; + (RNSVGCGFCRule)RNSVGCGFCRule:(id)json; + (RNSVGVBMOS)RNSVGVBMOS:(id)json; ++ (RNSVGUnits)RNSVGUnits:(id)json; + (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json; + (RNSVGBrush *)RNSVGBrush:(id)json; + (RNSVGPathParser *)RNSVGCGPath:(NSString *)d; diff --git a/ios/Utils/RCTConvert+RNSVG.m b/ios/Utils/RCTConvert+RNSVG.m index d205deea..d3554ed0 100644 --- a/ios/Utils/RCTConvert+RNSVG.m +++ b/ios/Utils/RCTConvert+RNSVG.m @@ -8,8 +8,7 @@ #import "RCTConvert+RNSVG.h" -#import "RNSVGBaseBrush.h" -#import "RNSVGPattern.h" +#import "RNSVGPainterBrush.h" #import "RNSVGSolidColorBrush.h" #import #import @@ -34,6 +33,11 @@ RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{ @"end": @(kRNSVGTextAnchorEnd) }), kRNSVGTextAnchorAuto, intValue) +RCT_ENUM_CONVERTER(RNSVGUnits, (@{ + @"objectBoundingBox": @(kRNSVGUnitsObjectBoundingBox), + @"userSpaceOnUse": @(kRNSVGUnitsUserSpaceOnUse), + }), kRNSVGUnitsObjectBoundingBox, intValue) + + (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json { NSArray *arr = [self NSNumberArray:json]; @@ -66,7 +70,7 @@ RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{ // We should memoize colors but look ups may be just as expensive. return [[RNSVGSolidColorBrush alloc] initWithArray:arr]; case 1: // brush - return [[RNSVGBaseBrush alloc] initWithArray:arr]; + return [[RNSVGPainterBrush alloc] initWithArray:arr]; default: RCTLogError(@"Unknown brush type: %zd", type); return nil; @@ -112,8 +116,8 @@ RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{ RNSVGCGFloatArray colorsAndOffsets = [self RNSVGCGFloatArray:arr]; size_t stops = colorsAndOffsets.count / 5; CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - - + + CGGradientRef gradient = CGGradientCreateWithColorComponents( rgb, colorsAndOffsets.array, diff --git a/ios/Utils/RNSVGPercentageConverter.m b/ios/Utils/RNSVGPercentageConverter.m index 73ad18f0..fe2cb778 100644 --- a/ios/Utils/RNSVGPercentageConverter.m +++ b/ios/Utils/RNSVGPercentageConverter.m @@ -20,7 +20,7 @@ static NSRegularExpression* percentageRegExp; + (CGFloat)stringToFloat:(NSString *)string relative:(CGFloat)relative offset:(CGFloat)offset { if (![self isPercentage:string]) { - return [string floatValue]; + return [string floatValue] + offset; } else { return [self percentageToFloat:string relative:relative offset:offset]; } diff --git a/ios/Utils/RNSVGUnits.h b/ios/Utils/RNSVGUnits.h new file mode 100644 index 00000000..0592bc52 --- /dev/null +++ b/ios/Utils/RNSVGUnits.h @@ -0,0 +1,12 @@ +/** + * 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. + */ + +typedef CF_ENUM(int32_t, RNSVGUnits) { + kRNSVGUnitsObjectBoundingBox, + kRNSVGUnitsUserSpaceOnUse +}; diff --git a/ios/ViewManagers/RNSVGLinearGradientManager.m b/ios/ViewManagers/RNSVGLinearGradientManager.m index 61840592..37f6ee93 100644 --- a/ios/ViewManagers/RNSVGLinearGradientManager.m +++ b/ios/ViewManagers/RNSVGLinearGradientManager.m @@ -23,5 +23,7 @@ RCT_EXPORT_VIEW_PROPERTY(y1, NSString) RCT_EXPORT_VIEW_PROPERTY(x2, NSString) RCT_EXPORT_VIEW_PROPERTY(y2, NSString) RCT_EXPORT_VIEW_PROPERTY(gradient, NSArray) +RCT_EXPORT_VIEW_PROPERTY(gradientUnits, RNSVGUnits) +RCT_EXPORT_VIEW_PROPERTY(gradientTransform, CGAffineTransform) @end diff --git a/ios/ViewManagers/RNSVGRadialGradientManager.m b/ios/ViewManagers/RNSVGRadialGradientManager.m index c316aa79..0aff4d2e 100644 --- a/ios/ViewManagers/RNSVGRadialGradientManager.m +++ b/ios/ViewManagers/RNSVGRadialGradientManager.m @@ -25,5 +25,7 @@ RCT_EXPORT_VIEW_PROPERTY(ry, NSString) RCT_EXPORT_VIEW_PROPERTY(cx, NSString) RCT_EXPORT_VIEW_PROPERTY(cy, NSString) RCT_EXPORT_VIEW_PROPERTY(gradient, NSArray) +RCT_EXPORT_VIEW_PROPERTY(gradientUnits, RNSVGUnits) +RCT_EXPORT_VIEW_PROPERTY(gradientTransform, CGAffineTransform) @end diff --git a/lib/PATTERN_UNITS.js b/lib/PATTERN_UNITS.js new file mode 100644 index 00000000..3b9c2e41 --- /dev/null +++ b/lib/PATTERN_UNITS.js @@ -0,0 +1,5 @@ + +export default { + objectBoundingBox: 0, + userSpaceOnUse: 1 +}; diff --git a/lib/attributes.js b/lib/attributes.js index 86695445..167c05d7 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -112,15 +112,22 @@ const ClipPathAttributes = { name: true }; +const GradientAttributes = merge({ + gradient: { + diff: arrayDiffer + }, + gradientUnits: true, + gradientTransform: { + diff: arrayDiffer + } +}, ClipPathAttributes); + const LinearGradientAttributes = merge({ x1: true, y1: true, x2: true, - y2: true, - gradient: { - diff: arrayDiffer - } -}, ClipPathAttributes); + y2: true +}, GradientAttributes); const RadialGradientAttributes = merge({ fx: true, @@ -129,11 +136,8 @@ const RadialGradientAttributes = merge({ ry: true, cx: true, cy: true, - r: true, - gradient: { - diff: arrayDiffer - } -}, ClipPathAttributes); + r: true +}, GradientAttributes); const CircleAttributes = merge({ diff --git a/lib/extract/extractGradient.js b/lib/extract/extractGradient.js new file mode 100644 index 00000000..9d82e398 --- /dev/null +++ b/lib/extract/extractGradient.js @@ -0,0 +1,61 @@ +import {Children, Component} from 'react'; +import _ from 'lodash'; +import Color from 'color'; + +import extractOpacity from './extractOpacity'; +import extractTransform from './extractTransform'; +import PATTERN_UNITS from '../PATTERN_UNITS'; +import percentToFloat from '../percentToFloat'; +import Stop from '../../elements/Stop'; + +export default function(props) { + if (!props.id) { + return null; + } + + const stops = {}; + Children.forEach(props.children, child => { + if (child.type === Stop) { + if (child.props.stopColor && child.props.offset) { + // convert percent to float. + let offset = percentToFloat(child.props.offset); + + // add stop + stops[offset] = Color(child.props.stopColor).alpha(extractOpacity(child.props.stopOpacity)); + } + } else { + console.warn('`Gradient` elements only accept `Stop` elements as children'); + } + }); + + const sorted = _.sortBy(_.map(stops, (stop, offset) => { + return {stop, offset}; + }), 'offset'); + + const gradient = []; + + sorted.forEach(({stop}) => { + let channels = stop.rgbaArray(); + gradient.push(channels[0] / 255); + gradient.push(channels[1] / 255); + gradient.push(channels[2] / 255); + gradient.push(channels[3]); + }); + + gradient.push(...sorted.map(({offset}) => +offset)); + + + let gradientTransform; + if (props.transform) { + gradientTransform = extractTransform(props.transform); + } else { + gradientTransform = extractTransform(props); + } + + return { + gradient, + name: props.id, + gradientTransform, + gradientUnits: PATTERN_UNITS[props.gradientUnits] || 0 + }; +}