Skip to content

Commit ceee5ff

Browse files
committed
feat(android): implement marker rendering
1 parent 589363d commit ceee5ff

File tree

5 files changed

+98
-7
lines changed

5 files changed

+98
-7
lines changed

android/src/main/java/com/horcrux/svg/MarkerView.java

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
package com.horcrux.svg;
1111

1212
import android.annotation.SuppressLint;
13+
import android.graphics.Canvas;
14+
import android.graphics.Matrix;
15+
import android.graphics.Paint;
1316
import android.graphics.RectF;
17+
import android.view.View;
1418

1519
import com.facebook.react.bridge.Dynamic;
1620
import com.facebook.react.bridge.ReactContext;
@@ -109,16 +113,52 @@ public void setMeetOrSlice(int meetOrSlice) {
109113
invalidate();
110114
}
111115

112-
113-
RectF getViewBox() {
114-
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
115-
}
116-
117116
@Override
118117
void saveDefinition() {
119118
if (mName != null) {
120119
SvgView svg = getSvgView();
121120
svg.defineMarker(this, mName);
121+
for (int i = 0; i < getChildCount(); i++) {
122+
View node = getChildAt(i);
123+
if (node instanceof VirtualView) {
124+
((VirtualView)node).saveDefinition();
125+
}
126+
}
122127
}
123128
}
129+
130+
void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
131+
int count = saveAndSetupCanvas(canvas, mCTM);
132+
133+
Point origin = position.origin;
134+
Matrix transform = new Matrix();
135+
transform.setTranslate((float)origin.x, (float)origin.y);
136+
137+
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
138+
transform.postRotate((float)(markerAngle == -1 ? position.angle : markerAngle));
139+
140+
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
141+
if (useStrokeWidth) {
142+
transform.postScale(strokeWidth, strokeWidth);
143+
}
144+
145+
canvas.concat(transform);
146+
147+
double width = relativeOnWidth(mMarkerWidth) / mScale;
148+
double height = relativeOnHeight(mMarkerHeight) / mScale;
149+
RectF eRect = new RectF(0, 0, (float)width, (float)height);
150+
if (mAlign != null) {
151+
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
152+
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
153+
canvas.concat(viewBoxMatrix);
154+
}
155+
156+
double x = relativeOnWidth(mRefX);
157+
double y = relativeOnHeight(mRefY);
158+
canvas.translate((float)-x, (float)-y);
159+
160+
drawGroup(canvas, paint, opacity);
161+
162+
restoreCanvas(canvas, count);
163+
}
124164
}

android/src/main/java/com/horcrux/svg/PathParser.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,25 @@
33
import android.graphics.Path;
44
import android.graphics.RectF;
55

6+
import java.util.ArrayList;
7+
8+
class PathElement {
9+
ElementType type;
10+
Point[] points;
11+
PathElement(ElementType type, Point[] points) {
12+
this.type = type;
13+
this.points = points;
14+
}
15+
}
16+
617
class PathParser {
718
static float mScale;
819

920
private static int i;
1021
private static int l;
1122
private static String s;
1223
private static Path mPath;
24+
static ArrayList<PathElement> elements;
1325

1426
private static float mPenX;
1527
private static float mPenY;
@@ -20,6 +32,7 @@ class PathParser {
2032
private static boolean mPenDown;
2133

2234
static Path parse(String d) {
35+
elements = new ArrayList<>();
2336
char prev_cmd = ' ';
2437
mPath = new Path();
2538
l = d.length();
@@ -189,6 +202,7 @@ private static void moveTo(float x, float y) {
189202
mPenDownX = mPivotX = mPenX = x;
190203
mPenDownY = mPivotY = mPenY = y;
191204
mPath.moveTo(x * mScale, y * mScale);
205+
elements.add(new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[]{new Point(x,y)}));
192206
}
193207

194208
private static void line(float x, float y) {
@@ -201,6 +215,7 @@ private static void lineTo(float x, float y) {
201215
mPivotX = mPenX = x;
202216
mPivotY = mPenY = y;
203217
mPath.lineTo(x * mScale, y * mScale);
218+
elements.add(new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[]{new Point(x,y)}));
204219
}
205220

206221
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
219234
mPenX = ex;
220235
mPenY = ey;
221236
mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
237+
elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
222238
}
223239

224240
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
364380
(cy + rx) * mScale);
365381

