Skip to content

Commit 5745ab9

Browse files
committed
feat(android): merge with BackgroundImageDrawable
1 parent 9d48664 commit 5745ab9

8 files changed

Lines changed: 1001 additions & 1280 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt

Lines changed: 74 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,9 @@ import android.os.Build
2020
import android.view.View
2121
import android.widget.ImageView
2222
import androidx.annotation.ColorInt
23-
import com.facebook.imagepipeline.request.ImageRequest
24-
import com.facebook.imagepipeline.request.ImageRequestBuilder
25-
import com.facebook.imagepipeline.request.Postprocessor
2623
import com.facebook.react.bridge.ReadableArray
27-
import com.facebook.react.bridge.ReadableMap
2824
import com.facebook.react.common.annotations.UnstableReactNativeAPI
2925
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
30-
import com.facebook.react.modules.fresco.ReactNetworkImageRequest
3126
import com.facebook.react.uimanager.PixelUtil.dpToPx
3227
import com.facebook.react.uimanager.PixelUtil.pxToDp
3328
import com.facebook.react.uimanager.common.UIManagerType
@@ -36,12 +31,9 @@ import com.facebook.react.uimanager.drawable.BackgroundDrawable
3631
import com.facebook.react.uimanager.drawable.BackgroundImageDrawable
3732
import com.facebook.react.uimanager.drawable.BorderDrawable
3833
import com.facebook.react.uimanager.drawable.CompositeBackgroundDrawable
39-
import com.facebook.react.uimanager.drawable.ImageMaskDrawable
40-
import com.facebook.react.uimanager.drawable.GradientMaskDrawable
4134
import com.facebook.react.uimanager.drawable.InsetBoxShadowDrawable
4235
import com.facebook.react.uimanager.drawable.MIN_INSET_BOX_SHADOW_SDK_VERSION
4336
import com.facebook.react.uimanager.drawable.MIN_OUTSET_BOX_SHADOW_SDK_VERSION
44-
import com.facebook.react.uimanager.drawable.MaskDrawable
4537
import com.facebook.react.uimanager.drawable.OutlineDrawable
4638
import com.facebook.react.uimanager.drawable.OutsetBoxShadowDrawable
4739
import com.facebook.react.uimanager.style.BackgroundImageLayer
@@ -55,8 +47,6 @@ import com.facebook.react.uimanager.style.BorderStyle
5547
import com.facebook.react.uimanager.style.BoxShadow
5648
import com.facebook.react.uimanager.style.LogicalEdge
5749
import com.facebook.react.uimanager.style.OutlineStyle
58-
import com.facebook.react.views.image.MultiPostprocessor.Companion.from
59-
import com.facebook.react.views.imagehelper.ImageSource
6050

