diff --git a/android/src/main/java/com/horcrux/svg/RNSVGClipPathShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGClipPathShadowNode.java index 8401bd76..e9fd00f6 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGClipPathShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGClipPathShadowNode.java @@ -9,9 +9,6 @@ package com.horcrux.svg; -import android.graphics.Point; -import android.view.View; - /** * Shadow node for virtual RNSVGClipPath view */ @@ -21,9 +18,4 @@ public class RNSVGClipPathShadowNode extends RNSVGGroupShadowNode { protected void saveDefinition() { getSvgShadowNode().defineClipPath(this, mName); } - - @Override - public int hitTest(Point point, View view) { - return -1; - } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGDefinitionShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGDefinitionShadowNode.java index 42f6aae8..76cd756a 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGDefinitionShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGDefinitionShadowNode.java @@ -36,12 +36,7 @@ public class RNSVGDefinitionShadowNode extends RNSVGVirtualNode { } @Override - public int hitTest(Point point, View view, Matrix matrix) { - return -1; - } - - @Override - public int hitTest(Point point, View view) { + public int hitTest(Point point, Matrix matrix) { return -1; } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java index b367e462..195fab62 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java @@ -71,7 +71,7 @@ public class RNSVGGroupShadowNode extends RNSVGPathShadowNode { } @Override - public int hitTest(Point point, View view, @Nullable Matrix matrix) { + public int hitTest(Point point, @Nullable Matrix matrix) { int viewTag = -1; Matrix combinedMatrix = new Matrix(); @@ -89,21 +89,15 @@ public class RNSVGGroupShadowNode extends RNSVGPathShadowNode { RNSVGVirtualNode node = (RNSVGVirtualNode) child; - View childView = ((ViewGroup) view).getChildAt(i); - viewTag = node.hitTest(point, childView, combinedMatrix); + viewTag = node.hitTest(point, combinedMatrix); if (viewTag != -1) { - return (node.isResponsible() || viewTag != childView.getId()) ? viewTag : view.getId(); + return (node.isResponsible() || viewTag != child.getReactTag()) ? viewTag : getReactTag(); } } return viewTag; } - @Override - public int hitTest(Point point, View view) { - return this.hitTest(point, view, null); - } - protected void saveDefinition() { if (mName != null) { getSvgShadowNode().defineTemplate(this, mName); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java index f7c1c267..3b42d3b9 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java @@ -11,15 +11,14 @@ package com.horcrux.svg; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.net.Uri; +import android.util.Log; import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.logging.FLog; @@ -123,22 +122,15 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { } } - private void loadBitmap(@Nonnull final ImageRequest request, @Nonnull final Canvas canvas, @Nonnull final Paint paint) { + private void loadBitmap(ImageRequest request, final Canvas canvas, final Paint paint) { final DataSource> dataSource = Fresco.getImagePipeline().fetchDecodedImage(request, getThemedContext()); dataSource.subscribe(new BaseBitmapDataSubscriber() { @Override - public void onNewResultImpl(@Nullable Bitmap bitmap) { - if (bitmap != null) { - canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - paint.reset(); - mLoading.set(false); - - RNSVGSvgViewShadowNode svgShadowNode = getSvgShadowNode(); - svgShadowNode.drawChildren(canvas, paint); - svgShadowNode.invalidateView(); - } + public void onNewResultImpl(Bitmap bitmap) { + mLoading.set(false); + getSvgShadowNode().drawOutput(); } @Override @@ -163,7 +155,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { return new Rect((int) x, (int) y, (int) (x + w), (int) (y + h)); } - private void doRender(@Nonnull final Canvas canvas, @Nonnull final Paint paint, @Nonnull final Bitmap bitmap, final float opacity) { + private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) { final int count = saveAndSetupCanvas(canvas); canvas.concat(mMatrix); @@ -192,8 +184,8 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { viewBox.setMinY("0"); viewBox.setVbWidth(renderRect.width() / mScale + ""); viewBox.setVbHeight(renderRect.height() / mScale + ""); - viewBox.setWidth(rectWidth / mScale); - viewBox.setHeight(rectHeight / mScale); + viewBox.setWidth(rectWidth / mScale + ""); + viewBox.setHeight(rectHeight / mScale + ""); viewBox.setAlign(mAlign); viewBox.setMeetOrSlice(mMeetOrSlice); viewBox.setupDimensions(new Rect(0, 0, (int) rectWidth, (int) rectHeight)); @@ -233,7 +225,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { markUpdateSeen(); } - private void tryRender(@Nonnull final ImageRequest request, @Nonnull final Canvas canvas, @Nonnull final Paint paint, final float opacity) { + private void tryRender(ImageRequest request, Canvas canvas, Paint paint, float opacity) { final DataSource> dataSource = Fresco.getImagePipeline().fetchImageFromBitmapCache(request, getThemedContext()); @@ -248,10 +240,14 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { doRender(canvas, paint, bitmap, opacity); } } + } catch (Exception e) { + throw new IllegalStateException(e); } finally { CloseableReference.closeSafely(imageReference); } } + } catch (Exception e) { + throw new IllegalStateException(e); } finally { dataSource.close(); } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java index a9cd18c3..49d5e827 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java @@ -330,7 +330,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { } @Override - public int hitTest(Point point, View view, @Nullable Matrix matrix) { + public int hitTest(Point point, @Nullable Matrix matrix) { Bitmap bitmap = Bitmap.createBitmap( mCanvasWidth, mCanvasHeight, @@ -356,9 +356,10 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { canvas.setBitmap(bitmap); try { if (bitmap.getPixel(point.x, point.y) != 0) { - return view.getId(); + return getReactTag(); } } catch (Exception e) { + return -1; } finally { bitmap.recycle(); @@ -366,11 +367,6 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { return -1; } - @Override - public int hitTest(Point point, View view) { - return this.hitTest(point, view, null); - } - protected void setHitTestFill(Paint paint) { paint.reset(); paint.setARGB(255, 0, 0, 0); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGRenderableView.java b/android/src/main/java/com/horcrux/svg/RNSVGRenderableView.java deleted file mode 100644 index b91a7cb6..00000000 --- a/android/src/main/java/com/horcrux/svg/RNSVGRenderableView.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2015-present, Horcrux. - * All rights reserved. - * - * This source code is licensed under the MIT-style license found in the - * LICENSE file in the root directory of this source tree. - */ - - -package com.horcrux.svg; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; - -/** - * Custom {@link View} implementation that draws an RNSVGSvg React view and its \children. - */ -public class RNSVGRenderableView extends ViewGroup { - - public RNSVGRenderableView(Context context) { - super(context); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - - } -} diff --git a/android/src/main/java/com/horcrux/svg/RNSVGRenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RNSVGRenderableViewManager.java index 28343363..4b1e1ea5 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGRenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGRenderableViewManager.java @@ -9,17 +9,22 @@ package com.horcrux.svg; +import android.view.View; import android.view.ViewGroup; +import com.facebook.react.uimanager.BaseViewManager; +import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.ReactShadowNode; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManager; /** * ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered * into native views and don't need any logic (all the logic is in {@link RNSVGSvgView}), this * "stubbed" ViewManager is used for all of them. */ -public class RNSVGRenderableViewManager extends ViewGroupManager { +public class RNSVGRenderableViewManager extends ViewManager { /* package */ static final String CLASS_GROUP = "RNSVGGroup"; /* package */ static final String CLASS_PATH = "RNSVGPath"; @@ -38,7 +43,6 @@ public class RNSVGRenderableViewManager extends ViewGroupManager { private final String mClassName; - protected RNSVGVirtualNode mVirtualNode; public static RNSVGRenderableViewManager createRNSVGGroupViewManager() { return new RNSVGRenderableViewManager(CLASS_GROUP); @@ -106,60 +110,43 @@ public class RNSVGRenderableViewManager extends ViewGroupManager { } @Override - public RNSVGVirtualNode createShadowNodeInstance() { + public LayoutShadowNode createShadowNodeInstance() { switch (mClassName) { case CLASS_GROUP: - mVirtualNode = new RNSVGGroupShadowNode(); - break; + return new RNSVGGroupShadowNode(); case CLASS_PATH: - mVirtualNode = new RNSVGPathShadowNode(); - break; + return new RNSVGPathShadowNode(); case CLASS_CIRCLE: - mVirtualNode = new RNSVGCircleShadowNode(); - break; + return new RNSVGCircleShadowNode(); case CLASS_ELLIPSE: - mVirtualNode = new RNSVGEllipseShadowNode(); - break; + return new RNSVGEllipseShadowNode(); case CLASS_LINE: - mVirtualNode = new RNSVGLineShadowNode(); - break; + return new RNSVGLineShadowNode(); case CLASS_RECT: - mVirtualNode = new RNSVGRectShadowNode(); - break; + return new RNSVGRectShadowNode(); case CLASS_TEXT: - mVirtualNode = new RNSVGTextShadowNode(); - break; + return new RNSVGTextShadowNode(); case CLASS_IMAGE: - mVirtualNode = new RNSVGImageShadowNode(); - break; + return new RNSVGImageShadowNode(); case CLASS_CLIP_PATH: - mVirtualNode = new RNSVGClipPathShadowNode(); - break; + return new RNSVGClipPathShadowNode(); case CLASS_DEFS: - mVirtualNode = new RNSVGDefsShadowNode(); - break; + return new RNSVGDefsShadowNode(); case CLASS_USE: - mVirtualNode = new RNSVGUseShadowNode(); - break; + return new RNSVGUseShadowNode(); case CLASS_VIEW_BOX: - mVirtualNode = new RNSVGViewBoxShadowNode(); - break; + return new RNSVGViewBoxShadowNode(); case CLASS_LINEAR_GRADIENT: - mVirtualNode = new RNSVGLinearGradientShadowNode(); - break; + return new RNSVGLinearGradientShadowNode(); case CLASS_RADIAL_GRADIENT: - mVirtualNode = new RNSVGRadialGradientShadowNode(); - break; + return new RNSVGRadialGradientShadowNode(); default: throw new IllegalStateException("Unexpected type " + mClassName); } - - return mVirtualNode; - } @Override - public Class getShadowNodeClass() { + public Class getShadowNodeClass() { switch (mClassName) { case CLASS_GROUP: return RNSVGGroupShadowNode.class; @@ -195,7 +182,12 @@ public class RNSVGRenderableViewManager extends ViewGroupManager { } @Override - protected ViewGroup createViewInstance(ThemedReactContext reactContext) { - return new RNSVGRenderableView(reactContext); + protected View createViewInstance(ThemedReactContext reactContext) { + throw new IllegalStateException("SVG elements does not map into a native view"); + } + + @Override + public void updateExtraData(View root, Object extraData) { + throw new IllegalStateException("SVG elements does not map into a native view"); } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgView.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgView.java index 5f027a93..df972c6d 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgView.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgView.java @@ -19,13 +19,17 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.TextureView; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.common.SystemClock; +import com.facebook.react.touch.OnInterceptTouchEventListener; +import com.facebook.react.touch.ReactInterceptingViewGroup; +import com.facebook.react.uimanager.TouchTargetHelper; import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; @@ -35,8 +39,7 @@ import com.facebook.react.uimanager.events.EventDispatcher; /** * Custom {@link View} implementation that draws an RNSVGSvg React view and its \children. */ -public class RNSVGSvgView extends ViewGroup { - +public class RNSVGSvgView extends TextureView { public enum Events { EVENT_DATA_URL("onDataURL"); @@ -52,62 +55,36 @@ public class RNSVGSvgView extends ViewGroup { } } - private @Nullable Bitmap mBitmap; private RCTEventEmitter mEventEmitter; private EventDispatcher mEventDispatcher; - private RNSVGSvgViewShadowNode mSvgViewShadowNode; private int mTargetTag; private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper = new TouchEventCoalescingKeyHelper(); - public RNSVGSvgView(Context context) { - super(context); - } - public RNSVGSvgView(ReactContext reactContext) { super(reactContext); + setOpaque(false); mEventEmitter = reactContext.getJSModule(RCTEventEmitter.class); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); } - public void setBitmap(Bitmap bitmap) { - if (mBitmap != null) { - mBitmap.recycle(); - } - mBitmap = bitmap; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mBitmap != null) { - canvas.drawBitmap(mBitmap, 0, 0, null); - } + private RNSVGSvgViewShadowNode getShadowNode() { + return RNSVGSvgViewShadowNode.getShadowNodeByTag(getId()); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { - mTargetTag = mSvgViewShadowNode.hitTest(new Point((int) ev.getX(), (int) ev.getY()), this); + mTargetTag = getShadowNode().hitTest(new Point((int) ev.getX(), (int) ev.getY())); if (mTargetTag != -1) { handleTouchEvent(ev); + return true; } return super.dispatchTouchEvent(ev); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - - } - - public void setShadowNode(RNSVGSvgViewShadowNode shadowNode) { - mSvgViewShadowNode = shadowNode; - shadowNode.setSvgView(this); - } - private int getAbsoluteLeft(View view) { int left = view.getLeft() - view.getScrollX(); @@ -159,6 +136,7 @@ public class RNSVGSvgView extends ViewGroup { dispatch(ev, TouchEventType.END); mTargetTag = -1; } else if (action == MotionEvent.ACTION_MOVE) { + Log.e("asdasd", "asdasd"); // Update pointer position for current gesture dispatch(ev, TouchEventType.MOVE); } else if (action == MotionEvent.ACTION_POINTER_DOWN) { @@ -195,7 +173,7 @@ public class RNSVGSvgView extends ViewGroup { public void onDataURL() { WritableMap event = Arguments.createMap(); - event.putString("base64", mSvgViewShadowNode.getBase64()); + event.putString("base64", getShadowNode().getBase64()); mEventEmitter.receiveEvent(getId(), Events.EVENT_DATA_URL.toString(), event); } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java index 8c6673c9..4f270a16 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java @@ -10,10 +10,15 @@ package com.horcrux.svg; import android.graphics.Bitmap; +import android.util.Log; +import android.util.SparseArray; + import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.HashMap; @@ -25,14 +30,10 @@ import javax.annotation.Nullable; * ViewManager for RNSVGSvgView React views. Renders as a {@link RNSVGSvgView} and handles * invalidating the native view on shadow view updates happening in the underlying tree. */ -public class RNSVGSvgViewManager extends ViewGroupManager { +public class RNSVGSvgViewManager extends BaseViewManager { private static final String REACT_CLASS = "RNSVGSvgView"; - // TODO: use an ArrayList to connect RNSVGSvgViewShadowNode with RNSVGSvgView, not sure if there will be a race condition. - // TODO: find a better way to replace this - private ArrayList mSvgShadowNodes = new ArrayList<>(); - - public static final int COMMAND_TO_DATA_URL = 100; + private static final int COMMAND_TO_DATA_URL = 100; @Override public String getName() { @@ -71,28 +72,23 @@ public class RNSVGSvgViewManager extends ViewGroupManager { } } - - @Override - public RNSVGSvgViewShadowNode createShadowNodeInstance() { - RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode(); - mSvgShadowNodes.add(node); - return node; - } - @Override public Class getShadowNodeClass() { return RNSVGSvgViewShadowNode.class; } + @Override + public RNSVGSvgViewShadowNode createShadowNodeInstance() { + return new RNSVGSvgViewShadowNode(); + } + @Override protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) { - RNSVGSvgView svgView = new RNSVGSvgView(reactContext); - svgView.setShadowNode(mSvgShadowNodes.remove(0)); - return svgView; + return new RNSVGSvgView(reactContext); } @Override public void updateExtraData(RNSVGSvgView root, Object extraData) { - root.setBitmap((Bitmap) extraData); + root.setSurfaceTextureListener((RNSVGSvgViewShadowNode) extraData); } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java index 3f539104..48dfac77 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java @@ -14,12 +14,22 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.util.Base64; +import android.util.SparseArray; +import android.view.TextureView; import android.view.ViewGroup; +import android.graphics.Color; +import android.view.Surface; +import android.graphics.PorterDuff; +import android.graphics.SurfaceTexture; +import com.facebook.common.logging.FLog; import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.ReactShadowNode; import com.facebook.react.uimanager.UIViewOperationQueue; +import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; @@ -27,52 +37,60 @@ import java.util.Map; /** * Shadow node for RNSVG virtual tree root - RNSVGSvgView */ -public class RNSVGSvgViewShadowNode extends LayoutShadowNode { +public class RNSVGSvgViewShadowNode extends LayoutShadowNode implements TextureView.SurfaceTextureListener { + private static final SparseArray mTagToShadowNode = new SparseArray<>(); + + public static RNSVGSvgViewShadowNode getShadowNodeByTag(int tag) { + return mTagToShadowNode.get(tag); + } + + private @Nullable Surface mSurface; private boolean mResponsible = false; - private RNSVGSvgView mSvgView; private static final Map mDefinedClipPaths = new HashMap<>(); private static final Map mDefinedTemplates = new HashMap<>(); private static final Map mDefinedBrushes = new HashMap<>(); + @Override + public boolean isVirtual() { + return false; + } + + @Override + public boolean isVirtualAnchor() { + return true; + } + @Override public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) { super.onCollectExtraUpdates(uiUpdater); - uiUpdater.enqueueUpdateExtraData(getReactTag(), drawOutput()); + drawOutput(); + uiUpdater.enqueueUpdateExtraData(getReactTag(), this); } - private Object drawOutput() { - Bitmap bitmap = Bitmap.createBitmap( - (int) getLayoutWidth(), - (int) getLayoutHeight(), - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); + public void drawOutput() { + if (mSurface == null || !mSurface.isValid()) { + markChildrenUpdatesSeen(this); + return; + } + + try { + Canvas canvas = mSurface.lockCanvas(null); + drawChildren(canvas); + + if (mSurface != null) { + mSurface.unlockCanvasAndPost(canvas); + } + + } catch (IllegalArgumentException | IllegalStateException e) { + FLog.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Svg.unlockCanvasAndPost"); + } + } + + private void drawChildren(Canvas canvas) { + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); Paint paint = new Paint(); - drawChildren(canvas, paint); - return bitmap; - } - - public String getBase64() { - Bitmap bitmap = (Bitmap)drawOutput(); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - bitmap.recycle(); - byte[] bitmapBytes = stream.toByteArray(); - return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); - } - - /** - * Draw all of the child nodes of this root node - * - * This method is synchronized since - * {@link com.horcrux.svg.RNSVGImageShadowNode#loadBitmap(ImageRequest, Canvas, Paint)} calls it - * asynchronously after images have loaded and are ready to be drawn. - * - * @param canvas - * @param paint - */ - public synchronized void drawChildren(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { if (!(getChildAt(i) instanceof RNSVGVirtualNode)) { continue; @@ -89,13 +107,56 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { } } + private void markChildrenUpdatesSeen(ReactShadowNode shadowNode) { + for (int i = 0; i < shadowNode.getChildCount(); i++) { + ReactShadowNode child = shadowNode.getChildAt(i); + child.markUpdateSeen(); + markChildrenUpdatesSeen(child); + } + } + + public String getBase64() { + Bitmap bitmap = Bitmap.createBitmap( + (int) getLayoutWidth(), + (int) getLayoutHeight(), + Bitmap.Config.ARGB_8888); + + drawChildren(new Canvas(bitmap)); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + bitmap.recycle(); + byte[] bitmapBytes = stream.toByteArray(); + return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + mSurface = new Surface(surface); + mTagToShadowNode.put(getReactTag(), this); + drawOutput(); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + mTagToShadowNode.remove(getReactTag()); + surface.release(); + mSurface = null; + return true; + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} + public void enableTouchEvents() { if (!mResponsible) { mResponsible = true; } } - public int hitTest(Point point, ViewGroup view) { + public int hitTest(Point point) { if (!mResponsible) { return -1; } @@ -107,7 +168,7 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { continue; } - viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point, view.getChildAt(i)); + viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point); if (viewTag != -1) { break; } @@ -139,12 +200,4 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { public PropHelper.RNSVGBrush getDefinedBrush(String brushRef) { return mDefinedBrushes.get(brushRef); } - - public void setSvgView(RNSVGSvgView svgView) { - mSvgView = svgView; - } - - protected void invalidateView() { - mSvgView.invalidate(); - } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java index 55987d3c..e0d4cff6 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java @@ -194,7 +194,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { } @Override - public int hitTest(Point point, View view, @Nullable Matrix matrix) { + public int hitTest(Point point, @Nullable Matrix matrix) { Bitmap bitmap = Bitmap.createBitmap( mCanvasWidth, mCanvasHeight, @@ -225,7 +225,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { canvas.setBitmap(bitmap); try { if (bitmap.getPixel(point.x, point.y) != 0) { - return view.getId(); + return getReactTag(); } } catch (Exception e) { return -1; @@ -234,10 +234,4 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { } return -1; } - - - @Override - public int hitTest(Point point, View view) { - return this.hitTest(point, view, null); - } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java index 4bc28e83..eb487e4f 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java @@ -27,10 +27,6 @@ import com.facebook.react.uimanager.annotations.ReactProp; import javax.annotation.Nullable; -/** - * Base class for RNSVGView virtual nodes: {@link RNSVGGroupShadowNode}, {@link RNSVGPathShadowNode} and - * indirectly for {@link RNSVGTextShadowNode}. - */ public abstract class RNSVGVirtualNode extends LayoutShadowNode { protected static final float MIN_OPACITY_FOR_DRAW = 0.01f; @@ -42,6 +38,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { protected @Nullable Path mClipPath; protected @Nullable String mClipPathRef; + private static final int PATH_TYPE_CLOSE = 1; private static final int PATH_TYPE_CURVETO = 3; private static final int PATH_TYPE_LINETO = 2; @@ -49,6 +46,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { private static final int CLIP_RULE_EVENODD = 0; private static final int CLIP_RULE_NONZERO = 1; + protected final float mScale; private float[] mClipData; private int mClipRule; @@ -67,6 +65,11 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; } + @Override + public boolean isVirtual() { + return true; + } + public abstract void draw(Canvas canvas, Paint paint, float opacity); /** @@ -181,20 +184,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { mMatrix.setValues(sRawMatrix); } - /** - * Returns the floor modulus of the float arguments. Java modulus will return a negative remainder - * when the divisor is negative. Modulus should always be positive. This mimics the behavior of - * Math.floorMod, introduced in Java 8. - */ - private float modulus(float x, float y) { - float remainder = x % y; - float ret = remainder; - if (remainder < 0) { - ret += y; - } - return ret; - } - /** * Creates a {@link Path} from an array of instructions constructed by JS * (see RNSVGSerializablePath.js). Each instruction starts with a type (see PATH_TYPE_*) followed @@ -254,9 +243,11 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { } } - abstract public int hitTest(Point point, View view, @Nullable Matrix matrix); + abstract public int hitTest(Point point, @Nullable Matrix matrix); - abstract public int hitTest(Point point, View view); + public int hitTest(Point point) { + return this.hitTest(point, null); + } public boolean isResponsible() { return mResponsible;