Skip to content

Commit

Permalink
Cleanup border radius resolution
Browse files Browse the repository at this point in the history
Differential Revision: D55635743
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Apr 2, 2024
1 parent d1b294e commit db575ec
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 248 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,13 @@ public object FloatUtil {
java.lang.Float.isNaN(f1) && java.lang.Float.isNaN(f2)
} else abs(f2 - f1) < EPSILON
}

@JvmStatic
public fun floatsEqual(f1: Float?, f2: Float?): Boolean {
if (f1 == null) {
return f2 == null
}

return floatsEqual(f1, f2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.FloatUtil;
import com.facebook.react.uimanager.Spacing;
import java.util.Arrays;
import com.facebook.react.uimanager.style.ComputedBorderRadius;
import com.facebook.react.uimanager.style.DeclaredBorderRadius;
import java.util.Locale;

/**
Expand Down Expand Up @@ -99,7 +100,6 @@ private enum BorderStyle {
private @Nullable PointF mInnerBottomRightCorner;
private @Nullable PointF mInnerBottomLeftCorner;
private boolean mNeedUpdatePathForBorderRadius = false;
private float mBorderRadius = Float.NaN;

/* Used by all types of background and for drawing borders */
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Expand All @@ -112,25 +112,10 @@ private enum BorderStyle {
// the paths, overlapping them and closing the visible gap.
private final float mGapBetweenPaths = 0.8f;

private @Nullable float[] mBorderCornerRadii;
private DeclaredBorderRadius mBorderRadius = new DeclaredBorderRadius();
private final Context mContext;
private int mLayoutDirection;

public enum BorderRadiusLocation {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_RIGHT,
BOTTOM_LEFT,
TOP_START,
TOP_END,
BOTTOM_START,
BOTTOM_END,
END_END,
END_START,
START_END,
START_START
}

public CSSBackgroundDrawable(Context context) {
mContext = context;
}
Expand All @@ -146,19 +131,7 @@ public void draw(Canvas canvas) {
}

public boolean hasRoundedBorders() {
if (!Float.isNaN(mBorderRadius) && mBorderRadius > 0) {
return true;
}

if (mBorderCornerRadii != null) {
for (final float borderRadii : mBorderCornerRadii) {
if (!Float.isNaN(borderRadii) && borderRadii > 0) {
return true;
}
}
}

return false;
return mBorderRadius.hasRoundedBorders();
}

@Override
Expand Down Expand Up @@ -193,7 +166,7 @@ public int getOpacity() {
/* Android's elevation implementation requires this to be implemented to know where to draw the shadow. */
@Override
public void getOutline(Outline outline) {
if ((!Float.isNaN(mBorderRadius) && mBorderRadius > 0) || mBorderCornerRadii != null) {
if (hasRoundedBorders()) {
updatePath();

outline.setConvexPath(mPathForBorderRadiusOutline);
Expand Down Expand Up @@ -260,48 +233,36 @@ public void setBorderStyle(@Nullable String style) {
}
}

/**
* @deprecated Use {@link #setBorderRadius(DeclaredBorderRadius.Property, Float)} instead.
*/
public void setRadius(float radius) {
if (!FloatUtil.floatsEqual(mBorderRadius, radius)) {
mBorderRadius = radius;
mNeedUpdatePathForBorderRadius = true;
invalidateSelf();
}
@Nullable Float boxedRadius = Float.isNaN(radius) ? null : Float.valueOf(radius);
setBorderRadius(DeclaredBorderRadius.Property.BORDER_RADIUS, boxedRadius);
}

/**
* @deprecated Use {@link #setBorderRadius(DeclaredBorderRadius.Property, Float)} instead.
*/
public void setRadius(float radius, int position) {
if (mBorderCornerRadii == null) {
mBorderCornerRadii = new float[12];
Arrays.fill(mBorderCornerRadii, Float.NaN);
}
@Nullable Float boxedRadius = Float.isNaN(radius) ? null : Float.valueOf(radius);
setBorderRadius(DeclaredBorderRadius.Property.values()[position], boxedRadius);
}

if (!FloatUtil.floatsEqual(mBorderCornerRadii[position], radius)) {
mBorderCornerRadii[position] = radius;
public void setBorderRadius(DeclaredBorderRadius.Property property, @Nullable Float radius) {
if (!FloatUtil.floatsEqual(mBorderRadius.getUniform(), radius)) {
mBorderRadius.set(property, radius);
mNeedUpdatePathForBorderRadius = true;
invalidateSelf();
}
}

public float getFullBorderRadius() {
return Float.isNaN(mBorderRadius) ? 0 : mBorderRadius;
}

public float getBorderRadius(final BorderRadiusLocation location) {
return getBorderRadiusOrDefaultTo(Float.NaN, location);
public void setBorderRadius(DeclaredBorderRadius radius) {
mBorderRadius = radius;
}

public float getBorderRadiusOrDefaultTo(
final float defaultValue, final BorderRadiusLocation location) {
if (mBorderCornerRadii == null) {
return defaultValue;
}

final float radius = mBorderCornerRadii[location.ordinal()];

if (Float.isNaN(radius)) {
return defaultValue;
}

return radius;
public DeclaredBorderRadius getBorderRadius() {
return mBorderRadius;
}

public void setColor(int color) {
Expand Down Expand Up @@ -606,95 +567,11 @@ private void updatePath() {
mTempRectForCenterDrawPath.left += borderWidth.left * 0.5f;
mTempRectForCenterDrawPath.right -= borderWidth.right * 0.5f;

final float borderRadius = getFullBorderRadius();
float topLeftRadius = getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.TOP_LEFT);
float topRightRadius = getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.TOP_RIGHT);
float bottomLeftRadius =
getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.BOTTOM_LEFT);
float bottomRightRadius =
getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.BOTTOM_RIGHT);

final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float topStartRadius = getBorderRadius(BorderRadiusLocation.TOP_START);
float topEndRadius = getBorderRadius(BorderRadiusLocation.TOP_END);
float bottomStartRadius = getBorderRadius(BorderRadiusLocation.BOTTOM_START);
float bottomEndRadius = getBorderRadius(BorderRadiusLocation.BOTTOM_END);

float endEndRadius = getBorderRadius(BorderRadiusLocation.END_END);
float endStartRadius = getBorderRadius(BorderRadiusLocation.END_START);
float startEndRadius = getBorderRadius(BorderRadiusLocation.START_END);
float startStartRadius = getBorderRadius(BorderRadiusLocation.START_START);

if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(mContext)) {
if (Float.isNaN(topStartRadius)) {
topStartRadius = topLeftRadius;
}

if (Float.isNaN(topEndRadius)) {
topEndRadius = topRightRadius;
}

if (Float.isNaN(bottomStartRadius)) {
bottomStartRadius = bottomLeftRadius;
}

if (Float.isNaN(bottomEndRadius)) {
bottomEndRadius = bottomRightRadius;
}

final float logicalTopStartRadius =
Float.isNaN(topStartRadius) ? startStartRadius : topStartRadius;
final float logicalTopEndRadius = Float.isNaN(topEndRadius) ? startEndRadius : topEndRadius;
final float logicalBottomStartRadius =
Float.isNaN(bottomStartRadius) ? endStartRadius : bottomStartRadius;
final float logicalBottomEndRadius =
Float.isNaN(bottomEndRadius) ? endEndRadius : bottomEndRadius;

final float directionAwareTopLeftRadius = isRTL ? logicalTopEndRadius : logicalTopStartRadius;
final float directionAwareTopRightRadius =
isRTL ? logicalTopStartRadius : logicalTopEndRadius;
final float directionAwareBottomLeftRadius =
isRTL ? logicalBottomEndRadius : logicalBottomStartRadius;
final float directionAwareBottomRightRadius =
isRTL ? logicalBottomStartRadius : logicalBottomEndRadius;

topLeftRadius = directionAwareTopLeftRadius;
topRightRadius = directionAwareTopRightRadius;
bottomLeftRadius = directionAwareBottomLeftRadius;
bottomRightRadius = directionAwareBottomRightRadius;
} else {
final float logicalTopStartRadius =
Float.isNaN(topStartRadius) ? startStartRadius : topStartRadius;
final float logicalTopEndRadius = Float.isNaN(topEndRadius) ? startEndRadius : topEndRadius;
final float logicalBottomStartRadius =
Float.isNaN(bottomStartRadius) ? endStartRadius : bottomStartRadius;
final float logicalBottomEndRadius =
Float.isNaN(bottomEndRadius) ? endEndRadius : bottomEndRadius;

final float directionAwareTopLeftRadius = isRTL ? logicalTopEndRadius : logicalTopStartRadius;
final float directionAwareTopRightRadius =
isRTL ? logicalTopStartRadius : logicalTopEndRadius;
final float directionAwareBottomLeftRadius =
isRTL ? logicalBottomEndRadius : logicalBottomStartRadius;
final float directionAwareBottomRightRadius =
isRTL ? logicalBottomStartRadius : logicalBottomEndRadius;

if (!Float.isNaN(directionAwareTopLeftRadius)) {
topLeftRadius = directionAwareTopLeftRadius;
}

if (!Float.isNaN(directionAwareTopRightRadius)) {
topRightRadius = directionAwareTopRightRadius;
}

if (!Float.isNaN(directionAwareBottomLeftRadius)) {
bottomLeftRadius = directionAwareBottomLeftRadius;
}

if (!Float.isNaN(directionAwareBottomRightRadius)) {
bottomRightRadius = directionAwareBottomRightRadius;
}
}
ComputedBorderRadius radius = mBorderRadius.resolve(mLayoutDirection, mContext);
float topLeftRadius = radius.getTopLeft();
float topRightRadius = radius.getTopRight();
float bottomLeftRadius = radius.getBottomLeft();
float bottomRightRadius = radius.getBottomRight();

final float innerTopLeftRadiusX = Math.max(topLeftRadius - borderWidth.left, 0);
final float innerTopLeftRadiusY = Math.max(topLeftRadius - borderWidth.top, 0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.uimanager.style

/** Phsysical edge lengths (in DIPs) for a border-radius. */
public data class ComputedBorderRadius(
val topLeft: Float,
val topRight: Float,
val bottomLeft: Float,
val bottomRight: Float,
) {
public fun hasRoundedBorders(): Boolean {
return topLeft > 0f || topRight > 0f || bottomLeft > 0f || bottomRight > 0f
}
}
Loading

0 comments on commit db575ec

Please sign in to comment.