feat(RCTUIKit): Shim RCTUIGraphicsImageRenderer (#2209)

* feat(RCTUIKit): Shim RCTUIGraphicsImageRenderer

* Update RCTBorderDrawing.m

* `[RCTUIGraphicsImageRendererFormat defaultFormat]` doesn't return a singleton

* `[NSImage lockFocus]` is deprecated. Use a newer API.

* fix typo

* Implement `format.opaque`
This commit is contained in:
Saad Najmi 2024-10-03 18:56:07 -05:00 коммит произвёл GitHub
Родитель 3ebf897664
Коммит 2d866c826b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 100 добавлений и 126 удалений

Просмотреть файл

@ -25,23 +25,22 @@ UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
// convert to ARGB if it isn't
if (CGImageGetBitsPerPixel(imageRef) != 32 || !((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
#if !TARGET_OS_OSX // [macOS]
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.scale = inputImage.scale;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:inputImage.size
format:rendererFormat];
RCTUIGraphicsImageRendererFormat *const rendererFormat = [RCTUIGraphicsImageRendererFormat defaultFormat]; // [macOS]
rendererFormat.scale = UIImageGetScale(inputImage); // [macOS]
RCTUIGraphicsImageRenderer *const renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:inputImage.size // [macOS]
format:rendererFormat];
#if !TARGET_OS_OSX // [macOS]
imageRef = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
[inputImage drawAtPoint:CGPointZero];
}].CGImage;
#else // [macOS
UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, imageScale);
[inputImage drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
imageRef = (CGImageRef)CFAutorelease(CGBitmapContextCreateImage(UIGraphicsGetCurrentContext()));
UIGraphicsEndImageContext();
NSImage *image = [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull context) {
[inputImage drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
}];
imageRef = UIImageGetCGImageRef(image);
#endif // macOS]
}
vImage_Buffer buffer1, buffer2;
buffer1.width = buffer2.width = CGImageGetWidth(imageRef);
buffer1.height = buffer2.height = CGImageGetHeight(imageRef);

Просмотреть файл

@ -382,25 +382,19 @@ UIImage *__nullable RCTTransformImage(UIImage *image, CGSize destSize, CGFloat d
}
BOOL opaque = !RCTUIImageHasAlpha(image); // [macOS]
#if !TARGET_OS_OSX // [macOS]
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
RCTUIGraphicsImageRendererFormat *const rendererFormat = [RCTUIGraphicsImageRendererFormat defaultFormat]; // [macOS]
rendererFormat.opaque = opaque;
rendererFormat.scale = destScale;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:destSize
RCTUIGraphicsImageRenderer *const renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:destSize // [macOS]
format:rendererFormat];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) {
return [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull context) { // [macOS]
CGContextConcatCTM(context.CGContext, transform);
#if !TARGET_OS_OSX // [macOS]
[image drawAtPoint:CGPointZero];
}];
#else // [macOS
UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextConcatCTM(currentContext, transform);
[image drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
[image drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
#endif // macOS]
}];
}
BOOL RCTImageHasAlpha(CGImageRef image)

Просмотреть файл

