Skip to content

Commit

Permalink
Fix issue where scaling transform + box shadow break the shadow
Browse files Browse the repository at this point in the history
Summary:
Turns out bounds does not account for the proper size if a scaling transform is applied while frame does. Using the example listed in facebook#49134, the size of the frame is the expected 240, but the size of the bounds is 200.

We had a mismatch where we use bounds everywhere in shadow image creation, but use the frame when asking for the size of the box shadow layer. What ends up happening is we have a 240x240 layer that is getting scaled again.

I refactored the shadow creation to just take a CGSize instead of a whole CALayer (which we had only used for its size anyway) so that we can be consistent with frame and bounds, and use bounds everywhere so that we only scale once.

Differential Revision: D69320213
  • Loading branch information
joevilches authored and facebook-github-bot committed Feb 7, 2025
1 parent 14540e6 commit 943f984
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1047,13 +1047,13 @@ - (void)invalidateLayer
_boxShadowLayer = [CALayer layer];
[self.layer addSublayer:_boxShadowLayer];
_boxShadowLayer.zPosition = _borderLayer.zPosition;
_boxShadowLayer.frame = RCTGetBoundingRect(_props->boxShadow, self.layer.frame.size);
_boxShadowLayer.frame = RCTGetBoundingRect(_props->boxShadow, self.layer.bounds.size);

UIImage *boxShadowImage = RCTGetBoxShadowImage(
_props->boxShadow,
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
layer);
self.layer.bounds.size);

_boxShadowLayer.contents = (id)boxShadowImage.CGImage;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Fabric/Utils/RCTBoxShadow.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ RCT_EXTERN UIImage *RCTGetBoxShadowImage(
const std::vector<facebook::react::BoxShadow> &shadows,
RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets,
CALayer *layer);
CGSize layerSize);

RCT_EXTERN CGRect RCTGetBoundingRect(const std::vector<facebook::react::BoxShadow> &boxShadows, CGSize layerSize);
20 changes: 10 additions & 10 deletions packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static CGColorRef colorRefFromSharedColor(const SharedColor &color)
static void renderOutsetShadows(
std::vector<BoxShadow> &outsetShadows,
RCTCornerRadii cornerRadii,
CALayer *layer,
CGSize layerSize,
CGRect boundingRect,
CGContextRef context)
{
Expand All @@ -129,8 +129,8 @@ static void renderOutsetShadows(
// the blur radius since this rect is not the shadow itself.
const RCTCornerInsets shadowRectCornerInsets =
RCTGetCornerInsets(cornerRadiiForBoxShadow(cornerRadii, spreadDistance), UIEdgeInsetsZero);
CGSize shadowRectSize = CGSizeMake(
fmax(layer.bounds.size.width + 2 * spreadDistance, 0), fmax(layer.bounds.size.height + 2 * spreadDistance, 0));
CGSize shadowRectSize =
CGSizeMake(fmax(layerSize.width + 2 * spreadDistance, 0), fmax(layerSize.height + 2 * spreadDistance, 0));
// Ensure this is drawn offscreen and will not show in the image
CGRect shadowRect = CGRectMake(-shadowRectSize.width, 0, shadowRectSize.width, shadowRectSize.height);
CGPathRef shadowRectPath = RCTPathCreateWithRoundedRect(shadowRect, shadowRectCornerInsets, nil);
Expand Down Expand Up @@ -165,7 +165,7 @@ static void renderOutsetShadows(
// not cover its content
const RCTCornerInsets layerCornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero);
CGPathRef shadowPathAlignedWithLayer = RCTPathCreateWithRoundedRect(
CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layer.bounds.size.width, layer.bounds.size.height),
CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layerSize.width, layerSize.height),
layerCornerInsets,
nil);
CGContextAddPath(context, shadowPathAlignedWithLayer);
Expand All @@ -187,7 +187,7 @@ static void renderInsetShadows(
std::vector<BoxShadow> &insetShadows,
RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets,
CALayer *layer,
CGSize layerSize,
CGRect boundingRect,
CGContextRef context)
{
Expand All @@ -200,7 +200,7 @@ static void renderInsetShadows(
CGContextSaveGState(context);

CGRect layerFrameRelativeToBoundingRect =
CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layer.bounds.size.width, layer.bounds.size.height);
CGRectMake(-boundingRect.origin.x, -boundingRect.origin.y, layerSize.width, layerSize.height);
CGRect shadowFrame =
insetRect(layerFrameRelativeToBoundingRect, edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom);

Expand Down Expand Up @@ -279,9 +279,9 @@ static void renderInsetShadows(
const std::vector<BoxShadow> &shadows,
RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets,
CALayer *layer)
CGSize layerSize)
{
CGRect boundingRect = RCTGetBoundingRect(shadows, layer.bounds.size);
CGRect boundingRect = RCTGetBoundingRect(shadows, layerSize);
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:boundingRect.size
format:rendererFormat];
Expand All @@ -293,8 +293,8 @@ static void renderInsetShadows(
// clear out a region in the view so we do not block its contents.
// 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, edgeInsets, layer, boundingRect, context);
renderOutsetShadows(outsetShadows, cornerRadii, layerSize, boundingRect, context);
renderInsetShadows(insetShadows, cornerRadii, edgeInsets, layerSize, boundingRect, context);
}];

return boxShadowImage;
Expand Down

0 comments on commit 943f984

Please sign in to comment.