Nullable borderRadius optimization on shadows and outline (#46819)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46819

Just some preparation to implement the new `BackgroundDrawable` and `BorderDrawable`.

This makes shadows and outline rely on `CompositeBackgroundDrawable` to set the state of `BorderRadiusStyle` and also makes them have a nullable borderRadius variable and initialize it only if we have it set saving a bit of overhead and performance

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D63798853

fbshipit-source-id: 994327415ba160cf8e5ccb3135c4917809499f81
This commit is contained in:
Jorge Cabiedes Acosta
2024-10-17 17:37:25 -07:00
committed by Facebook GitHub Bot
parent 892ce1b980
commit 02e4f3e16a
5 changed files with 73 additions and 60 deletions
@@ -105,13 +105,14 @@ public object BackgroundStyleApplicator {
): Unit {
ensureCSSBackground(view).setBorderRadius(corner, radius)
val compositeBackgroundDrawable = ensureCompositeBackgroundDrawable(view)
compositeBackgroundDrawable.borderRadius =
compositeBackgroundDrawable.borderRadius ?: BorderRadiusStyle()
compositeBackgroundDrawable.borderRadius?.set(corner, radius)
if (Build.VERSION.SDK_INT >= MIN_OUTSET_BOX_SHADOW_SDK_VERSION) {
for (shadow in compositeBackgroundDrawable.outerShadows) {
if (shadow is OutsetBoxShadowDrawable) {
shadow.borderRadius = shadow.borderRadius ?: BorderRadiusStyle()
shadow.borderRadius?.set(corner, radius)
shadow.invalidateSelf()
shadow.borderRadius = compositeBackgroundDrawable.borderRadius
}
}
}
@@ -119,19 +120,13 @@ public object BackgroundStyleApplicator {
if (Build.VERSION.SDK_INT >= MIN_INSET_BOX_SHADOW_SDK_VERSION) {
for (shadow in compositeBackgroundDrawable.innerShadows) {
if (shadow is InsetBoxShadowDrawable) {
shadow.borderRadius = shadow.borderRadius ?: BorderRadiusStyle()
shadow.borderRadius?.set(corner, radius)
shadow.invalidateSelf()
shadow.borderRadius = compositeBackgroundDrawable.borderRadius
}
}
}
val outline = compositeBackgroundDrawable.outline
if (outline != null) {
outline.borderRadius = outline.borderRadius ?: BorderRadiusStyle()
outline.borderRadius?.set(corner, radius)
outline.invalidateSelf()
}
compositeBackgroundDrawable.outline?.borderRadius = compositeBackgroundDrawable.borderRadius
compositeBackgroundDrawable.invalidateSelf()
}
@JvmStatic
@@ -207,7 +202,9 @@ public object BackgroundStyleApplicator {
val outerShadows = mutableListOf<OutsetBoxShadowDrawable>()
val innerShadows = mutableListOf<InsetBoxShadowDrawable>()
val borderInsets = ensureCompositeBackgroundDrawable(view).borderInsets
val compositeBackgroundDrawable = ensureCompositeBackgroundDrawable(view)
val borderInsets = compositeBackgroundDrawable.borderInsets
val borderRadius = compositeBackgroundDrawable.borderRadius
for (boxShadow in shadows) {
val offsetX = boxShadow.offsetX
@@ -221,7 +218,7 @@ public object BackgroundStyleApplicator {
innerShadows.add(
InsetBoxShadowDrawable(
context = view.context,
borderRadius = ensureCSSBackground(view).borderRadius,
borderRadius = borderRadius,
borderInsets = borderInsets,
shadowColor = color,
offsetX = offsetX,
@@ -232,7 +229,7 @@ public object BackgroundStyleApplicator {
outerShadows.add(
OutsetBoxShadowDrawable(
context = view.context,
borderRadius = ensureCSSBackground(view).borderRadius,
borderRadius = borderRadius,
shadowColor = color,
offsetX = offsetX,
offsetY = offsetY,
@@ -329,7 +326,7 @@ public object BackgroundStyleApplicator {
outline =
OutlineDrawable(
context = view.context,
borderRadius = ensureCSSBackground(view).borderRadius.copy(),
borderRadius = compositeBackgroundDrawable.borderRadius,
outlineColor = Color.BLACK,
outlineOffset = 0f,
outlineStyle = OutlineStyle.SOLID,
@@ -11,6 +11,7 @@ import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.style.BorderInsets
import com.facebook.react.uimanager.style.BorderRadiusStyle
/**
* CompositeBackgroundDrawable can overlay multiple different layers, shadows, and native effects
@@ -59,6 +60,8 @@ internal class CompositeBackgroundDrawable(
// Holder value for currently set insets
public var borderInsets: BorderInsets? = null
// Holder value for currently set border radius
public var borderRadius: BorderRadiusStyle? = null
init {
// We want to overlay drawables, instead of placing future drawables within the content area of
@@ -71,7 +74,16 @@ internal class CompositeBackgroundDrawable(
cssBackground: CSSBackgroundDrawable?
): CompositeBackgroundDrawable {
return CompositeBackgroundDrawable(
originalBackground, outerShadows, cssBackground, feedbackUnderlay, innerShadows, outline)
originalBackground,
outerShadows,
cssBackground,
feedbackUnderlay,
innerShadows,
outline)
.also { composite ->
composite.borderInsets = this.borderInsets
composite.borderRadius = this.borderRadius
}
}
public fun withNewShadows(
@@ -79,16 +91,38 @@ internal class CompositeBackgroundDrawable(
innerShadows: List<Drawable>
): CompositeBackgroundDrawable {
return CompositeBackgroundDrawable(
originalBackground, outerShadows, cssBackground, feedbackUnderlay, innerShadows, outline)
originalBackground,
outerShadows,
cssBackground,
feedbackUnderlay,
innerShadows,
outline)
.also { composite ->
composite.borderInsets = this.borderInsets
composite.borderRadius = this.borderRadius
}
}
public fun withNewOutline(outline: OutlineDrawable): CompositeBackgroundDrawable {
return CompositeBackgroundDrawable(
originalBackground, outerShadows, cssBackground, feedbackUnderlay, innerShadows, outline)
originalBackground,
outerShadows,
cssBackground,
feedbackUnderlay,
innerShadows,
outline)
.also { composite ->
composite.borderInsets = this.borderInsets
composite.borderRadius = this.borderRadius
}
}
public fun withNewFeedbackUnderlay(newUnderlay: Drawable?): CompositeBackgroundDrawable {
return CompositeBackgroundDrawable(
originalBackground, outerShadows, cssBackground, newUnderlay, innerShadows, outline)
originalBackground, outerShadows, cssBackground, newUnderlay, innerShadows, outline)
.apply {
borderInsets = this@CompositeBackgroundDrawable.borderInsets
borderRadius = this@CompositeBackgroundDrawable.borderRadius
}
}
}
@@ -40,30 +40,18 @@ private val ZERO_RADII = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
@RequiresApi(MIN_INSET_BOX_SHADOW_SDK_VERSION)
internal class InsetBoxShadowDrawable(
private val context: Context,
borderRadius: BorderRadiusStyle? = null,
borderInsets: BorderInsets? = null,
private val shadowColor: Int,
private val offsetX: Float,
private val offsetY: Float,
private val blurRadius: Float,
private val spread: Float,
/*
* We assume borderRadius & borderInsets to be shared across multiple drawables
* therefore user should invalidate this drawable when changing either of them
*/
var borderInsets: BorderInsets? = null,
var borderRadius: BorderRadiusStyle? = null,
) : Drawable() {
public var borderRadius = borderRadius
set(value) {
if (value != field) {
field = value
invalidateSelf()
}
}
public var borderInsets = borderInsets
set(value) {
if (value != field) {
field = value
invalidateSelf()
}
}
private val shadowPaint =
Paint().apply {
color = shadowColor
@@ -23,14 +23,15 @@ import com.facebook.react.uimanager.style.ComputedBorderRadius
import com.facebook.react.uimanager.style.CornerRadii
import com.facebook.react.uimanager.style.OutlineStyle
import kotlin.math.roundToInt
import kotlin.properties.ObservableProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/** Draws outline https://drafts.csswg.org/css-ui/#outline */
internal class OutlineDrawable(
private val context: Context,
borderRadius: BorderRadiusStyle? = null,
/*
* We assume borderRadius to be shared across multiple drawables
* therefore we should manually invalidate this drawable when changing it
*/
var borderRadius: BorderRadiusStyle? = null,
outlineColor: Int,
outlineOffset: Float,
outlineStyle: OutlineStyle,
@@ -43,17 +44,14 @@ internal class OutlineDrawable(
*/
private val gapBetweenPaths = 0.8f
private fun <T> invalidatingChange(initialValue: T): ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
if (oldValue != newValue) {
invalidateSelf()
}
}
public var outlineOffset: Float = outlineOffset
set(value) {
if (value != field) {
field = value
invalidateSelf()
}
}
public var borderRadius: BorderRadiusStyle? by invalidatingChange(borderRadius)
public var outlineOffset: Float by invalidatingChange(outlineOffset)
public var outlineStyle: OutlineStyle = outlineStyle
set(value) {
if (value != field) {
@@ -37,21 +37,17 @@ private const val BLUR_RADIUS_SIGMA_SCALE = 0.5f
@RequiresApi(MIN_OUTSET_BOX_SHADOW_SDK_VERSION)
internal class OutsetBoxShadowDrawable(
private val context: Context,
borderRadius: BorderRadiusStyle? = null,
private val shadowColor: Int,
private val offsetX: Float,
private val offsetY: Float,
private val blurRadius: Float,
private val spread: Float,
/*
* We assume borderRadius to be shared across multiple drawables
* therefore we should manually invalidate this drawable when changing it
*/
var borderRadius: BorderRadiusStyle? = null,
) : Drawable() {
public var borderRadius = borderRadius
set(value) {
if (value != field) {
field = value
invalidateSelf()
}
}
private val shadowPaint =
Paint().apply {
color = shadowColor