diff --git a/android/src/main/java/com/horcrux/svg/MarkerView.java b/android/src/main/java/com/horcrux/svg/MarkerView.java index e8f723957..d988424a9 100644 --- a/android/src/main/java/com/horcrux/svg/MarkerView.java +++ b/android/src/main/java/com/horcrux/svg/MarkerView.java @@ -10,7 +10,11 @@ package com.horcrux.svg; import android.annotation.SuppressLint; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.RectF; +import android.view.View; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; @@ -109,16 +113,52 @@ public void setMeetOrSlice(int meetOrSlice) { invalidate(); } - - RectF getViewBox() { - return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); - } - @Override void saveDefinition() { if (mName != null) { SvgView svg = getSvgView(); svg.defineMarker(this, mName); + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + ((VirtualView)node).saveDefinition(); + } + } } } + + void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) { + int count = saveAndSetupCanvas(canvas, mCTM); + + Point origin = position.origin; + Matrix transform = new Matrix(); + transform.setTranslate((float)origin.x, (float)origin.y); + + double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient); + transform.postRotate((float)(markerAngle == -1 ? position.angle : markerAngle)); + + boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits); + if (useStrokeWidth) { + transform.postScale(strokeWidth, strokeWidth); + } + + canvas.concat(transform); + + double width = relativeOnWidth(mMarkerWidth) / mScale; + double height = relativeOnHeight(mMarkerHeight) / mScale; + RectF eRect = new RectF(0, 0, (float)width, (float)height); + if (mAlign != null) { + RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); + Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); + canvas.concat(viewBoxMatrix); + } + + double x = relativeOnWidth(mRefX); + double y = relativeOnHeight(mRefY); + canvas.translate((float)-x, (float)-y); + + drawGroup(canvas, paint, opacity); + + restoreCanvas(canvas, count); + } } diff --git a/android/src/main/java/com/horcrux/svg/PathParser.java b/android/src/main/java/com/horcrux/svg/PathParser.java index 18b3352cb..d1f393d74 100644 --- a/android/src/main/java/com/horcrux/svg/PathParser.java +++ b/android/src/main/java/com/horcrux/svg/PathParser.java @@ -3,6 +3,17 @@ import android.graphics.Path; import android.graphics.RectF; +import java.util.ArrayList; + +class PathElement { + ElementType type; + Point[] points; + PathElement(ElementType type, Point[] points) { + this.type = type; + this.points = points; + } +} + class PathParser { static float mScale; @@ -10,6 +21,7 @@ class PathParser { private static int l; private static String s; private static Path mPath; + static ArrayList elements; private static float mPenX; private static float mPenY; @@ -20,6 +32,7 @@ class PathParser { private static boolean mPenDown; static Path parse(String d) { + elements = new ArrayList<>(); char prev_cmd = ' '; mPath = new Path(); l = d.length(); @@ -189,6 +202,7 @@ private static void moveTo(float x, float y) { mPenDownX = mPivotX = mPenX = x; mPenDownY = mPivotY = mPenY = y; mPath.moveTo(x * mScale, y * mScale); + elements.add(new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[]{new Point(x,y)})); } private static void line(float x, float y) { @@ -201,6 +215,7 @@ private static void lineTo(float x, float y) { mPivotX = mPenX = x; mPivotY = mPenY = y; mPath.lineTo(x * mScale, y * mScale); + elements.add(new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[]{new Point(x,y)})); } private static void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { @@ -219,6 +234,7 @@ private static void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex mPenX = ex; mPenY = ey; mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); + elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); } private static void smoothCurve(float c1x, float c1y, float ex, float ey) { @@ -364,6 +380,7 @@ private static void arcTo(float rx, float ry, float rotation, boolean outer, boo (cy + rx) * mScale); mPath.arcTo(oval, start, sweep); + elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(x, y)})); } } @@ -373,6 +390,7 @@ private static void close() { mPenY = mPenDownY; mPenDown = false; mPath.close(); + elements.add(new PathElement(ElementType.kCGPathElementCloseSubpath, new Point[]{new Point(mPenX, mPenY)})); } } @@ -420,6 +438,7 @@ private static void arcToBezier(float cx, float cy, float rx, float ry, float sa float ey = (cy + xy * x + yy * y); mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); + elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); } } diff --git a/android/src/main/java/com/horcrux/svg/PathView.java b/android/src/main/java/com/horcrux/svg/PathView.java index 25cd3d209..5d3bab366 100644 --- a/android/src/main/java/com/horcrux/svg/PathView.java +++ b/android/src/main/java/com/horcrux/svg/PathView.java @@ -29,6 +29,7 @@ public PathView(ReactContext reactContext) { @ReactProp(name = "d") public void setD(String d) { mPath = PathParser.parse(d); + elements = PathParser.elements; invalidate(); } diff --git a/android/src/main/java/com/horcrux/svg/RenderableView.java b/android/src/main/java/com/horcrux/svg/RenderableView.java index 858ff7e65..6d63f912a 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableView.java +++ b/android/src/main/java/com/horcrux/svg/RenderableView.java @@ -12,7 +12,6 @@ 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.PorterDuff; @@ -370,9 +369,38 @@ void draw(Canvas canvas, Paint paint, float opacity) { } canvas.drawPath(path, paint); } + renderMarkers(canvas, paint, opacity); } } + void renderMarkers(Canvas canvas, Paint paint, float opacity) { + MarkerView markerStart = (MarkerView)getSvgView().getDefinedMarker(mMarkerStart); + MarkerView markerMid = (MarkerView)getSvgView().getDefinedMarker(mMarkerMid); + MarkerView markerEnd = (MarkerView)getSvgView().getDefinedMarker(mMarkerEnd); + if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) { + ArrayList positions = RNSVGMarkerPosition.fromPath(elements); + float width = (float)(this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1); + for (RNSVGMarkerPosition position : positions) { + RNSVGMarkerType type = position.type; + switch (type) { + case kStartMarker: + if (markerStart != null) markerStart.renderMarker(canvas, paint, opacity, position, width); + break; + + case kMidMarker: + if (markerMid != null) markerMid.renderMarker(canvas, paint, opacity, position, width); + break; + + case kEndMarker: + if (markerEnd != null) markerEnd.renderMarker(canvas, paint, opacity, position, width); + break; + + default: + break; + } + } + } + } /** * Sets up paint according to the props set on a view. Returns {@code true} * if the fill should be drawn, {@code false} if not. diff --git a/android/src/main/java/com/horcrux/svg/VirtualView.java b/android/src/main/java/com/horcrux/svg/VirtualView.java index 2589ee3df..d3e9359b0 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualView.java +++ b/android/src/main/java/com/horcrux/svg/VirtualView.java @@ -22,6 +22,8 @@ import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.view.ReactViewGroup; +import java.util.ArrayList; + import javax.annotation.Nullable; import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE; @@ -94,6 +96,7 @@ abstract public class VirtualView extends ReactViewGroup { Region mStrokeRegion; Region mClipRegion; Path mClipRegionPath; + ArrayList elements; @Override public void invalidate() { @@ -205,7 +208,7 @@ void render(Canvas canvas, Paint paint, float opacity) { * drawing code should apply opacity recursively. * * @param canvas the canvas to set up - * @param ctm + * @param ctm current transformation matrix */ int saveAndSetupCanvas(Canvas canvas, Matrix ctm) { int count = canvas.save();