diff --git a/android/src/main/java/com/horcrux/svg/ImageShadowNode.java b/android/src/main/java/com/horcrux/svg/ImageShadowNode.java index dd0a00a2..fbe7901e 100644 --- a/android/src/main/java/com/horcrux/svg/ImageShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/ImageShadowNode.java @@ -178,7 +178,7 @@ public class ImageShadowNode extends RenderableShadowNode { RectF vbRect = new RectF(0, 0, renderRect.width() / mScale, renderRect.height() / mScale); RectF eRect = new RectF(getCanvasLeft(), getCanvasTop(), rectWidth / mScale + getCanvasLeft(), rectHeight / mScale + getCanvasTop()); - Matrix transform = ViewBoxShadowNode.getTransform(vbRect, eRect, mAlign, mMeetOrSlice, false); + Matrix transform = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice, false); transform.mapRect(renderRect); Matrix translation = new Matrix(); diff --git a/android/src/main/java/com/horcrux/svg/PropHelper.java b/android/src/main/java/com/horcrux/svg/PropHelper.java index f30d702c..eeb0dd0d 100644 --- a/android/src/main/java/com/horcrux/svg/PropHelper.java +++ b/android/src/main/java/com/horcrux/svg/PropHelper.java @@ -176,8 +176,8 @@ class PropHelper { 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); + //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, diff --git a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java index 68291fa2..6a80ea0b 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java @@ -35,7 +35,7 @@ public class RenderableViewManager extends ViewManager { /* package */ static final String CLASS_CLIP_PATH = "RNSVGClipPath"; /* package */ static final String CLASS_DEFS = "RNSVGDefs"; /* package */ static final String CLASS_USE = "RNSVGUse"; - /* package */ static final String CLASS_VIEW_BOX = "RNSVGViewBox"; + /* package */ static final String CLASS_SYMBOL = "RNSVGSymbol"; /* package */ static final String CLASS_LINEAR_GRADIENT = "RNSVGLinearGradient"; /* package */ static final String CLASS_RADIAL_GRADIENT = "RNSVGRadialGradient"; @@ -94,8 +94,8 @@ public class RenderableViewManager extends ViewManager { return new RenderableViewManager(CLASS_USE); } - public static RenderableViewManager createViewBoxViewManager() { - return new RenderableViewManager(CLASS_VIEW_BOX); + public static RenderableViewManager createSymbolManager() { + return new RenderableViewManager(CLASS_SYMBOL); } public static RenderableViewManager createLinearGradientManager() { @@ -144,8 +144,8 @@ public class RenderableViewManager extends ViewManager { return new DefsShadowNode(); case CLASS_USE: return new UseShadowNode(); - case CLASS_VIEW_BOX: - return new ViewBoxShadowNode(); + case CLASS_SYMBOL: + return new SymbolShadowNode(); case CLASS_LINEAR_GRADIENT: return new LinearGradientShadowNode(); case CLASS_RADIAL_GRADIENT: @@ -184,8 +184,8 @@ public class RenderableViewManager extends ViewManager { return DefsShadowNode.class; case CLASS_USE: return UseShadowNode.class; - case CLASS_VIEW_BOX: - return ViewBoxShadowNode.class; + case CLASS_SYMBOL: + return SymbolShadowNode.class; case CLASS_LINEAR_GRADIENT: return LinearGradientShadowNode.class; case CLASS_RADIAL_GRADIENT: diff --git a/android/src/main/java/com/horcrux/svg/SvgPackage.java b/android/src/main/java/com/horcrux/svg/SvgPackage.java index fca7663e..87ca8908 100644 --- a/android/src/main/java/com/horcrux/svg/SvgPackage.java +++ b/android/src/main/java/com/horcrux/svg/SvgPackage.java @@ -38,7 +38,7 @@ public class SvgPackage implements ReactPackage { RenderableViewManager.createClipPathViewManager(), RenderableViewManager.createDefsViewManager(), RenderableViewManager.createUseViewManager(), - RenderableViewManager.createViewBoxViewManager(), + RenderableViewManager.createSymbolManager(), RenderableViewManager.createLinearGradientManager(), RenderableViewManager.createRadialGradientManager(), new SvgViewManager()); diff --git a/android/src/main/java/com/horcrux/svg/SvgView.java b/android/src/main/java/com/horcrux/svg/SvgView.java index 0583aae6..af817085 100644 --- a/android/src/main/java/com/horcrux/svg/SvgView.java +++ b/android/src/main/java/com/horcrux/svg/SvgView.java @@ -23,6 +23,7 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; diff --git a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java index 6b935a43..83644fea 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java @@ -11,15 +11,20 @@ package com.horcrux.svg; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.util.Base64; import android.graphics.Color; import android.graphics.PorterDuff; +import android.util.Log; +import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.UIViewOperationQueue; +import com.facebook.react.uimanager.annotations.ReactProp; import java.io.ByteArrayOutputStream; import java.util.HashMap; @@ -35,6 +40,54 @@ public class SvgViewShadowNode extends LayoutShadowNode { private final Map mDefinedTemplates = new HashMap<>(); private final Map mDefinedBrushes = new HashMap<>(); private Canvas mCanvas; + protected final float mScale; + + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + private String mAlign; + private int mMeetOrSlice; + + public SvgViewShadowNode() { + mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; + } + + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + markUpdated(); + } + + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + markUpdated(); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + markUpdated(); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + markUpdated(); + } + + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + markUpdated(); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + markUpdated(); + } @Override public boolean isVirtual() { @@ -66,7 +119,6 @@ public class SvgViewShadowNode extends LayoutShadowNode { mCanvas = new Canvas(bitmap); drawChildren(mCanvas); - mCanvas = null; return bitmap; } @@ -75,6 +127,14 @@ public class SvgViewShadowNode extends LayoutShadowNode { } private void drawChildren(Canvas canvas) { + + if (mAlign != null) { + RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); + RectF eRect = new RectF(0, 0, getLayoutWidth(), getLayoutHeight()); + Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice, false); + canvas.concat(viewBoxMatrix); + } + Paint paint = new Paint(); for (int i = 0; i < getChildCount(); i++) { diff --git a/android/src/main/java/com/horcrux/svg/SymbolShadowNode.java b/android/src/main/java/com/horcrux/svg/SymbolShadowNode.java new file mode 100644 index 00000000..e41e199c --- /dev/null +++ b/android/src/main/java/com/horcrux/svg/SymbolShadowNode.java @@ -0,0 +1,78 @@ +/** + * 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.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RectF; + +import com.facebook.react.uimanager.annotations.ReactProp; + +public class SymbolShadowNode extends GroupShadowNode { + + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + private String mAlign; + private int mMeetOrSlice; + + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + markUpdated(); + } + + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + markUpdated(); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + markUpdated(); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + markUpdated(); + } + + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + markUpdated(); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + markUpdated(); + } + + @Override + public void draw(Canvas canvas, Paint paint, float opacity) { + saveDefinition(); + } + + public void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) { + if (mAlign != null) { + RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); + RectF eRect = new RectF(0, 0, width, height); + Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice, false); + canvas.concat(viewBoxMatrix); + super.draw(canvas, paint, opacity); + } + } +} diff --git a/android/src/main/java/com/horcrux/svg/UseShadowNode.java b/android/src/main/java/com/horcrux/svg/UseShadowNode.java index 5f3775cf..cec8e88f 100644 --- a/android/src/main/java/com/horcrux/svg/UseShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/UseShadowNode.java @@ -60,7 +60,14 @@ public class UseShadowNode extends RenderableShadowNode { template.mergeProperties(this, mAttributeList, true); int count = template.saveAndSetupCanvas(canvas); clip(canvas, paint); - template.draw(canvas, paint, opacity * mOpacity); + + if (template instanceof SymbolShadowNode) { + SymbolShadowNode symbol = (SymbolShadowNode)template; + symbol.drawSymbol(canvas, paint, opacity, relativeOnWidth(mWidth), relativeOnHeight(mHeight)); + } else { + template.draw(canvas, paint, opacity * mOpacity); + } + template.restoreCanvas(canvas, count); template.resetProperties(); } else { diff --git a/android/src/main/java/com/horcrux/svg/ViewBoxShadowNode.java b/android/src/main/java/com/horcrux/svg/ViewBox.java similarity index 59% rename from android/src/main/java/com/horcrux/svg/ViewBoxShadowNode.java rename to android/src/main/java/com/horcrux/svg/ViewBox.java index f3cd83b4..508fc1ed 100644 --- a/android/src/main/java/com/horcrux/svg/ViewBoxShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/ViewBox.java @@ -9,103 +9,18 @@ package com.horcrux.svg; -import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.RectF; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.annotations.ReactProp; - /** * Shadow node for virtual ViewBox */ -public class ViewBoxShadowNode extends GroupShadowNode { +public class ViewBox extends GroupShadowNode { private static final int MOS_MEET = 0; private static final int MOS_SLICE = 1; private static final int MOS_NONE = 2; - private String mMinX; - private String mMinY; - private String mVbWidth; - private String mVbHeight; - private String mBoxWidth; - private String mBoxHeight; - private String mAlign; - private int mMeetOrSlice; - private boolean mFromSymbol = false; - - @ReactProp(name = "minX") - public void setMinX(String minX) { - mMinX = minX; - markUpdated(); - } - - @ReactProp(name = "minY") - public void setMinY(String minY) { - mMinY = minY; - markUpdated(); - } - - @ReactProp(name = "vbWidth") - public void setVbWidth(String vbWidth) { - mVbWidth = vbWidth; - markUpdated(); - } - - @ReactProp(name = "vbHeight") - public void setVbHeight(String vbHeight) { - mVbHeight = vbHeight; - markUpdated(); - } - - - @ReactProp(name = "width") - public void setWidth(String width) { - mBoxWidth = width; - markUpdated(); - } - - @ReactProp(name = "height") - public void setHeight(String height) { - mBoxHeight = height; - markUpdated(); - } - - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - markUpdated(); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - markUpdated(); - } - - @Override - protected int saveAndSetupCanvas(Canvas canvas) { - mMatrix = getTransformFromProps(); - return super.saveAndSetupCanvas(canvas); - } - - private Matrix getTransformFromProps() { - - float vbX = relativeOnWidth(mMinX); - float vbY = relativeOnHeight(mMinY); - float vbWidth = relativeOnWidth(mVbWidth); - float vbHeight = relativeOnHeight(mVbHeight); - - - float eX = getCanvasLeft(); - float eY = getCanvasTop(); - float eWidth = mBoxWidth != null ? relativeOnWidth(mBoxWidth) : getCanvasWidth(); - float eHeight = mBoxHeight != null ? relativeOnHeight(mBoxHeight) : getCanvasHeight(); - - return getTransform(new RectF(vbX, vbY, vbWidth + vbX, vbHeight + vbY), new RectF(eX, eY, eWidth + eX, eHeight + eY), mAlign, mMeetOrSlice, mFromSymbol); - } - static public Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice, boolean fromSymbol) { // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform @@ -188,19 +103,4 @@ public class ViewBoxShadowNode extends GroupShadowNode { transform.postScale(scaleX, scaleY); return transform; } - - @Override - public void mergeProperties(VirtualNode target, ReadableArray mergeList, boolean inherited) { - if (target instanceof UseShadowNode) { - mFromSymbol = true; - mBoxWidth = ((UseShadowNode)target).getWidth(); - mBoxHeight = ((UseShadowNode)target).getHeight(); - } - } - - @Override - public void resetProperties() { - mBoxWidth = mBoxHeight = null; - mFromSymbol = false; - } } diff --git a/android/src/main/java/com/horcrux/svg/VirtualNode.java b/android/src/main/java/com/horcrux/svg/VirtualNode.java index e10e21b6..e478db3f 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/VirtualNode.java @@ -206,12 +206,12 @@ public abstract class VirtualNode extends LayoutShadowNode { return mSvgShadowNode; } - protected float relativeOnWidth(String position) { - return PropHelper.fromPercentageToFloat(position, getCanvasWidth(), 0, mScale); + protected float relativeOnWidth(String length) { + return PropHelper.fromPercentageToFloat(length, getCanvasWidth(), 0, mScale); } - protected float relativeOnHeight(String position) { - return PropHelper.fromPercentageToFloat(position, getCanvasHeight(), 0, mScale); + protected float relativeOnHeight(String length) { + return PropHelper.fromPercentageToFloat(length, getCanvasHeight(), 0, mScale); } protected float getCanvasWidth() { diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m index 3c12fbaf..349fdd53 100644 --- a/ios/Elements/RNSVGUse.m +++ b/ios/Elements/RNSVGUse.m @@ -28,13 +28,12 @@ if (template) { [self beginTransparencyLayer:context]; [self clip:context]; + [template mergeProperties:self]; if ([template class] == [RNSVGSymbol class]) { - [template mergeProperties:self]; RNSVGSymbol *symbol = template; [symbol renderSymbolTo:context width:[self relativeOnWidth:self.width] height:[self relativeOnWidth:self.height]]; } else { - [template mergeProperties:self]; [template renderTo:context]; }