Anti-alias rounded borders on overflow: hidden views
Summary: When a view is rendered with a combination of `overflow: hidden` and `borderRadius: >0`, the `ReactViewGroup` would apply the border radius using Canvas `clipPath`. In Android graphics, clipPath is not an anti-aliased operation and caused aliasing artifacts. Changing the method to a bitmask using the `PorterDuff` method results in the same functionality, but with hardware accelerated antialiasing. https://github.com/facebook/react-native/issues/24486 Changelog: [Android][Change] - Views with overflow: hidden and borderRadius: >0 now render anti-aliased borders. Reviewed By: javache Differential Revision: D38914878 fbshipit-source-id: 45ac7e4aece7a76c4216412175e49d3d73b6f391
This commit is contained in:
Родитель
18542b6ef5
Коммит
7708cdccef
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,10 @@ import android.annotation.TargetApi;
|
|||
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 class ReactViewGroup extends ViewGroup
|
|||
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 @@ public class ReactViewGroup extends ViewGroup
|
|||
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 class ReactViewGroup extends ViewGroup
|
|||
@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 @@ public class ReactViewGroup extends ViewGroup
|
|||
|
||||
boolean hasClipPath = false;
|
||||
|
||||
if (mReactBackgroundDrawable != null) {
|
||||
if (mReactBackgroundDrawable != null && mReactBackgroundDrawable.hasRoundedBorders()) {
|
||||
final RectF borderWidth = mReactBackgroundDrawable.getDirectionAwareBorderInsets();
|
||||
|
||||
if (borderWidth.top > 0
|
||||
|
@ -980,7 +993,21 @@ public class ReactViewGroup extends ViewGroup
|
|||
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 @@ public class ReactViewGroup extends ViewGroup
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче