diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index a9961cb2f4d236..74ecedbba968fe 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -144,4 +144,10 @@ public class ReactFeatureFlags { /** Temporary flag to allow execution of mount items up to 15ms earlier than normal. */ public static boolean enableEarlyScheduledMountItemExecution = false; + + /** + * Use a bitmap mask instead of clipPath for rounding corners so that they are antialiased in + * Android + */ + public static boolean antiAliasRoundedOverflowCorners = false; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index 93afe79dc7f519..f3867ec2badce6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -14,7 +14,10 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -129,6 +132,7 @@ public void onLayoutChange( private boolean mNeedsOffscreenAlphaCompositing; private @Nullable ViewGroupDrawingOrderHelper mDrawingOrderHelper; private @Nullable Path mPath; + private @Nullable Paint mPaint; private int mLayoutDirection; private float mBackfaceOpacity; private String mBackfaceVisibility; @@ -159,6 +163,7 @@ private void initView() { mNeedsOffscreenAlphaCompositing = false; mDrawingOrderHelper = null; mPath = null; + mPaint = null; mLayoutDirection = 0; // set when background is created mBackfaceOpacity = 1.f; mBackfaceVisibility = "visible"; @@ -806,8 +811,16 @@ public Rect getOverflowInset() { @Override protected void dispatchDraw(Canvas canvas) { try { - dispatchOverflowDraw(canvas); - super.dispatchDraw(canvas); + if (ReactFeatureFlags.antiAliasRoundedOverflowCorners && hasRoundedOverflow()) { + int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null); + super.dispatchDraw(canvas); + dispatchOverflowDraw(canvas); + canvas.restoreToCount(saveCount); + } else { + dispatchOverflowDraw(canvas); + super.dispatchDraw(canvas); + } + } catch (NullPointerException | StackOverflowError e) { // Adding special exception management for StackOverflowError for logging purposes. // This will be removed in the future. @@ -860,7 +873,7 @@ private void dispatchOverflowDraw(Canvas canvas) { boolean hasClipPath = false; - if (mReactBackgroundDrawable != null) { + if (mReactBackgroundDrawable != null && mReactBackgroundDrawable.hasRoundedBorders()) { final RectF borderWidth = mReactBackgroundDrawable.getDirectionAwareBorderInsets(); if (borderWidth.top > 0 @@ -980,7 +993,21 @@ private void dispatchOverflowDraw(Canvas canvas) { Math.max(bottomLeftBorderRadius - borderWidth.bottom, 0), }, Path.Direction.CW); - canvas.clipPath(mPath); + + if (ReactFeatureFlags.antiAliasRoundedOverflowCorners) { + mPath.setFillType(Path.FillType.INVERSE_WINDING); + + if (mPaint == null) { + mPaint = new Paint(); + mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + mPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + mPaint.setColor(getContext().getColor(android.R.color.white)); + } + + canvas.drawPath(mPath, mPaint); + } else { + canvas.clipPath(mPath); + } hasClipPath = true; } } @@ -995,6 +1022,13 @@ private void dispatchOverflowDraw(Canvas canvas) { } } + private boolean hasRoundedOverflow() { + return mOverflow != null + && (mOverflow.equals(ViewProps.HIDDEN.toString()) + || mOverflow.equals(ViewProps.SCROLL.toString())) + && (mReactBackgroundDrawable != null && mReactBackgroundDrawable.hasRoundedBorders()); + } + public void setOpacityIfPossible(float opacity) { mBackfaceOpacity = opacity; setBackfaceVisibilityDependantOpacity();