diff --git a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java index 991b1a5d..8f07eace 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java @@ -16,12 +16,10 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.net.Uri; -import android.util.Log; import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.logging.FLog; import com.facebook.common.references.CloseableReference; import com.facebook.datasource.DataSource; -import com.facebook.common.logging.FLog; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableBitmap; @@ -31,6 +29,9 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.concurrent.atomic.AtomicBoolean; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -44,7 +45,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { private String mW; private String mH; private Uri mUri; - private boolean mLoading; + private AtomicBoolean mLoading = new AtomicBoolean(false); @ReactProp(name = "x") public void setX(String x) { @@ -86,13 +87,14 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { @Override public void draw(final Canvas canvas, final Paint paint, final float opacity) { - final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(mUri).build(); - final boolean inMemoryCache = Fresco.getImagePipeline().isInBitmapMemoryCache(request); + if (!mLoading.get()) { + final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(mUri).build(); - if (inMemoryCache) { - tryRender(request, canvas, paint, opacity * mOpacity); - } else if (!mLoading) { - loadBitmap(request, canvas, paint); + if (Fresco.getImagePipeline().isInBitmapMemoryCache(request)) { + tryRender(request, canvas, paint, opacity * mOpacity); + } else { + loadBitmap(request, canvas, paint); + } } } @@ -106,8 +108,10 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { if (bitmap != null) { canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); paint.reset(); - mLoading = false; + mLoading.set(false); + getSvgShadowNode().drawChildren(canvas, paint); + getSvgShadowNode().invalidateView(getRect()); } } @@ -115,7 +119,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { public void onFailureImpl(DataSource dataSource) { // No cleanup required here. // TODO: more details about this failure - mLoading = false; + mLoading.set(false); FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!"); } }, @@ -123,18 +127,25 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { ); } - private void doRender(@Nonnull final Canvas canvas, @Nonnull final Paint paint, @Nonnull final Bitmap bitmap, final float opacity) { - final int count = saveAndSetupCanvas(canvas); - - clip(canvas, paint); + @Nonnull + private Rect getRect() { float x = PropHelper.fromPercentageToFloat(mX, mCanvasWidth, 0, mScale); float y = PropHelper.fromPercentageToFloat(mY, mCanvasHeight, 0, mScale); float w = PropHelper.fromPercentageToFloat(mW, mCanvasWidth, 0, mScale); float h = PropHelper.fromPercentageToFloat(mH, mCanvasHeight, 0, mScale); + + 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) { + final int count = saveAndSetupCanvas(canvas); + + clip(canvas, paint); + Paint alphaPaint = new Paint(); alphaPaint.setAlpha((int) (opacity * 255)); - canvas.drawBitmap(bitmap, null, new Rect((int) x, (int) y, (int) (x + w), (int) (y + h)), alphaPaint); + canvas.drawBitmap(bitmap, null, getRect(), alphaPaint); restoreCanvas(canvas, count); markUpdateSeen(); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java index 50dbaa16..9bd1126e 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java @@ -310,7 +310,9 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { mPath.computeBounds(box, true); } PropHelper.RNSVGBrush brush = getSvgShadowNode().getDefinedBrush(colors.getString(1)); - brush.setupPaint(paint, box, mScale, opacity); + if (brush != null) { + brush.setupPaint(paint, box, mScale, opacity); + } } else { // TODO: Support pattern. FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!"); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java index 19ec1be3..0089e790 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewManager.java @@ -10,13 +10,14 @@ package com.horcrux.svg; import android.graphics.Bitmap; -import android.util.Log; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import java.util.ArrayList; +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. @@ -24,6 +25,7 @@ import java.util.ArrayList; public class RNSVGSvgViewManager extends ViewGroupManager { private static final String REACT_CLASS = "RNSVGSvgView"; + private RNSVGSvgView svgView = null; // 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 @@ -34,9 +36,14 @@ public class RNSVGSvgViewManager extends ViewGroupManager { return REACT_CLASS; } + @Nullable + public RNSVGSvgView getSvgView() { + return this.svgView; + } + @Override public RNSVGSvgViewShadowNode createShadowNodeInstance() { - RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode(); + RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode(this); SvgShadowNodes.add(node); return node; } @@ -50,7 +57,8 @@ public class RNSVGSvgViewManager extends ViewGroupManager { protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) { RNSVGSvgViewShadowNode shadowNode = SvgShadowNodes.get(0); SvgShadowNodes.remove(0); - return new RNSVGSvgView(reactContext, shadowNode); + this.svgView = new RNSVGSvgView(reactContext, shadowNode); + return this.svgView; } @Override diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java index a7d15345..ac46c588 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java @@ -13,15 +13,19 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; -import android.util.Log; +import android.graphics.Rect; +import android.view.View; import android.view.ViewGroup; +import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.UIViewOperationQueue; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nonnull; + /** * Shadow node for RNSVG virtual tree root - RNSVGSvgView */ @@ -32,6 +36,13 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { private static final Map mDefinedTemplates = new HashMap<>(); private static final Map mDefinedBrushes = new HashMap<>(); + @Nonnull private final RNSVGSvgViewManager viewManager; + + public RNSVGSvgViewShadowNode(@Nonnull final RNSVGSvgViewManager viewManager) { + super(); + this.viewManager = viewManager; + } + @Override public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) { super.onCollectExtraUpdates(uiUpdater); @@ -54,7 +65,7 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { * Draw all of the child nodes of this root node * * This method is synchronized since - * {@link com.horcrux.svg.RNSVGImageShadowNode#loadImage(ImageRequest, Canvas, Paint)} calls it + * {@link com.horcrux.svg.RNSVGImageShadowNode#loadBitmap(ImageRequest, Canvas, Paint)} calls it * asynchronously after images have loaded and are ready to be drawn. * * @param canvas @@ -77,6 +88,16 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { } } + protected void invalidateView(@Nonnull final Rect dirtyRect) { + final RNSVGSvgView svgView = this.viewManager.getSvgView(); + if (svgView != null) { + final View rootView = svgView.getRootView(); + if (rootView != null) { + rootView.invalidate(dirtyRect); + } + } + } + public void enableTouchEvents() { if (!mResponsible) { mResponsible = true; diff --git a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java index e3acd2a1..d07ee609 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java @@ -9,8 +9,6 @@ package com.horcrux.svg; -import javax.annotation.Nullable; - import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; @@ -18,16 +16,16 @@ import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; -import android.util.Log; import android.view.View; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.ReactShadowNode; +import com.facebook.react.uimanager.annotations.ReactProp; +import javax.annotation.Nullable; /** * Base class for RNSVGView virtual nodes: {@link RNSVGGroupShadowNode}, {@link RNSVGPathShadowNode} and @@ -81,8 +79,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { * @param canvas the canvas to set up */ protected final int saveAndSetupCanvas(Canvas canvas) { - int count = canvas.getSaveCount(); - canvas.save(); + final int count = canvas.save(); if (mMatrix != null) { canvas.concat(mMatrix); }