@ -269,9 +269,6 @@ extern "C" {
// UIGraphics.h
CGContextRef UIGraphicsGetCurrentContext(void);
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);
NSImage *UIGraphicsGetImageFromCurrentImageContext(void);
void UIGraphicsEndImageContext(void);
CGImageRef UIImageGetCGImageRef(NSImage *image);
#ifdef __cplusplus
@ -641,3 +638,31 @@ NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
@end
#endif
#if !TARGET_OS_OSX
typedef UIGraphicsImageRendererContext RCTUIGraphicsImageRendererContext;
typedef UIGraphicsImageDrawingActions RCTUIGraphicsImageDrawingActions;
typedef UIGraphicsImageRendererFormat RCTUIGraphicsImageRendererFormat;
typedef UIGraphicsImageRenderer RCTUIGraphicsImageRenderer;
#else
NS_ASSUME_NONNULL_BEGIN
typedef NSGraphicsContext RCTUIGraphicsImageRendererContext;
typedef void (^RCTUIGraphicsImageDrawingActions)(RCTUIGraphicsImageRendererContext *rendererContext);
@interface RCTUIGraphicsImageRendererFormat : NSObject
+ (instancetype)defaultFormat;
@property (nonatomic) CGFloat scale;
@property (nonatomic) BOOL opaque;
@end
@interface RCTUIGraphicsImageRenderer : NSObject
- (instancetype)initWithSize:(CGSize)size format:(RCTUIGraphicsImageRendererFormat *)format;
- (NSImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions;
@end
NS_ASSUME_NONNULL_END
#endif

Просмотреть файл

@ -32,37 +32,6 @@ CGContextRef UIGraphicsGetCurrentContext(void)
return [[NSGraphicsContext currentContext] CGContext];
}
void UIGraphicsBeginImageContextWithOptions(CGSize size, __unused BOOL opaque, CGFloat scale)
{
if (scale == 0.0)
{
// TODO: Assert. We can't assume a display scale on macOS
scale = 1.0;
}
size_t width = ceilf(size.width * scale);
size_t height = ceilf(size.height * scale);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8/*bitsPerComponent*/, width * 4/*bytesPerRow*/, colorSpace, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (ctx != NULL)
{
// flip the context (top left at 0, 0) and scale it
CGContextTranslateCTM(ctx, 0.0, height);
CGContextScaleCTM(ctx, scale, -scale);
NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
objc_setAssociatedObject(graphicsContext, &RCTGraphicsContextSizeKey, [NSValue valueWithSize:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:graphicsContext];
CFRelease(ctx);
}
}
NSImage *UIGraphicsGetImageFromCurrentImageContext(void)
{
NSImage *image = nil;
@ -83,12 +52,6 @@ NSImage *UIGraphicsGetImageFromCurrentImageContext(void)
return image;
}
void UIGraphicsEndImageContext(void)
{
RCTAssert(objc_getAssociatedObject([NSGraphicsContext currentContext], &RCTGraphicsContextSizeKey), @"The current graphics context is not a React image context!");
[NSGraphicsContext restoreGraphicsState];
}
//
// functionally equivalent types
//
@ -1040,4 +1003,45 @@ BOOL RCTUIViewSetClipsToBounds(RCTPlatformView *view)
@end
@implementation RCTUIGraphicsImageRendererFormat
+ (nonnull instancetype)defaultFormat {
RCTUIGraphicsImageRendererFormat *format = [RCTUIGraphicsImageRendererFormat new];
return format;
}
@end
@implementation RCTUIGraphicsImageRenderer
{
CGSize _size;
RCTUIGraphicsImageRendererFormat *_format;
}
- (nonnull instancetype)initWithSize:(CGSize)size format:(nonnull RCTUIGraphicsImageRendererFormat *)format {
if (self = [super init]) {
self->_size = size;
self->_format = format;
}
return self;
}
- (nonnull NSImage *)imageWithActions:(NS_NOESCAPE RCTUIGraphicsImageDrawingActions)actions {
NSImage *image = [NSImage imageWithSize:_size
flipped:YES
drawingHandler:^BOOL(NSRect dstRect) {
RCTUIGraphicsImageRendererContext *context = [NSGraphicsContext currentContext];
if (self->_format.opaque) {
CGContextSetAlpha([context CGContext], 1.0);
}
actions(context);
return YES;
}];
return image;
}
@end
#endif

Просмотреть файл

@ -171,27 +171,16 @@ static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCorn
return RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
}
#if !TARGET_OS_OSX // [macOS]
static UIGraphicsImageRenderer *
RCTUIGraphicsImageRenderer(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge)
static RCTUIGraphicsImageRenderer * // [macOS]
RCTMakeUIGraphicsImageRenderer(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge) // [macOS]
{
const CGFloat alpha = CGColorGetAlpha(backgroundColor);
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
RCTUIGraphicsImageRendererFormat *const rendererFormat = [RCTUIGraphicsImageRendererFormat defaultFormat]; // [macOS]
rendererFormat.opaque = opaque;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:rendererFormat];
RCTUIGraphicsImageRenderer *const renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:size format:rendererFormat]; // [macOS]
return renderer;
}
#else // [macOS
static CGContextRef
RCTUIGraphicsBeginImageContext(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge, CGFloat scaleFactor)
{
const CGFloat alpha = CGColorGetAlpha(backgroundColor);
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
UIGraphicsBeginImageContextWithOptions(size, opaque, scaleFactor);
return UIGraphicsGetCurrentContext();
}
#endif // macOS]
static UIImage *RCTGetSolidBorderImage(
RCTCornerRadii cornerRadii,
@ -237,16 +226,11 @@ static UIImage *RCTGetSolidBorderImage(
return nil;
} // macOS]
#if !TARGET_OS_OSX // [macOS]
UIGraphicsImageRenderer *const imageRenderer =
RCTUIGraphicsImageRenderer(size, backgroundColor, hasCornerRadii, drawToEdge);
UIImage *image = [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
RCTUIGraphicsImageRenderer *const imageRenderer =
RCTMakeUIGraphicsImageRenderer(size, backgroundColor, hasCornerRadii, drawToEdge); // [macOS]
CGColorRetain(backgroundColor); // [macOS] CGColorRefs are not atuomtically retained when passed into a block
UIImage *image = [imageRenderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
const CGContextRef context = rendererContext.CGContext;
#else // [macOS
CGContextRef context = RCTUIGraphicsBeginImageContext(size, backgroundColor, hasCornerRadii, drawToEdge, scaleFactor);
// Add extra braces for scope to match the indentation level of the iOS block
{
#endif // macOS]
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
@ -255,6 +239,7 @@ static UIImage *RCTGetSolidBorderImage(
CGContextAddPath(context, path);
CGContextFillPath(context);
}
CGColorRelease(backgroundColor); // [macOS]
CGContextAddPath(context, path);
CGPathRelease(path);
@ -402,13 +387,7 @@ static UIImage *RCTGetSolidBorderImage(
}
CGPathRelease(insetPath);
#if !TARGET_OS_OSX // [macOS]
}];
#else // [macOS
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
#endif // macOS]
if (makeStretchable) {
#if !TARGET_OS_OSX // [macOS]
@ -509,16 +488,10 @@ static UIImage *RCTGetDashedOrDottedBorderImage(
} // macOS]
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
#if !TARGET_OS_OSX // [macOS]
UIGraphicsImageRenderer *const imageRenderer =
RCTUIGraphicsImageRenderer(viewSize, backgroundColor, hasCornerRadii, drawToEdge);
return [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
RCTUIGraphicsImageRenderer *const imageRenderer = // [macOS]
RCTMakeUIGraphicsImageRenderer(viewSize, backgroundColor, hasCornerRadii, drawToEdge); // [macOS]
return [imageRenderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
const CGContextRef context = rendererContext.CGContext;
#else // [macOS
CGContextRef context = RCTUIGraphicsBeginImageContext(viewSize, backgroundColor, hasCornerRadii, drawToEdge, scaleFactor);
// Add extra braces for scope to match the indentation level of the iOS block
{
#endif // macOS]
const CGRect rect = {.size = viewSize};
if (backgroundColor) {
@ -549,14 +522,7 @@ static UIImage *RCTGetDashedOrDottedBorderImage(
CGContextStrokePath(context);
CGPathRelease(path);
#if !TARGET_OS_OSX // [macOS]
}];
#else // [macOS
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
#endif // macOS]
}
UIImage *RCTGetBorderImage(

Просмотреть файл

@ -16,20 +16,13 @@
}
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
#if !TARGET_OS_OSX // [macOS]
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
RCTUIGraphicsImageRendererFormat *const rendererFormat = [RCTUIGraphicsImageRendererFormat defaultFormat]; // [macOS]
rendererFormat.opaque = YES;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize
format:rendererFormat];
RCTUIGraphicsImageRenderer *const renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:imageSize // [macOS]
format:rendererFormat];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
return [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *_Nonnull rendererContext) { // [macOS]
const CGContextRef context = rendererContext.CGContext;
#else // [macOS
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Add extra braces for scope to match the indentation level of the iOS block
{
#endif // macOS]
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
CGContextSetAlpha(context, 0.5f);
CGContextBeginTransparencyLayer(context, NULL);
@ -38,14 +31,7 @@
CGContextSetFillColorWithColor(context, [RCTUIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
CGContextEndTransparencyLayer(context);
#if !TARGET_OS_OSX // [macOS]
}];
#else // [macOS
}
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImage;
#endif // macOS]
}
@end