Skip to content

Commit

Permalink
Fix issue where inset border radius was off when there is a border (f…
Browse files Browse the repository at this point in the history
…acebook#45658)

Summary:
Pull Request resolved: facebook#45658

The border radius of the inner "clear region" for inset shadows is based off of the border radius of the padding box path (i.e. the shadow path). Notably, this is not the View's given border radius iff there is a border present.

To get this "inner border radius" I added a new method inside of `CSSBackgroundDrawable`. This logic was already present [here](https://www.internalfb.com/code/fbsource/[33e35cbf387a]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java?lines=643-650), so I tried to share most of the code there. There may be a better way to do this, but this seems to be the quickest.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D60083309

fbshipit-source-id: ace1ffa15fc3c0c09d4df096b604c9a2c91382c8
  • Loading branch information
joevilches authored and facebook-github-bot committed Jul 27, 2024
1 parent a694e4b commit 8fe3ae3
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ public BorderRadiusStyle getBorderRadius() {
return mBorderRadius;
}

// Here, "inner" refers to the border radius on the inside of the border. So
// it ends up being the "outer" border radius inset by the respective width.
public float getInnerBorderRadius(float computedRadius, float borderWidth) {
return Math.max(computedRadius - borderWidth, 0);
}

public ComputedBorderRadius getComputedBorderRadius() {
return mComputedBorderRadius;
}
Expand Down Expand Up @@ -640,14 +646,16 @@ private void updatePath() {
float bottomLeftRadius = mComputedBorderRadius.getBottomLeft();
float bottomRightRadius = mComputedBorderRadius.getBottomRight();

final float innerTopLeftRadiusX = Math.max(topLeftRadius - borderWidth.left, 0);
final float innerTopLeftRadiusY = Math.max(topLeftRadius - borderWidth.top, 0);
final float innerTopRightRadiusX = Math.max(topRightRadius - borderWidth.right, 0);
final float innerTopRightRadiusY = Math.max(topRightRadius - borderWidth.top, 0);
final float innerBottomRightRadiusX = Math.max(bottomRightRadius - borderWidth.right, 0);
final float innerBottomRightRadiusY = Math.max(bottomRightRadius - borderWidth.bottom, 0);
final float innerBottomLeftRadiusX = Math.max(bottomLeftRadius - borderWidth.left, 0);
final float innerBottomLeftRadiusY = Math.max(bottomLeftRadius - borderWidth.bottom, 0);
final float innerTopLeftRadiusX = getInnerBorderRadius(topLeftRadius, borderWidth.left);
final float innerTopLeftRadiusY = getInnerBorderRadius(topLeftRadius, borderWidth.top);
final float innerTopRightRadiusX = getInnerBorderRadius(topRightRadius, borderWidth.right);
final float innerTopRightRadiusY = getInnerBorderRadius(topRightRadius, borderWidth.top);
final float innerBottomRightRadiusX =
getInnerBorderRadius(bottomRightRadius, borderWidth.right);
final float innerBottomRightRadiusY =
getInnerBorderRadius(bottomRightRadius, borderWidth.bottom);
final float innerBottomLeftRadiusX = getInnerBorderRadius(bottomLeftRadius, borderWidth.left);
final float innerBottomLeftRadiusY = getInnerBorderRadius(bottomLeftRadius, borderWidth.bottom);

mInnerClipPathForBorderRadius.addRoundRect(
mInnerClipTempRectForBorderRadius,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import androidx.annotation.RequiresApi
import com.facebook.common.logging.FLog
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.FilterHelper
import com.facebook.react.uimanager.LengthPercentage
import com.facebook.react.uimanager.LengthPercentageType
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.style.BorderRadiusProp
import com.facebook.react.uimanager.style.BorderRadiusStyle
import kotlin.math.roundToInt

private const val TAG = "InsetBoxShadowDrawable"
Expand Down Expand Up @@ -76,13 +80,10 @@ internal class InsetBoxShadowDrawable(
clearRegionBounds.inset(spreadExtent, spreadExtent)
clearRegionBounds.offset(
PixelUtil.toPixelFromDIP(offsetX).roundToInt() + padding / 2,
PixelUtil.toPixelFromDIP(offsetY).roundToInt() + padding / 2)
PixelUtil.toPixelFromDIP(offsetY).roundToInt() + padding / 2,
)
val clearRegionBorderRadii =
getShadowBorderRadii(
-spreadExtent.toFloat(),
background.borderRadius,
clearRegionBounds.width().toFloat(),
clearRegionBounds.height().toFloat())
getClearRegionBorderRadii(spreadExtent, background, clearRegionBounds)

if (shadowPaint.colorFilter != colorFilter ||
clearRegionDrawable.layoutDirection != layoutDirection ||
Expand Down Expand Up @@ -129,4 +130,49 @@ internal class InsetBoxShadowDrawable(
canvas.restore()
}
}

private fun getClearRegionBorderRadii(
spread: Int,
background: CSSBackgroundDrawable,
clearRegionBounds: Rect,
): BorderRadiusStyle {
val computedBorderRadii = background.computedBorderRadius
val borderWidth = background.getDirectionAwareBorderInsets()

val topLeftRadius = computedBorderRadii.topLeft
val topRightRadius = computedBorderRadii.topRight
val bottomLeftRadius = computedBorderRadii.bottomLeft
val bottomRightRadius = computedBorderRadii.bottomRight

val innerTopLeftRadius = background.getInnerBorderRadius(topLeftRadius, borderWidth.left)
val innerTopRightRadius = background.getInnerBorderRadius(topRightRadius, borderWidth.right)
val innerBottomRightRadius =
background.getInnerBorderRadius(bottomRightRadius, borderWidth.right)
val innerBottomLeftRadius = background.getInnerBorderRadius(bottomLeftRadius, borderWidth.left)

val innerBorderRadii = BorderRadiusStyle()
innerBorderRadii.set(
BorderRadiusProp.BORDER_TOP_LEFT_RADIUS,
LengthPercentage(innerTopLeftRadius, LengthPercentageType.POINT),
)
innerBorderRadii.set(
BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS,
LengthPercentage(innerTopRightRadius, LengthPercentageType.POINT),
)
innerBorderRadii.set(
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS,
LengthPercentage(innerBottomRightRadius, LengthPercentageType.POINT),
)
innerBorderRadii.set(
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS,
LengthPercentage(innerBottomLeftRadius, LengthPercentageType.POINT),
)

return getShadowBorderRadii(
-spread.toFloat(),
innerBorderRadii,
clearRegionBounds.width().toFloat(),
clearRegionBounds.height().toFloat(),
)
}
}

0 comments on commit 8fe3ae3

Please sign in to comment.