From 404bdf37829238df137cf6ce208066dbd84adf5c Mon Sep 17 00:00:00 2001 From: Horcrux Date: Sat, 21 May 2016 16:21:54 +0800 Subject: [PATCH] fix clip bug --- Example/examples/Clipping.js | 8 ++++ Example/main.js | 14 +++---- .../horcrux/svg/RNSVGEllipseShadowNode.java | 2 - .../com/horcrux/svg/RNSVGGroupShadowNode.java | 15 +++---- .../com/horcrux/svg/RNSVGImageShadowNode.java | 29 ++++++++----- .../com/horcrux/svg/RNSVGLineShadowNode.java | 1 - .../com/horcrux/svg/RNSVGPathShadowNode.java | 33 ++++++++------- .../com/horcrux/svg/RNSVGRectShadowNode.java | 1 - .../horcrux/svg/RNSVGSvgViewShadowNode.java | 42 +++++++++++++------ .../com/horcrux/svg/RNSVGTextShadowNode.java | 14 +++---- .../com/horcrux/svg/RNSVGVirtualNode.java | 33 +++++++++------ 11 files changed, 114 insertions(+), 78 deletions(-) diff --git a/Example/examples/Clipping.js b/Example/examples/Clipping.js index 6edf1184..ee98d6e5 100644 --- a/Example/examples/Clipping.js +++ b/Example/examples/Clipping.js @@ -49,6 +49,14 @@ class ClipPathAttr extends Component{ height="100" fill="url(#grad)" clipPath="M50,5L20,99L95,39L5,39L80,99z" + onStartShouldSetResponder={() => alert("1111111")} + /> + ; } diff --git a/Example/main.js b/Example/main.js index 7716bdc8..0fbf6e85 100644 --- a/Example/main.js +++ b/Example/main.js @@ -1,8 +1,10 @@ /** - * Sample React Native App - * https://github.com/facebook/react-native + * Sample React Native App for react-native-svg library + * https://github.com/magicismight/react-native-svg/tree/master/Example */ 'use strict'; +import React, {Component} from 'react'; + import { AppRegistry, StyleSheet, @@ -15,8 +17,6 @@ import { Easing } from 'react-native'; -import React, {Component} from 'react'; - import { Svg, Circle, @@ -108,7 +108,7 @@ const styles = StyleSheet.create({ const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image']; -class ArtSvgExample extends Component { +class SvgExample extends Component { constructor() { super(...arguments); this.state = { @@ -239,7 +239,7 @@ class ArtSvgExample extends Component { - SVG by ART! + SVG library for React Native ArtSvgExample); +AppRegistry.registerComponent('ArtSvgExample', () => SvgExample); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGEllipseShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGEllipseShadowNode.java index 906e2e90..aa51185e 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGEllipseShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGEllipseShadowNode.java @@ -63,8 +63,6 @@ public class RNSVGEllipseShadowNode extends RNSVGPathShadowNode { @Override protected Path getPath(Canvas canvas, Paint paint) { Path path = new Path(); - - // draw ellipse float cx = PropHelper.fromPercentageToFloat(mCx, mWidth, 0, mScale); float cy = PropHelper.fromPercentageToFloat(mCy, mHeight, 0, mScale); float rx = PropHelper.fromPercentageToFloat(mRx, mWidth, 0, mScale); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java index 189652e3..facda1ec 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGGroupShadowNode.java @@ -36,25 +36,26 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode { public void draw(Canvas canvas, Paint paint, float opacity) { opacity *= mOpacity; if (opacity > MIN_OPACITY_FOR_DRAW) { - saveAndSetupCanvas(canvas); + int count = saveAndSetupCanvas(canvas); clip(canvas, paint); RNSVGSvgViewShadowNode svg = getSvgShadowNode(); if (mAsClipPath == null) { for (int i = 0; i < getChildCount(); i++) { RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i); - child.setDimensions(mWidth, mHeight); + child.setupDimensions(canvas); child.draw(canvas, paint, opacity); //child.markUpdateSeen(); - if (child.isTouchable() && !svg.touchable) { - svg.touchable = true; + if (child.isTouchable()) { + svg.enableTouchEvents(); } } } else { defineClipPath(getPath(canvas, paint), mAsClipPath); } - restoreCanvas(canvas); + + restoreCanvas(canvas, count); } } @@ -64,6 +65,7 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode { for (int i = 0; i < getChildCount(); i++) { RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i); + child.setupDimensions(canvas); path.addPath(child.getPath(canvas, paint)); } return path; @@ -71,8 +73,7 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode { @Override public int hitTest(Point point, View view) { - // TODO: run hit test only if necessary - // TODO: ClipPath never run hitTest + if (mAsClipPath != null) { return -1; } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java index 40b4d938..195afa94 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGImageShadowNode.java @@ -16,6 +16,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; @@ -24,8 +25,10 @@ import android.os.AsyncTask; import android.provider.MediaStore; import android.util.Log; +import com.facebook.common.logging.FLog; import com.facebook.common.util.UriUtil; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; import java.net.URL; @@ -40,10 +43,11 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { private String mY; private String mW; private String mH; - private ReadableMap mSrc; private Uri mUri; private Bitmap mBitmap; private boolean mLocalImage; + private boolean mLoading; + private class BitmapWorkerTask extends AsyncTask { private final Canvas mCanvas; private final Paint mPaint; @@ -53,7 +57,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { mCanvas = canvas; mPaint = paint; mSvgShadowNode = node; - mSvgShadowNode.mBitmapCount++; + mSvgShadowNode.increaseCounter(); } // Decode image in background. @@ -70,6 +74,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream()); } } catch (Exception e) { + FLog.w(ReactConstants.TAG, "RNSVG: load Image load failed!:" + e.getMessage()); } return bitmap; @@ -80,10 +85,10 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { protected void onPostExecute(@Nullable Bitmap bitmap) { if (bitmap != null) { mBitmap = bitmap; + mSvgShadowNode.decreaseCounter(); mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mPaint.reset(); - mSvgShadowNode.mBitmapCount--; - mSvgShadowNode.drawChildren(mCanvas, mPaint, mWidth, mHeight); + mSvgShadowNode.drawChildren(mCanvas, mPaint); } } } @@ -114,8 +119,6 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { @ReactProp(name = "src") public void setSrc(@Nullable ReadableMap src) { - mSrc = src; - if (src != null) { String uri = src.getString("uri"); if (uri != null) { @@ -143,24 +146,28 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode { public void draw(Canvas canvas, Paint paint, float opacity) { RNSVGSvgViewShadowNode node = getSvgShadowNode(); if (mBitmap != null) { - // TODO: fixme - // clip(canvas, paint); + int count = saveAndSetupCanvas(canvas); + + clip(canvas, paint); float x = PropHelper.fromPercentageToFloat(mX, mWidth, 0, mScale); float y = PropHelper.fromPercentageToFloat(mY, mHeight, 0, mScale); float w = PropHelper.fromPercentageToFloat(mW, mWidth, 0, mScale); float h = PropHelper.fromPercentageToFloat(mH, mHeight, 0, mScale); canvas.drawBitmap(mBitmap, null, new Rect((int) x, (int) y, (int) (x + w), (int)(y + h)), null); - if (node.mBitmapCount == 0) { + restoreCanvas(canvas, count); + markUpdateSeen(); + + if (node.isCounterEmpty()) { mBitmap.recycle(); } - } else { + } else if (!mLoading) { + mLoading = true; loadBitmap(getResourceDrawableId(getThemedContext(), null), canvas, paint, node); } } public void loadBitmap(int resId, Canvas canvas, Paint paint, RNSVGSvgViewShadowNode node) { - BitmapWorkerTask task = new BitmapWorkerTask(canvas, paint, node); task.execute(resId); } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGLineShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGLineShadowNode.java index da16ae9b..c0b5ebe7 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGLineShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGLineShadowNode.java @@ -68,7 +68,6 @@ public class RNSVGLineShadowNode extends RNSVGPathShadowNode { @Override protected Path getPath(Canvas canvas, Paint paint) { Path path = new Path(); - float x1 = PropHelper.fromPercentageToFloat(mX1, mWidth, 0, mScale); float y1 = PropHelper.fromPercentageToFloat(mY1, mHeight, 0, mScale); float x2 = PropHelper.fromPercentageToFloat(mX2, mWidth, 0, mScale); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java index a13ebfa8..da9a5a3a 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGPathShadowNode.java @@ -61,14 +61,12 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { private Path.FillType mFillRule = Path.FillType.WINDING; private boolean mFillRuleSet; protected Path mPath; - private boolean mPathSet; private float[] mD; @ReactProp(name = "d") public void setPath(@Nullable ReadableArray shapePath) { mD = PropHelper.toFloatArray(shapePath); - mPathSet = true; setupPath(); markUpdated(); } @@ -169,8 +167,9 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { @Override public void draw(Canvas canvas, Paint paint, float opacity) { opacity *= mOpacity; + if (opacity > MIN_OPACITY_FOR_DRAW) { - saveAndSetupCanvas(canvas); + int count = saveAndSetupCanvas(canvas); if (mPath == null) { throw new JSApplicationIllegalArgumentException( "Paths should have a valid path (d) prop"); @@ -185,17 +184,17 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { canvas.drawPath(mPath, paint); } - restoreCanvas(canvas); + restoreCanvas(canvas, count); } markUpdateSeen(); } private void setupPath() { // init path after both fillRule and path have been set - if (mFillRuleSet && mPathSet) { - mPath = getPath(null, null); - RectF box = new RectF(); - mPath.computeBounds(box, true); + if (mFillRuleSet && mD != null) { + mPath = new Path(); + mPath.setFillType(mFillRule); + super.createPath(mD, mPath); } } @@ -327,28 +326,30 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode { } - protected Path getPath(@Nullable Canvas canvas, @Nullable Paint paint) { - Path path = new Path(); - path.setFillType(mFillRule); - super.createPath(mD, path); - return path; + @Override + protected Path getPath(Canvas canvas, Paint paint) { + return mPath; } @Override public int hitTest(Point point, View view) { Bitmap bitmap = Bitmap.createBitmap( - (int) mWidth, - (int) mHeight, + mWidth, + mHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - canvas.concat(mMatrix); + if (mMatrix != null) { + canvas.concat(mMatrix); + } Paint paint = new Paint(); clip(canvas, paint); setHitTestFill(paint); canvas.drawPath(mPath, paint); + + if (setHitTestStroke(paint)) { canvas.drawPath(mPath, paint); } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGRectShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGRectShadowNode.java index c1ed87a4..7d4875cd 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGRectShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGRectShadowNode.java @@ -87,7 +87,6 @@ public class RNSVGRectShadowNode extends RNSVGPathShadowNode { @Override protected Path getPath(Canvas canvas, Paint paint) { Path path = new Path(); - float x = PropHelper.fromPercentageToFloat(mX, mWidth, 0, mScale); float y = PropHelper.fromPercentageToFloat(mY, mHeight, 0, mScale); float w = PropHelper.fromPercentageToFloat(mW, mWidth, 0, mScale); diff --git a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java index 23cf2ed3..b2214303 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGSvgViewShadowNode.java @@ -14,7 +14,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; -import android.graphics.PorterDuff; import android.util.Log; import android.view.ViewGroup; @@ -25,9 +24,10 @@ import com.facebook.react.uimanager.UIViewOperationQueue; * Shadow node for RNSVG virtual tree root - RNSVGSvgView */ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { - public boolean touchable = false; - public int mBitmapCount = 0; + private int mCounter = 0; + + private boolean mTouchable = false; @Override public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) { @@ -36,35 +36,39 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { } private Object drawOutput() { - float width = (int) getLayoutWidth(); - float height = (int) getLayoutHeight(); Bitmap bitmap = Bitmap.createBitmap( - (int) width, - (int) height, + (int) getLayoutWidth(), + (int) getLayoutHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); - drawChildren(canvas, paint, width, height); + drawChildren(canvas, paint); return bitmap; } - public void drawChildren(Canvas canvas, Paint paint, float width, float height) { + public void drawChildren(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i); - child.setDimensions(width, height); + child.setupDimensions(canvas); child.draw(canvas, paint, 1f); //child.markUpdateSeen(); - if (child.isTouchable() && !touchable) { - touchable = true; + if (child.isTouchable() && !mTouchable) { + mTouchable = true; } } } + public void enableTouchEvents() { + if (!mTouchable) { + mTouchable = true; + } + } + public int hitTest(Point point, ViewGroup view) { - if (!touchable) { + if (!mTouchable) { return -1; } @@ -79,4 +83,16 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode { return viewTag; } + + public void increaseCounter() { + mCounter++; + } + + public void decreaseCounter() { + mCounter--; + } + + public boolean isCounterEmpty() { + return mCounter == 0; + } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java b/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java index 5c591639..f2820dfb 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGTextShadowNode.java @@ -83,7 +83,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { } // only set up the canvas if we have something to draw - saveAndSetupCanvas(canvas); + int count = saveAndSetupCanvas(canvas); clip(canvas, paint); RectF box = getBox(paint, text); @@ -94,7 +94,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { drawText(canvas, paint, text); } - restoreCanvas(canvas); + restoreCanvas(canvas, count); markUpdateSeen(); } @@ -197,14 +197,14 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode { @Override public int hitTest(Point point, View view) { Bitmap bitmap = Bitmap.createBitmap( - (int) mWidth, - (int) mHeight, + mWidth, + mHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - canvas.concat(mMatrix); - - // todo clip detect + if (mMatrix != null) { + canvas.concat(mMatrix); + } String text = formatText(); if (text == null) { diff --git a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java index 14b5ac09..7a12e24c 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java @@ -39,7 +39,7 @@ import java.util.Map; * indirectly for {@link RNSVGTextShadowNode}. */ public abstract class RNSVGVirtualNode extends LayoutShadowNode { - protected static Map CLIP_PATHS = new HashMap<>(); + private static final Map CLIP_PATHS = new HashMap<>(); protected static final float MIN_OPACITY_FOR_DRAW = 0.01f; @@ -64,10 +64,9 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { private int mClipRule; private boolean mClipRuleSet; private boolean mClipDataSet; - - protected float mWidth = 0; - protected float mHeight = 0; protected boolean mTouchable; + protected int mWidth; + protected int mHeight; public RNSVGVirtualNode() { mScale = DisplayMetricsHolder.getWindowDisplayMetrics().density; @@ -84,11 +83,14 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { * * @param canvas the canvas to set up */ - protected final void saveAndSetupCanvas(Canvas canvas) { + protected final int saveAndSetupCanvas(Canvas canvas) { + int count = canvas.getSaveCount(); canvas.save(); if (mMatrix != null) { canvas.concat(mMatrix); } + + return count; } /** @@ -97,8 +99,8 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { * * @param canvas the canvas to restore */ - protected void restoreCanvas(Canvas canvas) { - canvas.restore(); + protected void restoreCanvas(Canvas canvas, int count) { + canvas.restoreToCount(count); } @ReactProp(name = "clipPath") @@ -274,11 +276,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { } } - public void setDimensions(float width, float height) { - mWidth = width; - mHeight = height; - } - abstract public int hitTest(Point point, View view); protected void defineClipPath(Path clipPath, String clipPathId) { @@ -296,11 +293,21 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode { ReactShadowNode parent = getParent(); while (!(parent instanceof RNSVGSvgViewShadowNode)) { - parent = parent.getParent(); + if (parent == null) { + return null; + } else { + parent = parent.getParent(); + } } return (RNSVGSvgViewShadowNode)parent; } + protected void setupDimensions(Canvas canvas) { + mWidth = canvas.getWidth(); + mHeight = canvas.getHeight(); + } + + protected void finalize() { if (mDefinedClipPathId != null) { CLIP_PATHS.remove(mDefinedClipPathId);