Support rounded clipping

Summary:
Support rounded clipping in Nodes. Before, if a view had
a radius and had overflow of hidden, its children could still draw
outside of it (specifically, in the area between the rounded rect
and square rect) - this is due to the fact that clipping is, by
default, rectangular. This patch supports this type of rounded
clipping.

Differential Revision: D3634861
This commit is contained in:
Ahmed El-Helw
2016-07-28 14:45:22 -07:00
parent 498fc63952
commit 2d8cbd70bc
4 changed files with 104 additions and 6 deletions

View File

@@ -9,9 +9,16 @@
package com.facebook.react.flat;
import javax.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
/* package */ final class DrawView extends AbstractDrawCommand {
// the minimum rounded clipping value before we actually do rounded clipping
/* package */ static final float MINIMUM_ROUNDED_CLIPPING_VALUE = 0.5f;
private final RectF TMP_RECT = new RectF();
/* package */ final int reactTag;
// Indicates whether this DrawView has been previously mounted to a clipping FlatViewGroup. This
@@ -22,6 +29,13 @@ import android.graphics.Canvas;
// quickest way to create unreproducible super bugs.
/* package */ boolean mWasMounted;
// the clipping radius - if this is greater than MINIMUM_ROUNDED_CLIPPING_VALUE, we clip using
// a rounded path, otherwise we clip in a rectangular fashion.
private float mClipRadius;
// the path to clip against if we're doing path clipping for rounded borders.
@Nullable private Path mPath;
public DrawView(int reactTag) {
this.reactTag = reactTag;
}
@@ -41,10 +55,39 @@ import android.graphics.Canvas;
float clipLeft,
float clipTop,
float clipRight,
float clipBottom) {
float clipBottom,
float clipRadius) {
DrawView drawView = (DrawView)
updateBoundsAndFreeze(left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom);
updateBoundsAndFreeze(
left,
top,
right,
bottom,
clipLeft,
clipTop,
clipRight,
clipBottom);
boolean clipRadiusChanged = Math.abs(mClipRadius - clipRadius) > 0.001f;
if (clipRadiusChanged && drawView == this) {
// everything matches except the clip radius, so we clone the old one so that we can update
// the clip radius in the block below.
try {
drawView = (DrawView) clone();
} catch (CloneNotSupportedException e) {
// This should not happen since AbstractDrawCommand implements Cloneable
throw new RuntimeException(e);
}
}
if (drawView != this) {
drawView.mClipRadius = clipRadius;
if (clipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
// update the path that we'll clip based on
updateClipPath(drawView);
} else {
drawView.mPath = null;
}
// It is very important that we unset this, as our spec is that newly created DrawViews are
// handled differently by the FlatViewGroup. This is needed because updateBoundsAndFreeze
// uses .clone(), so we maintain the previous state.
@@ -56,7 +99,7 @@ import android.graphics.Canvas;
@Override
public void draw(FlatViewGroup parent, Canvas canvas) {
onPreDraw(parent, canvas);
if (mNeedsClipping) {
if (mNeedsClipping || mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
canvas.save(Canvas.CLIP_SAVE_FLAG);
applyClipping(canvas);
parent.drawNextChild(canvas);
@@ -66,6 +109,43 @@ import android.graphics.Canvas;
}
}
/**
* Update the path with which we'll clip this view
* @param drawView the drawView to update the path for
*/
private void updateClipPath(DrawView drawView) {
// make or reset the path
if (drawView.mPath == null) {
drawView.mPath = new Path();
} else {
drawView.mPath.reset();
}
TMP_RECT.set(
getLeft(),
getTop(),
getRight(),
getBottom());
// set the path
drawView.mPath.addRoundRect(
TMP_RECT,
drawView.mClipRadius,
drawView.mClipRadius,
Path.Direction.CW);
}
@Override
protected void applyClipping(Canvas canvas) {
// only clip using a path if our radius is greater than some minimum threshold, because
// clipPath is more expensive than clipRect.
if (mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
canvas.clipPath(mPath);
} else {
super.applyClipping(canvas);
}
}
@Override
protected void onDraw(Canvas canvas) {
// no op as we override draw.