366382
mPath.arcTo(oval, start, sweep);
383+
elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(x, y)}));
367384
}
368385
}
369386

@@ -373,6 +390,7 @@ private static void close() {
373390
mPenY = mPenDownY;
374391
mPenDown = false;
375392
mPath.close();
393+
elements.add(new PathElement(ElementType.kCGPathElementCloseSubpath, new Point[]{new Point(mPenX, mPenY)}));
376394
}
377395
}
378396

@@ -420,6 +438,7 @@ private static void arcToBezier(float cx, float cy, float rx, float ry, float sa
420438
float ey = (cy + xy * x + yy * y);
421439

422440
mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
441+
elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
423442
}
424443
}
425444

android/src/main/java/com/horcrux/svg/PathView.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public PathView(ReactContext reactContext) {
2929
@ReactProp(name = "d")
3030
public void setD(String d) {
3131
mPath = PathParser.parse(d);
32+
elements = PathParser.elements;
3233
invalidate();
3334
}
3435

android/src/main/java/com/horcrux/svg/RenderableView.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import android.graphics.Bitmap;
1313
import android.graphics.Canvas;
1414
import android.graphics.DashPathEffect;
15-
import android.graphics.Matrix;
1615
import android.graphics.Paint;
1716
import android.graphics.Path;
1817
import android.graphics.PorterDuff;
@@ -370,9 +369,38 @@ void draw(Canvas canvas, Paint paint, float opacity) {
370369
}
371370
canvas.drawPath(path, paint);
372371
}
372+
renderMarkers(canvas, paint, opacity);
373373
}
374374
}
375375

376+
void renderMarkers(Canvas canvas, Paint paint, float opacity) {
377+
MarkerView markerStart = (MarkerView)getSvgView().getDefinedMarker(mMarkerStart);
378+
MarkerView markerMid = (MarkerView)getSvgView().getDefinedMarker(mMarkerMid);
379+
MarkerView markerEnd = (MarkerView)getSvgView().getDefinedMarker(mMarkerEnd);
380+
if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) {
381+
ArrayList<RNSVGMarkerPosition> positions = RNSVGMarkerPosition.fromPath(elements);
382+
float width = (float)(this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1);
383+
for (RNSVGMarkerPosition position : positions) {
384+
RNSVGMarkerType type = position.type;
385+
switch (type) {
386+
case kStartMarker:
387+
if (markerStart != null) markerStart.renderMarker(canvas, paint, opacity, position, width);
388+
break;
389+
390+
case kMidMarker:
391+
if (markerMid != null) markerMid.renderMarker(canvas, paint, opacity, position, width);
392+
break;
393+
394+
case kEndMarker:
395+
if (markerEnd != null) markerEnd.renderMarker(canvas, paint, opacity, position, width);
396+
break;
397+
398+
default:
399+
break;
400+
}
401+
}
402+
}
403+
}
376404
/**
377405
* Sets up paint according to the props set on a view. Returns {@code true}
378406
* if the fill should be drawn, {@code false} if not.

android/src/main/java/com/horcrux/svg/VirtualView.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import com.facebook.react.uimanager.events.EventDispatcher;
2323
import com.facebook.react.views.view.ReactViewGroup;
2424

25+
import java.util.ArrayList;
26+
2527
import javax.annotation.Nullable;
2628

2729
import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE;
@@ -94,6 +96,7 @@ abstract public class VirtualView extends ReactViewGroup {
9496
Region mStrokeRegion;
9597
Region mClipRegion;
9698
Path mClipRegionPath;
99+
ArrayList<PathElement> elements;
97100

98101
@Override
99102
public void invalidate() {
@@ -205,7 +208,7 @@ void render(Canvas canvas, Paint paint, float opacity) {
205208
* drawing code should apply opacity recursively.
206209
*
207210
* @param canvas the canvas to set up
208-
* @param ctm
211+
* @param ctm current transformation matrix
209212
*/
210213
int saveAndSetupCanvas(Canvas canvas, Matrix ctm) {
211214
int count = canvas.save();

0 commit comments

Comments
 (0)