Skip to content
Closed
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 @@ -127,6 +127,11 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
*/
experimental_mixBlendMode: true,

/**
* Isolation
*/
isolation: true,

/*
* BoxShadow
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ const validAttributesForNonEventProps = {
process: require('../StyleSheet/processFilter').default,
},
experimental_mixBlendMode: true,
isolation: true,
opacity: true,
elevation: true,
shadowColor: {process: require('../StyleSheet/processColor').default},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ const validAttributesForNonEventProps = {
experimental_boxShadow: {
process: require('../StyleSheet/processBoxShadow').default,
},
experimental_mixBlendMode: true,
isolation: true,

borderTopWidth: true,
borderTopColor: {process: require('../StyleSheet/processColor').default},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export interface ViewStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
* Controls whether the View can be the target of touch events.
*/
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined;
isolation?: 'auto' | 'isolate' | undefined;
cursor?: CursorValue | undefined;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
experimental_filter?: $ReadOnlyArray<FilterFunction> | string,
experimental_mixBlendMode?: ____BlendMode_Internal,
experimental_backgroundImage?: $ReadOnlyArray<GradientValue> | string,
isolation?: 'auto' | 'isolate',
}>;

export type ____ViewStyle_Internal = $ReadOnly<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8408,6 +8408,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
experimental_filter?: $ReadOnlyArray<FilterFunction> | string,
experimental_mixBlendMode?: ____BlendMode_Internal,
experimental_backgroundImage?: $ReadOnlyArray<GradientValue> | string,
isolation?: \\"auto\\" | \\"isolate\\",
}>;
export type ____ViewStyle_Internal = $ReadOnly<{
...____ViewStyle_InternalCore,
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -8221,6 +8221,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
public fun dispatchGenericMotionEvent (Landroid/view/MotionEvent;)Z
public fun dispatchProvideStructure (Landroid/view/ViewStructure;)V
protected fun dispatchSetPressed (Z)V
public fun draw (Landroid/graphics/Canvas;)V
protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
protected fun getChildDrawingOrder (II)I
public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<275859c437f60e7733da2b238c911152>>
* @generated SignedSource<<b352d8e6c8dd42936ee48024320f6b77>>
*/

/**
Expand Down Expand Up @@ -70,6 +70,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableAlignItemsBaselineOnFabricIOS(): Boolean = accessor.enableAlignItemsBaselineOnFabricIOS()

/**
* Enables mix-blend-mode prop on Android.
*/
@JvmStatic
public fun enableAndroidMixBlendModeProp(): Boolean = accessor.enableAndroidMixBlendModeProp()

/**
* Use BackgroundStyleApplicator in place of other background/border drawing code
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e58ecac6e67cd870b8ac077cbdfd7469>>
* @generated SignedSource<<50f259bf80948b672a8677307456d413>>
*/

/**
Expand All @@ -27,6 +27,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null
private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null
private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null
private var enableAndroidMixBlendModePropCache: Boolean? = null
private var enableBackgroundStyleApplicatorCache: Boolean? = null
private var enableCleanTextInputYogaNodeCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
Expand Down Expand Up @@ -128,6 +129,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}

override fun enableAndroidMixBlendModeProp(): Boolean {
var cached = enableAndroidMixBlendModePropCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableAndroidMixBlendModeProp()
enableAndroidMixBlendModePropCache = cached
}
return cached
}

override fun enableBackgroundStyleApplicator(): Boolean {
var cached = enableBackgroundStyleApplicatorCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1d79b037e6e8cf2e7306b7f5b3798b9a>>
* @generated SignedSource<<3b5998555d0f27d3f83923cb516edb36>>
*/

/**
Expand Down Expand Up @@ -42,6 +42,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun enableAlignItemsBaselineOnFabricIOS(): Boolean

@DoNotStrip @JvmStatic public external fun enableAndroidMixBlendModeProp(): Boolean

@DoNotStrip @JvmStatic public external fun enableBackgroundStyleApplicator(): Boolean

@DoNotStrip @JvmStatic public external fun enableCleanTextInputYogaNode(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c9d11f2cf414d546f5c9be0a05b22e85>>
* @generated SignedSource<<86db65691c73cff2843e81e053268892>>
*/

/**
Expand Down Expand Up @@ -37,6 +37,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun enableAlignItemsBaselineOnFabricIOS(): Boolean = true

override fun enableAndroidMixBlendModeProp(): Boolean = false

override fun enableBackgroundStyleApplicator(): Boolean = true

override fun enableCleanTextInputYogaNode(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<408d66cdad8b708c06ca844cd6524ba4>>
* @generated SignedSource<<b460c7106715c0ea405dd6b284802d27>>
*/

/**
Expand All @@ -31,6 +31,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null
private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null
private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null
private var enableAndroidMixBlendModePropCache: Boolean? = null
private var enableBackgroundStyleApplicatorCache: Boolean? = null
private var enableCleanTextInputYogaNodeCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
Expand Down Expand Up @@ -139,6 +140,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun enableAndroidMixBlendModeProp(): Boolean {
var cached = enableAndroidMixBlendModePropCache
if (cached == null) {
cached = currentProvider.enableAndroidMixBlendModeProp()
accessedFeatureFlags.add("enableAndroidMixBlendModeProp")
enableAndroidMixBlendModePropCache = cached
}
return cached
}

override fun enableBackgroundStyleApplicator(): Boolean {
var cached = enableBackgroundStyleApplicatorCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<040b9cb9cec727e2eb31769712980528>>
* @generated SignedSource<<7260472dccd3ec3c9612727c6c337f24>>
*/

/**
Expand Down Expand Up @@ -37,6 +37,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun enableAlignItemsBaselineOnFabricIOS(): Boolean

@DoNotStrip public fun enableAndroidMixBlendModeProp(): Boolean

@DoNotStrip public fun enableBackgroundStyleApplicator(): Boolean

@DoNotStrip public fun enableCleanTextInputYogaNode(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

package com.facebook.react.uimanager;

import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
Expand Down Expand Up @@ -118,7 +117,8 @@ public BaseViewManager(@Nullable ReactApplicationContext reactContext) {

view.setTag(R.id.use_hardware_layer, null);
view.setTag(R.id.filter, null);
LayerEffectsHelper.apply(view, null, null, null);
view.setTag(R.id.mix_blend_mode, null);
LayerEffectsHelper.apply(view, null, null);

// setShadowColor
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Expand Down Expand Up @@ -522,10 +522,7 @@ public void setAccessibilityLiveRegion(@NonNull T view, @Nullable String liveReg
// hitting the unknown BlendMode type
private static class LayerEffectsHelper {
public static void apply(
@NonNull View view,
@Nullable ReadableArray filter,
@Nullable BlendMode mixBlendMode,
@Nullable Boolean useHWLayer) {
@NonNull View view, @Nullable ReadableArray filter, @Nullable Boolean useHWLayer) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
view.setRenderEffect(null);
}
Expand All @@ -541,11 +538,6 @@ public static void apply(
}
}

if (mixBlendMode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
p = p == null ? new Paint() : p;
p.setBlendMode(mixBlendMode);
}

if (p == null) {
int layerType =
useHWLayer != null && useHWLayer ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
Expand Down Expand Up @@ -666,13 +658,9 @@ protected void onAfterUpdateTransaction(@NonNull T view) {
}

ReadableArray filter = (ReadableArray) view.getTag(R.id.filter);
BlendMode mixBlendMode =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? (BlendMode) view.getTag(R.id.mix_blend_mode)
: null;
Boolean useHWLayer = (Boolean) view.getTag(R.id.use_hardware_layer);

LayerEffectsHelper.apply(view, filter, mixBlendMode, useHWLayer);
LayerEffectsHelper.apply(view, filter, useHWLayer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ package com.facebook.react.uimanager

import android.annotation.TargetApi
import android.graphics.BlendMode
import android.os.Build

@TargetApi(29)
internal object BlendModeHelper {

/** @see https://www.w3.org/TR/compositing-1/#mix-blend-mode */
@JvmStatic
public fun parseMixBlendMode(mixBlendMode: String?): BlendMode? {
mixBlendMode ?: return null
if (mixBlendMode == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return null
}

return when (mixBlendMode) {
"normal" -> null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -27,6 +30,7 @@
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.R;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
Expand Down Expand Up @@ -819,6 +823,20 @@ private void removeFromArray(int index) {
}
}

private boolean needsIsolatedLayer() {
if (!ReactNativeFeatureFlags.enableAndroidMixBlendModeProp()) {
return false;
}

for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i).getTag(R.id.mix_blend_mode) != null) {
return true;
}
}