6151
/**
6252
* Utility object responsible for applying backgrounds, borders, and related visual effects to
@@ -821,40 +811,49 @@ public object BackgroundStyleApplicator {
821811
view.setLayerType(View.LAYER_TYPE_NONE, null)
822812
}
823813

824-
// Create new masks from array
825-
val newMasks = if (maskImage != null && maskImage.size() > 0) {
826-
val masks = mutableListOf<MaskDrawable>()
814+
// Parse mask image layers
815+
val maskLayers = if (maskImage != null && maskImage.size() > 0) {
816+
val layers = mutableListOf<BackgroundImageLayer>()
827817
for (i in 0 until maskImage.size()) {
828818
val maskImageMap = maskImage.getMap(i)
829-
val existingMask = composite.masks.getOrNull(i)
830-
val newMask = MaskDrawable.fromMaskImageArray(
831-
com.facebook.react.bridge.JavaOnlyArray.of(maskImageMap),
832-
view.context,
833-
existingMask
834-
)
835-
if (newMask != null) {
836-
masks.add(newMask)
819+
val layer = BackgroundImageLayer.parse(maskImageMap, view.context)
820+
if (layer != null) {
821+
layers.add(layer)
837822
}
838823
}
839-
masks
824+
layers
840825
} else {
841-
emptyList()
826+
null
842827
}
843828

844-
// Apply stored size/position/repeat if they were set before masks were created
845-
newMasks.forEachIndexed { index, mask ->
846-
composite.maskSizes.getOrNull(index)?.let { mask.setSize(it) }
847-
composite.maskPositions.getOrNull(index)?.let { mask.setPosition(it) }
848-
composite.maskRepeats.getOrNull(index)?.let { mask.setRepeat(it) }
829+
// Get or create mask drawable
830+
val maskDrawable = if (maskLayers != null && maskLayers.isNotEmpty()) {
831+
val existing = composite.mask ?: BackgroundImageDrawable(
832+
view.context,
833+
composite.borderRadius,
834+
composite.borderInsets
835+
)
836+
existing.backgroundImageLayers = maskLayers
837+
// Apply stored properties if they exist
838+
composite.maskSize?.let { existing.backgroundSize = it }
839+
composite.maskPosition?.let { existing.backgroundPosition = it }
840+
composite.maskRepeat?.let { existing.backgroundRepeat = it }
841+
existing
842+
} else {
843+
// Detach old mask if it exists
844+
composite.mask?.onDetach(view)
845+
null
849846
}
850847

851-
// Update composite with new masks (preserves maskSizes, maskPositions, maskRepeats)
852-
val updatedComposite = composite.withNewMasks(newMasks)
848+
// Update composite with new mask
849+
val updatedComposite = composite.withNewMask(maskDrawable)
853850
view.background = updatedComposite
854851

855-
if (view.width > 0 && view.height > 0) {
856-
updatedComposite.masks.forEach { mask ->
857-
mask.drawable.setBounds(0, 0, view.width, view.height)
852+
// Attach and set bounds if we have a mask
853+
if (maskDrawable != null) {
854+
maskDrawable.onAttach(view)
855+
if (view.width > 0 && view.height > 0) {
856+
maskDrawable.setBounds(0, 0, view.width, view.height)
858857
}
859858
}
860859
}
@@ -870,23 +869,22 @@ public object BackgroundStyleApplicator {
870869
val parsedSize = BackgroundSize.parse(sizes.getDynamic(i))
871870
sizeList.add(parsedSize)
872871
}
873-
sizeList
872+
sizeList.filterNotNull()
874873
} else {
875-
emptyList()
874+
null
876875
}
877876

878-
// Store in composite for later application (when masks are created)
879-
val updatedComposite = composite.withMaskSizes(parsedSizes)
877+
// Store in composite for later application when mask is created
878+
val updatedComposite = composite.withMaskSize(parsedSizes)
880879
view.background = updatedComposite
881880

882-
// Apply to existing masks if present
883-
updatedComposite.masks.forEachIndexed { index, mask ->
884-
parsedSizes.getOrNull(index)?.let { mask.setSize(it) }
885-
}
886-
887-
// Update bounds if view has dimensions and sizes are set
888-
if (view.width > 0 && view.height > 0 && parsedSizes.isNotEmpty()) {
889-
updateMaskBounds(view, view.width, view.height)
881+
// Apply to mask if it already exists
882+
updatedComposite.mask?.let { mask ->
883+
mask.backgroundSize = parsedSizes
884+
// Update bounds if view has dimensions
885+
if (view.width > 0 && view.height > 0) {
886+
updateMaskBounds(view, view.width, view.height)
887+
}
890888
}
891889
}
892890
}
@@ -902,19 +900,19 @@ public object BackgroundStyleApplicator {
902900
val parsedPosition = BackgroundPosition.parse(positionMap)
903901
positionList.add(parsedPosition)
904902
}
905-
positionList
903+
positionList.filterNotNull()
906904
} else {
907-
emptyList()
905+
null
908906
}
909907

910-
// Store in composite for later application (when masks are created)
911-
val updatedComposite = composite.withMaskPositions(parsedPositions)
908+
// Store in composite for later application when mask is created
909+
val updatedComposite = composite.withMaskPosition(parsedPositions)
912910
view.background = updatedComposite
913911

914-
// Apply to existing masks if present
915-
updatedComposite.masks.forEachIndexed { index, mask ->
916-
parsedPositions.getOrNull(index)?.let { mask.setPosition(it) }
917-
mask.drawable.invalidateSelf()
912+
// Apply to mask if it already exists
913+
updatedComposite.mask?.let { mask ->
914+
mask.backgroundPosition = parsedPositions
915+
mask.invalidateSelf()
918916
}
919917
}
920918
}
@@ -930,32 +928,25 @@ public object BackgroundStyleApplicator {
930928
val parsedRepeat = BackgroundRepeat.parse(repeatMap)
931929
repeatList.add(parsedRepeat)
932930
}
933-
repeatList
931+
repeatList.filterNotNull()
934932
} else {
935-
emptyList()
933+
null
936934
}
937935

938-
// Store in composite for later application (when masks are created)
939-
val updatedComposite = composite.withMaskRepeats(parsedRepeats)
936+
// Store in composite for later application when mask is created
937+
val updatedComposite = composite.withMaskRepeat(parsedRepeats)
940938
view.background = updatedComposite
941939

942-
// Apply to existing masks if present
943-
updatedComposite.masks.forEachIndexed { index, mask ->
944-
parsedRepeats.getOrNull(index)?.let { mask.setRepeat(it) }
945-
mask.drawable.invalidateSelf()
940+
// Apply to mask if it already exists
941+
updatedComposite.mask?.let { mask ->
942+
mask.backgroundRepeat = parsedRepeats
943+
mask.invalidateSelf()
946944
}
947945
}
948946
}
949947

950948
@JvmStatic
951-
public fun getMask(view: View): Drawable? = ensureCompositeBackgroundDrawable(view).masks.firstOrNull()?.drawable
952-
953-
@JvmStatic
954-
internal fun getMaskSize(view: View): BackgroundSize? {
955-
val composite = ensureCompositeBackgroundDrawable(view)
956-
// Return from first mask if it exists, otherwise return first stored value
957-
return composite.masks.firstOrNull()?.size ?: composite.maskSizes.firstOrNull()
958-
}
949+
public fun getMask(view: View): Drawable? = getCompositeBackgroundDrawable(view)?.mask
959950

960951
/**
961952
* Creates a Paint configured for Porter-Duff DST_IN mode mask compositing.
@@ -979,8 +970,8 @@ public object BackgroundStyleApplicator {
979970
*/
980971
@JvmStatic
981972
public fun drawWithMask(canvas: Canvas, view: View, drawContent: () -> Unit) {
982-
val masks = ensureCompositeBackgroundDrawable(view).masks
983-
if (masks.isEmpty() || view.width <= 0 || view.height <= 0) {
973+
val mask = getCompositeBackgroundDrawable(view)?.mask
974+
if (mask == null || view.width <= 0 || view.height <= 0) {
984975
drawContent()
985976
return
986977
}
@@ -993,24 +984,9 @@ public object BackgroundStyleApplicator {
993984
// Draw the content
994985
drawContent()
995986

996-
// Now apply the combined mask using DST_IN mode
997-
// All masks will be drawn with DST_IN, but we need them to combine as a union
998-
// The trick is to use SRC_OVER for combining masks, then DST_IN for the final composite
999-
1000-
// Create a separate layer for combining all masks
987+
// Now apply the mask using DST_IN mode
1001988
val maskPaint = createMaskPaint()
1002-
1003-
// For union of masks, we draw each mask normally (SRC_OVER) in a separate layer,
1004-
// then apply that layer with DST_IN
1005-
val maskLayerCount = canvas.saveLayer(bounds, maskPaint)
1006-
1007-
// Draw all masks - they combine with SRC_OVER (union)
1008-
masks.forEach { mask ->
1009-
mask.drawable.draw(canvas)
1010-
}
1011-
1012-
// Restore mask layer - this applies DST_IN to the content with the combined masks
1013-
canvas.restoreToCount(maskLayerCount)
989+
mask.drawWithMaskMode(canvas, maskPaint)
1014990

1015991
// Restore the main layer
1016992
canvas.restoreToCount(saveCount)
@@ -1033,9 +1009,8 @@ public object BackgroundStyleApplicator {
10331009
*/
10341010
@JvmStatic
10351011
public fun updateMaskBounds(view: View, w: Int, h: Int) {
1036-
val composite = ensureCompositeBackgroundDrawable(view)
1037-
composite.masks.forEach { mask ->
1038-
mask.drawable.setBounds(0, 0, w, h)
1012+
getCompositeBackgroundDrawable(view)?.mask?.let { mask ->
1013+
mask.setBounds(0, 0, w, h)
10391014
}
10401015
}
10411016

@@ -1046,9 +1021,10 @@ public object BackgroundStyleApplicator {
10461021
*/
10471022
@JvmStatic
10481023
public fun attachMask(view: View) {
1049-
ensureCompositeBackgroundDrawable(view).masks.forEach { mask ->
1050-
mask.onAttach(view)
1051-
}
1024+
val composite = getCompositeBackgroundDrawable(view)
1025+
composite?.mask?.onAttach(view)
1026+
// Also attach background image drawable if it exists
1027+
composite?.backgroundImage?.onAttach(view)
10521028
}
10531029

10541030
/**
@@ -1058,8 +1034,9 @@ public object BackgroundStyleApplicator {
10581034
*/
10591035
@JvmStatic
10601036
public fun detachMask(view: View) {
1061-
ensureCompositeBackgroundDrawable(view).masks.forEach { mask ->
1062-
mask.onDetach(view)
1063-
}
1037+
val composite = getCompositeBackgroundDrawable(view)
1038+
composite?.mask?.onDetach(view)
1039+
// Also detach background image drawable if it exists
1040+
composite?.backgroundImage?.onDetach(view)
10641041
}
10651042
}

0 commit comments

Comments
 (0)