Skip to content
Open
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
21 changes: 16 additions & 5 deletions packages/react-native/React/Base/RCTUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ UIKIT_STATIC_INLINE void UIBezierPathAppendPath(UIBezierPath *path, UIBezierPath
#define RCTUIView UIView
#define RCTUIScrollView UIScrollView
#define RCTPlatformImage UIImage

#define RCTUIImage UIImage

UIKIT_STATIC_INLINE RCTPlatformView *RCTUIViewHitTestWithEvent(RCTPlatformView *view, CGPoint point, __unused UIEvent *__nullable event)
{
Expand Down Expand Up @@ -268,7 +268,6 @@ extern "C" {

// UIGraphics.h
CGContextRef UIGraphicsGetCurrentContext(void);
CGImageRef UIImageGetCGImageRef(NSImage *image);

#ifdef __cplusplus
}
Expand Down Expand Up @@ -334,18 +333,30 @@ NS_INLINE NSEdgeInsets UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat botto
#define UIApplication NSApplication

// UIImage
// RCTUIImage is a subclass of NSImage that caches its CGImage representation.
// This is needed because NSImage's CGImageForProposedRect: returns a new autoreleased
// CGImage each time, which causes issues when used with CALayer.contents.
@interface RCTUIImage : NSImage
@property (nonatomic, readonly, nullable) CGImageRef CGImage;
@property (nonatomic, readonly) CGFloat scale;
@end

typedef NS_ENUM(NSInteger, UIImageRenderingMode) {
UIImageRenderingModeAlwaysOriginal,
UIImageRenderingModeAlwaysTemplate,
};

#ifdef __cplusplus
extern "C"
extern "C" {
#endif
CGFloat UIImageGetScale(NSImage *image);

CGFloat UIImageGetScale(NSImage *image);
CGImageRef UIImageGetCGImageRef(NSImage *image);

#ifdef __cplusplus
}
#endif

NS_INLINE NSImage *UIImageWithContentsOfFile(NSString *filePath)
{
return [[NSImage alloc] initWithContentsOfFile:filePath];
Expand Down Expand Up @@ -626,7 +637,7 @@ typedef void (^RCTUIGraphicsImageDrawingActions)(RCTUIGraphicsImageRendererConte

- (instancetype)initWithSize:(CGSize)size;
- (instancetype)initWithSize:(CGSize)size format:(RCTUIGraphicsImageRendererFormat *)format;
- (NSImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions;
- (RCTUIImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions;

@end
NS_ASSUME_NONNULL_END
Expand Down
50 changes: 45 additions & 5 deletions packages/react-native/React/Base/macOS/RCTUIKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,43 @@ CGFloat UIImageGetScale(NSImage *image)
return 1.0;
}

// RCTUIImage - NSImage subclass with cached CGImage

@implementation RCTUIImage {
CGImageRef _cachedCGImage;
}

- (void)dealloc {
if (_cachedCGImage != NULL) {
CGImageRelease(_cachedCGImage);
}
}

- (CGImageRef)CGImage {
if (_cachedCGImage == NULL) {
CGImageRef cgImage = [self CGImageForProposedRect:NULL context:NULL hints:NULL];
if (cgImage != NULL) {
_cachedCGImage = CGImageRetain(cgImage);
}
}
return _cachedCGImage;
}

- (CGFloat)scale {
return UIImageGetScale(self);
}

@end

CGImageRef __nullable UIImageGetCGImageRef(NSImage *image)
{
// If it's an RCTUIImage, use the cached CGImage property
if ([image isKindOfClass:[RCTUIImage class]]) {
return ((RCTUIImage *)image).CGImage;
}

// Otherwise, fall back to the standard NSImage method
// Note: This returns an autoreleased CGImageRef
return [image CGImageForProposedRect:NULL context:NULL hints:NULL];
}

Expand Down Expand Up @@ -825,11 +860,10 @@ - (nonnull instancetype)initWithSize:(CGSize)size format:(nonnull RCTUIGraphicsI
return self;
}

- (nonnull NSImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions {

NSImage *image = [NSImage imageWithSize:_size
flipped:YES
drawingHandler:^BOOL(NSRect dstRect) {
- (nonnull RCTUIImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions {
RCTUIImage *image = [RCTUIImage imageWithSize:_size
flipped:YES
drawingHandler:^BOOL(NSRect dstRect) {

RCTUIGraphicsImageRendererContext *context = [NSGraphicsContext currentContext];
if (self->_format.opaque) {
Expand All @@ -838,6 +872,12 @@ - (nonnull NSImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActi
actions(context);
return YES;
}];

// Calling these in succession forces the image to render its contents immediately,
// rather than deferring until later.
[image lockFocus];
[image unlockFocus];

return image;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ static void RCTAddContourEffectToLayer(
const UIEdgeInsets &contourInsets,
const RCTBorderStyle &contourStyle)
{
RCTPlatformImage *image = RCTGetBorderImage( // [macOS]
RCTUIImage *image = RCTGetBorderImage( // [macOS]
contourStyle, layer.bounds.size, cornerRadii, contourInsets, contourColors, [RCTUIColor clearColor], NO); // [macOS]

if (image == nil) {
Expand All @@ -850,13 +850,8 @@ static void RCTAddContourEffectToLayer(
CGRect contentsCenter = CGRect{
CGPoint{imageCapInsets.left / imageSize.width, imageCapInsets.top / imageSize.height},
CGSize{(CGFloat)1.0 / imageSize.width, (CGFloat)1.0 / imageSize.height}};
#if !TARGET_OS_OSX // [macOS]
layer.contents = (id)image.CGImage;
layer.contentsScale = image.scale;
#else // [macOS
layer.contents = (__bridge id) UIImageGetCGImageRef(image);
layer.contentsScale = UIImageGetScale(image);
#endif // macOS]

BOOL isResizable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (isResizable) {
Expand Down Expand Up @@ -1050,8 +1045,15 @@ - (void)invalidateLayer
}

#if TARGET_OS_OSX // [macOS
// clipsToBounds is stubbed out on macOS because it's not part of NSView
layer.masksToBounds = self.clipsToBounds;
// On macOS, clipsToBounds doesn't automatically set layer.masksToBounds like iOS does.
// When _useCustomContainerView is true (boxShadow + overflow:hidden), the container
// view handles clipping children while the main layer stays unclipped for the shadow.
// The container view's masksToBounds is set in currentContainerView getter.
if (_useCustomContainerView) {
layer.masksToBounds = NO;
} else {
layer.masksToBounds = _props->getClipsContentToBounds();
}
#endif // macOS]

const auto borderMetrics = _props->resolveBorderMetrics(_layoutMetrics);
Expand Down Expand Up @@ -1292,17 +1294,13 @@ - (void)invalidateLayer
_boxShadowLayer.zPosition = _borderLayer.zPosition;
_boxShadowLayer.frame = RCTGetBoundingRect(_props->boxShadow, self.layer.bounds.size);

RCTPlatformImage *boxShadowImage = RCTGetBoxShadowImage( // [macOS]
RCTUIImage *boxShadowImage = RCTGetBoxShadowImage( // [macOS]
_props->boxShadow,
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
self.layer.bounds.size);

#if !TARGET_OS_OSX // [macOS]
_boxShadowLayer.contents = (id)boxShadowImage.CGImage;
#else // [macOS
_boxShadowLayer.contents = (__bridge id)UIImageGetCGImageRef(boxShadowImage);
#endif // macOS]
}

// clipping
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 @@ -12,7 +12,7 @@
#import <React/RCTUIKit.h>
#import <react/renderer/graphics/BoxShadow.h>

RCT_EXTERN RCTPlatformImage *RCTGetBoxShadowImage( // [macOS]
RCT_EXTERN RCTUIImage *RCTGetBoxShadowImage( // [macOS]
const std::vector<facebook::react::BoxShadow> &shadows,
RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets,
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ static void renderInsetShadows(
CGContextRestoreGState(context);
}

RCTPlatformImage *RCTGetBoxShadowImage( // [macOS]
RCTUIImage *RCTGetBoxShadowImage( // [macOS]
const std::vector<BoxShadow> &shadows,
RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets,
Expand All @@ -293,7 +293,7 @@ static void renderInsetShadows(
RCTUIGraphicsImageRenderer *const renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:boundingRect.size
format:rendererFormat];
// macOS]
RCTPlatformImage *const boxShadowImage = // [macOS]
RCTUIImage *const boxShadowImage = // [macOS]
[renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
auto [outsetShadows, insetShadows] = splitBoxShadowsByInset(shadows);
const CGContextRef context = rendererContext.CGContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ + (CALayer *)gradientLayerWithSize:(CGSize)size gradient:(const LinearGradient &
{
RCTUIGraphicsImageRenderer *renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:size]; // [macOS]
const auto &direction = gradient.direction;
RCTPlatformImage *gradientImage = [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
RCTUIImage *gradientImage = [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
CGPoint startPoint;
CGPoint endPoint;

Expand Down Expand Up @@ -65,11 +65,7 @@ + (CALayer *)gradientLayerWithSize:(CGSize)size gradient:(const LinearGradient &
}];

CALayer *gradientLayer = [CALayer layer];
#if !TARGET_OS_OSX // [macOS]
gradientLayer.contents = (__bridge id)gradientImage.CGImage;
#else // [macOS
gradientLayer.contents = (__bridge id)UIImageGetCGImageRef(gradientImage);
#endif // macOS]
gradientLayer.contents = (id)gradientImage.CGImage;

return gradientLayer;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Views/RCTBorderDrawing.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ RCTPathCreateWithRoundedRect(CGRect bounds, RCTCornerInsets cornerInsets, const
* `borderInsets` defines the border widths for each edge.
* `scaleFactor` defines the backing scale factor of the device for supporting high-resolution drawing. // [macOS]
*/
RCT_EXTERN RCTPlatformImage *RCTGetBorderImage( // [macOS]
RCT_EXTERN RCTUIImage *RCTGetBorderImage( // [macOS]
RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
Expand Down
8 changes: 4 additions & 4 deletions packages/react-native/React/Views/RCTBorderDrawing.m
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn
return renderer;
}

static RCTPlatformImage *RCTGetSolidBorderImage( // [macOS]
static RCTUIImage *RCTGetSolidBorderImage( // [macOS]
RCTCornerRadii cornerRadii,
CGSize viewSize,
UIEdgeInsets borderInsets,
Expand Down Expand Up @@ -231,7 +231,7 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn

RCTUIGraphicsImageRenderer *const imageRenderer =
RCTMakeUIGraphicsImageRenderer(size, backgroundColor, hasCornerRadii, drawToEdge);
RCTPlatformImage *image = [imageRenderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
RCTUIImage *image = [imageRenderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
const CGContextRef context = rendererContext.CGContext;
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
Expand Down Expand Up @@ -461,7 +461,7 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn
// of gradients _along_ a path (NB: clipping a path and drawing a linear gradient
// is _not_ equivalent).

static RCTPlatformImage *RCTGetDashedOrDottedBorderImage( // [macOS]
static RCTUIImage *RCTGetDashedOrDottedBorderImage( // [macOS]
RCTBorderStyle borderStyle,
RCTCornerRadii cornerRadii,
CGSize viewSize,
Expand Down Expand Up @@ -525,7 +525,7 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn
}];
}

RCTPlatformImage *RCTGetBorderImage( // [macOS]
RCTUIImage *RCTGetBorderImage( // [macOS]
RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,7 @@ - (void)displayLayer:(CALayer *)layer
return;
}

RCTPlatformImage *image = RCTGetBorderImage( // [macOS]
RCTUIImage *image = RCTGetBorderImage( // [macOS]
_borderStyle, layer.bounds.size, cornerRadii, borderInsets, borderColors, backgroundColor, self.clipsToBounds);

layer.backgroundColor = NULL;
Expand Down
Loading