return false;
}

@VisibleForTesting
public int getBackgroundColor() {
if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) {
Expand Down Expand Up @@ -892,6 +910,13 @@ public void setOverflow(@Nullable String overflow) {

@Override
public void setOverflowInset(int left, int top, int right, int bottom) {
if (needsIsolatedLayer()
&& (mOverflowInset.left != left
|| mOverflowInset.top != top
|| mOverflowInset.right != right
|| mOverflowInset.bottom != bottom)) {
invalidate();
}
mOverflowInset.set(left, top, right, bottom);
}

Expand All @@ -911,6 +936,29 @@ public Rect getOverflowInset() {
super.setBackground(drawable);
}

@Override
public void draw(Canvas canvas) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& ViewUtil.getUIManagerType(this) == UIManagerType.FABRIC
&& needsIsolatedLayer()) {

// Check if the view is a stacking context and has children, if it does, do the rendering
// offscreen and then composite back. This follows the idea of group isolation on blending
// https://www.w3.org/TR/compositing-1/#isolationblending
Rect overflowInset = getOverflowInset();
canvas.saveLayer(
overflowInset.left,
overflowInset.top,
getWidth() + -overflowInset.right,
getHeight() + -overflowInset.bottom,
null);
super.draw(canvas);
canvas.restore();
} else {
super.draw(canvas);
}
}

@Override
protected void dispatchDraw(Canvas canvas) {
if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) {
Expand Down Expand Up @@ -950,8 +998,28 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
CanvasUtil.enableZ(canvas, true);
}

BlendMode mixBlendMode = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && needsIsolatedLayer()) {
mixBlendMode = (BlendMode) child.getTag(R.id.mix_blend_mode);
if (mixBlendMode != null) {
Paint p = new Paint();
p.setBlendMode(mixBlendMode);
Rect overflowInset = getOverflowInset();
canvas.saveLayer(
overflowInset.left,
overflowInset.top,
getWidth() + -overflowInset.right,
getHeight() + -overflowInset.bottom,
p);
}
}

boolean result = super.drawChild(canvas, child, drawingTime);

if (mixBlendMode != null) {
canvas.restore();
}

if (drawWithZ) {
CanvasUtil.enableZ(canvas, false);
}
Expand Down
Loading