Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public object BackgroundStyleApplicator {
composite.borderInsets?.setBorderWidth(edge, width)

if (Build.VERSION.SDK_INT >= MIN_INSET_BOX_SHADOW_SDK_VERSION) {
for (shadow in composite.innerShadows.filterIsInstance<InsetBoxShadowDrawable>()) {
for (shadow in composite.innerShadows) {
shadow.borderInsets = composite.borderInsets
}
}
Expand Down Expand Up @@ -224,15 +224,13 @@ public object BackgroundStyleApplicator {
compositeBackgroundDrawable.border?.invalidateSelf()

if (Build.VERSION.SDK_INT >= MIN_OUTSET_BOX_SHADOW_SDK_VERSION) {
for (shadow in
compositeBackgroundDrawable.outerShadows.filterIsInstance<OutsetBoxShadowDrawable>()) {
for (shadow in compositeBackgroundDrawable.outerShadows) {
shadow.borderRadius = compositeBackgroundDrawable.borderRadius
}
}

if (Build.VERSION.SDK_INT >= MIN_INSET_BOX_SHADOW_SDK_VERSION) {
for (shadow in
compositeBackgroundDrawable.innerShadows.filterIsInstance<InsetBoxShadowDrawable>()) {
for (shadow in compositeBackgroundDrawable.innerShadows) {
shadow.borderRadius = compositeBackgroundDrawable.borderRadius
}
}
Expand Down Expand Up @@ -375,6 +373,12 @@ public object BackgroundStyleApplicator {
val borderInsets = compositeBackgroundDrawable.borderInsets
val borderRadius = compositeBackgroundDrawable.borderRadius

// An animated shadow sets a list of the same shape on every frame, so its drawables take the
// new values in place instead of the view getting new drawables and a new background per frame.
val reuseShadows = holdsShadowDrawablesFor(compositeBackgroundDrawable, shadows)
var innerIndex = 0
var outerIndex = 0

/**
* z-ordering of user-provided shadow-list is opposite direction of LayerDrawable z-ordering
* https://drafts.csswg.org/css-backgrounds/#shadow-layers
Expand All @@ -388,36 +392,75 @@ public object BackgroundStyleApplicator {
val inset = boxShadow.inset ?: false

if (inset && Build.VERSION.SDK_INT >= MIN_INSET_BOX_SHADOW_SDK_VERSION) {
innerShadows.add(
InsetBoxShadowDrawable(
context = view.context,
borderRadius = borderRadius,
borderInsets = borderInsets,
shadowColor = color,
offsetX = offsetX,
offsetY = offsetY,
blurRadius = blurRadius,
spread = spreadDistance,
),
)
if (reuseShadows) {
val shadow = compositeBackgroundDrawable.innerShadows[innerIndex++]
shadow.shadowColor = color
shadow.offsetX = offsetX
shadow.offsetY = offsetY
shadow.blurRadius = blurRadius
shadow.spread = spreadDistance
} else {
innerShadows.add(
InsetBoxShadowDrawable(
context = view.context,
borderRadius = borderRadius,
borderInsets = borderInsets,
shadowColor = color,
offsetX = offsetX,
offsetY = offsetY,
blurRadius = blurRadius,
spread = spreadDistance,
),
)
}
} else if (!inset && Build.VERSION.SDK_INT >= MIN_OUTSET_BOX_SHADOW_SDK_VERSION) {
outerShadows.add(
OutsetBoxShadowDrawable(
context = view.context,
borderRadius = borderRadius,
shadowColor = color,
offsetX = offsetX,
offsetY = offsetY,
blurRadius = blurRadius,
spread = spreadDistance,
),
)
if (reuseShadows) {
val shadow = compositeBackgroundDrawable.outerShadows[outerIndex++]
shadow.shadowColor = color
shadow.offsetX = offsetX
shadow.offsetY = offsetY
shadow.blurRadius = blurRadius
shadow.spread = spreadDistance
} else {
outerShadows.add(
OutsetBoxShadowDrawable(
context = view.context,
borderRadius = borderRadius,
shadowColor = color,
offsetX = offsetX,
offsetY = offsetY,
blurRadius = blurRadius,
spread = spreadDistance,
),
)
}
}
}

view.background =
ensureCompositeBackgroundDrawable(view)
.withNewShadows(outerShadows = outerShadows, innerShadows = innerShadows)
if (!reuseShadows) {
view.background =
ensureCompositeBackgroundDrawable(view)
.withNewShadows(outerShadows = outerShadows, innerShadows = innerShadows)
}
}

/** Whether the background already holds one shadow drawable per inset and per outset shadow. */
private fun holdsShadowDrawablesFor(
compositeBackgroundDrawable: CompositeBackgroundDrawable,
shadows: List<BoxShadow>,
): Boolean {
var innerCount = 0
var outerCount = 0
for (boxShadow in shadows) {
val inset = boxShadow.inset ?: false
if (inset && Build.VERSION.SDK_INT >= MIN_INSET_BOX_SHADOW_SDK_VERSION) {
innerCount++
} else if (!inset && Build.VERSION.SDK_INT >= MIN_OUTSET_BOX_SHADOW_SDK_VERSION) {
outerCount++
}
}
return innerCount == compositeBackgroundDrawable.innerShadows.size &&
outerCount == compositeBackgroundDrawable.outerShadows.size
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.drawable

import android.graphics.BlurMaskFilter
import com.facebook.react.uimanager.FilterHelper

// "the resulting shadow must approximate {...} a Gaussian blur with a standard deviation equal
// to half the blur radius"
// https://www.w3.org/TR/css-backgrounds-3/#shadow-blur
private const val BLUR_RADIUS_SIGMA_SCALE = 0.5f

/** The mask filter that blurs a box shadow by [blurRadius], or null when no blur shows. */
internal fun createBoxShadowMaskFilter(blurRadius: Float): BlurMaskFilter? {
val convertedBlurRadius = FilterHelper.sigmaToRadius(blurRadius * BLUR_RADIUS_SIGMA_SCALE)
return if (convertedBlurRadius > 0) {
BlurMaskFilter(convertedBlurRadius, BlurMaskFilter.Blur.NORMAL)
} else {
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal class CompositeBackgroundDrawable(
val originalBackground: Drawable? = null,

/** Non-inset box shadows */
val outerShadows: List<Drawable> = emptyList(),
val outerShadows: List<OutsetBoxShadowDrawable> = emptyList(),

/** Background rendering Layer */
val background: BackgroundDrawable? = null,
Expand All @@ -51,7 +51,7 @@ internal class CompositeBackgroundDrawable(
val feedbackUnderlay: Drawable? = null,

/** Inset box-shadows */
val innerShadows: List<Drawable> = emptyList(),
val innerShadows: List<InsetBoxShadowDrawable> = emptyList(),

/** Outline */
val outline: OutlineDrawable? = null,
Expand Down Expand Up @@ -117,8 +117,8 @@ internal class CompositeBackgroundDrawable(
}

fun withNewShadows(
outerShadows: List<Drawable>,
innerShadows: List<Drawable>,
outerShadows: List<OutsetBoxShadowDrawable>,
innerShadows: List<InsetBoxShadowDrawable>,
): CompositeBackgroundDrawable {
return CompositeBackgroundDrawable(
context,
Expand Down
Loading
Loading