@@ -20,14 +20,9 @@ import android.os.Build
2020import android.view.View
2121import android.widget.ImageView
2222import androidx.annotation.ColorInt
23- import com.facebook.imagepipeline.request.ImageRequest
24- import com.facebook.imagepipeline.request.ImageRequestBuilder
25- import com.facebook.imagepipeline.request.Postprocessor
2623import com.facebook.react.bridge.ReadableArray
27- import com.facebook.react.bridge.ReadableMap
2824import com.facebook.react.common.annotations.UnstableReactNativeAPI
2925import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
30- import com.facebook.react.modules.fresco.ReactNetworkImageRequest
3126import com.facebook.react.uimanager.PixelUtil.dpToPx
3227import com.facebook.react.uimanager.PixelUtil.pxToDp
3328import com.facebook.react.uimanager.common.UIManagerType
@@ -36,12 +31,9 @@ import com.facebook.react.uimanager.drawable.BackgroundDrawable
3631import com.facebook.react.uimanager.drawable.BackgroundImageDrawable
3732import com.facebook.react.uimanager.drawable.BorderDrawable
3833import com.facebook.react.uimanager.drawable.CompositeBackgroundDrawable
39- import com.facebook.react.uimanager.drawable.ImageMaskDrawable
40- import com.facebook.react.uimanager.drawable.GradientMaskDrawable
4134import com.facebook.react.uimanager.drawable.InsetBoxShadowDrawable
4235import com.facebook.react.uimanager.drawable.MIN_INSET_BOX_SHADOW_SDK_VERSION
4336import com.facebook.react.uimanager.drawable.MIN_OUTSET_BOX_SHADOW_SDK_VERSION
44- import com.facebook.react.uimanager.drawable.MaskDrawable
4537import com.facebook.react.uimanager.drawable.OutlineDrawable
4638import com.facebook.react.uimanager.drawable.OutsetBoxShadowDrawable
4739import com.facebook.react.uimanager.style.BackgroundImageLayer
@@ -55,8 +47,6 @@ import com.facebook.react.uimanager.style.BorderStyle
5547import com.facebook.react.uimanager.style.BoxShadow
5648import com.facebook.react.uimanager.style.LogicalEdge
5749import 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