From 13c741434ea374f4137511e00c7ea2d3466bd935 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Thu, 15 Feb 2024 16:34:46 -0700 Subject: [PATCH 1/6] add support to display-p3 backgrounds and borders to android view --- .../react/uimanager/BaseViewManager.java | 33 +++- .../uimanager/BaseViewManagerInterface.java | 2 + .../facebook/react/uimanager/BorderColor.java | 176 ++++++++++++++++++ .../uimanager/ViewManagersPropertyCache.java | 61 ++++++ .../view/ReactViewBackgroundDrawable.java | 2 +- .../view/ReactViewBackgroundManager.java | 17 +- .../react/views/view/ReactViewGroup.java | 12 +- .../react/views/view/ReactViewManager.java | 8 +- 8 files changed, 295 insertions(+), 16 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 3e3d86e87dcf0c..3fef764d881ed4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -30,6 +30,7 @@ import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.PointerEventHelper; import com.facebook.react.uimanager.util.ReactFindViewUtil; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -42,6 +43,7 @@ public abstract class BaseViewManager extends ViewManager implements BaseViewManagerInterface, View.OnLayoutChangeListener { + private static final String TAG = "BaseViewManager"; private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2; private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = (float) Math.sqrt(5); @@ -164,14 +166,26 @@ public void onLayoutChange( } @Override - @ReactProp( - name = ViewProps.BACKGROUND_COLOR, - defaultInt = Color.TRANSPARENT, - customType = "Color") public void setBackgroundColor(@NonNull T view, int backgroundColor) { view.setBackgroundColor(backgroundColor); } + @ReactProp( + name = ViewProps.BACKGROUND_COLOR, + defaultInt = Color.TRANSPARENT, + customType = "Color") + public void setBackgroundColor(@NonNull T view, long backgroundColor) { + try { + Method setBackgroundColorMethod = view.getClass().getMethod("setBackgroundColor", long.class); + setBackgroundColorMethod.invoke(view, backgroundColor); + } catch (NoSuchMethodException e) { + FLog.e(TAG, "setBackgroundColor(long) method not found in view class", e); + view.setBackgroundColor(Color.toArgb(backgroundColor)); + } catch (Exception e) { + FLog.e(TAG, "Failed to invoke setBackgroundColor(long)", e); + } + } + @Override @ReactProp(name = ViewProps.TRANSFORM) public void setTransform(@NonNull T view, @Nullable ReadableArray matrix) { @@ -204,7 +218,6 @@ public void setElevation(@NonNull T view, float elevation) { } @Override - @ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color") public void setShadowColor(@NonNull T view, int shadowColor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { view.setOutlineAmbientShadowColor(shadowColor); @@ -212,6 +225,14 @@ public void setShadowColor(@NonNull T view, int shadowColor) { } } + @ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color") + public void setShadowColor(@NonNull T view, long shadowColor) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + view.setOutlineAmbientShadowColor(Color.toArgb(shadowColor)); + view.setOutlineSpotShadowColor(Color.toArgb(shadowColor)); + } + } + @Override @ReactProp(name = ViewProps.Z_INDEX) public void setZIndex(@NonNull T view, float zIndex) { @@ -856,4 +877,4 @@ public void setTouchEnd(@NonNull T view, boolean value) { public void setTouchCancel(@NonNull T view, boolean value) { // no-op, handled by JSResponder } -} +} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java index 6ef232813dbc3f..2f8208a4012e31 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java @@ -35,6 +35,7 @@ public interface BaseViewManagerInterface { void setViewState(T view, @Nullable ReadableMap accessibilityState); void setBackgroundColor(T view, int backgroundColor); + void setBackgroundColor(T view, long backgroundColor); void setBorderRadius(T view, float borderRadius); @@ -49,6 +50,7 @@ public interface BaseViewManagerInterface { void setElevation(T view, float elevation); void setShadowColor(T view, int shadowColor); + void setShadowColor(T view, long shadowColor); void setImportantForAccessibility(T view, @Nullable String importantForAccessibility); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java new file mode 100644 index 00000000000000..d0a9e74be339cd --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java @@ -0,0 +1,176 @@ +/* + * 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; +import android.graphics.Color; +import java.util.Arrays; + +/** + * Class representing CSS spacing border colors. Modified from {@link Spacing} to support long values. + */ +public class BorderColor { + + /** Spacing type that represents the left direction. E.g. {@code marginLeft}. */ + public static final int LEFT = 0; + /** Spacing type that represents the top direction. E.g. {@code marginTop}. */ + public static final int TOP = 1; + /** Spacing type that represents the right direction. E.g. {@code marginRight}. */ + public static final int RIGHT = 2; + /** Spacing type that represents the bottom direction. E.g. {@code marginBottom}. */ + public static final int BOTTOM = 3; + /** + * Spacing type that represents start direction e.g. left in left-to-right, right in + * right-to-left. + */ + public static final int START = 4; + /** + * Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left. + */ + public static final int END = 5; + /** + * Spacing type that represents horizontal direction (left and right). E.g. {@code + * marginHorizontal}. + */ + public static final int HORIZONTAL = 6; + /** + * Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}. + */ + public static final int VERTICAL = 7; + /** + * Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}. + */ + public static final int ALL = 8; + /** Spacing type that represents block directions (top, bottom). E.g. {@code marginBlock}. */ + public static final int BLOCK = 9; + /** Spacing type that represents the block end direction (bottom). E.g. {@code marginBlockEnd}. */ + public static final int BLOCK_END = 10; + /** + * Spacing type that represents the block start direction (top). E.g. {@code marginBlockStart}. + */ + public static final int BLOCK_START = 11; + + private static final int[] sFlagsMap = { + 1, /*LEFT*/ 2, /*TOP*/ 4, /*RIGHT*/ 8, /*BOTTOM*/ 16, /*START*/ 32, /*END*/ 64, /*HORIZONTAL*/ + 128, /*VERTICAL*/ 256, /*ALL*/ 512, /*BLOCK*/ 1024, /*BLOCK_END*/ 2048, /*BLOCK_START*/ + }; + + private final long[] mSpacing; + private int mValueFlags = 0; + private final long mDefaultValue; + private boolean mHasAliasesSet; + + public BorderColor() { + this(0); + } + + public BorderColor(long defaultValue) { + mDefaultValue = defaultValue; + mSpacing = newFullBorderColorArray(); + } + + public BorderColor(BorderColor original) { + mDefaultValue = original.mDefaultValue; + mSpacing = Arrays.copyOf(original.mSpacing, original.mSpacing.length); + mValueFlags = original.mValueFlags; + mHasAliasesSet = original.mHasAliasesSet; + } + + /** + * Set a spacing value. + * + * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link + * #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL} + * @param value the value for this direction + * @return {@code true} if the spacing has changed, or {@code false} if the same value was already + * set + */ + public boolean set(int spacingType, long value) { + if (mSpacing[spacingType] != value) { + mSpacing[spacingType] = value; + + mValueFlags |= sFlagsMap[spacingType]; + + mHasAliasesSet = + (mValueFlags & sFlagsMap[ALL]) != 0 + || (mValueFlags & sFlagsMap[VERTICAL]) != 0 + || (mValueFlags & sFlagsMap[HORIZONTAL]) != 0 + || (mValueFlags & sFlagsMap[BLOCK]) != 0; + + return true; + } + + return false; + } + + /** + * Get the spacing for a direction. This takes into account any default values that have been set. + * + * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM} + */ + public long get(int spacingType) { + long defaultValue = + (spacingType == START + || spacingType == END + || spacingType == BLOCK + || spacingType == BLOCK_END + || spacingType == BLOCK_START + ? 0 + : mDefaultValue); + + if (mValueFlags == 0) { + return defaultValue; + } + + if ((mValueFlags & sFlagsMap[spacingType]) != 0) { + return mSpacing[spacingType]; + } + + if (mHasAliasesSet) { + int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL; + if ((mValueFlags & sFlagsMap[secondType]) != 0) { + return mSpacing[secondType]; + } else if ((mValueFlags & sFlagsMap[ALL]) != 0) { + return mSpacing[ALL]; + } + } + + return defaultValue; + } + + /** + * Get the raw value (that was set using {@link #set(int, float)}), without taking into account + * any default values. + * + * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link + * #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL} + */ + public long getRaw(int spacingType) { + return mSpacing[spacingType]; + } + + /** + * Resets the spacing instance to its default state. This method is meant to be used when + * recycling {@link Spacing} instances. + */ + public void reset() { + Arrays.fill(mSpacing, 0); + mHasAliasesSet = false; + mValueFlags = 0; + } + + /** + * Try to get start value and fallback to given type if not defined. This is used privately by the + * layout engine as a more efficient way to fetch direction-aware values by avoid extra method + * invocations. + */ + float getWithFallback(int spacingType, int fallbackType) { + return (mValueFlags & sFlagsMap[spacingType]) != 0 ? mSpacing[spacingType] : get(fallbackType); + } + + private static long[] newFullBorderColorArray() { + return new long[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + } +} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java index 09ae7a272ad0e6..7f7cf4db3a2a59 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java @@ -8,6 +8,7 @@ package com.facebook.react.uimanager; import android.content.Context; +import android.graphics.Color; import android.view.View; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; @@ -219,6 +220,34 @@ protected Object getValueOrDefault(Object value, Context context) { } } + private static class ColorLongPropSetter extends PropSetter { + + private final int mDefaultValue; + + public ColorLongPropSetter(ReactProp prop, Method setter) { + this(prop, setter, 0); + } + + public ColorLongPropSetter(ReactProp prop, Method setter, int defaultValue) { + super(prop, "mixed", setter); + mDefaultValue = defaultValue; + } + + public ColorLongPropSetter(ReactPropGroup prop, Method setter, int index, int defaultValue) { + super(prop, "mixed", setter, index); + mDefaultValue = defaultValue; + } + + @Override + protected Object getValueOrDefault(Object value, Context context) { + Color color = ColorPropConverter.getColorInstance(value, context); + if (color == null) { + return mDefaultValue; + } + return color.pack(); + } + } + private static class BooleanPropSetter extends PropSetter { private final boolean mDefaultValue; @@ -349,6 +378,26 @@ public BoxedColorPropSetter(ReactPropGroup prop, Method setter, int index) { } } + private static class BoxedColorLongPropSetter extends PropSetter { + + public BoxedColorLongPropSetter(ReactProp prop, Method setter) { + super(prop, "mixed", setter); + } + + public BoxedColorLongPropSetter(ReactPropGroup prop, Method setter, int index) { + super(prop, "mixed", setter, index); + } + + @Override + protected @Nullable Object getValueOrDefault(Object value, Context context) { + Color color = ColorPropConverter.getColorInstance(value, context); + if (color != null) { + return color.pack(); + } + return null; + } + } + /*package*/ static Map getNativePropsForView( Class viewManagerTopClass, Class shadowNodeTopClass) { @@ -437,6 +486,8 @@ private static PropSetter createPropSetter( return new ColorPropSetter(annotation, method, annotation.defaultInt()); } return new IntPropSetter(annotation, method, annotation.defaultInt()); + } else if (propTypeClass == long.class && "Color".equals(annotation.customType())) { + return new ColorLongPropSetter(annotation, method, annotation.defaultInt()); } else if (propTypeClass == float.class) { return new FloatPropSetter(annotation, method, annotation.defaultFloat()); } else if (propTypeClass == double.class) { @@ -450,6 +501,8 @@ private static PropSetter createPropSetter( return new BoxedColorPropSetter(annotation, method); } return new BoxedIntPropSetter(annotation, method); + } else if (propTypeClass == Long.class && "Color".equals(annotation.customType())) { + return new BoxedColorLongPropSetter(annotation, method); } else if (propTypeClass == ReadableArray.class) { return new ArrayPropSetter(annotation, method); } else if (propTypeClass == ReadableMap.class) { @@ -483,6 +536,10 @@ private static void createPropSetters( props.put(names[i], new IntPropSetter(annotation, method, i, annotation.defaultInt())); } } + } else if (propTypeClass == long.class && "Color".equals(annotation.customType())) { + for (int i = 0; i < names.length; i++) { + props.put(names[i], new ColorLongPropSetter(annotation, method, i, annotation.defaultInt())); + } } else if (propTypeClass == float.class) { for (int i = 0; i < names.length; i++) { props.put(names[i], new FloatPropSetter(annotation, method, i, annotation.defaultFloat())); @@ -500,6 +557,10 @@ private static void createPropSetters( props.put(names[i], new BoxedIntPropSetter(annotation, method, i)); } } + } else if (propTypeClass == Long.class && "Color".equals(annotation.customType())) { + for (int i = 0; i < names.length; i++) { + props.put(names[i], new BoxedColorLongPropSetter(annotation, method, i)); + } } else { throw new RuntimeException( "Unrecognized type: " diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index f9c43850864a56..9bba3490881195 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -20,4 +20,4 @@ public class ReactViewBackgroundDrawable extends CSSBackgroundDrawable { public ReactViewBackgroundDrawable(Context context) { super(context); } -} +} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java index 5c616597d502cd..25aaf78fdb9716 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java @@ -57,6 +57,14 @@ public void setBackgroundColor(int color) { } } + public void setBackgroundColor(long color) { + if (color == Color.pack(Color.TRANSPARENT) && mReactBackgroundDrawable == null) { + // don't do anything, no need to allocate ReactBackgroundDrawable for transparent background + } else { + getOrCreateReactViewBackground().setColor(color); + } + } + public int getBackgroundColor() { return mColor; } @@ -66,11 +74,16 @@ public void setBorderWidth(int position, float width) { } public void setBorderColor(int position, float color, float alpha) { - getOrCreateReactViewBackground().setBorderColor(position, color, alpha); + int combinedColor = (Math.round(alpha) << 24) | (Math.round(color) & 0x00FFFFFF); + getOrCreateReactViewBackground().setBorderColor(position, Color.pack(combinedColor)); + } + + public void setBorderColor(int position, long color) { + getOrCreateReactViewBackground().setBorderColor(position, color); } public int getBorderColor(int position) { - return getOrCreateReactViewBackground().getBorderColor(position); + return Color.toArgb(getOrCreateReactViewBackground().getBorderColor(position)); } public void setBorderRadius(float borderRadius) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index cd467967563ddb..ee9a1dcc198ec1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -232,6 +232,14 @@ public void setBackgroundColor(int color) { } } + public void setBackgroundColor(long color) { + if (color == Color.pack(Color.TRANSPARENT) && mReactBackgroundDrawable == null) { + // don't do anything, no need to allocate ReactBackgroundDrawable for transparent background + } else { + getOrCreateReactViewBackground().setColor(color); + } + } + @Override public void setBackground(Drawable drawable) { throw new UnsupportedOperationException( @@ -313,8 +321,8 @@ public void setBorderWidth(int position, float width) { getOrCreateReactViewBackground().setBorderWidth(position, width); } - public void setBorderColor(int position, float rgb, float alpha) { - getOrCreateReactViewBackground().setBorderColor(position, rgb, alpha); + public void setBorderColor(int position, long color) { + getOrCreateReactViewBackground().setBorderColor(position, color); } /** diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index dde735192aafa6..423f0e0ecac312 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -252,11 +252,9 @@ public void setBorderWidth(ReactViewGroup view, int index, float width) { ViewProps.BORDER_BLOCK_START_COLOR }, customType = "Color") - public void setBorderColor(ReactViewGroup view, int index, Integer color) { - float rgbComponent = - color == null ? YogaConstants.UNDEFINED : (float) ((int) color & 0x00FFFFFF); - float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int) color >>> 24); - view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent); + public void setBorderColor(ReactViewGroup view, int index, Long color) { + long borderColor = color == null ? 0 : color; + view.setBorderColor(SPACING_TYPES[index], borderColor); } @ReactProp(name = ViewProps.COLLAPSABLE) From 28ac2ec958dbf440d2b23a8b86bf80ba36e62cbd Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 16 Apr 2024 18:25:01 -0600 Subject: [PATCH 2/6] move updates from ReactViewBackgroundDrawable.java to CSSBackgroundDrawable.java --- .../drawable/CSSBackgroundDrawable.java | 193 +++++++++--------- 1 file changed, 96 insertions(+), 97 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java index 21094fc73d5a3b..146843a2c4b287 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java @@ -11,21 +11,25 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ColorSpace; import android.graphics.DashPathEffect; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathEffect; +import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.view.View; +import androidx.annotation.ColorLong; import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.modules.i18nmanager.I18nUtil; +import com.facebook.react.uimanager.BorderColor; import com.facebook.react.uimanager.FloatUtil; import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.style.BorderRadiusProp; @@ -47,9 +51,7 @@ */ public class CSSBackgroundDrawable extends Drawable { - private static final int DEFAULT_BORDER_COLOR = Color.BLACK; - private static final int DEFAULT_BORDER_RGB = 0x00FFFFFF & DEFAULT_BORDER_COLOR; - private static final int DEFAULT_BORDER_ALPHA = (0xFF000000 & DEFAULT_BORDER_COLOR) >>> 24; + private static final long DEFAULT_BORDER_COLOR = Color.pack(Color.BLACK); // ~0 == 0xFFFFFFFF, all bits set to 1. private static final int ALL_BITS_SET = ~0; // 0 == 0x00000000, all bits set to 0. @@ -81,8 +83,7 @@ private enum BorderStyle { /* Value at Spacing.ALL index used for rounded borders, whole array used by rectangular borders */ private @Nullable Spacing mBorderWidth; - private @Nullable Spacing mBorderRGB; - private @Nullable Spacing mBorderAlpha; + private @Nullable BorderColor mBorderColor; private @Nullable BorderStyle mBorderStyle; private @Nullable Path mInnerClipPathForBorderRadius; @@ -104,7 +105,7 @@ private enum BorderStyle { /* Used by all types of background and for drawing borders */ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private int mColor = Color.TRANSPARENT; + private long mColor = Color.pack(Color.TRANSPARENT); private int mAlpha = 255; // There is a small gap between the edges of adjacent paths @@ -154,6 +155,13 @@ public int getAlpha() { return mAlpha; } + public long useColor() { + Color color = Color.valueOf(mColor); + float colorAlpha = color.alpha(); + float combinedAlpha = colorAlpha * (mAlpha / 255.0f); + return Color.pack(color.red(), color.green(), color.blue(), combinedAlpha, color.getColorSpace()); + } + @Override public void setColorFilter(ColorFilter cf) { // do nothing @@ -161,7 +169,14 @@ public void setColorFilter(ColorFilter cf) { @Override public int getOpacity() { - return (Color.alpha(mColor) * mAlpha) >> 8; + float alpha = Color.valueOf(mColor).alpha() * (mAlpha / 255.0f); + if (alpha == 1.0f) { + return PixelFormat.OPAQUE; + } else if (alpha == 0.0f) { + return PixelFormat.TRANSPARENT; + } else { + return PixelFormat.TRANSLUCENT; + } } /* Android's elevation implementation requires this to be implemented to know where to draw the shadow. */ @@ -196,32 +211,15 @@ public void setBorderWidth(int position, float width) { } } - public void setBorderColor(int position, float rgb, float alpha) { - this.setBorderRGB(position, rgb); - this.setBorderAlpha(position, alpha); - mNeedUpdatePathForBorderRadius = true; - } - - private void setBorderRGB(int position, float rgb) { - // set RGB component - if (mBorderRGB == null) { - mBorderRGB = new Spacing(DEFAULT_BORDER_RGB); - } - if (!FloatUtil.floatsEqual(mBorderRGB.getRaw(position), rgb)) { - mBorderRGB.set(position, rgb); - invalidateSelf(); - } - } - - private void setBorderAlpha(int position, float alpha) { - // set Alpha component - if (mBorderAlpha == null) { - mBorderAlpha = new Spacing(DEFAULT_BORDER_ALPHA); + public void setBorderColor(int position, long color) { + if (mBorderColor == null) { + mBorderColor = new BorderColor(DEFAULT_BORDER_COLOR); } - if (!FloatUtil.floatsEqual(mBorderAlpha.getRaw(position), alpha)) { - mBorderAlpha.set(position, alpha); + if (mBorderColor.getRaw(position) != color) { + mBorderColor.set(position, color); invalidateSelf(); } + mNeedUpdatePathForBorderRadius = true; } public void setBorderStyle(@Nullable String style) { @@ -267,6 +265,11 @@ public BorderRadiusStyle getBorderRadius() { } public void setColor(int color) { + mColor = Color.pack(color); + invalidateSelf(); + } + + public void setColor(long color) { mColor = color; invalidateSelf(); } @@ -292,7 +295,7 @@ public boolean onResolvedLayoutDirectionChanged(int layoutDirection) { @VisibleForTesting public int getColor() { - return mColor; + return Color.toArgb(mColor); } public Path borderBoxPath() { @@ -313,22 +316,22 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { canvas.clipPath(mOuterClipPathForBorderRadius, Region.Op.INTERSECT); // Draws the View without its border first (with background color fill) - int useColor = ColorUtils.setAlphaComponent(mColor, getOpacity()); - if (Color.alpha(useColor) != 0) { // color is not transparent - mPaint.setColor(useColor); + long color = useColor(); + if (Color.alpha(color) != 0.0f) { // color is not transparent + mPaint.setColor(color); mPaint.setStyle(Paint.Style.FILL); canvas.drawPath(mBackgroundColorRenderPath, mPaint); } final RectF borderWidth = getDirectionAwareBorderInsets(); - int colorLeft = getBorderColor(Spacing.LEFT); - int colorTop = getBorderColor(Spacing.TOP); - int colorRight = getBorderColor(Spacing.RIGHT); - int colorBottom = getBorderColor(Spacing.BOTTOM); + long colorLeft = getBorderColor(Spacing.LEFT); + long colorTop = getBorderColor(Spacing.TOP); + long colorRight = getBorderColor(Spacing.RIGHT); + long colorBottom = getBorderColor(Spacing.BOTTOM); - int colorBlock = getBorderColor(Spacing.BLOCK); - int colorBlockStart = getBorderColor(Spacing.BLOCK_START); - int colorBlockEnd = getBorderColor(Spacing.BLOCK_END); + long colorBlock = getBorderColor(Spacing.BLOCK); + long colorBlockStart = getBorderColor(Spacing.BLOCK_START); + long colorBlockEnd = getBorderColor(Spacing.BLOCK_END); if (isBorderColorDefined(Spacing.BLOCK)) { colorBottom = colorBlock; @@ -348,7 +351,7 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { // If it's a full and even border draw inner rect path with stroke final float fullBorderWidth = getFullBorderWidth(); - int borderColor = getBorderColor(Spacing.ALL); + long borderColor = getBorderColor(Spacing.ALL); if (borderWidth.top == fullBorderWidth && borderWidth.bottom == fullBorderWidth && borderWidth.left == fullBorderWidth @@ -358,7 +361,14 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { && colorRight == borderColor && colorBottom == borderColor) { if (fullBorderWidth > 0) { - mPaint.setColor(multiplyColorAlpha(borderColor, mAlpha)); + long alphaAdjustedBorderColor = + Color.pack( + Color.red(borderColor), + Color.green(borderColor), + Color.blue(borderColor), + Color.alpha(borderColor) * (mAlpha / 255.0f), + Color.colorSpace(borderColor)); + mPaint.setColor(alphaAdjustedBorderColor); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(fullBorderWidth); canvas.drawPath(mCenterDrawPath, mPaint); @@ -372,8 +382,8 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { canvas.clipPath(mInnerClipPathForBorderRadius, Region.Op.DIFFERENCE); final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - int colorStart = getBorderColor(Spacing.START); - int colorEnd = getBorderColor(Spacing.END); + long colorStart = getBorderColor(Spacing.START); + long colorEnd = getBorderColor(Spacing.END); if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(mContext)) { if (!isBorderColorDefined(Spacing.START)) { @@ -384,14 +394,14 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { colorEnd = colorRight; } - final int directionAwareColorLeft = isRTL ? colorEnd : colorStart; - final int directionAwareColorRight = isRTL ? colorStart : colorEnd; + final long directionAwareColorLeft = isRTL ? colorEnd : colorStart; + final long directionAwareColorRight = isRTL ? colorStart : colorEnd; colorLeft = directionAwareColorLeft; colorRight = directionAwareColorRight; } else { - final int directionAwareColorLeft = isRTL ? colorEnd : colorStart; - final int directionAwareColorRight = isRTL ? colorStart : colorEnd; + final long directionAwareColorLeft = isRTL ? colorEnd : colorStart; + final long directionAwareColorRight = isRTL ? colorStart : colorEnd; final boolean isColorStartDefined = isBorderColorDefined(Spacing.START); final boolean isColorEndDefined = isBorderColorDefined(Spacing.END); @@ -529,15 +539,15 @@ private void updatePath() { final RectF borderWidth = getDirectionAwareBorderInsets(); - int colorLeft = getBorderColor(Spacing.LEFT); - int colorTop = getBorderColor(Spacing.TOP); - int colorRight = getBorderColor(Spacing.RIGHT); - int colorBottom = getBorderColor(Spacing.BOTTOM); - int borderColor = getBorderColor(Spacing.ALL); + long colorLeft = getBorderColor(Spacing.LEFT); + long colorTop = getBorderColor(Spacing.TOP); + long colorRight = getBorderColor(Spacing.RIGHT); + long colorBottom = getBorderColor(Spacing.BOTTOM); + long borderColor = getBorderColor(Spacing.ALL); - int colorBlock = getBorderColor(Spacing.BLOCK); - int colorBlockStart = getBorderColor(Spacing.BLOCK_START); - int colorBlockEnd = getBorderColor(Spacing.BLOCK_END); + long colorBlock = getBorderColor(Spacing.BLOCK); + long colorBlockStart = getBorderColor(Spacing.BLOCK_START); + long colorBlockEnd = getBorderColor(Spacing.BLOCK_END); if (isBorderColorDefined(Spacing.BLOCK)) { colorBottom = colorBlock; @@ -992,21 +1002,21 @@ public float getFullBorderWidth() { * * @return A compatible border color, or zero if the border colors are not compatible. */ - private static int fastBorderCompatibleColorOrZero( + private static long fastBorderCompatibleColorOrZero( int borderLeft, int borderTop, int borderRight, int borderBottom, - int colorLeft, - int colorTop, - int colorRight, - int colorBottom) { - int andSmear = + long colorLeft, + long colorTop, + long colorRight, + long colorBottom) { + long andSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_SET) & (borderTop > 0 ? colorTop : ALL_BITS_SET) & (borderRight > 0 ? colorRight : ALL_BITS_SET) & (borderBottom > 0 ? colorBottom : ALL_BITS_SET); - int orSmear = + long orSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_UNSET) | (borderTop > 0 ? colorTop : ALL_BITS_UNSET) | (borderRight > 0 ? colorRight : ALL_BITS_UNSET) @@ -1017,9 +1027,9 @@ private static int fastBorderCompatibleColorOrZero( private void drawRectangularBackgroundWithBorders(Canvas canvas) { mPaint.setStyle(Paint.Style.FILL); - int useColor = multiplyColorAlpha(mColor, mAlpha); - if (Color.alpha(useColor) != 0) { // color is not transparent - mPaint.setColor(useColor); + long color = useColor(); + if (Color.alpha(color) != 0.0f) { // color is not transparent + mPaint.setColor(color); canvas.drawRect(getBounds(), mPaint); } @@ -1034,14 +1044,14 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { if (borderLeft > 0 || borderRight > 0 || borderTop > 0 || borderBottom > 0) { Rect bounds = getBounds(); - int colorLeft = getBorderColor(Spacing.LEFT); - int colorTop = getBorderColor(Spacing.TOP); - int colorRight = getBorderColor(Spacing.RIGHT); - int colorBottom = getBorderColor(Spacing.BOTTOM); + long colorLeft = getBorderColor(Spacing.LEFT); + long colorTop = getBorderColor(Spacing.TOP); + long colorRight = getBorderColor(Spacing.RIGHT); + long colorBottom = getBorderColor(Spacing.BOTTOM); - int colorBlock = getBorderColor(Spacing.BLOCK); - int colorBlockStart = getBorderColor(Spacing.BLOCK_START); - int colorBlockEnd = getBorderColor(Spacing.BLOCK_END); + long colorBlock = getBorderColor(Spacing.BLOCK); + long colorBlockStart = getBorderColor(Spacing.BLOCK_START); + long colorBlockEnd = getBorderColor(Spacing.BLOCK_END); if (isBorderColorDefined(Spacing.BLOCK)) { colorBottom = colorBlock; @@ -1055,8 +1065,8 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { } final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - int colorStart = getBorderColor(Spacing.START); - int colorEnd = getBorderColor(Spacing.END); + long colorStart = getBorderColor(Spacing.START); + long colorEnd = getBorderColor(Spacing.END); if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(mContext)) { if (!isBorderColorDefined(Spacing.START)) { @@ -1067,14 +1077,14 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { colorEnd = colorRight; } - final int directionAwareColorLeft = isRTL ? colorEnd : colorStart; - final int directionAwareColorRight = isRTL ? colorStart : colorEnd; + final long directionAwareColorLeft = isRTL ? colorEnd : colorStart; + final long directionAwareColorRight = isRTL ? colorStart : colorEnd; colorLeft = directionAwareColorLeft; colorRight = directionAwareColorRight; } else { - final int directionAwareColorLeft = isRTL ? colorEnd : colorStart; - final int directionAwareColorRight = isRTL ? colorStart : colorEnd; + final long directionAwareColorLeft = isRTL ? colorEnd : colorStart; + final long directionAwareColorRight = isRTL ? colorStart : colorEnd; final boolean isColorStartDefined = isBorderColorDefined(Spacing.START); final boolean isColorEndDefined = isBorderColorDefined(Spacing.END); @@ -1096,7 +1106,7 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { int top = bounds.top; // Check for fast path to border drawing. - int fastBorderColor = + long fastBorderColor = fastBorderCompatibleColorOrZero( borderLeft, borderTop, @@ -1224,7 +1234,7 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { private void drawQuadrilateral( Canvas canvas, - int fillColor, + long fillColor, float x1, float y1, float x2, @@ -1251,24 +1261,13 @@ private void drawQuadrilateral( canvas.drawPath(mPathForBorder, mPaint); } - private static int colorFromAlphaAndRGBComponents(float alpha, float rgb) { - int rgbComponent = 0x00FFFFFF & (int) rgb; - int alphaComponent = 0xFF000000 & ((int) alpha) << 24; - - return rgbComponent | alphaComponent; - } - private boolean isBorderColorDefined(int position) { - final float rgb = mBorderRGB != null ? mBorderRGB.get(position) : Float.NaN; - final float alpha = mBorderAlpha != null ? mBorderAlpha.get(position) : Float.NaN; - return !Float.isNaN(rgb) && !Float.isNaN(alpha); + long color = mBorderColor != null ? mBorderColor.get(position) : 0; + return color != 0; } - public int getBorderColor(int position) { - float rgb = mBorderRGB != null ? mBorderRGB.get(position) : DEFAULT_BORDER_RGB; - float alpha = mBorderAlpha != null ? mBorderAlpha.get(position) : DEFAULT_BORDER_ALPHA; - - return CSSBackgroundDrawable.colorFromAlphaAndRGBComponents(alpha, rgb); + public long getBorderColor(int position) { + return mBorderColor != null ? mBorderColor.get(position) : DEFAULT_BORDER_COLOR; } public RectF getDirectionAwareBorderInsets() { From e5c6cb3a5b283742829f63c71a577967132cc395 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 16 Apr 2024 18:25:36 -0600 Subject: [PATCH 3/6] convert BorderColor.java to kotlin --- .../facebook/react/uimanager/BorderColor.java | 176 ------------------ .../facebook/react/uimanager/BorderColor.kt | 116 ++++++++++++ 2 files changed, 116 insertions(+), 176 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java deleted file mode 100644 index d0a9e74be339cd..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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; -import android.graphics.Color; -import java.util.Arrays; - -/** - * Class representing CSS spacing border colors. Modified from {@link Spacing} to support long values. - */ -public class BorderColor { - - /** Spacing type that represents the left direction. E.g. {@code marginLeft}. */ - public static final int LEFT = 0; - /** Spacing type that represents the top direction. E.g. {@code marginTop}. */ - public static final int TOP = 1; - /** Spacing type that represents the right direction. E.g. {@code marginRight}. */ - public static final int RIGHT = 2; - /** Spacing type that represents the bottom direction. E.g. {@code marginBottom}. */ - public static final int BOTTOM = 3; - /** - * Spacing type that represents start direction e.g. left in left-to-right, right in - * right-to-left. - */ - public static final int START = 4; - /** - * Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left. - */ - public static final int END = 5; - /** - * Spacing type that represents horizontal direction (left and right). E.g. {@code - * marginHorizontal}. - */ - public static final int HORIZONTAL = 6; - /** - * Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}. - */ - public static final int VERTICAL = 7; - /** - * Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}. - */ - public static final int ALL = 8; - /** Spacing type that represents block directions (top, bottom). E.g. {@code marginBlock}. */ - public static final int BLOCK = 9; - /** Spacing type that represents the block end direction (bottom). E.g. {@code marginBlockEnd}. */ - public static final int BLOCK_END = 10; - /** - * Spacing type that represents the block start direction (top). E.g. {@code marginBlockStart}. - */ - public static final int BLOCK_START = 11; - - private static final int[] sFlagsMap = { - 1, /*LEFT*/ 2, /*TOP*/ 4, /*RIGHT*/ 8, /*BOTTOM*/ 16, /*START*/ 32, /*END*/ 64, /*HORIZONTAL*/ - 128, /*VERTICAL*/ 256, /*ALL*/ 512, /*BLOCK*/ 1024, /*BLOCK_END*/ 2048, /*BLOCK_START*/ - }; - - private final long[] mSpacing; - private int mValueFlags = 0; - private final long mDefaultValue; - private boolean mHasAliasesSet; - - public BorderColor() { - this(0); - } - - public BorderColor(long defaultValue) { - mDefaultValue = defaultValue; - mSpacing = newFullBorderColorArray(); - } - - public BorderColor(BorderColor original) { - mDefaultValue = original.mDefaultValue; - mSpacing = Arrays.copyOf(original.mSpacing, original.mSpacing.length); - mValueFlags = original.mValueFlags; - mHasAliasesSet = original.mHasAliasesSet; - } - - /** - * Set a spacing value. - * - * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link - * #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL} - * @param value the value for this direction - * @return {@code true} if the spacing has changed, or {@code false} if the same value was already - * set - */ - public boolean set(int spacingType, long value) { - if (mSpacing[spacingType] != value) { - mSpacing[spacingType] = value; - - mValueFlags |= sFlagsMap[spacingType]; - - mHasAliasesSet = - (mValueFlags & sFlagsMap[ALL]) != 0 - || (mValueFlags & sFlagsMap[VERTICAL]) != 0 - || (mValueFlags & sFlagsMap[HORIZONTAL]) != 0 - || (mValueFlags & sFlagsMap[BLOCK]) != 0; - - return true; - } - - return false; - } - - /** - * Get the spacing for a direction. This takes into account any default values that have been set. - * - * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM} - */ - public long get(int spacingType) { - long defaultValue = - (spacingType == START - || spacingType == END - || spacingType == BLOCK - || spacingType == BLOCK_END - || spacingType == BLOCK_START - ? 0 - : mDefaultValue); - - if (mValueFlags == 0) { - return defaultValue; - } - - if ((mValueFlags & sFlagsMap[spacingType]) != 0) { - return mSpacing[spacingType]; - } - - if (mHasAliasesSet) { - int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL; - if ((mValueFlags & sFlagsMap[secondType]) != 0) { - return mSpacing[secondType]; - } else if ((mValueFlags & sFlagsMap[ALL]) != 0) { - return mSpacing[ALL]; - } - } - - return defaultValue; - } - - /** - * Get the raw value (that was set using {@link #set(int, float)}), without taking into account - * any default values. - * - * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link - * #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL} - */ - public long getRaw(int spacingType) { - return mSpacing[spacingType]; - } - - /** - * Resets the spacing instance to its default state. This method is meant to be used when - * recycling {@link Spacing} instances. - */ - public void reset() { - Arrays.fill(mSpacing, 0); - mHasAliasesSet = false; - mValueFlags = 0; - } - - /** - * Try to get start value and fallback to given type if not defined. This is used privately by the - * layout engine as a more efficient way to fetch direction-aware values by avoid extra method - * invocations. - */ - float getWithFallback(int spacingType, int fallbackType) { - return (mValueFlags & sFlagsMap[spacingType]) != 0 ? mSpacing[spacingType] : get(fallbackType); - } - - private static long[] newFullBorderColorArray() { - return new long[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - } -} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.kt new file mode 100644 index 00000000000000..e6354b627e7a9f --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BorderColor.kt @@ -0,0 +1,116 @@ +package com.facebook.react.uimanager + +import java.util.Arrays + +/** + * Class representing CSS spacing border colors. Modified from Spacing to support long values. + */ +public class BorderColor { + + private val mBorder: LongArray = LongArray(12) + private var mValueFlags = 0 + private val mDefaultValue: Long + private var mHasAliasesSet = false + + public constructor() : this(0L) + + public constructor(defaultValue: Long) { + mDefaultValue = defaultValue + } + + public constructor(original: BorderColor) { + mDefaultValue = original.mDefaultValue + System.arraycopy(original.mBorder, 0, mBorder, 0, original.mBorder.size) + mValueFlags = original.mValueFlags + mHasAliasesSet = original.mHasAliasesSet + } + + /** + * Set a border value. + * + * @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM], [VERTICAL], [HORIZONTAL], [ALL] + * @param value the value for this direction + * @return `true` if the spacing has changed, or `false` if the same value was already set + */ + public fun set(borderType: Int, value: Long): Boolean { + if (mBorder[borderType] != value) { + mBorder[borderType] = value + mValueFlags = mValueFlags or sFlagsMap[borderType] + mHasAliasesSet = mValueFlags and (sFlagsMap[ALL] or sFlagsMap[VERTICAL] or sFlagsMap[HORIZONTAL] or sFlagsMap[BLOCK]) != 0 + return true + } + return false + } + + /** + * Get the border for a direction. This takes into account any default values that have been set. + * + * @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM] + */ + public fun get(borderType: Int): Long { + val defaultValue = if (borderType in setOf(START, END, BLOCK, BLOCK_END, BLOCK_START)) 0 else mDefaultValue + + if (mValueFlags == 0) return defaultValue + + if (mValueFlags and sFlagsMap[borderType] != 0) return mBorder[borderType] + + if (mHasAliasesSet) { + val secondType = if (borderType == TOP || borderType == BOTTOM) VERTICAL else HORIZONTAL + if (mValueFlags and sFlagsMap[secondType] != 0) { + return mBorder[secondType] + } else if (mValueFlags and sFlagsMap[ALL] != 0) { + return mBorder[ALL] + } + } + + return defaultValue + } + + /** + * Get the raw value (that was set using [set]), without taking into account + * any default values. + * + * @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM], [VERTICAL], [HORIZONTAL], [ALL] + */ + public fun getRaw(borderType: Int): Long = mBorder[borderType] + + /** + * Resets the border instance to its default state. This method is meant to be used when + * recycling Border instances. + */ + public fun reset() { + Arrays.fill(mBorder, 0) + mValueFlags = 0 + mHasAliasesSet = false + } + + private companion object { + const val LEFT = 0 + const val TOP = 1 + const val RIGHT = 2 + const val BOTTOM = 3 + const val START = 4 + const val END = 5 + const val HORIZONTAL = 6 + const val VERTICAL = 7 + const val ALL = 8 + const val BLOCK = 9 + const val BLOCK_END = 10 + const val BLOCK_START = 11 + + private val sFlagsMap = intArrayOf( + 1, // LEFT + 2, // TOP + 4, // RIGHT + 8, // BOTTOM + 16, // START + 32, // END + 64, // HORIZONTAL + 128, // VERTICAL + 256, // ALL + 512, // BLOCK + 1024, // BLOCK_END + 2048 // BLOCK_START + ) + } +} From 49dd241e56fdc5c44bb7212e250964b4b61ddbf4 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 16 Apr 2024 19:49:19 -0600 Subject: [PATCH 4/6] refactor BaseViewManager.java changes to avoid reflection --- .../com/facebook/react/uimanager/BaseViewManager.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 3fef764d881ed4..c91f4396d6b026 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -30,6 +30,7 @@ import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.PointerEventHelper; import com.facebook.react.uimanager.util.ReactFindViewUtil; +import com.facebook.react.views.view.ReactViewGroup; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -175,14 +176,10 @@ public void setBackgroundColor(@NonNull T view, int backgroundColor) { defaultInt = Color.TRANSPARENT, customType = "Color") public void setBackgroundColor(@NonNull T view, long backgroundColor) { - try { - Method setBackgroundColorMethod = view.getClass().getMethod("setBackgroundColor", long.class); - setBackgroundColorMethod.invoke(view, backgroundColor); - } catch (NoSuchMethodException e) { - FLog.e(TAG, "setBackgroundColor(long) method not found in view class", e); + if (view instanceof ReactViewGroup) { + ((ReactViewGroup) view).setBackgroundColor(backgroundColor); + } else { view.setBackgroundColor(Color.toArgb(backgroundColor)); - } catch (Exception e) { - FLog.e(TAG, "Failed to invoke setBackgroundColor(long)", e); } } From b867b30525e9f3f1cf1e91b2be42d41e34255bbe Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 16 Apr 2024 19:55:07 -0600 Subject: [PATCH 5/6] clean up diff --- .../java/com/facebook/react/uimanager/BaseViewManager.java | 4 +--- .../react/views/view/ReactViewBackgroundDrawable.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index c91f4396d6b026..41e653d1384f2f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -31,7 +31,6 @@ import com.facebook.react.uimanager.events.PointerEventHelper; import com.facebook.react.uimanager.util.ReactFindViewUtil; import com.facebook.react.views.view.ReactViewGroup; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -44,7 +43,6 @@ public abstract class BaseViewManager extends ViewManager implements BaseViewManagerInterface, View.OnLayoutChangeListener { - private static final String TAG = "BaseViewManager"; private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2; private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = (float) Math.sqrt(5); @@ -874,4 +872,4 @@ public void setTouchEnd(@NonNull T view, boolean value) { public void setTouchCancel(@NonNull T view, boolean value) { // no-op, handled by JSResponder } -} \ No newline at end of file +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index 9bba3490881195..f9c43850864a56 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -20,4 +20,4 @@ public class ReactViewBackgroundDrawable extends CSSBackgroundDrawable { public ReactViewBackgroundDrawable(Context context) { super(context); } -} \ No newline at end of file +} From d9c4a2bf965135f1a699bfbb69d0aca9f72b57f4 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 22 Apr 2024 16:14:00 -0600 Subject: [PATCH 6/6] add ReactWideGamutView interface to implement in ReactViewGroup --- .../facebook/react/uimanager/BaseViewManager.java | 5 +++-- .../facebook/react/uimanager/ReactWideGamutView.kt | 13 +++++++++++++ .../facebook/react/views/view/ReactViewGroup.java | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactWideGamutView.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 41e653d1384f2f..5c91d1fbfd9312 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -27,6 +27,7 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole; import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role; +import com.facebook.react.uimanager.ReactWideGamutView; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.PointerEventHelper; import com.facebook.react.uimanager.util.ReactFindViewUtil; @@ -174,8 +175,8 @@ public void setBackgroundColor(@NonNull T view, int backgroundColor) { defaultInt = Color.TRANSPARENT, customType = "Color") public void setBackgroundColor(@NonNull T view, long backgroundColor) { - if (view instanceof ReactViewGroup) { - ((ReactViewGroup) view).setBackgroundColor(backgroundColor); + if (view instanceof ReactWideGamutView) { + ((ReactWideGamutView) view).setBackgroundColor(backgroundColor); } else { view.setBackgroundColor(Color.toArgb(backgroundColor)); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactWideGamutView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactWideGamutView.kt new file mode 100644 index 00000000000000..0d12a864d48e93 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactWideGamutView.kt @@ -0,0 +1,13 @@ +/* + * 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; + +/** View that supports wide gamut color. */ +public interface ReactWideGamutView { + public fun setBackgroundColor(color: Long) +} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index ee9a1dcc198ec1..d186378a768052 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -45,6 +45,7 @@ import com.facebook.react.uimanager.ReactClippingViewGroupHelper; import com.facebook.react.uimanager.ReactOverflowViewWithInset; import com.facebook.react.uimanager.ReactPointerEventsView; +import com.facebook.react.uimanager.ReactWideGamutView; import com.facebook.react.uimanager.ReactZIndexedViewGroup; import com.facebook.react.uimanager.RootView; import com.facebook.react.uimanager.RootViewUtil; @@ -65,6 +66,7 @@ public class ReactViewGroup extends ViewGroup ReactClippingViewGroup, ReactPointerEventsView, ReactHitSlopView, + ReactWideGamutView, ReactZIndexedViewGroup, ReactOverflowViewWithInset {