Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RenderScript Impl of Box Shadow #43988

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup border radius resolution
Differential Revision: D55635743
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Apr 9, 2024
commit 92a9572c821bae3fe0cff2534136d508b4bfcc2f
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ 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
} else if (f2 == null) {
return false
}

return floatsEqual(f1, f2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
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.BorderRadiusProp;
import com.facebook.react.uimanager.style.BorderRadiusStyle;
import com.facebook.react.uimanager.style.ComputedBorderRadius;
import java.util.Locale;

/**
Expand Down Expand Up @@ -99,7 +101,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 +113,10 @@ private enum BorderStyle {
// the paths, overlapping them and closing the visible gap.
private final float mGapBetweenPaths = 0.8f;

private @Nullable float[] mBorderCornerRadii;
private BorderRadiusStyle mBorderRadius = new BorderRadiusStyle();
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 +132,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 +167,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 +234,36 @@ public void setBorderStyle(@Nullable String style) {
}
}

/**
* @deprecated Use {@link #setBorderRadius(BorderRadiusProp, 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(BorderRadiusProp.BORDER_RADIUS, boxedRadius);
}

/**
* @deprecated Use {@link #setBorderRadius(BorderRadiusProp, 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(BorderRadiusProp.values()[position], boxedRadius);
}

if (!FloatUtil.floatsEqual(mBorderCornerRadii[position], radius)) {
mBorderCornerRadii[position] = radius;
public void setBorderRadius(BorderRadiusProp 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(BorderRadiusStyle 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 BorderRadiusStyle getBorderRadius() {
return mBorderRadius;
}

public void setColor(int color) {
Expand Down Expand Up @@ -606,95 +568,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,131 @@
/*
* 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

import android.content.Context
import android.util.LayoutDirection
import com.facebook.react.modules.i18nmanager.I18nUtil

/** Represents the collection of possible border radius style properties. */
public enum class BorderRadiusProp {
BORDER_TOP_LEFT_RADIUS,
BORDER_TOP_RIGHT_RADIUS,
BORDER_BOTTOM_RIGHT_RADIUS,
BORDER_BOTTOM_LEFT_RADIUS,
BORDER_TOP_START_RADIUS,
BORDER_TOP_END_RADIUS,
BORDER_BOTTOM_START_RADIUS,
BORDER_BOTTOM_END_RADIUS,
BORDER_END_END_RADIUS,
BORDER_END_START_RADIUS,
BORDER_START_END_RADIUS,
BORDER_START_START_RADIUS,
BORDER_RADIUS,
}

/** Represents all logical properties and shorthands for border radius. */
public data class BorderRadiusStyle(
var uniform: Float? = null,
var topLeft: Float? = null,
var topRight: Float? = null,
var bottomLeft: Float? = null,
var bottomRight: Float? = null,
var topStart: Float? = null,
var topEnd: Float? = null,
var bottomStart: Float? = null,
var bottomEnd: Float? = null,
var startStart: Float? = null,
var startEnd: Float? = null,
var endStart: Float? = null,
var endEnd: Float? = null
) {
public constructor(properties: List<Pair<BorderRadiusProp, Float>>) : this() {
properties.forEach { (k, v) -> set(k, v) }
}

public fun set(property: BorderRadiusProp, value: Float?) {
when (property) {
BorderRadiusProp.BORDER_RADIUS -> uniform = value
BorderRadiusProp.BORDER_TOP_LEFT_RADIUS -> topLeft = value
BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS -> topRight = value
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS -> bottomLeft = value
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS -> bottomRight = value
BorderRadiusProp.BORDER_TOP_START_RADIUS -> topStart = value
BorderRadiusProp.BORDER_TOP_END_RADIUS -> topEnd = value
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS -> bottomStart = value
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS -> bottomEnd = value
BorderRadiusProp.BORDER_START_START_RADIUS -> startStart = value
BorderRadiusProp.BORDER_START_END_RADIUS -> startEnd = value
BorderRadiusProp.BORDER_END_START_RADIUS -> endStart = value
BorderRadiusProp.BORDER_END_END_RADIUS -> endEnd = value
}
}

public fun get(property: BorderRadiusProp): Float? {
return when (property) {
BorderRadiusProp.BORDER_RADIUS -> uniform
BorderRadiusProp.BORDER_TOP_LEFT_RADIUS -> topLeft
BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS -> topRight
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS -> bottomLeft
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS -> bottomRight
BorderRadiusProp.BORDER_TOP_START_RADIUS -> topStart
BorderRadiusProp.BORDER_TOP_END_RADIUS -> topEnd
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS -> bottomStart
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS -> bottomEnd
BorderRadiusProp.BORDER_START_START_RADIUS -> startStart
BorderRadiusProp.BORDER_START_END_RADIUS -> startEnd
BorderRadiusProp.BORDER_END_START_RADIUS -> endStart
BorderRadiusProp.BORDER_END_END_RADIUS -> endEnd
}
}

public fun hasRoundedBorders(): Boolean {
return ((uniform ?: 0f) > 0f) ||
((topLeft ?: 0f) > 0f) ||
((topRight ?: 0f) > 0f) ||
((bottomLeft ?: 0f) > 0f) ||
((bottomRight ?: 0f) > 0f) ||
((topStart ?: 0f) > 0f) ||
((topEnd ?: 0f) > 0f) ||
((bottomStart ?: 0f) > 0f) ||
((bottomEnd ?: 0f) > 0f) ||
((startStart ?: 0f) > 0f) ||
((startEnd ?: 0f) > 0f) ||
((endStart ?: 0f) > 0f) ||
((endEnd ?: 0f) > 0f)
}

public fun resolve(
layoutDirection: Int,
context: Context,
): ComputedBorderRadius {
return when (layoutDirection) {
LayoutDirection.LTR ->
ComputedBorderRadius(
topLeft = startStart ?: topStart ?: topLeft ?: uniform ?: 0f,
topRight = endStart ?: topEnd ?: topRight ?: uniform ?: 0f,
bottomLeft = startEnd ?: bottomStart ?: bottomLeft ?: uniform ?: 0f,
bottomRight = endEnd ?: bottomEnd ?: bottomRight ?: uniform ?: 0f)
LayoutDirection.RTL ->
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(context)) {
ComputedBorderRadius(
topLeft = endStart ?: topEnd ?: topRight ?: uniform ?: 0f,
topRight = startStart ?: topStart ?: topLeft ?: uniform ?: 0f,
bottomLeft = endEnd ?: bottomStart ?: bottomRight ?: uniform ?: 0f,
bottomRight = startEnd ?: bottomEnd ?: bottomLeft ?: uniform ?: 0f)
} else {
ComputedBorderRadius(
topLeft = endStart ?: topEnd ?: topLeft ?: uniform ?: 0f,
topRight = startStart ?: topStart ?: topRight ?: uniform ?: 0f,
bottomLeft = endEnd ?: bottomStart ?: bottomLeft ?: uniform ?: 0f,
bottomRight = startEnd ?: bottomEnd ?: bottomRight ?: uniform ?: 0f)
}
else -> throw IllegalArgumentException("Expected resolved layout direction")
}
}
}
Loading