diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 41a9eb10601..4e58cbd653c 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -838,8 +838,11 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) _boxShadowLayer.zPosition = CGFLOAT_MIN; _boxShadowLayer.frame = RCTGetBoundingRect(_props->boxShadow, self.layer.frame.size); - UIImage *boxShadowImage = - RCTGetBoxShadowImage(_props->boxShadow, RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), layer); + UIImage *boxShadowImage = RCTGetBoxShadowImage( + _props->boxShadow, + RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), + RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths), + layer); _boxShadowLayer.contents = (id)boxShadowImage.CGImage; } diff --git a/packages/react-native/React/Fabric/Utils/RCTBoxShadow.h b/packages/react-native/React/Fabric/Utils/RCTBoxShadow.h index b9b89b5662d..73d76796c9a 100644 --- a/packages/react-native/React/Fabric/Utils/RCTBoxShadow.h +++ b/packages/react-native/React/Fabric/Utils/RCTBoxShadow.h @@ -12,7 +12,10 @@ #import #import -RCT_EXTERN UIImage * -RCTGetBoxShadowImage(std::vector shadows, RCTCornerRadii cornerRadii, CALayer *layer); +RCT_EXTERN UIImage *RCTGetBoxShadowImage( + const std::vector &shadows, + RCTCornerRadii cornerRadii, + UIEdgeInsets edgeInsets, + CALayer *layer); -RCT_EXTERN CGRect RCTGetBoundingRect(std::vector boxShadows, CGSize layerSize); +RCT_EXTERN CGRect RCTGetBoundingRect(const std::vector &boxShadows, CGSize layerSize); diff --git a/packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm b/packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm index 15de0945f2a..3d46f08652e 100644 --- a/packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm +++ b/packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm @@ -39,7 +39,7 @@ static RCTCornerRadii cornerRadiiForBoxShadow(RCTCornerRadii cornerRadii, CGFloa // Returns the smallest CGRect that will contain all shadows and the layer itself. // The origin represents the location of this box relative to the layer the shadows // are attached to. -CGRect RCTGetBoundingRect(std::vector boxShadows, CGSize layerSize) +CGRect RCTGetBoundingRect(const std::vector &boxShadows, CGSize layerSize) { CGFloat smallestX = 0; CGFloat smallestY = 0; @@ -76,6 +76,12 @@ static std::pair, std::vector> splitBoxShadows return std::make_pair(outsetShadows, insetShadows); } +static CGRect insetRect(CGRect rect, CGFloat left, CGFloat top, CGFloat right, CGFloat bottom) +{ + return CGRectMake( + rect.origin.x + left, rect.origin.y + top, rect.size.width - right - left, rect.size.height - bottom - top); +} + // Core graphics has support for shadows that looks similar to web and are very // fast to apply. The only issue is that this shadow does not take a spread // radius like on web. To get around this, we draw the shadow rect (the rect @@ -168,6 +174,7 @@ static void renderOutsetShadows( static void renderInsetShadows( std::vector &insetShadows, RCTCornerRadii cornerRadii, + UIEdgeInsets edgeInsets, CALayer *layer, CGRect boundingRect, CGContextRef context) @@ -180,17 +187,19 @@ static void renderInsetShadows( // graphical state carrying over after this function returns CGContextSaveGState(context); + CGRect layerFrameRelativeToBoundingRect = + CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layer.bounds.size.width, layer.bounds.size.height); + CGRect shadowFrame = + insetRect(layerFrameRelativeToBoundingRect, edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom); + // First, create a clipping area so we only draw within the view's bounds. // If we do not do this, blur artifacts will show up outside the view. CGRect outerClippingRect = CGRectMake(0, 0, boundingRect.size.width, boundingRect.size.height); // Add the path twice so we only draw inside the view with the EO crop rule CGContextAddRect(context, outerClippingRect); CGContextAddRect(context, outerClippingRect); - const RCTCornerInsets cornerInsetsForLayer = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); - CGPathRef layerPath = RCTPathCreateWithRoundedRect( - CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layer.bounds.size.width, layer.bounds.size.height), - cornerInsetsForLayer, - nil); + const RCTCornerInsets cornerInsetsForLayer = RCTGetCornerInsets(cornerRadii, edgeInsets); + CGPathRef layerPath = RCTPathCreateWithRoundedRect(shadowFrame, cornerInsetsForLayer, nil); CGContextAddPath(context, layerPath); CGContextEOClip(context); CGPathRelease(layerPath); @@ -211,39 +220,29 @@ static void renderInsetShadows( // We also pad the size of the shadow rect by the blur radius so that the // edges of the shadow remain a solid color and do not blend with outside // of the view. - CGSize shadowRectSize = - CGSizeMake(layer.bounds.size.width + 2 * blurRadius, layer.bounds.size.height + 2 * blurRadius); - CGSize clearRegionSize = - CGSizeMake(layer.bounds.size.width - 2 * spreadDistance, layer.bounds.size.height - 2 * spreadDistance); - CGRect shadowRect = CGRectMake( - -fmax(shadowRectSize.width, clearRegionSize.width + offsetX + blurRadius + spreadDistance), - 0, - shadowRectSize.width, - shadowRectSize.height); - CGContextAddRect(context, shadowRect); + CGRect shadowCastingRect = CGRectInset(shadowFrame, -blurRadius, -blurRadius); + CGRect clearRegionRect = CGRectInset(shadowFrame, spreadDistance, spreadDistance); + CGPoint offsetToMoveOffscreen = CGPointMake( + -fmax( + shadowCastingRect.size.width, + clearRegionRect.size.width + offsetX + (clearRegionRect.origin.x - shadowCastingRect.origin.x)) - + shadowCastingRect.origin.x, + 0); + shadowCastingRect = CGRectOffset(shadowCastingRect, offsetToMoveOffscreen.x, offsetToMoveOffscreen.y); + clearRegionRect = + CGRectOffset(clearRegionRect, offsetToMoveOffscreen.x + offsetX, offsetToMoveOffscreen.y + offsetY); + CGContextAddRect(context, shadowCastingRect); const RCTCornerInsets cornerInsetsForClearRegion = - RCTGetCornerInsets(cornerRadiiForBoxShadow(cornerRadii, -spreadDistance), UIEdgeInsetsZero); - CGPathRef clearRegionPath = RCTPathCreateWithRoundedRect( - CGRectMake( - shadowRect.origin.x + offsetX + blurRadius + spreadDistance, - shadowRect.origin.y + offsetY + blurRadius + spreadDistance, - clearRegionSize.width, - clearRegionSize.height), - cornerInsetsForClearRegion, - nil); + RCTGetCornerInsets(cornerRadiiForBoxShadow(cornerRadii, -spreadDistance), edgeInsets); + CGPathRef clearRegionPath = RCTPathCreateWithRoundedRect(clearRegionRect, cornerInsetsForClearRegion, nil); CGContextAddPath(context, clearRegionPath); // Third, set the shadow graphics state with the appropriate offset such that // it is positioned on top of the view. We subtract blurRadius because the // shadow rect is padded. CGContextSetShadowWithColor( - context, - CGSizeMake( - -shadowRect.origin.x - boundingRect.origin.x - blurRadius, - -shadowRect.origin.y - boundingRect.origin.y - blurRadius), - blurRadius, - color); + context, CGSizeMake(-offsetToMoveOffscreen.x, -offsetToMoveOffscreen.y), blurRadius, color); // Fourth, the Core Graphics functions to actually draw the shadow rect // and thus the shadow itself. Note we use an EO fill path so that the @@ -259,7 +258,11 @@ static void renderInsetShadows( CGContextRestoreGState(context); } -UIImage *RCTGetBoxShadowImage(std::vector shadows, RCTCornerRadii cornerRadii, CALayer *layer) +UIImage *RCTGetBoxShadowImage( + const std::vector &shadows, + RCTCornerRadii cornerRadii, + UIEdgeInsets edgeInsets, + CALayer *layer) { CGRect boundingRect = RCTGetBoundingRect(shadows, layer.bounds.size); UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat]; @@ -274,7 +277,7 @@ UIImage *RCTGetBoxShadowImage(std::vector shadows, RCTCornerRadii cor // Inset shadows could draw over those outset shadows but if the shadow // colors have alpha < 1 then we will have inaccurate alpha compositing renderOutsetShadows(outsetShadows, cornerRadii, layer, boundingRect, context); - renderInsetShadows(insetShadows, cornerRadii, layer, boundingRect, context); + renderInsetShadows(insetShadows, cornerRadii, edgeInsets, layer, boundingRect, context); }]; return boxShadowImage;