Skip to content

Commit

Permalink
Use TextureView instead of Bitmap in SvgView on Android
Browse files Browse the repository at this point in the history
Mirror ReactNativeART facebook/react-native#9486
  • Loading branch information
msand committed Feb 23, 2018
1 parent 8ff052c commit f3ea54c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 31 deletions.
44 changes: 24 additions & 20 deletions android/src/main/java/com/horcrux/svg/SvgView.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
package com.horcrux.svg;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.util.Log;
import android.view.MotionEvent;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;

Expand All @@ -28,8 +28,6 @@
import com.facebook.react.uimanager.events.TouchEventType;
import com.facebook.react.views.view.ReactViewGroup;

import javax.annotation.Nullable;

/**
* Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
*/
Expand All @@ -53,7 +51,7 @@ public String toString() {
}
}

private @Nullable Bitmap mBitmap;
private TextureView mTextureView;
private final EventDispatcher mEventDispatcher;
private long mGestureStartTime = TouchEvent.UNSET;
private int mTargetTag;
Expand All @@ -63,29 +61,33 @@ public String toString() {

public SvgView(ReactContext reactContext) {
super(reactContext);
mTextureView = new TextureView(reactContext);
mTextureView.setOpaque(false);
addView(mTextureView);
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
}

@Override
public void setId(int id) {
super.setId(id);
SvgViewManager.setSvgView(this);
/**
* Sets the {@link TextureView.SurfaceTextureListener} used to listen to surface
* texture events.
*
* @see TextureView#getSurfaceTextureListener
* @see TextureView.SurfaceTextureListener
*/
public void setSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
mTextureView.setSurfaceTextureListener(listener);
}

public void setBitmap(Bitmap bitmap) {
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = bitmap;
invalidate();
@Override
public final void draw(Canvas canvas) {
super.draw(canvas);
mTextureView.draw(canvas);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
public void setId(int id) {
super.setId(id);
SvgViewManager.setSvgView(this);
}

private SvgViewShadowNode getShadowNode() {
Expand All @@ -109,7 +111,9 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
ReactShadowNodeImpl node = getShadowNode();
for (int i = 0; i < this.getChildCount(); i++) {
View child = this.getChildAt(i);
if (child instanceof ReactViewGroup) {
if (child instanceof TextureView) {
child.layout(l, t, r , b);
} else if (child instanceof ReactViewGroup) {
int id = child.getId();
for (int j = 0; j < node.getChildCount(); j++) {
ReactShadowNodeImpl nodeChild = node.getChildAt(j);
Expand Down
2 changes: 1 addition & 1 deletion android/src/main/java/com/horcrux/svg/SvgViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ protected SvgView createViewInstance(ThemedReactContext reactContext) {

@Override
public void updateExtraData(SvgView root, Object extraData) {
root.setBitmap((Bitmap) extraData);
root.setSurfaceTextureListener((SvgViewShadowNode) extraData);
}

}
83 changes: 74 additions & 9 deletions android/src/main/java/com/horcrux/svg/SvgViewShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,50 @@

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.graphics.Typeface;
import android.util.Base64;
import android.view.Surface;
import android.view.TextureView;

import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

/**
* Shadow node for RNSVG virtual tree root - RNSVGSvgView
*/
public class SvgViewShadowNode extends LayoutShadowNode {
public class SvgViewShadowNode extends LayoutShadowNode
implements TextureView.SurfaceTextureListener {

private @Nullable Surface mSurface;

private @Nullable Integer mBackgroundColor;

@ReactProp(name = ViewProps.BACKGROUND_COLOR, customType = "Color")
public void setBackgroundColor(Integer color) {
mBackgroundColor = color;
markUpdated();
}

private boolean mResponsible = false;

private final Map<String, VirtualNode> mDefinedClipPaths = new HashMap<>();
Expand Down Expand Up @@ -117,7 +139,8 @@ public boolean isVirtualAnchor() {
@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) {
super.onCollectExtraUpdates(uiUpdater);
uiUpdater.enqueueUpdateExtraData(getReactTag(), drawOutput());
drawOutput();
uiUpdater.enqueueUpdateExtraData(getReactTag(), this);
}

@Override
Expand All @@ -126,14 +149,37 @@ public void setReactTag(int reactTag) {
SvgViewManager.setShadowNode(this);
}

private Object drawOutput() {
Bitmap bitmap = Bitmap.createBitmap(
(int) getLayoutWidth(),
(int) getLayoutHeight(),
Bitmap.Config.ARGB_8888);
private void drawOutput() {
if (mSurface == null || !mSurface.isValid()) {
markChildrenUpdatesSeen(this);
return;
}

drawChildren(new Canvas(bitmap));
return bitmap;
try {
Canvas canvas = mSurface.lockCanvas(null);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
if (mBackgroundColor != null) {
canvas.drawColor(mBackgroundColor);
}

drawChildren(canvas);

if (mSurface == null) {
return;
}

mSurface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException | IllegalStateException e) {
FLog.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Surface.unlockCanvasAndPost");
}
}

private void markChildrenUpdatesSeen(ReactShadowNode shadowNode) {
for (int i = 0; i < shadowNode.getChildCount(); i++) {
ReactShadowNode child = shadowNode.getChildAt(i);
child.markUpdateSeen();
markChildrenUpdatesSeen(child);
}
}

Rect getCanvasBounds() {
Expand Down Expand Up @@ -272,4 +318,23 @@ void traverseChildren(VirtualNode.NodeRunnable runner) {
runner.run(child);
}
}

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
drawOutput();
}

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
surface.release();
mSurface = null;
return true;
}

@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
}
1 change: 0 additions & 1 deletion android/src/main/java/com/horcrux/svg/TSpanShadowNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Build;

Expand Down

0 comments on commit f3ea54c

Please sign in to comment.